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("
");
}
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 + "
2 " + message + "
3 " + message + "
";
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()));
}
}
And here's the 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