这是一个使用ComponentFactory的示例,该示例应符合您的需求(并包含一个简单的集成测试以帮助解决您的其他问题)。免责声明;举例来说,代码编写得不好。
一些服务接口:
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; @Activateprotected void activate(final ComponentContext componentContext){ repeat = Integer.parseInt(componentContext.getProperties().get(REPEAT_PARAMETER).toString());}@Overridepublic 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;@Component@Servicepublic 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 Pom:
<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</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>需要注意的几件事;我喜欢我的集成模块内的集成测试,这样,如果集成测试成功, mvn clean install部署 就会失败-
但是常见的情况是,对于所有集成测试,只有一个集成模块的项目就会出现。这说明了
findFileInCurrentDirectoryAndBelow(Patternpattern)用于在目标目录中定位当前模块捆绑包的难看方法,还说明了maven-bundle-plugin和maven-scr-
plugin插件的非标准设置。
同样,Pax-
Exam获取依赖项的方式还要求您对依赖项和配置中的每个更改(例如,捆绑软件导入/导出,DS更改)运行Maven构建。但是一旦完成,就可以从Eclipse运行/调试测试。
我把这个项目放到了这里
HTH =)



