无法在 OSGi Apache Felix Web 控制台中添加加号按钮来创建新的配置实例
Can't add the plus button in OSGi Apache Felix Web Console to create new configuration instances
我使用 ManagedServiceFactory 通过配置文件中包含的参数创建新实例。我想通过 Apache Felix Web 控制台 来完成,但它没有给我添加新配置的加号按钮。
我想我错过了什么。你能帮帮我吗?
这是我的 Apache Felix Web 控制台 image
这是实现 ManagedServiceFactory
的 class
@org.osgi.service.component.annotations.Component(
name = "camel_config",
property = {
"service.pid=camel",
"factory=true"
},
configurationPolicy = ConfigurationPolicy.IGNORE
)
public class ConfigReaderFactory implements ManagedServiceFactory {
private static final String DELETE_CONDITIONS = "readLock=changed&idempotent=false&noop=true&delete=true";
private static final String NON_DELETE_CONDITIONS = "noop=true";
private volatile DependencyManager dependencyManager;
private final Map<String, Component> components = new HashMap<>();
private List<String> attributes;
private List<String> csvTypes;
private CamelService camel;
private TypeConverter converter;
private EventPublisher publisher;
private String url;
private String name;
private int confLine;
private String endpointType;
private String ip;
private String username;
private String password;
private String folder;
private boolean delete;
private Double latitude;
private Double longitude;
private String email;
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary props) throws ConfigurationException {
if (components.containsKey(pid)) {
return;
}
if (props != null) {
attributes = new ArrayList<>();
csvTypes = new ArrayList<>();
int count = 1;
String configurationLine;
while ((configurationLine = (String) props.get(Integer.toString(count++))) != null) {
List<String> values = Utils.getValuesFromLine(configurationLine);
attributes.add(values.size() >= 1 ? values.get(0) : TypeConverter.NAMELESS);
csvTypes.add(values.size() >= 2 ? values.get(1) : TypeConverter.NAMELESS);
}
confLine = Integer.parseInt((String) props.get(Config.CONFIG_LINE));
name = (String) props.get(Config.NAME);
initConfigParameters(pid, props);
buildURL();
System.out.println("[URL] " + url);
try {
Map<String, Object> params = new HashMap<>();
putParameters(params);
camel.start(params);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void deleted(String pid) {
Component component = components.remove(pid);
dependencyManager.remove(component);
component.stop();
}
private void buildURL() {
url = "";
switch(endpointType) {
case Constants.FTP:
url += "ftp://" + username + "@" + ip + "/" + folder + "?";
if(!password.equals("")) {
url += "password=" + password + "&";
}
break;
case Constants.FILE:
url += "file://" + folder + "?";
break;
case Constants.EMAIL:
url += "imaps://imap.gmail.com?username="+email+"&password="+password
+"&delete=false&unseen=false";
}
if(endpointType.equals(Constants.FTP) || endpointType.equals(Constants.FILE)) {
if (delete) {
url += DELETE_CONDITIONS;
} else {
url += NON_DELETE_CONDITIONS;
}
}
}
private void initConfigParameters(String pid, @SuppressWarnings("rawtypes") Dictionary props) {
confLine = Integer.parseInt((String) props.get(Config.CONFIG_LINE));
name = (String) props.get(Config.NAME);
endpointType = (String) props.get(Config.ENDPOINT_TYPE);
ip = (String) props.get(Config.IP_ADDRESS);
username = (String) props.get(Config.USERNAME);
password = (String) props.get(Config.PASSWORD);
folder = (String) props.get(Config.FOLDER);
email = (String) props.get(Config.EMAIL);
delete = ((String) props.get(Config.DELETE)).equals("true");
if((String) props.get(Config.LATITUDE)!=null) {
latitude = Double.parseDouble((String) props.get(Config.LATITUDE));
}
if((String) props.get(Config.LONGITUDE)!=null) {
longitude = Double.parseDouble((String) props.get(Config.LONGITUDE));
}
printParameters(pid);
}
private void putParameters(Map<String, Object> params) {
params.put(Constants.ATTRIBUTES, attributes);
params.put(Constants.TYPES, csvTypes);
params.put(Constants.CONF_LINE, confLine);
params.put(Constants.SOURCE, name);
params.put(Constants.PUBLISHER, publisher);
params.put(Constants.CONVERTER, converter);
params.put(Constants.LATITUDE, latitude);
params.put(Constants.LONGITUDE, longitude);
params.put(Constants.ENDPOINT_TYPE, endpointType);
params.put(Constants.EMAIL, email);
params.put(Constants.CONTEXT, FrameworkUtil.getBundle(this.getClass()).getBundleContext());
Processor processor = new CSVProcessor(params);
params.put(Constants.PROCESSOR, processor);
params.put(Constants.URL, url);
}
private void printParameters(String pid) {
System.out.println("\nStarting camel with parameters:");
System.out.println("Config file name "+pid);
System.out.println("[sensor_name]::" + name);
if(latitude!=null && longitude!=null) {
System.out.println("[latitude]::" + latitude);
System.out.println("[longitude]::" + longitude);
}
System.out.println("[endpoint_type]::" + endpointType);
if(endpointType.equals("ftp")) {
System.out.println("[ip_address]::" + ip);
System.out.println("[folder]::" + folder);
System.out.println("[user]::" + username);
System.out.println("[password]::" + password);
} else if(endpointType.equals("file")) {
System.out.println("[folder]::" + folder);
} else if(endpointType.equals("email")) {
System.out.println("[email]::" + email);
System.out.println("[password]::" + password);
}
System.out.println("[delete]::" + delete);
}
@Reference(service = TypeConverter.class)
public void setTypeConverter(TypeConverter converter) {
this.converter = converter;
}
public void unsetTypeConverter(TypeConverter converter) {
this.converter = null;
}
@Reference(service = EventPublisher.class)
public void setEventPublisher(EventPublisher publisher) {
this.publisher = publisher;
}
public void unsetEventPublisher(EventPublisher publisher) {
this.publisher = null;
}
@Reference(service = CamelService.class)
public void setCamelService(CamelService camel) {
this.camel = camel;
}
public void unsetCamelService(CamelService camel) {
this.camel = null;
}
}
可能有些事情我不知道,但据我所知,您无法通过 Configuration Admin Service(这是 Web 控制台配置使用的)创建托管服务的新实例。
ManagedServiceFactory
是您(或代表您的框架)调用以按需创建服务的工厂。您可以使用 Configuration Admin Service 来配置工厂本身,但工厂负责配置它创建的服务实例。
另一方面,您可以使用 Metatype service 来描述任何非托管服务的配置。如果您这样做,那么 Felix Web Console 和 Apache File Install 等工具可以使用该信息在提供新配置时创建服务的新实例。这是您在 Felix Web 控制台配置屏幕中看到加号按钮的时候。
我认为您根本不需要实施 ManagedServiceFactory
。要得到
多次创建的组件,即每个配置管理员一个实例
记录。您只需要创建一个带有
"require" 的配置策略。例如:
@Component(name = "camel, configurationPolicy=ConfigurationPolicy.REQUIRE)
public class ConfigReaderComponent {
@Activate
void activate(Map<String, Object> configProps) {
// process config...
}
}
整个 class 将为每个配置记录实例化一次
camel
的工厂 PID。 WebConsole 将意识到它是一个工厂,并将启用加号按钮。
I use a ManagedServiceFactory to create new instances by the parameters that are included in the configuration files.
您正在使用声明式服务(您的 class 带有注释 @Component
),但也在实施 ManagedServiceFactory。正如其他人提到的那样,这是一种不好的做法,您不应该这样做。此外,您的代码不是线程安全的,Map<String, Component> components
可能会损坏。
一个更好的解决方案是遵循创建组件 configurationPolicy=ConfigurationPolicy.REQUIRE
并使用 @Activate
方法接收配置和使用 @Destroy
方法处理删除的建议。然后你根本不需要地图。
I want to do it by the Apache Felix Web Console but it doesn't give me the plus button to add new configurations.
Felix Web 控制台正在使用 Metatype 生成此用户界面。写元类型 XML 是可怕的,人类不应该尝试,但幸运的是你可以使用 org.osgi.service.metatype.annotations
来定义你的元类型。这些注释应用于描述配置布局的界面或注释。例如:
@ObjectClassDefinition
public @interface MyConfig {
// This defines the property key "myProp" with
// a default value of "foo" and it will show
// up in the UI with the name "My Prop"
String myProp() default "foo";
// This defines the property key "my.prop"
// with a default value of 42. The @AD
// allows customisation of the UI
@AD(description="Something descriptive")
int my_num() default 42;
}
您 link 此配置 属性 使用 @Designate 注释在您的组件中键入:
@org.osgi.service.component.annotations.Component(
name = "camel_config", configurationPid=camel",
configurationPolicy = ConfigurationPolicy.REQUIRE
)
@Designate(ocd=MyConfig.class)
public class ConfigReaderFactory {
...
}
使用 DS 1.3 时,您还可以将此配置类型直接注入激活方法。
@Activate
void activate(MyConfig config) {
// process config...
}
如果你愿意,你也可以注入地图:
@Activate
void activate(MyConfig config, Map<String, Object> rawConfig) {
// process config...
}
定义元类型后,将出现 +
图标,以及用于配置组件的自定义 UI。
我使用 ManagedServiceFactory 通过配置文件中包含的参数创建新实例。我想通过 Apache Felix Web 控制台 来完成,但它没有给我添加新配置的加号按钮。
我想我错过了什么。你能帮帮我吗?
这是我的 Apache Felix Web 控制台 image
这是实现 ManagedServiceFactory
的 class@org.osgi.service.component.annotations.Component(
name = "camel_config",
property = {
"service.pid=camel",
"factory=true"
},
configurationPolicy = ConfigurationPolicy.IGNORE
)
public class ConfigReaderFactory implements ManagedServiceFactory {
private static final String DELETE_CONDITIONS = "readLock=changed&idempotent=false&noop=true&delete=true";
private static final String NON_DELETE_CONDITIONS = "noop=true";
private volatile DependencyManager dependencyManager;
private final Map<String, Component> components = new HashMap<>();
private List<String> attributes;
private List<String> csvTypes;
private CamelService camel;
private TypeConverter converter;
private EventPublisher publisher;
private String url;
private String name;
private int confLine;
private String endpointType;
private String ip;
private String username;
private String password;
private String folder;
private boolean delete;
private Double latitude;
private Double longitude;
private String email;
@Override
public String getName() {
return this.getClass().getName();
}
@Override
public void updated(String pid, @SuppressWarnings("rawtypes") Dictionary props) throws ConfigurationException {
if (components.containsKey(pid)) {
return;
}
if (props != null) {
attributes = new ArrayList<>();
csvTypes = new ArrayList<>();
int count = 1;
String configurationLine;
while ((configurationLine = (String) props.get(Integer.toString(count++))) != null) {
List<String> values = Utils.getValuesFromLine(configurationLine);
attributes.add(values.size() >= 1 ? values.get(0) : TypeConverter.NAMELESS);
csvTypes.add(values.size() >= 2 ? values.get(1) : TypeConverter.NAMELESS);
}
confLine = Integer.parseInt((String) props.get(Config.CONFIG_LINE));
name = (String) props.get(Config.NAME);
initConfigParameters(pid, props);
buildURL();
System.out.println("[URL] " + url);
try {
Map<String, Object> params = new HashMap<>();
putParameters(params);
camel.start(params);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void deleted(String pid) {
Component component = components.remove(pid);
dependencyManager.remove(component);
component.stop();
}
private void buildURL() {
url = "";
switch(endpointType) {
case Constants.FTP:
url += "ftp://" + username + "@" + ip + "/" + folder + "?";
if(!password.equals("")) {
url += "password=" + password + "&";
}
break;
case Constants.FILE:
url += "file://" + folder + "?";
break;
case Constants.EMAIL:
url += "imaps://imap.gmail.com?username="+email+"&password="+password
+"&delete=false&unseen=false";
}
if(endpointType.equals(Constants.FTP) || endpointType.equals(Constants.FILE)) {
if (delete) {
url += DELETE_CONDITIONS;
} else {
url += NON_DELETE_CONDITIONS;
}
}
}
private void initConfigParameters(String pid, @SuppressWarnings("rawtypes") Dictionary props) {
confLine = Integer.parseInt((String) props.get(Config.CONFIG_LINE));
name = (String) props.get(Config.NAME);
endpointType = (String) props.get(Config.ENDPOINT_TYPE);
ip = (String) props.get(Config.IP_ADDRESS);
username = (String) props.get(Config.USERNAME);
password = (String) props.get(Config.PASSWORD);
folder = (String) props.get(Config.FOLDER);
email = (String) props.get(Config.EMAIL);
delete = ((String) props.get(Config.DELETE)).equals("true");
if((String) props.get(Config.LATITUDE)!=null) {
latitude = Double.parseDouble((String) props.get(Config.LATITUDE));
}
if((String) props.get(Config.LONGITUDE)!=null) {
longitude = Double.parseDouble((String) props.get(Config.LONGITUDE));
}
printParameters(pid);
}
private void putParameters(Map<String, Object> params) {
params.put(Constants.ATTRIBUTES, attributes);
params.put(Constants.TYPES, csvTypes);
params.put(Constants.CONF_LINE, confLine);
params.put(Constants.SOURCE, name);
params.put(Constants.PUBLISHER, publisher);
params.put(Constants.CONVERTER, converter);
params.put(Constants.LATITUDE, latitude);
params.put(Constants.LONGITUDE, longitude);
params.put(Constants.ENDPOINT_TYPE, endpointType);
params.put(Constants.EMAIL, email);
params.put(Constants.CONTEXT, FrameworkUtil.getBundle(this.getClass()).getBundleContext());
Processor processor = new CSVProcessor(params);
params.put(Constants.PROCESSOR, processor);
params.put(Constants.URL, url);
}
private void printParameters(String pid) {
System.out.println("\nStarting camel with parameters:");
System.out.println("Config file name "+pid);
System.out.println("[sensor_name]::" + name);
if(latitude!=null && longitude!=null) {
System.out.println("[latitude]::" + latitude);
System.out.println("[longitude]::" + longitude);
}
System.out.println("[endpoint_type]::" + endpointType);
if(endpointType.equals("ftp")) {
System.out.println("[ip_address]::" + ip);
System.out.println("[folder]::" + folder);
System.out.println("[user]::" + username);
System.out.println("[password]::" + password);
} else if(endpointType.equals("file")) {
System.out.println("[folder]::" + folder);
} else if(endpointType.equals("email")) {
System.out.println("[email]::" + email);
System.out.println("[password]::" + password);
}
System.out.println("[delete]::" + delete);
}
@Reference(service = TypeConverter.class)
public void setTypeConverter(TypeConverter converter) {
this.converter = converter;
}
public void unsetTypeConverter(TypeConverter converter) {
this.converter = null;
}
@Reference(service = EventPublisher.class)
public void setEventPublisher(EventPublisher publisher) {
this.publisher = publisher;
}
public void unsetEventPublisher(EventPublisher publisher) {
this.publisher = null;
}
@Reference(service = CamelService.class)
public void setCamelService(CamelService camel) {
this.camel = camel;
}
public void unsetCamelService(CamelService camel) {
this.camel = null;
}
}
可能有些事情我不知道,但据我所知,您无法通过 Configuration Admin Service(这是 Web 控制台配置使用的)创建托管服务的新实例。
ManagedServiceFactory
是您(或代表您的框架)调用以按需创建服务的工厂。您可以使用 Configuration Admin Service 来配置工厂本身,但工厂负责配置它创建的服务实例。
另一方面,您可以使用 Metatype service 来描述任何非托管服务的配置。如果您这样做,那么 Felix Web Console 和 Apache File Install 等工具可以使用该信息在提供新配置时创建服务的新实例。这是您在 Felix Web 控制台配置屏幕中看到加号按钮的时候。
我认为您根本不需要实施 ManagedServiceFactory
。要得到
多次创建的组件,即每个配置管理员一个实例
记录。您只需要创建一个带有
"require" 的配置策略。例如:
@Component(name = "camel, configurationPolicy=ConfigurationPolicy.REQUIRE)
public class ConfigReaderComponent {
@Activate
void activate(Map<String, Object> configProps) {
// process config...
}
}
整个 class 将为每个配置记录实例化一次
camel
的工厂 PID。 WebConsole 将意识到它是一个工厂,并将启用加号按钮。
I use a ManagedServiceFactory to create new instances by the parameters that are included in the configuration files.
您正在使用声明式服务(您的 class 带有注释 @Component
),但也在实施 ManagedServiceFactory。正如其他人提到的那样,这是一种不好的做法,您不应该这样做。此外,您的代码不是线程安全的,Map<String, Component> components
可能会损坏。
一个更好的解决方案是遵循创建组件 configurationPolicy=ConfigurationPolicy.REQUIRE
并使用 @Activate
方法接收配置和使用 @Destroy
方法处理删除的建议。然后你根本不需要地图。
I want to do it by the Apache Felix Web Console but it doesn't give me the plus button to add new configurations.
Felix Web 控制台正在使用 Metatype 生成此用户界面。写元类型 XML 是可怕的,人类不应该尝试,但幸运的是你可以使用 org.osgi.service.metatype.annotations
来定义你的元类型。这些注释应用于描述配置布局的界面或注释。例如:
@ObjectClassDefinition
public @interface MyConfig {
// This defines the property key "myProp" with
// a default value of "foo" and it will show
// up in the UI with the name "My Prop"
String myProp() default "foo";
// This defines the property key "my.prop"
// with a default value of 42. The @AD
// allows customisation of the UI
@AD(description="Something descriptive")
int my_num() default 42;
}
您 link 此配置 属性 使用 @Designate 注释在您的组件中键入:
@org.osgi.service.component.annotations.Component(
name = "camel_config", configurationPid=camel",
configurationPolicy = ConfigurationPolicy.REQUIRE
)
@Designate(ocd=MyConfig.class)
public class ConfigReaderFactory {
...
}
使用 DS 1.3 时,您还可以将此配置类型直接注入激活方法。
@Activate
void activate(MyConfig config) {
// process config...
}
如果你愿意,你也可以注入地图:
@Activate
void activate(MyConfig config, Map<String, Object> rawConfig) {
// process config...
}
定义元类型后,将出现 +
图标,以及用于配置组件的自定义 UI。