无法在 OSGi Apache Felix Web 控制台中添加加号按钮来创建新的配置实例



i使用托管serviceFactory来通过包含在配置文件中的参数创建新实例。我想通过 Apache Felix Web Console 进行此操作,但它并没有给我添加新配置的加上按钮。
我想我缺少一些东西。你能帮我吗?
这是我的Apache Felix Web控制台的图像

这是实现托管ServiceFactory的类

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

i使用托管服务factory来创建通过配置文件中包含的参数创建新实例。

您正在使用声明服务(您的类是注释的@Component),但也实现了托管服务。正如其他人提到的那样,这是一种不好的做法,您不应该这样做。此外,您的代码不是安全的,并且Map<String, Component> components可能会损坏。

一个更好的解决方案是遵循制作组件configurationPolicy=ConfigurationPolicy.REQUIRE的建议,并使用@Activate方法接收配置和@Destroy方法来处理删除。然后,您根本不需要地图。

我想通过Apache Felix Web控制台执行此操作,但这并没有给我加上新配置的加上按钮。

Felix Web控制台使用Metatype来生成此用户界面。编写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;
}

您使用@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。

可能有我不知道的东西,但是AFAIK您无法通过配置管理服务创建托管服务的新实例(这是Web-Console Configuration使用的用途)。

ManagedServiceFactory是您(或代表您的框架)致电以按需创建服务的工厂。您可以使用配置管理服务来配置工厂本身,但正是工厂负责配置其创建的服务实例。

另一方面,您可以使用Metatype服务来描述任何非管理服务的配置。如果您这样做,那么诸如Felix Web控制台和Apache文件安装之类的工具可以使用该信息在提供新配置时创建服务的新实例。这是当您在Felix Web控制台配置屏幕中看到加号时。

我认为您根本不需要实现ManagedServiceFactory。要得到多次创建的组件,即每个配置管理员一个实例记录。您只需要使用" requient"的配置policy。例如:

@Component(name = "camel, configurationPolicy=ConfigurationPolicy.REQUIRE)
public class ConfigReaderComponent {
    @Activate
    void activate(Map<String, Object> configProps) {
        // process config...
    }
}

每个班级将对每个配置记录进行实例化一次camel的工厂PID。WebConsole将意识到它是工厂,将启用Plus按钮。

最新更新