2011-08-11 10 views
7

मैं वर्तमान में एक सेवा फैक्टरी युक्त एक सरल बंडल प्राप्त करने की कोशिश कर रहा हूं।ओजीआई: सेवा कारखानों का उपयोग करना?

public class ServiceBImpl implements ServiceB { 

    private ServiceA svcA; 

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

} 

अंत में OSGi-INF/component.xml

<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 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) { 

    } 

} 

यह मेरी सेवा है कि कारखाने द्वारा निर्मित किया जाना चाहिए है:

यह मेरा कारखाने वर्ग है

यदि मैं विषुव के भीतर अपने परीक्षण बंडल (ए, बी और सी) चलाता हूं तो मुझे निम्न त्रुटि मिल रही है:

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 

मुझे इंटरनेट पर घटक परिभाषा में घोषित सेवाफैक्टरीज का उपयोग करने के बारे में अधिक जानकारी नहीं मिल रही है। यहां तक ​​कि पुस्तक "ओएसजीआई और विषुव" ने मुझे उनका उपयोग करने के बारे में बहुत कुछ नहीं बताया। क्या कोई मुझे बता सकता है कि मैं क्या गलत कर रहा हूं?

+0

आपकी सेवा वाले बंडल में ServiceB.class फ़ाइल नहीं होनी चाहिए। यदि ऐसा होता है, तो यह अलग-अलग बंडल के लिए अलग-अलग सेवा बी का उपयोग करेगा - यह गलत है। –

+0

मुझे लगता है कि आप गलत समझा। यदि आप सर्विसफैक्टरी विशेषता निर्दिष्ट करते हैं तो डीएस प्रत्येक बंडल के लिए एक नया उदाहरण तैयार करेगा। आप उस घटक को प्राप्त कर सकते हैं जिसे आप घटक कॉन्टेक्स्ट से असाइन किया गया था। –

उत्तर

12

यहां कंपोनेंट फैक्ट्री का उपयोग करने वाला एक उदाहरण है जो आपकी आवश्यकताओं को पूरा करना चाहिए (और आपके other question के साथ सहायता के लिए एक सरल एकीकरण परीक्षण शामिल है)। अस्वीकरण; कोड अच्छी तरह से लिखा नहीं है, बस उदाहरण के लिए।

कुछ सेवा इंटरफेस:

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); 
} 

और:

package net.earcam.example.servicecomponent; 

public interface SequenceService { 
    long next(); 
} 

फिर कार्यान्वयन:

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; 
} 

}

और:

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; 
    } 
} 

एकीकरण परीक्षण जो पूरी चीज को चलाता है (नोट; एक मुख्य विधि है ताकि आप इसे चालू/बंद करने के दौरान इसे चलाएं)।

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())); 
    } 
} 

और यहाँ Maven पोम है:

<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> 

बातों का ध्यान रखना के एक जोड़े; मुझे परीक्षण के मॉड्यूल के अंदर मेरे एकीकरण परीक्षण पसंद हैं, इस तरह एमवीएन स्वच्छ स्थापित विफल रहता है अगर मेरा एकीकरण परीक्षण करता है - लेकिन सभी एकीकरण परीक्षणों के लिए एक एकल एकीकरण मॉड्यूल के साथ परियोजनाओं को देखना आम बात है। यह बदसूरत विधि findFileInCurrentDirectoryAndBelow(Pattern pattern) बताता है जिसका उपयोग लक्ष्य निर्देशिका में वर्तमान मॉड्यूल के बंडल का पता लगाने के लिए किया जाता है, और यह मैवेन-बंडल-प्लगइन और मेवेन-स्क्र-प्लगइन प्लगइन के गैर-मानक सेटअप को भी समझाता है।

पैक्स-परीक्षा निर्भरता को उठाता है जिस तरह से आप निर्भरता और कॉन्फ़िगरेशन (जैसे बंडल आयात/निर्यात, डीएस परिवर्तन) में हर बदलाव के लिए मेवेन निर्माण को चलाने की आवश्यकता होती है। लेकिन एक बार ऐसा करने के बाद आप एक्लिप्स से परीक्षण चला सकते हैं/डीबग कर सकते हैं।

मैं एक टारबॉल में परियोजना डाल दिया here

HTH =)

+0

यह बढ़िया है! आपके विस्तृत उत्तर और धैर्य के लिए बहुत धन्यवाद। –

+0

धन्यवाद विस्तृत जानकारी के लिए @earcam, पैक्स-परीक्षा दस्तावेज़ों से कहीं बेहतर, इससे मुझे बहुत मदद मिली। लेकिन कुछ भ्रम है, यह जावा 6 के साथ काम कर रहा है लेकिन जावा 8 पर नहीं, मैंने विभिन्न पैक्स निर्भरता संस्करण के साथ बहुत कुछ करने की कोशिश की लेकिन जावा 8 के साथ संघर्ष को समझने में सक्षम नहीं है, क्या आप संभवतः संस्करण निर्भरता प्रवाह प्रदान कर सकते हैं? –

0

ServiceFactory आपके कोड को विभिन्न बंडलों के लिए अनुकूलित सेवा ऑब्जेक्ट प्रदान करने की अनुमति देता है। ध्यान दें कि ServiceFactory के साथ, जब भी नया इंस्टेंस बनाया जाता है, तब भी आपकी सेवा के ग्राहक नियंत्रण नहीं करते हैं, वे सामान्य रूप से इसके इंटरफ़ेस (ServiceB) द्वारा सेवा खोजते हैं। इसलिए, उनके लिए, यदि आपकी सेवा ServiceFactory के रूप में पंजीकृत है या नहीं, तो इसमें कोई फर्क नहीं पड़ता है।

घोषणात्मक सेवाओं के साथ, आपको ServiceFactory स्वयं लागू नहीं करना चाहिए। <service> तत्व (आपने पहले ही किया है) में विशेषता जोड़ें और विभिन्न घटक बंडलों के लिए स्वचालित रूप से आपके घटक वर्ग के विभिन्न उदाहरण बनाए जाएंगे (सक्रिय)। आपको घटक के कार्यान्वयन वर्ग के रूप में ServiceBImpl निर्दिष्ट करने की आवश्यकता है।

+0

और ओएसजीआई प्लेटफार्म कैसे जानता है कि किस कारखाने वर्ग का उपयोग करना है? –

+0

मैं अपने कोड की बेहतर टेस्टेबिलिटी का समर्थन करने के लिए अपना कारखाना कार्यान्वयन प्रदान करना चाहता हूं। इस चर्चा को देखें: http://stackoverflow.com/questions/7004165/unit-testing-osgi-components/7009717#7009717 मैंने http://www.knopflerfish.org/osgi_service_tutorial.html पर पाया कि यह कैसे करें, लेकिन मुझे एक्टिवेटर में सेवा कारखाना पंजीकृत करना है। क्या घटक घोषणा के भीतर ऐसा करने का कोई तरीका नहीं है ताकि डीएस मेरे लिए ऐसा करे? –

+0

यह अपना कारखाना बना देगा जो मूल रूप से getService() {Object comp = clazz.newInstance() के समान कुछ करेगा। activateComponent (COMP); वापसी COMP; } –

1

यह वास्तव में नहीं बल्कि सरल है ... डी एस तो डी एस के साथ सेवा फैक्टरी को लागू नहीं करते, हर बंडल एक उदाहरण बनाता है , डीएस सभी कड़ी मेहनत करता है। उदाहरण के लिए:

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

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

हर बार एक और बंडल इस XyzService को प्राप्त करता है, डीएस एक नया उदाहरण तैयार करेगा। आप का उपयोग कर रहे बंडल को प्राप्त करने के लिए आप ComponentContext (वैकल्पिक रूप से सक्रिय विधि में पारित) का उपयोग कर सकते हैं।

+0

github लिंक अब टूटा हुआ है –

संबंधित मुद्दे