以这篇文章为起点,我将开始写一系列关于Apache Karaf的文章,Apache Karaf是一个基于Equinox或者Felix框架开发的一个OSGI容器。
与这些框架最大的不同在于它带来了优秀的管理功能。
Karaf突出的特性如下:
- 可扩展控制台和Bash相似的命令补齐功能
- ssh控制台
- 从maven仓库中获取bundles和features的部署文件
- 可以在命令行中轻松的创建一个新的实例
所有这些特性使开发服务端OSGi应用和开发一般的java应用几乎一样的简单。
从某个层面上,部署和管理比目前我知道所有应用服务器都好。所有这些karaf特性的结合带来了最终的程序。在我看来,Karaf允许轻量级的开发风格就像J2EE结合spring应用程序的灵活性一样。
命令 | 描述 |
---|---|
la | 显示所有安装的bundle |
list | 显示用户的bundle |
service:list | 显示active的OSGI服务。这个列表十分长。这里有一个方便的方法,就是你可以使用unix的管道,如 “ls |
exports | 显示导出的packages和bundles,这个命令可以帮助你查找出package是来自哪里 |
feature:list | 显示所有feature包括已安装和未安装的 |
feature:install webconsole | 访问http://localhost:8181/system/console,用karaf/karaf登录并花些时间看下它所提供的信息 |
diag | 显示那些无法启动的bundle的诊断信息 |
log:tail | 显示日志。可以按ctrl-c返回控制台 |
Ctrl-d | 退出控制台。如果是主控制台,则karaf被关闭 |
重启后OSGI容器会保存状态
注意Karaf和其他所有的OSGI容器一样会维护已安装和已启动的bundles的最后状态。所以如果有些组件再也不能工作,重启也不一定有所帮助。为了启动一个全新的Karaf,可以停止karaf然后删除data目录或者启动的时候加个clean参数bin/karaf clean。
检查日志
Karaf运行的时候非常安静。为了不错过错误消息,可以运行tail -f data/karaf.log让日志文件处于打开状态。
没有任何有用的应用,Karaf是一个很棒但是没用的容器。所以让我们来创建第一个应用吧。好消息是创建一个OSGI应用非常的简单,maven会处理很多事情。和一个正常的maven工程的差异是非常少的。我推荐使用Eclipse 4写应用程序,Eclipse 4默认已经安装m2eclipse插件。
从github的Karaf-Tutorial仓库中获取源码。
git clone https://github.com/cschneider/Karaf-Tutorial.git
或者从 https://github.com/cschneider/Karaf-Tutorial/zipball/master 下载示例工程,然后解压到某个目录下。
导入到Eclipse
- 启动Eclipse Neon或者更新版本。
- 在Eclipse中选择File -> Import -> Existing maven project -> Browse到刚才解压的目录选择tasklist 子文件夹。
- Eclipse将显示所有它找到的maven工程。
- 单击默认导入所有工程。
Eclipse将导入所有的工程并使用m2eclipse插件管理它们之间所有的依赖。
tasklist有以下这些工程组成。
模块 | 描述 |
---|---|
tasklist-model | 服务接口和Task类 |
tasklist-persistence | 提供了一个TaskService接口的简单持久化实现 |
tasklist-ui | 使用TaskService显示任务列表的servlet |
tasklist-features | 为应用提供features的描述信息,使应用能够很容易的安装到Karaf容器中 |
pom.xml打包bundle,而maven-bundle-plugin创建具有OSGi Manifest信息的jar包。默认情况,插件导入java文件import的或者在bluneprint上下文引用的所有的包。同时导出不包含内部实现的所有包。在我们的例子中我们想要model包被导入,但是persistence的实现包不导入。按照命名约定,我们不需要额外的配置。
这个工程包含domain model,这里指Task类和TaskService接口。model同时被persistence工程和用户界面工程使用。任意TaskService服务的使用者只需要依赖model工程。所以不用直接绑定到当前的实现。
简单持久化实现的TaskServiceImpl类用一个简单的HashMap管理task。类用@Singleton注解标记自己为blueprint bean。注解@OsgiServiceProvider用于标记为自己是一个OSGi服务,而@Properties注解允许添加服务属性。在这个例子中我们设置的属性service.exported.interfaces可以被我们在后续的教程要介绍的CXF-DOSGI使用。目前该教程这个属性可以删除。
@OsgiServiceProvider
@Properties(@Property(name = "service.exported.interfaces", value = "*"))
@Singleton
public class TaskServiceImpl implements TaskService {
...
}
blueprint-maven-plugin插件将处理上面的类,且自动创建合适的blueprint xml文件。这样可以帮我们节省手动写blueprint xml文件的时间。
自动创建的blueprint xml文件位于target/generated-resources文件夹下
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<bean id="taskService" class="net.lr.tasklist.persistence.impl.TaskServiceImpl" />
<service ref="taskService" interface="net.lr.tasklist.model.TaskService" />
blueprint>
UI工程使用一个TaskServlet显示任务列表和单独的任务。为了和tasks任务协作,这个servlet需要TaskService服务。我们通过使用注解@Inject和@OsgiService注入TaskService服务,@Inject注解能够根据类型注入任意的实例。而注解@OsgiService将创建一个指定类型的OSGiSerivce服务的blueprint引用。(译注:这里我是这么理解的,@OsgiService表示从Karaf中获取一个bean,@Inject则负责注入)
这个类被暴露为一个OSGi服务,该类是一个有特别属性alias=/tasklist的接口java.http.Servlet实现。这样做触发了pax web的whiteboard扩展器接收这个服务,并使用url /tasklist映射到该servlet。
相关代码小片段
@OsgiServiceProvider(classes = Servlet.class)
@Properties(@Property(name = "alias", value = "/tasklist"))
@Singleton
public class TaskListServlet extends HttpServlet {
@Inject @OsgiService
TaskService taskService;
}
自动创建的blueprint xml文件位于target/generated-resources文件夹下
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
<reference id="taskService" availability="mandatory" interface="net.lr.tasklist.model.TaskService" />
<bean id="taskServlet" class="net.lr.tasklist.ui.TaskListServlet">
<property name="taskService" ref="taskService">property>
bean>
<service ref="taskServlet" interface="javax.servlet.http.HttpServlet">
<service-properties>
<entry key="alias" value="/tasklist" />
service-properties>
service>
blueprint>
最后一个工程仅安装一个feature描述信息文件到一个maven仓库,这样我们可以很容易的安装应用到Karaf。描述信息定义了一个叫tasklist的feature和将从maven仓库下载安装的bundles
<feature name="example-tasklist-persistence" version="${pom.version}">
<bundle>mvn:net.lr.tasklist/tasklist-model/${pom.version}bundle>
<bundle>mvn:net.lr.tasklist/tasklist-persistence/${pom.version}bundle>
feature>
<feature name="example-tasklist-ui" version="${pom.version}">
<feature>httpfeature>
<feature>http-whiteboardfeature>
<bundle>mvn:net.lr.tasklist/tasklist-model/${pom.version}bundle>
<bundle>mvn:net.lr.tasklist/tasklist-ui/${pom.version}bundle>
feature>
一个feature可以由其他features和bundles组成,这些features和bundles也应该被安装。一般bundles使用mvn urls。这也意味着它们从配置好的仓库或者你的本地maven仓库~/.m2/repository中加载。
feature:repo-add mvn:net.lr.tasklist/tasklist-features/1.0.0-SNAPSHOT/xml
feature:install example-tasklist-persistence example-tasklist-ui
添加features描述信息文件到Karaf,所以它被添加到可用的features列表中,然后安装和启动tasklist feature。运行命令之后tasklist应用应该运行了。
list
检查tasklist的所有bundles是在active状态。如果没有,尝试启动它们和检查日志。
http:list
ID | Servlet | Servlet-Name | State | Alias | Url
-------------------------------------------------------------------------------
56 | TaskListServlet | ServletModel-2 | Deployed | /tasklist | [/tasklist/*]
应该显示TaskListServlet。默认情况下这个例子应该运行在http://localhost:8181/tasklist。
你可以通过创建一个文本文件”etc/org.ops4j.pax.web.cfg”改变端口。文本内容为”org.osgi.service.http.port=8080”。这个方式将告诉HttpService使用这个端口8080. 现在tasklist应用应该运行在http://localhost:8080/tasklist地址。
在该教程中我们已经安装了Karaf和学习了一些命令。然后我们创建了一个小的OSGi应用,这个应用涉及到servlets,OSGi服务,blueprint和whiteboard模式。下一个教程我们看下在OSGi中Apache Camel和Apache CXF的使用。
Karaf Tutorial Part 1 - Installation and First application