osgi:使用ServiceFactories? [英] osgi: Using ServiceFactories?

查看:111
本文介绍了osgi:使用ServiceFactories?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在尝试获取一个包含Service Factory运行的简单包。

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

这是我的工厂类:

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

    }

}

这是我应该由工厂创建的服务:

This is my service that should be created by the factory:

public class ServiceBImpl implements ServiceB {

    private ServiceA svcA;

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

}

最后是OSGI-INF / component.xml

And finally the 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>

如果我在equinox中运行我的测试包(A,B和C)我得到以下内容错误:

If I run my test bundles (A, B and C) within equinox I'm getting the following error:

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

我找不到多少有关使用Internet上组件定义中声明的ServiceFeactories的信息。甚至连OSGi和Equinox这本书都没有告诉我使用它们的情况。
有谁能请我解释一下我做错了什么?

I can't find much information about using ServiceFeactories declared in a component definition on the internet. Even the book "OSGi and Equinox" didn't tell me much about using them. Could anyone please explain to me what I'm doing wrong?

推荐答案

这是一个使用ComponentFactory的例子,它应该适合您的需求(并包含一个简单的集成测试,以帮助您的其他问题)。免责声明;代码编写得不好,仅举例说明。

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.

一些服务接口:

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

整合测试驱动整个事情(注意;有一个主要方法,所以你在启动/停止捆绑等时运行它。)

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

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

有几点需要注意;我喜欢我们测试的模块内部的集成测试,如果我的集成测试确实如此, mvn clean install deploy 会失败 - 但是通常会看到带有单个集成模块的项目用于所有集成测试。这解释了丑陋的方法 findFileInCurrentDirectoryAndBelow(Pattern pattern),它用于在目标目录中找到当前模块的包,并解释了maven-bundle的非标准设置-plugin和maven-scr-plugin插件。

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.

此外,Pax-Exam选择依赖项的方式要求您为依赖项和配置中的每个更改运行maven构建(例如,捆绑导入/导出,DS更改)。但是一旦完成,你可以从Eclipse运行/调试测试。

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.

我把项目放在tarball中这里

I've put the project in a tarball here

HTH =)

这篇关于osgi:使用ServiceFactories?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆