Karaf教程第2部分使用Configuration Admin服务
在Karaf教程的第1部分,我们学习了如何使用maven和blueprint提供和使用pojo服务,如何使用http服务发布servlet。
在第2部分,我们集中精力关注OSGi bundle的配置。不像servlet容器,OSGi容器包含一个非常好的配置规范:来自企业级规范的Config Admin服务。在本教程中,我们将在纯OSGi和blueprint中使用Config Admin服务,学习如何在bundle中自动部署配置文件。
这个教程的练习可以在github上找到:https://github.com/cschneider/Karaf-Tutorial/tree/master/configadmin。
2.1 Configuration Admin服务规范
我们首先快速概览一下Configuration Admin服务规范。对于我们来说,主要有两个接口可以使用:
1、ConfigurationAdmin:允许获取和改变配置。这个服务由Config Admin服务实现提供。
2、ManagedService:允许对配置改变产生影响。必须实现这个接口,并将它注册给要被通知的服务。
所以,基本上在Config Admin服务中的配置是一个字典,这个字典包含了属性和他们的值。字典由持久性标识PID标识。PID就是一个简单的字符串,它唯一标识了配置。
2.2 如何处理配置?
虽然你可以使用ConfigurationAdmin.getConfiguration接口获取配置,但是我不推荐你这么做。OSGi是动态的,所以有可能发生bundle在Config Admin服务之前启动或者Config Admin服务还没有读取这个bundle的配置。所以有时候你获取的配置可能为null。
所以,推荐的方式是使用ManagedService,并对更新做出反应。如果你的bundle没有配置无法启动,那么在收到第一个更新时创建一个要被配置的pojo类是个好主意。
2.3 介绍要被配置的非常简单的类
由于我们想要实现一个整洁风格的配置,那么要被配置的类应该是纯pojo。虽然可以简单的实现ManagedService接口,直接使用Dictionary,但这将使你依赖于OSGi和当前的Config Admin规范。所以,替代做法是我们使用一个具有title熟悉的简单的bean类。另外,我增加了一个刷新方法,当配置发生改变的时候来调用刷新方法。
public class MyApp {
String title;
public void setTitle(Stringtitle) {
this.title =title;
}
public void refresh() {
System.out.println("Configuration updated (title=" +title +")");
}
}
所以我们的目标就是当配置发生改变时,配置title,然后调用refresh方法。我们将在纯OSGi和blueprint中做这件事。
2.4 练习一下:使用纯OSGi接口处理配置
本教程的第1个练习演示如何只用OSGi接口使用Config Admin服务。虽然这可能不是你以后使用的方式,但这种方式可以帮助你理解更深层次的东西。
你可以在子目录configapp中找到实现:(https://github.com/cschneider/Karaf-Tutorial/tree/master/configadmin/configapp)。
所以首先我们需要一个pom文件用于maven构建。你最好从configapp示例程序的pom文件开始。如果你新建了新的工程,你必须是使用maven-bundle-plugin插件使你的工程成为一个bundle,你需要添加两个依赖:
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.compendium</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.2.0</version>
</dependency>
第一个依赖用于获取config admin服务接口,第二个依赖用于创建Activator,它包含基本的OSGi接口。
现在,我们关心如何更新MyApp类。下面这个类解决了这个问题。我们实现了ManagedService接口,来与Config Admin服务交互。所以无论什么时候,配置发生改变,我们的方法都会被调用。第一件事是检查是否为null,当config被移除时这是有可能发生的。这时我们可以停止MyApp,但为了简单起见,我们只是忽略它。下一步是创建MyApp类。通常你可能会在Activator中实例化它,但是你不得不处理空配置,这是我们不希望的。最后是简单地用从config中获取的值调用setter方法,在所有的设置完成后再调用refresh方法。
private final class ConfigUpdaterimplements ManagedService {
@SuppressWarnings("rawtypes")
@Override
public void updated(Dictionaryconfig) throws ConfigurationException {
if (config ==null) {
return;
}
if (app ==null) {
app = new MyApp();
}
app.setTitle((String)config.get("title"));
app.refresh();
}
}
当然,这还是什么事情都没做。最后一步就是在Activator.start方法中注册ConfigUpdater。我们简单地使用registerService方法,就像每一个其他的服务一样。唯一特殊的地方是你必须设置SERVICE_PID为config pid,这样Config Admin服务就知道你想要监视的配置了。
Hashtable<String, Object> properties =new Hashtable<String, Object>();
properties.put(Constants.SERVICE_PID,CONFIG_PID);
serviceReg = context.registerService(ManagedService.class.getName(),new ConfigUpdater() ,properties);
2.5 运行这个简单的示例
•用mvn install命令构建这个工程
•启动一个全新的karaf实例
•将configapp.jar从target目录复制到Karaf的deploy目录
现在我们注意到似乎没有发生任何事情。在Karaf控制台调用list,你会看到这个bundle确实已经启动了,但是它没有任何输出,因为它还没有配置。我们仍然需要创建配置文件,并设置title。
•复制已有的文件/configadmin-features/src/main/resources/ConfigApp.cfg到Karaf的/etc目录
这里重要的部分是文件名必须是<pid>.cfg。这样config admin服务才能找到它。
现在fileinstall bundle会在etc目录检测到新的文件。由于文件结尾是.cfg,它认为这是一个config admin资源,创建或更新由文件名确定的pid的Config Admin服务配置。
所以现在在Karaf控制台你应该能看到下面的打印。这个打印显示了配置改变被正确地检测和转发。如果你现在用编辑器改变了这个文件并保存,那么这个变化也会被传播。
2.6 使用Karaf config命令探究配置
在Karaf控制台键入如下命令:
在其他的配置中,你应该找到上面的配置"ConfigApp"。这个配置显示它从哪儿加载的,pid,当然还有在文件中设置的所有属性。
我们也可以改变这个配置:
我们看到这个修改直接传播到了我们的bundle。如果你看看etc下面的配置文件,你会发现这个改变已经持久化到了这个文件。所以如果我们重启了Karaf,修改还是生效的。
2.7 使用Blueprint配置
在纯OSGi环境中处理了Config Admin服务之后,现在我们将看一看如何在Blueprint中实现同样的功能。幸运地是,这是相当容易,就Blueprint为我们做的大多数的工作一样。
我们仅仅定义了一个cm:property-placeholder元素。这个处理文件的属性占位符的功能很相似,但是这里是处理Config Admin服务。我们需要提供一个配置的PID和更新策略。更新策略我们选择“reload”。这意味着在配置改变之后,blueprint上下文会被重新加载或反射改变。我们也设置了默认的属性,当配置的PID找不到或者属性不存在的时候,就会使用这些默认值。
集成我们的bean类通常是一个简单的bean定义。在这个类中我们定义了title熟悉,分配一个占位符,这个占位符会使用config admin服务进行解析。唯一特别的是初始化方法。这个给了我们机会以便对配置改变之后做出反应,就像纯OSGi示例中一样。
对于bluenprint来说,我们不需要任何maven的依赖,因为我们的Java代码是纯Java bean。只要将Blueprint上下文放在OSGI-INF/blueprint目录中并且blueprint extender加载了,那么blueprint上下文就被会简单地激活。由于As blueprint总是在Karaf中被加载,所以我们什么都不需要做。
<?xml version="1.0" encoding="UTF-8"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0 http://svn.apache.org/repos/asf/aries/trunk/blueprint/blueprint-cm/src/main/resources/org/apache/aries/blueprint/compendium/cm/blueprint-cm-1.1.0.xsd
">
<cm:property-placeholder persistent-id="ConfigApp" update-strategy="reload" >
<cm:default-properties>
<cm:property name="title" value="Default Title"/>
</cm:default-properties>
</cm:property-placeholder>
<bean id="myApp" class="net.lr.tutorial.configadmin.MyApp" init-method="refresh">
<property name="title" value="${title}"></property>
</bean>
</blueprint>
2.8 Deploying config files
在我们成功地使用了Config Admin服务之后,剩下要做的唯一一件事就是部署带默认配置的bundle。这可以使用Karaf feature文件实现。我们定义一个feature,带有它需要的bundle,简单地添加一个configfile元素。这使得Karaf部署给定的文件到Karaf安装位置的etc目录。如果这个文件已经存在,那么它不会被覆盖。
<feature name="tutorial-configadmin" version="${pom.version}">
<bundle>mvn:net.lr.tutorial.configadmin/configapp/${pom.version}</bundle>
<bundle>mvn:net.lr.tutorial.configadmin/configapp-blueprint/${pom.version}</bundle>
<configfile finalname="/etc/ConfigApp.cfg">mvn:net.lr.tutorial.configadmin/configadmin-features/${pom.version}/cfg</configfile>
</feature>
这样,最后一个问题是如何将配置部署到maven,让configfileonfig元素能够找到它。这好像属于Karaf中的the build-helper-maven-plugin的特性。请参见pom文件了解使用细节。
2.9 Summing it up and a look into the future
在本教程中,我们学习了如何使用Config Admin服务以及如何在纯OSGi和blueprint中使用该服务。我们也看到了如何构建和部署我们的工程。
虽然这已经很有用了,但在我看来,有一点点小问题。第一个问题是configfile似乎看起来与config admin不一致。实际上,Karaf不使用config admin服务部署文件。所以我想要看到的是已经存在的config元素不仅仅是为了写入配置,而且还用于持久化。幸运地是,我的同事Jean Baptiste正在研究这个问题。参见https://issues.apache.org/jira/browse/KARAF-888。
另一个问题是对于企业级环境,需要具有附加特性的config admin服务。一件是要可能在整个服务器网络中进行配置,具有配置的中心源和友好的UI。另一件事是你不仅想要部署默认的配置,而且要部署管理员真正想要为系统进行的配置。所以,我想你能够定义一个部署计划,不仅要安装bundle和feature,还需要配置的改变。如果这个正确的完成,将允许部署、配置改变的良好检查,也允许管理员一旦在出错的情况下回滚配置的变化。我希望我们可以在下一个Talend ESB EE发布版中提供这样的计划。