2011-08-11 18 views
7

Şu anda çalışan bir Hizmet Fabrikası içeren basit bir paket almaya çalışıyorum. Sonunda OSGI-INF/component.xmlosgi: ServiceFactories'i Kullanma?

<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="bundleb.internal.SvcFactory"> 

    <implementation class="bundleb.internal.SvcFactory"/> 

    <reference bind="setA" cardinality="1..1" interface="bundlea.ServiceA" name="ServiceA" policy="static"/> 

    <service servicefactory="true"> 
     <provide interface="bundleb.ServiceB"/> 
    </service> 
</scr:component> 

public class ServiceBImpl implements ServiceB { 

    private ServiceA svcA; 

    public void setA(ServiceA a) { 
     svcA = a; 
    } 

} 

Ve: Bu

public class SvcFactory implements ServiceFactory<ServiceB> { 

    @Override 
    public ServiceB getService(Bundle bundle, 
      ServiceRegistration<ServiceB> registration) { 

     return new ServiceBImpl(); 
    } 

    @Override 
    public void ungetService(Bundle bundle, ServiceRegistration<ServiceB> registration, 
      ServiceB service) { 

    } 

} 

fabrikada tarafından oluşturulmalıdır benim hizmettir:

Bu benim fabrika sınıfıdır

aşağıdaki hatayı alıyorum gündönümü içinde benim test paketleri (A, B ve C) çalıştırırsanız:

org.osgi.framework.ServiceException: org.eclipse.equinox.internal.ds.FactoryReg.getService() returned a service object that is not an instance of the service class bundleb.ServiceB 

internette bir bileşen tanımı beyan ServiceFeactories kullanma hakkında çok fazla bilgi bulamıyorum. Hatta "OSGi ve Equinox" kitabı bile bunları kullanma konusunda fazla bir şey söylemedi. Herkes bana yanlış yaptığımı açıklayabilir mi?

+0

Eğer varsa, farklı paketler için farklı ServiceB kullanırdı - bu yanlış. –

+0

Sanırım yanlış anladınız. Servicefactory özniteliğini belirtirseniz DS her bir paket için yeni bir örnek oluşturur. ComponentContext'ten atadığınız paketi alabilirsiniz. –

cevap

12

İşte ihtiyaçlarına uygun hangi ComponentFactory kullanıldığı bir örnek (ve other question ile yardımcı olmak için basit bir entegrasyon testi içerir). Yasal Uyarı; Kod iyi yazılmadı, sadece örneğin uğruna.

Bazı servis arayüzleri:

package net.earcam.example.servicecomponent; 

public interface EchoService { 

    String REPEAT_PARAMETER = "repeat"; 
    String FACTORY_DS = "echo.factory"; 
    String NAME_DS = "echo"; 

    String echo(String message); 
} 

Ve:

package net.earcam.example.servicecomponent; 

public interface SequenceService { 
    long next(); 
} 

Sonra uygulamalar:

import static net.earcam.example.servicecomponent.EchoService.FACTORY_DS; 
import static net.earcam.example.servicecomponent.EchoService.NAME_DS; 
import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY; 
import static org.apache.felix.scr.annotations.ReferencePolicy.DYNAMIC; 
import net.earcam.example.servicecomponent.EchoService; 
import net.earcam.example.servicecomponent.SequenceService; 

import org.apache.felix.scr.annotations.Activate; 
import org.apache.felix.scr.annotations.Component; 
import org.apache.felix.scr.annotations.Reference; 
import org.osgi.service.component.ComponentContext; 

@Component(factory = FACTORY_DS, name = NAME_DS) 
public class EchoServiceImp implements EchoService { 

    @Reference(cardinality = MANDATORY_UNARY, policy = DYNAMIC) 
    private SequenceService sequencer = null; 
    private transient int repeat = 1; 

    @Activate 
protected void activate(final ComponentContext componentContext) 
{ 
    repeat = Integer.parseInt(componentContext.getProperties().get(REPEAT_PARAMETER).toString()); 
} 


@Override 
public String echo(final String message) 
{ 
    StringBuilder stringBuilder = new StringBuilder(); 
    for(int i = 0; i < repeat; i++) { 
     addEchoElement(stringBuilder, message); 
    } 
    return stringBuilder.toString(); 
} 


private void addEchoElement(final StringBuilder stringBuilder, final String message) { 
    stringBuilder.append(sequencer.next()).append(' ').append(message).append("\n");   
} 


protected void unbindSequencer() 
{ 
    sequencer = null; 
} 


protected void bindSequencer(final SequenceService sequencer) 
{ 
    this.sequencer = sequencer; 
} 

}

Ve:

package net.earcam.example.servicecomponent.internal; 

import java.util.concurrent.atomic.AtomicLong; 

import net.earcam.example.servicecomponent.SequenceService; 

import org.apache.felix.scr.annotations.Activate; 
import org.apache.felix.scr.annotations.Component; 
import org.apache.felix.scr.annotations.Deactivate; 
import org.apache.felix.scr.annotations.Service; 

/** 
* @author caspar 
*/ 
@Component 
@Service 
public class SequenceServiceImp implements SequenceService { 

    private AtomicLong sequence; 


    @Override 
    public long next() 
    { 
     return sequence.incrementAndGet(); 
    } 


    @Activate 
    protected void activate() 
    { 
     sequence = new AtomicLong(); 
    } 


    @Deactivate 
    protected void deactivate() 
    { 
     sequence = null; 
    } 
} 

şeyi yönlendiren bir entegrasyon testi (not; Ana bir yöntem var, bu yüzden paketi başlatırken/durdururken vb.

package net.earcam.example.servicecomponent.test; 

import static org.ops4j.pax.exam.CoreOptions.*; 
import static org.ops4j.pax.exam.OptionUtils.combine; 
import static org.ops4j.pax.exam.spi.container.PaxExamRuntime.createContainer; 
import static org.ops4j.pax.exam.spi.container.PaxExamRuntime.createTestSystem; 

import java.io.File; 
import java.io.FileFilter; 
import java.io.FileNotFoundException; 
import java.util.Dictionary; 
import java.util.Hashtable; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

import net.earcam.example.servicecomponent.EchoService; 
import net.earcam.example.servicecomponent.SequenceService; 

import org.junit.Assert; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.ops4j.pax.exam.Option; 
import org.ops4j.pax.exam.junit.Configuration; 
import org.ops4j.pax.exam.junit.ExamReactorStrategy; 
import org.ops4j.pax.exam.junit.JUnit4TestRunner; 
import org.ops4j.pax.exam.spi.reactors.EagerSingleStagedReactorFactory; 
import org.osgi.framework.BundleContext; 
import org.osgi.framework.ServiceReference; 
import org.osgi.service.component.ComponentFactory; 
import org.osgi.service.component.ComponentInstance; 


@ExamReactorStrategy(EagerSingleStagedReactorFactory.class) 
@RunWith(JUnit4TestRunner.class) 
public class EchoServiceIntegrationTest { 


    public static void main(String[] args) { 
     try { 
      createContainer(
        createTestSystem(
          combine(
            new EchoServiceIntegrationTest().config(), 
            profile("gogo")) 
        )).start(); 
     } catch(Throwable t) { 
      t.printStackTrace(); 
     } 
    } 



    @Configuration 
    public Option[] config() 
    { 
     return options(
       felix(), 
       equinox(), 
       junitBundles(), 
       systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("TRACE"), 
       mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.scr").versionAsInProject(), 
       bundle("file:" + findFileInCurrentDirectoryAndBelow(
         Pattern.compile("net\\.earcam\\.example\\.servicecomponent\\-[\\.\\d]+(\\-SNAPSHOT)?\\.[wj]ar"))) 
     ); 
    } 


    @Test 
    public void bundleContextIsAvailable(BundleContext context) 
    { 
     Assert.assertNotNull("PAX Exam BundleContext available", context); 
    } 


    @Test 
    public void sequenceServiceIsAvailable(BundleContext context) 
    { 
     Assert.assertNotNull("SequenceService available", fetchService(context, SequenceService.class)); 
    } 


    @Test 
    public void serviceResponseContainsThreeEchos(BundleContext context) throws Exception 
    { 
     final String message = "message"; 
     final String expected = "1 " + message + "\n2 " + message + "\n3 " + message + "\n"; 

     ComponentFactory factory = fetchComponentFactory(context, EchoService.FACTORY_DS); 

     Dictionary<String, String> properties = new Hashtable<String, String>(); 
     properties.put(EchoService.REPEAT_PARAMETER, "3"); 
     ComponentInstance instance = factory.newInstance(properties); 
     EchoService service = (EchoService) instance.getInstance(); 
     String actual = service.echo(message); 
     Assert.assertEquals("Expected response", expected, actual); 
    } 


    private ComponentFactory fetchComponentFactory(BundleContext context, String componentFactoryId) throws Exception 
    { 
     String filter = "(component.factory=" + componentFactoryId + ")"; 
     ServiceReference[] references = context.getServiceReferences(ComponentFactory.class.getCanonicalName(), filter); 
     return (references.length) == 0 ? null : (ComponentFactory) context.getService(references[0]); 
    } 


    private <T> T fetchService(BundleContext context, Class<T> clazz) 
    { 
     ServiceReference reference = context.getServiceReference(clazz.getCanonicalName()); 
     @SuppressWarnings("unchecked") 
     T service = (T) context.getService(reference); 
     return service; 
    } 


    private String findFileInCurrentDirectoryAndBelow(final Pattern filePattern) { 
     FileFilter filter = new FileFilter() { 
      @Override 
      public boolean accept(File pathname) { 
       Matcher matcher = filePattern.matcher(pathname.getName()); 
       return (matcher.matches()); 
      } 
     }; 
     return findFile(new File("."), filter, filePattern); 
    } 


    private String findFile(File directory, FileFilter filter, Pattern filePattern) { 
     File[] matches = directory.listFiles(filter); 
     if(matches != null && matches.length > 0) { 
      return matches[0].getAbsolutePath(); 
     } 
     File[] subdirs = directory.listFiles(new FileFilter() { 
      @Override 
      public boolean accept(File pathname) { 
       return pathname.isDirectory(); 
      } 
     }); 
     for(final File subdir : subdirs) { 
      String found = findFile(subdir, filter, filePattern); 
      if(!"".equals(found)) { 
       return found; 
      } 
     } 
     throw new RuntimeException(new FileNotFoundException("No match for pattern: " + filePattern.pattern())); 
    } 
} 

Ve burada maven pom var:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 
    <groupId>net.earcam</groupId> 
    <artifactId>net.earcam.example.servicecomponent</artifactId> 
    <version>0.0.1-SNAPSHOT</version> 
    <packaging>jar</packaging> 

    <properties> 
     <version.java.source>1.6</version.java.source> 
     <version.java.target>1.6</version.java.target> 

     <version.osgi>4.2.0</version.osgi> 
     <version.paxexam>2.1.0</version.paxexam> 
     <version.paxrunner>1.7.4</version.paxrunner> 
     <version.cometd>2.3.1</version.cometd> 
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    </properties> 

    <dependencies> 
     <dependency> 
      <groupId>org.osgi</groupId> 
      <artifactId>org.osgi.core</artifactId> 
      <version>${version.osgi}</version> 
      <scope>provided</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.osgi</groupId> 
      <artifactId>org.osgi.compendium</artifactId> 
      <version>${version.osgi}</version> 
      <scope>provided</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.apache.felix</groupId> 
      <artifactId>org.apache.felix.scr.annotations</artifactId> 
      <version>1.4.0</version> 
      <scope>provided</scope> 
     </dependency> 

     <dependency> 
      <groupId>junit</groupId> 
      <artifactId>junit</artifactId> 
      <version>4.8.2</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.hamcrest</groupId> 
      <artifactId>hamcrest-core</artifactId> 
      <version>1.3.RC2</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.jmock</groupId> 
      <artifactId>jmock-junit4</artifactId> 
      <version>2.5.1</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.slf4j</groupId> 
      <artifactId>slf4j-simple</artifactId> 
      <version>1.6.1</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.ops4j.pax.exam</groupId> 
      <artifactId>pax-exam-junit4</artifactId> 
      <version>${version.paxexam}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.ops4j.pax.exam</groupId> 
      <artifactId>pax-exam-container-paxrunner</artifactId> 
      <version>${version.paxexam}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.ops4j.pax.exam</groupId> 
      <artifactId>pax-exam-link-assembly</artifactId> 
      <version>${version.paxexam}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.ops4j.pax.exam</groupId> 
      <artifactId>pax-exam-testforge</artifactId> 
      <version>${version.paxexam}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.ops4j.pax.runner</groupId> 
      <artifactId>pax-runner-no-jcl</artifactId> 
      <version>${version.paxrunner}</version> 
      <scope>test</scope> 
     </dependency> 

     <dependency> 
      <groupId>org.apache.felix</groupId> 
      <artifactId>org.apache.felix.scr</artifactId> 
      <version>1.6.0</version> 
      <scope>test</scope> 
     </dependency> 
    </dependencies> 

    <build> 
     <plugins> 
      <plugin> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-compiler-plugin</artifactId> 
       <version>2.3.2</version> 
       <configuration> 
        <source>${version.java.source}</source> 
        <target>${version.java.target}</target> 
        <encoding>${project.build.sourceEncoding}</encoding> 
       </configuration> 
      </plugin> 

      <plugin> 
       <!-- Unit Tests --> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-surefire-plugin</artifactId> 
       <version>2.8.1</version> 
       <executions> 
        <execution> 
         <goals> 
          <goal>test</goal> 
         </goals> 
        </execution> 
       </executions> 
       <configuration> 
        <excludes> 
         <exclude>**/*IntegrationTest.java</exclude> 
        </excludes> 
       </configuration> 
      </plugin> 

      <plugin> 
       <!-- Integration Tests --> 
       <groupId>org.codehaus.mojo</groupId> 
       <artifactId>failsafe-maven-plugin</artifactId> 
       <version>2.4.3-alpha-1</version> 
       <executions> 
        <execution> 
         <goals> 
          <goal>integration-test</goal> 
          <goal>verify</goal> 
         </goals> 
         <phase>integration-test</phase> 
        </execution> 
       </executions> 
       <configuration> 
        <includes> 
         <include>**/*IntegrationTest.java</include> 
        </includes> 
       </configuration> 
      </plugin> 

      <plugin> 
       <groupId>org.ops4j.pax.exam</groupId> 
       <artifactId>maven-paxexam-plugin</artifactId> 
       <version>1.2.3</version> 
       <executions> 
        <execution> 
         <id>generate-config</id> 
         <goals> 
          <goal>generate-depends-file</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 

      <plugin> 
       <!-- Process the DS annotations --> 
       <groupId>org.apache.felix</groupId> 
       <artifactId>maven-scr-plugin</artifactId> 
       <version>1.6.0</version> 
       <executions> 
        <execution> 
         <id>generate-scr-descriptor</id> 
         <goals> 
          <goal>scr</goal> 
         </goals> 
         <phase>process-classes</phase> 
         <configuration> 
          <strictMode>true</strictMode> 
          <outputDirectory>${project.build.outputDirectory}/</outputDirectory> 
         </configuration> 
        </execution> 
       </executions> 
      </plugin> 


      <plugin> 
       <!-- Generate OSGi bundle MAINFEST.MF entries --> 
       <groupId>org.apache.felix</groupId> 
       <artifactId>maven-bundle-plugin</artifactId> 
       <version>2.3.4</version> 
       <extensions>true</extensions> 
       <configuration> 
        <supportedProjectTypes> 
         <supportedProjectType>jar</supportedProjectType> 
        </supportedProjectTypes> 
        <instructions> 
         <Bundle-Vendor>earcam</Bundle-Vendor> 
         <Service-Component>OSGI-INF/serviceComponents.xml</Service-Component> 
         <!-- PAX mangles this, it uses the name of the project for the symbolicname 
          of test bundle? <Bundle-SymbolicName>${project.name}</Bundle-SymbolicName> --> 
         <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName> 
         <Bundle-Version>${project.version}</Bundle-Version> 
         <Export-Package>!${project.artifactId}.internal,${project.artifactId}.*</Export-Package> 
         <Import-Package>*</Import-Package> 
        </instructions> 
       </configuration> 
       <executions> 
        <execution> 
         <id>bundle-manifest</id> 
         <phase>process-classes</phase> 
         <goals> 
          <goal>manifest</goal> 
         </goals> 
        </execution> 
       </executions> 
      </plugin> 

      <plugin> 
       <groupId>org.apache.maven.plugins</groupId> 
       <artifactId>maven-jar-plugin</artifactId> 
       <version>2.3.1</version> 
       <configuration> 
        <archive> 
         <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> 
        </archive> 
       </configuration> 
      </plugin> 

     </plugins> 
    </build> 
</project> 

nota birkaç şey; Entegrasyon testlerimi test ettikleri modül içinde, mvn temiz kurulum dağıtımı entegrasyon testim yaparsa başarısız olur - ancak tüm entegrasyon testleri için projeleri tek bir entegrasyon modülü ile görmemiz yaygındır. Bu, mevcut dizinin paketini hedef dizinde bulmak için kullanılan çirkin yöntem olan findFileInCurrentDirectoryAndBelow(Pattern pattern)'u açıklar ve ayrıca maven-bundle-plugin ve maven-scr-plugin eklentilerinin standart olmayan kurulumunu açıklar.

Ayrıca yolu Pax-Sınav Eğer maven bağımlılıkları hem de yapılandırma (örneğin paket ithalat/ihracat DS değişiklikleri) her değişiklik için inşa koşmak gerektirir bağımlılıkları alır. Ancak bu yapıldıktan sonra Eclipse'den testler çalıştırabilir/hata ayıklayabilirsiniz.

Bir tarball'daki projeyi koyduk here

HTH =) DS Service Fabrikası uygulamak kalmamak

+0

Bu harika! Detaylı cevap ve sabrınız için çok teşekkürler. –

+0

Teşekkürler @earcam için çok detaylı bir cevap, çok daha iyi bir sınav doktorları, bu bana çok yardımcı oldu. Ama biraz karışıklık var, bu JAVA 6 ile çalışıyor ama JAVA 8'de değil, farklı PAX bağımlılık sürümüyle çok çalıştım ama Java 8 ile çakışmayı çözemedim, mümkünse sürüm bağımlılığı akışını sağlayabilir misiniz? –

0

ServiceFactory kodunuzu farklı paketler için özelleştirilmiş hizmet nesnesi sağlamamıza olanak tanır. ServiceFactory ile hizmetinizin istemcileri, yeni örnek oluşturulduğunda hala denetlemediğini, aralarındaki hizmeti (ServiceB) her zamanki gibi aradığını unutmayın. Yani, onlar için hizmetinizin ServiceFactory olarak kayıtlı olup olmadığı farketmez. bildirim hizmetleri ile

, kendinizi ServiceFactory uygulamak gerekir. Yalnızca <service> öğesinin (zaten yaptıysanız) servicefactory="true" özniteliğini ekleyin ve farklı istekte bulunan paketler için bileşen sınıfınızın farklı örnekleri otomatik olarak oluşturulur (etkinleştirilir). Bileşenin uygulama sınıfı olarak ServiceBImpl belirtmeniz gerekir.

+0

Ve OSGi platformu hangi fabrika sınıfını kullanacağını biliyor? –

+0

Kodumun daha iyi test edilebilirliğini desteklemek için kendi fabrika uygulamamı sağlamak istiyorum. Bu tartışmaya bakın: http://stackoverflow.com/questions/7004165/unit-testing-osgi-components/7009717#7009717 http://www.knopflerfish.org/osgi_service_tutorial.html adresinde bunun nasıl yapılacağını örnek olarak buldum. ama servis fabrikasını aktivatöre kaydetmem gerekiyor. Bunu bileşen beyannamesi içinde yapmanın bir yolu yok, öyle ki DS bunu benim için yapacak? –

+0

Temelde getService() {Object comp = clazz.newInstance(); activateComponent (bileşik); comp dönmek; } –

1

Aslında oldukça basit ... DS, her paket için bir örneğini oluşturur DS tüm zor işleri yapıyor. Örneğin:

@Service(serviceFactory=true) 
public class MyServiceFactory implements XyzService { 

    ... 
    @Activate 
    void activate(ComponentContext ctx) { 
     System.out.println("Using bundle: " + ctx.getUsingBundle()); 
    } 
} 

başka paket bu XyzService alır her zaman, DS, yeni bir örneğini oluşturur. Kullanmakta olduğunuz paketi almak için ComponentContext'i (isteğe bağlı olarak etkinleştirme yönteminde geçirilen) kullanabilirsiniz. Hizmetinizi içeren paketin ServiceB.class dosyası bulunmamalıdır.

+0

github bağlantısı artık bozuk –

İlgili konular