`

基于idea+maven的OSGI示例

    博客分类:
  • OSGI
阅读更多

前言

 

在目前流行的java开发工具中,EclipseIdea都占有很大的市场份额。Eclipse 3.0以后的插件就是基于OSGI开发的,理论上使用Eclipse开发OSGI模块是最佳的选择。OSGI模块开发与普通的jar包开发最大的不同就是配置模块元数据, 而Eclipse中提供了可视化工具进行模块元数据配置。本人比较习惯使用Ideajava开发,本次demo讲解也是基于Idea开发工具进行。

 

OSGI模块是在OSGI框架中运行的,要搭建自己的开发环境首先就要选择一个OSGI框架,本示例选择的是Eclipseequinox。关于如何在Idea开发工具中配置equinox,这里不进行讲解,可以参考http://blog.csdn.net/love_taylor/article/details/75194394 这篇文章。

 

示例讲解

 

本示例开发了4Bundle模块:managerserverclientapi



 

4个模块的关系:

api模块的作用是定义服务接口,没有具体实现;

server模块实现了api中定义的接口,是具体的服务实现;

client模块作为服务使用方法,使用server模块提供的服务;

manager模块的作用仅仅是用来启动serverclient模块,以及模拟定时启动或者停止server模块,用于测试服务的消失和出现;

 

这个程序整体目的很简单,就是用于测试服务的是可以热插拔的。下面分别来看下各个模块的实现:

 

api模块

这个Bundle只定义了一个HelloService接口,由于这个模块不需要与OSGI框架交互,也不需要对外提供服务,所有不需要创建激活器。HelloService接口定义如下:

public interface HelloService {
    void sayHello();
}
 

 

元数据配置也很简单(这里没有使用Maven插件进行元数据配置),配置界面如下:



 

也许你会疑惑为什么不需要使用Export-Package导出包呢,否则其他模块不能通过Import-Package使用HelloService接口啊。这就是使用开发工具的好处,在同一个工程的多个模块中,Idea可以自动识别导入导出包,并自动添加到Bundle jar中(经过实验自动导入没有问题,导出有时候需要手动处理)。

 

server模块

该模块主要就是对api模块的接口进行实现,并注册一个服务到注册中心,即发布服务的过程。所以除了实现类以为,该模块还需要一个激活器 才能发布服务:

public class ServerAtivictor implements BundleActivator{
    private BundleContext context;
 
    @Override
    public void start(BundleContext context) throws Exception {
        this.context = context;
 
        //元数据,客户端在查询服务是可以根据元数据过滤
        Dictionary dictionary = new Properties();
        dictionary.put("test","test");
        //注册服务
        ServiceRegistration registration = context.registerService(HelloService.class.getName(),new HelloServiceImpl("小明"),dictionary);
        System.out.println("start server bundle");
 
    }
 
    @Override
    public void stop(BundleContext context) throws Exception {
        System.out.println("stop server bundle");
    }
}

实现类的内容很简单,就是打印一句话:

public class HelloServiceImpl implements HelloService {
 
    private String name;
 
    public HelloServiceImpl(String name) {
        this.name = name;
    }
 
    @Override
    public void sayHello() {
        System.out.println("Hello,"+name);
    }
}
 

 

再来看server模块的元数据配置,也很简单 比起api模块来说只是多了一个激活器配置:



 

理论上需要在Additional properties中添加Import-Package配置项 导入com.sky.osgi.api包(HelloService接口所在的包),但无需我们动手 IdeaBuild jar时会在元数据配置文件MANIFEST.MF中自动加入。最终server模块的元数据配置内容如下:

Manifest-Version: 1.0
Bnd-LastModified: 1516779536888
Bundle-Activator: com.sky.osgi.server.ServerAtivictor
Bundle-ManifestVersion: 2
Bundle-Name: com.sky.osgi.server
Bundle-SymbolicName: com.sky.osgi.server
Bundle-Version: 1.0.0
Created-By: 1.8.0_65 (Oracle Corporation)
Export-Package: com.sky.osgi.server;uses:="org.osgi.framework";version
 ="1.0.0"
Import-Package: com.sky.osgi.api,org.osgi.framework
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Tool: Bnd-3.3.0.201609221906
 

 

可见server模块自动导入了com.sky.osgi.api包和org.osgi.framework包(激活器在这个包中)。

 

client模块

该模块相比前两个模块稍微复杂些,主要作用是:模拟当server模块提供的服务可用时,使用该服务,当服务不可用时 提示服务不存在,但不影响client模块自己的业务运行。这里使用“服务追踪器”来感知服务的变化。首先看下激活器的实现内容:

public class ClientActivator implements BundleActivator {
 
    private ServiceTracker serviceTracker;
    private ExecutorService executorService;
 
    @Override
    public void start(BundleContext context) throws Exception {
        //创建一个带定制器的 追踪器
        serviceTracker = new ServiceTracker(context, HelloService.class.getName(), new MyServiceTrackerCustomizer(context));
        serviceTracker.open();//打开追踪器
 
        //新开一个线程,模拟调用服务
        executorService = Executors.newSingleThreadExecutor();
        executorService.submit(new TestTask(serviceTracker));
    }
 
    @Override
    public void stop(BundleContext context) throws Exception {
        serviceTracker.close();//关闭追踪器
        executorService.shutdown();
    }
}
 

 

再来看下模拟服务调用的任务类:

public class TestTask implements Runnable{
    public ServiceTracker serviceTracker;
 
    public TestTask(ServiceTracker serviceTracker) {
        this.serviceTracker = serviceTracker;
    }
 
    @Override
    public void run() {
            boolean flag = true;
            while (flag){
                try {
                    HelloService helloService = (HelloService)serviceTracker.getService();
                    if (helloService!= null) {
                        helloService.sayHello();
                    } else {
                        System.out.println("没有可用的服务");
                    }
                }catch (Exception e){
                    System.out.println("业务异常");
                }
 
                //睡5秒重试
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println("线程池关闭");
                    flag = false;
                }
            }
    }
}

 

定制器MyServiceTrackerCustomizer的代码就不贴了,本文中所有的代码详见文章末尾提供的github地址。

 

由于idea自动配置导入导出,client模块的元数据配置界面也很简单:




manager模块

这个模块的作用很简单,就是控制server模块和client模块的启动和停止,该模块主要使用的是OSGI生命周期层的api。在OSGI框架启动是,把所有的4个模块加载到OSGI框架,并只启动manager模块,再由manager模块的激活器启动server模块和client模块。manager模块的激活器实现内容如下:

public class ManagerActivator implements BundleActivator{
    private Map<String,Bundle> bundleMap = new HashMap<>();
    private Thread thread;
 
    @Override
    public void start(BundleContext bundleContext) throws Exception {
        System.out.println("开始启动Bundle");
 
        //获取OSGI框架中已经所有Bundle
        Bundle[] bundles = bundleContext.getBundles();
        for (Bundle bundle:bundles){
            //启动server Bundle
            if(bundle.getSymbolicName().equals("com.sky.osgi.server")){
                bundle.start();
                bundleMap.put("com.sky.osgi.server",bundle);
            }
            //启动client Bundle
            if(bundle.getSymbolicName().equals("com.sky.osgi.client")){
                bundle.start();
                bundleMap.put("com.sky.osgi.client",bundle);
            }
            //为什么不启动api bundle呢?
            // 因为它不需要提供服务,也不需要与OSGI框架交互,只是作为导出api接口使用
        }
 
        System.out.println("所需Bundle启动结束");
 
        //启动线程修改server Bundle状态
        thread = new Thread(new UpdateTask());
        thread.start();
 
    }
 
    @Override
    public void stop(BundleContext bundleContext) throws Exception {
        thread.interrupt();//停止线程
        bundleMap.clear();
    }
 
    class UpdateTask implements Runnable{
        @Override
        public void run() {
            Bundle server = bundleMap.get("com.sky.osgi.server");
            boolean flag = true;
            while(flag){
                try {
                    if (server.getState() != Bundle.ACTIVE){
                        server.start();
                    }else{
                        server.stop();
                    }
                } catch (BundleException e) {
                    e.printStackTrace();
                    System.out.println("Bundle启动停止出现异常");
                }
 
                try {
                    //模拟每隔10秒交替启动和停止Bundle
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    flag = false;
                    System.out.println("管理Bundle线程停止");
                }
            }
 
        }
    }
}
 

 

在该激活器中,创建了一个新的线程,模拟每隔10秒 启动或者停止server模块。client模块会自动感知服务的变化,并及时做出正确的相应,元数据配置方式以上面类似就不上图了。至此整个demo内容讲解完毕。

 

demo测试运行

如要正确的运行本示例代码,需要注意idea运行配置界面的配置:



 

注意“Start after insall”,意思是说框架加载模块完成后 就启动模块。这里只需要启动manager模块,在该模块中再来控制其他模块的启动。最后先build整个工程打包,点击运行或者debug运行,即可启动框架并执行整个工程代码。



 



 

 

测试结果表面 client模块可以实时的感知到server模块中服务的变化,并且彼此运行互不干扰,感兴趣的朋友可以自己运行试试。

 

总结

 

本次示例模拟内容虽然简单,但已经覆盖OSGI的模块层、生命周期层、服务层。这里再提下模块层元数据配置,本示例中主要使用的是idea自动的导入和导出机制。在开发一个大型的Bundle时,最繁琐的就是确定导入导出包,使用Idea开发工具可以减少这部分工作量。如果一定要手动配置元数据,可以使用Maven插件在pom.xml中配置:

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <Bundle-ManifestVersion>2</Bundle-ManifestVersion>
                            <Bundle-Name>server</Bundle-Name>
                            <Bundle-SymbolicName>com.sky.osgi.server</Bundle-SymbolicName>
                            <Bundle-Version>1.0.0</Bundle-Version>
                            <Bundle-Vendor>sky</Bundle-Vendor>
                            <Bundle-Activator>com.sky.osgi.server.ServerAtivictor</Bundle-Activator>
                            <!--<Export-Package>-->
                                <!--com.sky.osgi.server-->
                            <!--</Export-Package>-->
                            <Import-Package>
                                org.osgi.framework
                            </Import-Package>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>

 

可见manifestEntries节点里的配置项跟Bundle的元数据配置项是一一对应的。

 

 

另外本示例中,使用Maven引入包的方式和平时开发完全一样,就不再贴出pom.xml文件的配置内容,上述代码已上传GitHub: https://github.com/gantianxing/osgi_demo1.git

 

 

 

  • 大小: 21 KB
  • 大小: 26 KB
  • 大小: 54.6 KB
  • 大小: 67.1 KB
  • 大小: 48.6 KB
  • 大小: 15.6 KB
  • 大小: 8.8 KB
0
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics