如何使用声明性服务管理多个服务实例?

时间:2016-01-10 13:38:14

标签: java osgi declarative-services maven-bundle-plugin

我正在尝试在OSGi中构建一个可以读取给定格式文件的服务。

服务界面如下所示:

public interface FileReaderService {
    /**
     * Reads the given file.
     * @param filePath Path of the file to read
     * @return the data object built from the file
     * @throws IOException if there is an error while reading the file
     */
    Data readFile(Path filePath) throws IOException;

    /**
     * Detects if the format of the provided file is supported.
     * @param filePath the file to check
     * @return true if the format of the file is supported, false otherwise
     * @throws IOException if there is an error while reading the file
     */
    boolean isFormatSupported(Path filePath) throws IOException;
}

Data对象是一个定义要读取的文件的数据结构的类(它们应该包含相同类型的数据)。

我们的想法是拥有不同的服务实现,例如:

public class TxtFileReader implements FileReaderService {

    @Override
    public Data readFile(Path filePath) throws IOException {
            // Do something smart with the file
            return data;
        }
    }

    @Override
    public boolean isFormatSupported(Path filePath) throws IOException {
        PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:*.txt");
        return matcher.matches(filePath);
    }
}

还可能有其他实现,例如XmlFileReader,MdFileReader等。

最后,我想要一个像这样的FileReaderFactory:

@Component
public class FileReaderFactory implements FileReaderService {

    private List<FileReaderService> availableServices = new ArrayList<>();

    @Override
    public Data readFile(Path filePath) throws IOException {

        for (FileReaderService reader : availableServices) {
            if (reader.isFormatSupported(filePath)) {
                return reader.readFile(filePath);
            }
        }

        return null;
    }

    @Override
    public boolean isFormatSupported(Path filePath) throws IOException {
        for (FileReaderService reader : availableServices) {
            if (reader.isFormatSupported(filePath)) {
                return true;
            }
        }
        return false;
    }
}

我想要的是DS动态地在工厂列表中注入FileReaderServices。根据提供的服务数量,我支持(或不支持)给定的文件格式。

我的问题是:

1)DS是否可行?

2)如果是,如何使用DS注释?

3)如果没有,你会怎么做?

由于

编辑:我尝试过克里斯蒂安的解决方案,但它还没有奏效。完整代码可以在此处下载:https://github.com/neopium/FileReader

2 个答案:

答案 0 :(得分:2)

使用@Reference注释列表:

@Reference(service = FileReaderService.class)
private List<FileReaderService> availableServices;

您需要DS 1.3才能使用此功能。最新的felix scr版本支持此功能。

我不建议将FileReaderFactory导出为FileReaderService,因为它可能会导致递归。

答案 1 :(得分:0)

如Christian Schneider所示,解决方案是使用@Reference注释。

然而,当我按照他的回答中的说明使用时,卡拉夫确实崩溃了:

Cannot register Component
org.osgi.service.component.ComponentException: Component org.test.reader.service.factory.FileReaderFactory validation failed: Field value type must not be set for unary field references.

通过查看Felix SCR源代码,我在类org.apache.felix.scr.impl.metadata.ReferenceMetadata:

中找到了导致异常的行。
// field value type
if ( !m_isMultiple )
{
    // value type must not be specified for unary references
    if ( m_field_collection_type != null )
    {
        throw componentMetadata.validationFailure( "Field value type must not be set for unary field references." );
    }
}

似乎Felix SCR不满意,因为属性m_field_collection_type在组件XML中设置为“service”(即非null),而没有指定基数......

我因此改变了我的注释:

@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE)
private List<FileReaderService> availableServices;

我还添加了一个工厂服务使用者,以便测试应用程序并且它可以工作!

对于那些感兴趣的人,可以在这里获得固定的源代码: https://github.com/neopium/FileReader