osgi: Using ServiceFactories?

后端 未结 3 1394
暖寄归人
暖寄归人 2020-12-09 22:05

I\'m currently trying to get a simple bundle containing a Service Factory running.

This is my factory class:

public class SvcFactory implements Servi         


        
3条回答
  •  无人及你
    2020-12-09 22:34

    Here's an example using ComponentFactory which should fit your needs (and contains a simple integration test to aid with your other question). Disclaimer; the code isn't well written, just for example's sake.

    Some service interfaces:

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

    And:

    package net.earcam.example.servicecomponent;
    
    public interface SequenceService {
        long next();
    }
    

    Then the implementations:

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

    }

    And:

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

    An integration test that drives the whole thing (note; there's a main method so you run it while start/stopping bundles etc).

    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 properties = new Hashtable();
            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 fetchService(BundleContext context, Class 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()));
        }
    }
    

    And here's the maven pom:

    
        4.0.0
        net.earcam
        net.earcam.example.servicecomponent
        0.0.1-SNAPSHOT
        jar
    
        
            1.6
            1.6
    
            4.2.0
            2.1.0
            1.7.4
            2.3.1
            UTF-8
        
    
        
            
                org.osgi
                org.osgi.core
                ${version.osgi}
                provided
            
    
            
                org.osgi
                org.osgi.compendium
                ${version.osgi}
                provided
            
    
            
                org.apache.felix
                org.apache.felix.scr.annotations
                1.4.0
                provided
            
    
            
                junit
                junit
                4.8.2
                test
            
    
            
                org.hamcrest
                hamcrest-core
                1.3.RC2
                test
            
    
            
                org.jmock
                jmock-junit4
                2.5.1
                test
            
    
            
                org.slf4j
                slf4j-simple
                1.6.1
                test
            
    
            
                org.ops4j.pax.exam
                pax-exam-junit4
                ${version.paxexam}
                test
            
    
            
                org.ops4j.pax.exam
                pax-exam-container-paxrunner
                ${version.paxexam}
                test
            
    
            
                org.ops4j.pax.exam
                pax-exam-link-assembly
                ${version.paxexam}
                test
            
    
            
                org.ops4j.pax.exam
                pax-exam-testforge
                ${version.paxexam}
                test
            
    
            
                org.ops4j.pax.runner
                pax-runner-no-jcl
                ${version.paxrunner}
                test
            
    
            
                org.apache.felix
                org.apache.felix.scr
                1.6.0
                test
            
        
    
        
            
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    2.3.2
                    
                        ${version.java.source}
                        ${version.java.target}
                        ${project.build.sourceEncoding}
                    
                
    
                
                    
                    org.apache.maven.plugins
                    maven-surefire-plugin
                    2.8.1
                    
                        
                            
                                test
                            
                        
                    
                    
                        
                            **/*IntegrationTest.java
                        
                    
                
    
                
                    
                    org.codehaus.mojo
                    failsafe-maven-plugin
                    2.4.3-alpha-1
                    
                        
                            
                                integration-test
                                verify
                            
                            integration-test
                        
                    
                    
                        
                            **/*IntegrationTest.java
                        
                    
                
    
                
                    org.ops4j.pax.exam
                    maven-paxexam-plugin
                    1.2.3
                    
                        
                            generate-config
                            
                                generate-depends-file
                            
                        
                    
                
    
                
                    
                    org.apache.felix
                    maven-scr-plugin
                    1.6.0
                    
                        
                            generate-scr-descriptor
                            
                                scr
                            
                            process-classes
                            
                                true
                                ${project.build.outputDirectory}/
                            
                        
                    
                
    
    
                
                    
                    org.apache.felix
                    maven-bundle-plugin
                    2.3.4
                    true
                    
                        
                            jar
                        
                        
                            earcam
                            OSGI-INF/serviceComponents.xml
                            
                            ${project.artifactId}
                            ${project.version}
                            !${project.artifactId}.internal,${project.artifactId}.*
                            *
                        
                    
                    
                        
                            bundle-manifest
                            process-classes
                            
                                manifest
                            
                        
                    
                
    
                
                    org.apache.maven.plugins
                    maven-jar-plugin
                    2.3.1
                    
                        
                            ${project.build.outputDirectory}/META-INF/MANIFEST.MF
                        
                    
                
    
            
        
    
    

    A couple of things to note; I like my integration tests inside the module they test, that way mvn clean install deploy fails if my integration test does - but it's common to see projects with a single integration module for all integration tests. This explains the ugly method findFileInCurrentDirectoryAndBelow(Pattern pattern) which is used to locate the current module's bundle in the target directory, and also explains the non-standard setup of the maven-bundle-plugin and maven-scr-plugin plugins.

    Also the way Pax-Exam picks up the dependencies requires you run the maven build for every change in dependencies and config (e.g. bundle imports/exports, DS changes). But once this is done you can run/debug the tests from Eclipse.

    I've put the project in a tarball here

    HTH =)

提交回复
热议问题