制造一个基于OSGi的Web Application——设置初始化bundle的StartLevel

打造一个基于OSGi的Web Application——设置初始化bundle的StartLevel

在前几天的文章中描述了如何在Web Application中启动OSGi,参见

打造一 个基于OSGi的Web Application——在WebApplication中启动OSGi

后来发现其中在初始化时加载bundle的方式,还有一些美中不足。这种方式加载的bundle都具有相同的启动顺序,即bundle的初始化默认start level,在之前均没有做过特别的设置,所以默认值都是1,这样会导致所有的bundle的启动顺序无法控制,在某些希望特殊bundle优先加载的场合(如日志功能,需要最先加载),我们希望能够在bundle初始化的时候就能指定特别的start level,这样所有的bundle就能按照我们预设的启动顺序来加载了。下面就是我优化过的初始化代码,能够解决启动顺序问题。

工作原理是这样的,首先,在原来存放初始化bundle的目录,也就是OSGi-Web工程的/WEB-INF/osgi/plugins目录下面再增加一个名为start的目录,在start目录下,再按照期望设置的start level来建立子目录,例如,期望设置start level为1的bundle,放到plugins/start/1目录下面;期望设置start level为2的bundle,放到plugins/start/2目录下面,以此类推。

代码方面,设置bundle的start level,需要使用StartLevel Service,可以通过下面代码获得:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1                   //  StartLevel Service,用于设置bundle的startlevel
2                  ServiceReference slRef  =  bundleContext.getServiceReference(StartLevel. class .getName());
3                  StartLevel sl  =  slRef  ==   null   ?   null  : (StartLevel) bundleContext.getService(slRef);

然后设置initial bundle start level:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1                   //  设置新bundle的初始startlevel为系统配置项:org.osgi.framework.startlevel.beginning的值
2                  String bsl  =  bundleContext.getProperty( " org.osgi.framework.startlevel.beginning " );
3                   if  (bsl  !=   null   &&  isInteger(bsl)) sl.setInitialBundleStartLevel(Integer.parseInt(bsl));

这样所有新安装的bundle的初始化start level都将被设置为和系统配置项:org.osgi.framework.startlevel.beginning相同的值,以确保所有默认安装的bundle都能启动。
修改osgi.properties中关于org.osgi.framework.startlevel.beginning的配置项,我改成了5:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1  #Specifies the beginning start level of the framework. See Start
2  #Level Service Specification on page  235  for more information.
3  #
4  org.osgi.framework.startlevel.beginning  =   5



增加一个方法,用于安装一个目录下所有的直属bundle,并且设置start level:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1       private   static   void  installBundles(BundleContext bundleContext, File bundleRoot, StartLevel sl,  int  bsl) {
 2          File bundleFiles[]  =  bundleRoot.listFiles( new  FilenameFilter() {
 3               public   boolean  accept(File dir, String name) {
 4                   return  name.endsWith( " .jar " );
 5              }
 6          });
 7  
 8           if  (bundleFiles  !=   null   &&  bundleFiles.length  >   0 ) {
 9               for  (File bundleFile : bundleFiles) {
10                   try  {
11                      Bundle bundle  =  bundleContext.installBundle(bundleFile.toURL().toExternalForm());
12                       if  (sl  !=   null   &&  bsl  >   0 ) sl.setBundleStartLevel(bundle, bsl);
13                       if  (logger.isInfoEnabled()) logger.info( " Install bundle success:  "   +  bundleFile.getName());
14                  }  catch  (Throwable e) {
15                       if  (logger.isWarnEnabled()) logger.warn( " Install bundle error:  "   +  bundleFile, e);
16                  }
17              }
18          }
19      }


最后,遍历start目录下的子目录来安装所有的bundle:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1                   //  安装bundle并设置相应的start level
 2                  File slRoot  =   new  File(bundleRoot,  " start " );
 3                   if  (slRoot.isDirectory()) {
 4                      File slDirs[]  =  slRoot.listFiles( new  FileFilter() {
 5                           public   boolean  accept(File file) {
 6                               return  file.isDirectory()  &&  isInteger(file.getName());
 7                          }
 8                      });
 9  
10                       for  (File slDir : slDirs) {
11                          installBundles(bundleContext, slDir, sl, Integer.parseInt(slDir.getName()));
12                      }
13                  }
14  
15                   //  安装直属目录下面的bundle
16                  installBundles(bundleContext, bundleRoot, sl,  0 );

 

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->1       private   static   boolean  isInteger(String value) {
2           try  {
3              Integer.parseInt(value);
4               return   true ;
5          }  catch  (NumberFormatException e) {
6               return   false ;
7          }
8      }


最后,由于Declarative Services的存在,稍微调整了一下启动策略,所有包含Service-Component的header定义的bundle,也调用start方法来启动:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1                   for  (Bundle bundle : bundleContext.getBundles()) {
 2                       if  (bundle.getState()  ==  Bundle.INSTALLED  ||  bundle.getState()  ==  Bundle.RESOLVED) {
 3                           if  (bundle.getHeaders().get(Constants.BUNDLE_ACTIVATOR)  !=   null   ||  bundle.getHeaders().get( " Service-Component " !=   null ) {
 4                               try  {
 5                                  bundle.start(Bundle.START_ACTIVATION_POLICY);
 6                                   if  (logger.isInfoEnabled()) logger.info( " Start bundle:  "   +  bundle);
 7                              }  catch  (Throwable e) {
 8                                   if  (logger.isWarnEnabled()) logger.warn( " Start bundle error:  "   +  bundle, e);
 9                              }
10                          }
11                      }
12                  }


clean Server然后启动Server,我们可以看到初始化后的bundle已经被赋予了指定Start Level。


附上initFramework方法的完整代码,更多的代码请参加以前的帖子:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> 1       //  初始化Framework环境
 2       private   static   void  initFramework(Framework framework, ServletContextEvent event)  throws  IOException {
 3          BundleContext bundleContext  =  framework.getBundleContext();
 4          ServletContext servletContext  =  event.getServletContext();
 5  
 6           //  将ServletContext注册为服务
 7          registerContext(bundleContext, servletContext);
 8  
 9          File file  =  bundleContext.getDataFile( " .init " );
10           if  ( ! file.isFile()) {  //  第一次初始化
11               if  (logger.isInfoEnabled()) logger.info( " Init Framework制造一个基于OSGi的Web Application——设置初始化bundle的StartLevel " );
12  
13              String pluginLocation  =  servletContext.getInitParameter(CONTEXT_PARAM_OSGI_PLUGINS_LOCATION);
14               if  (pluginLocation  ==   null ) pluginLocation  =  DEFAULT_OSGI_PLUGINS_LOCATION;
15               else   if  ( ! pluginLocation.startsWith( " / " )) pluginLocation  =   " / " .concat(pluginLocation);
16  
17               //  安装bundle
18              File bundleRoot  =   new  File(servletContext.getRealPath(pluginLocation));
19               if  (bundleRoot.isDirectory()) {
20                   if  (logger.isInfoEnabled()) logger.info( " Load Framework bundles from:  "   +  pluginLocation);
21  
22                   //  StartLevel Service,用于设置bundle的startlevel
23                  ServiceReference slRef  =  bundleContext.getServiceReference(StartLevel. class .getName());
24                  StartLevel sl  =  slRef  ==   null   ?   null  : (StartLevel) bundleContext.getService(slRef);
25                   //  设置新bundle的初始startlevel为系统配置项:org.osgi.framework.startlevel.beginning的值
26                  String bsl  =  bundleContext.getProperty( " org.osgi.framework.startlevel.beginning " );
27                   if  (bsl  !=   null   &&  isInteger(bsl)) sl.setInitialBundleStartLevel(Integer.parseInt(bsl));
28  
29                   //  安装bundle并设置相应的start level
30                  File slRoot  =   new  File(bundleRoot,  " start " );
31                   if  (slRoot.isDirectory()) {
32                      File slDirs[]  =  slRoot.listFiles( new  FileFilter() {
33                           public   boolean  accept(File file) {
34                               return  file.isDirectory()  &&  isInteger(file.getName());
35                          }
36                      });
37  
38                       for  (File slDir : slDirs) {
39                          installBundles(bundleContext, slDir, sl, Integer.parseInt(slDir.getName()));
40                      }
41                  }
42  
43                   //  安装直属目录下面的bundle
44                  installBundles(bundleContext, bundleRoot, sl,  0 );
45  
46                   for  (Bundle bundle : bundleContext.getBundles()) {
47                       if  (bundle.getState()  ==  Bundle.INSTALLED  ||  bundle.getState()  ==  Bundle.RESOLVED) {
48                           if  (bundle.getHeaders().get(Constants.BUNDLE_ACTIVATOR)  !=   null   ||  bundle.getHeaders().get( " Service-Component " !=   null ) {
49                               try  {
50                                  bundle.start(Bundle.START_ACTIVATION_POLICY);
51                                   if  (logger.isInfoEnabled()) logger.info( " Start bundle:  "   +  bundle);
52                              }  catch  (Throwable e) {
53                                   if  (logger.isWarnEnabled()) logger.warn( " Start bundle error:  "   +  bundle, e);
54                              }
55                          }
56                      }
57                  }
58  
59                   if  (slRef  !=   null ) bundleContext.ungetService(slRef);
60              }
61  
62               new  FileWriter(file).close();
63               if  (logger.isInfoEnabled()) logger.info( " Framework inited. " );
64          }
65      }