如何使用声明式服务管理多个服务实例?
How to manage several service instances with declarative services?
我正在尝试在 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 对象是一个 class,它定义了要读取的文件的数据结构(它们应该包含相同类型的数据)。
想法是有不同的服务实现,例如:
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) 如果没有,你会怎么做?
谢谢
编辑:我尝试了 Christian 的解决方案,但它(还)没有奏效。完整代码可以在这里下载:https://github.com/neopium/FileReader
用@Reference注释列表:
@Reference(service = FileReaderService.class)
private List<FileReaderService> availableServices;
您需要 DS 1.3 才能工作。最新的felix scr版本支持这个。
我不建议将 FileReaderFactory 导出为 FileReaderService,因为它可能导致递归。
正如 Christian Schneider 所指出的,解决方案是使用 @Reference 注释。
然而,当我按照他的回答中指定的方式使用它时,Karaf 确实崩溃了:
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 源代码,我在 class 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"(即不为空),而没有指定基数...
因此我这样更改了我的注释:
@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE)
private List<FileReaderService> availableServices;
我还添加了一个工厂服务消费者来测试应用程序并且它有效!
对于那些感兴趣的人,可以在此处获得固定的源代码:
https://github.com/neopium/FileReader
我正在尝试在 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 对象是一个 class,它定义了要读取的文件的数据结构(它们应该包含相同类型的数据)。
想法是有不同的服务实现,例如:
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) 如果没有,你会怎么做?
谢谢
编辑:我尝试了 Christian 的解决方案,但它(还)没有奏效。完整代码可以在这里下载:https://github.com/neopium/FileReader
用@Reference注释列表:
@Reference(service = FileReaderService.class)
private List<FileReaderService> availableServices;
您需要 DS 1.3 才能工作。最新的felix scr版本支持这个。
我不建议将 FileReaderFactory 导出为 FileReaderService,因为它可能导致递归。
正如 Christian Schneider 所指出的,解决方案是使用 @Reference 注释。
然而,当我按照他的回答中指定的方式使用它时,Karaf 确实崩溃了:
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 源代码,我在 class 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"(即不为空),而没有指定基数...
因此我这样更改了我的注释:
@Reference(cardinality = ReferenceCardinality.AT_LEAST_ONE)
private List<FileReaderService> availableServices;
我还添加了一个工厂服务消费者来测试应用程序并且它有效!
对于那些感兴趣的人,可以在此处获得固定的源代码: https://github.com/neopium/FileReader