OSGI+Spring+Hibernate+...

导论

“世间本无事,庸人自扰之”

SpringDM 就是一庸人!

 

最近,我做一个项目需要编写 Eclipse 的插件。我想在 Eclipse 插件中使用 Spring Hibernate 。但却遇到了巨大的问题。

按照 Spring 组织的提示,我使用 SpringDM1.02 编写 Spring 程序(后来用 SpringDM1.1 SpringDM 又叫 SpringOSGI )。但是总是遇到种种问题。特别是 Spring 管理下的 Hibernate ,总是无法找到 Hibernate 的配置文件。

 

到了后来,连 Eclipse 也挂了。每次重设 Eclipse 的目标插件集合的时候, Eclipse 都会死掉!

 

终于,我再也无法忍受 SpringDM 的折磨了!

我告诉自己,是时候反思了!

 

终于,我发现了 SpringDM 根本就是一个完全、彻底失败,无用的废物!

所有的问题都是 SpringDM 造成的!

而不是 SpringDM 所说的, OSGI Hibernate 等等其他软件造成的!

 

我终于发现了 OSGI+Spring+Hibernate+... 完美解决方案。

不敢藏私,拿出来给各位共享。

 

 

 

问题的根源 —ClassLoader

OSGI 提供了自己的 ClassLoader 。每一个 OSGI 的插件,都有一个独立的 ClassLoader 被使用。这样就实现了各个插件的独立性。

一个 JVM 上可以运行无数个 OSGI 插件,每一个 OSGI 插件都是独立的,除非发布为 OSGI 服务,或者发布自己 Package

SpringDM 的工作原理是这样子的:(我猜想的)

1 SpringDM 是一个 OSGI 插件。作为一个一般的插件进行部署。

2 SpringDM 一旦启动,就会探查所有其他 Active 状态的插件。

1 MANIFEST.MF 中没有 Spring 的头,如果有,执行该配置。

2 )如果 MANIFEST.MF 中没有 Spring 的头,探查 META-INF/spring/ 目录下有没有 .xml 文件,如果有,它也是一个 SpringDM 项目。

    SpringDM 探查其他插件的资源,应该使用的是这些插件的 BundleContext 对象来实现的。

另外, SpringDM 应该注册了 OSGI 的事件,这样,其他插件启动,关闭,都会通知 SpringDM

 

如果 SpringDM 发现一个插件是 SpringDM 插件。就导入该插件中的 Spring 配置文件,创建 ApplicationContext 。然后,把该 ApplicationContext 作为 OSGI 服务发布。

如果插件中需要显式的 getBean() 方法获得对象,就使用它来获得。

 

SpringDM 的问题

SpringDM DM---- 动态管理,这个设计似乎很巧妙!

刚开始我也被 SpringDM 的这一“强大功能”所折服。

但是,实际上, SpringDM 的所有问题根源,就在这里!

 

SpringDM 实际上是在 SpringDM 这个插件中为所有 Spring 插件创建 ApplicationContext 的!

显然,此时使用的 ClassLoader SpringDM 这个插件的 ClassLoader ,而不是实际的 Spring 项目的插件的 ClassLoader

并且,当前线程中的 ClassLoader ,也是 SpringDM 插件的 ClassLoader

这样,就产生了问题。

如, Hibernate ,它内部使用当前线程中的 ClassLoader 来载入配置文件。

Thread.currentThread().getContextClassLoader()

绝大部分现有 Java 代码都使用这样的代码来获得 ClassLoader 来载入资源,动态创建类。

现在,使用了 SpringDM ,所有这些代码的 ClassLoader 都会出现问题!!!都无法找到资源!!!

这些日子里,我都快被这样的问题给逼疯了!

SpringDM 的谎言

SpringDM 的官方文档,把这些问题的责任推给了 Hibernate OSGI 。它说,由于 OSGI 环境下 ClassLoader 的特殊性,因此使用 Thread.currentThread().getContextClassLoader() 得到的 ClassLoader 都是不正确的。

应该使用 BundleActivator 接口的实现类的 Classloader 来得到正确的 ClassLoader

 

Hibernate 这样的设计时未考虑 OSGI 环境的类库,在 OSGI 环境中出错是不可避免的。

因此, Hibernate 应该修改代码,以适应 OSGI 环境。

 

而我上了 Hibernate 网站,没有看到 HibernateOSGI 项目。

 

我看到有不少朋友在网上写了一些补丁代码,让 Hibernate SpringDM 环境下正确运行。

看来,我们还应该忙着修改数以亿行计的 Java 代码,把获得 ClassLoader 的代码修改掉,否则在 SpringDM OSGI 环境下就会报错!

 

悲惨!!!如果真是如此, OSGI 真的没有存在的价值了!!!

 

事实

但是,事实并非如此!!!

 

如果不使用 SpringDM ,直接在 OSGI 插件中使用 Hibernate ,或者 Thread.currentThread().getContextClassLoader() ,都可以正确载入资源。

因为, OSGI 插件使用的 ClassLoader 是可以直接载入 OSGI 插件的所有资源的。并且,当前线程中的 ClassLoader ,也是当前 OSGI 插件使用的 ClassLoader

 

    因此, SpringDM 在说谎!

    SpringDM 的动态创建 ApplicationContext 的机制是一系列问题的源泉。

 

OSGI+Spring+Hibernate+... 完美解决方案 [ SpringDM]

只要不使用 SpringDM 的动态创建 ApplicationContext 机制,就可以避免上述种种问题。

试一下在 BundleActivator 接口中直接使用 new  ClassPathXmlApplicationContext() 的方式创建 ApplicationContext

结果失败了!

 

通过 Debug ,我发现是 Spring 自己的一些特殊处理方式使它在 OSGI 环境下失败。

 

然后,我研究了 SpringDM1.1 API ,找到了在插件中直接创建 ApplicationContext 的方法。

 

 

需要的环境

Spring jar 包,还有 SpringDM1.1 core和extender jar 包。以及相应的依赖 jar 包。

这些 Jar 包,可以放在插件中。

也可以发布为一个 OSGI 插件,让所有需要创建 ApplicationContext 的插件依赖它。

 

 

源代码

ConfigableClassPathDefaultConfigurationScanner

/**

  *

  */

package net.sf.oxmled.springOsgiNoDM;

 

import org.osgi.framework.Bundle;

import org.springframework.osgi.extender.support.scanning.DefaultConfigurationScanner;

import org.springframework.osgi.io.OsgiBundleResource;

 

/**

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class ConfigableClassPathDefaultConfigurationScanner extends

                   DefaultConfigurationScanner {

        

         private String[] configFiles=null;

        

        

        

         @Override

         public String[] getConfigurations(Bundle bundle) {

                  

                   String[] myConfigFiles=new String[configFiles.length];

                  for(int i=0;i<configFiles.length;i++){

                            String configFile=this.getConfigFiles()[i];

                            if(configFile.indexOf(":")==-1){

                                      if(configFile.indexOf("/")!=0){

                                               configFile="/"+configFile;

                                      }

                                      configFile="classpath:"+configFile;

                                     

                            }

                            myConfigFiles[i]=configFile;

                  }              

                  this.setConfigFiles(myConfigFiles);

                                     return myConfigFiles;

        

                  

         }

 

         public String[] getConfigFiles() {

                   return configFiles;

         }

 

         public void setConfigFiles(String[] configFiles) {

                   this.configFiles = configFiles;

         }

 

         /**

           *

           */

         public ConfigableClassPathDefaultConfigurationScanner() {

                   // TODO Auto-generated constructor stub

         }

         public ConfigableClassPathDefaultConfigurationScanner(String[] configFiles) {

                   // TODO Auto-generated constructor stub

                   this.configFiles=configFiles;

         }

 

         /**

           * @param args

           */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

 

}

 

StandAloneOsgiBundleXmlApplicationContext

/**

  *

  */

package net.sf.oxmled.springOsgiNoDM;

 

import org.springframework.context.ApplicationContext;

import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;

 

/**

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class StandAloneOsgiBundleXmlApplicationContext extends

                  OsgiBundleXmlApplicationContext {

         private String[] customConfigLocations=new String[]{};

        

    @Override

         public String[] getDefaultConfigLocations(){

                  

             return this.getCustomConfigLocations();

                  

         }

         /**

           *

           */

         public StandAloneOsgiBundleXmlApplicationContext() {

                   // TODO Auto-generated constructor stub

         }

 

         /**

           * @param parent

           */

         public StandAloneOsgiBundleXmlApplicationContext(ApplicationContext parent) {

                   super(parent);

                   // TODO Auto-generated constructor stub

         }

 

         /**

           * @param configLocations

           */

         public StandAloneOsgiBundleXmlApplicationContext(String[] configLocations) {

                   super(configLocations);

                   this.setCustomConfigLocations(configLocations);

                   // TODO Auto-generated constructor stub

         }

 

         /**

           * @param configLocations

           * @param parent

           */

         public StandAloneOsgiBundleXmlApplicationContext(String[] configLocations,

                            ApplicationContext parent) {

                   super(configLocations, parent);

                   this.setCustomConfigLocations(configLocations);

                   // TODO Auto-generated constructor stub

         }

 

         /**

           * @param args

           */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

         public String[] getCustomConfigLocations() {

                   return customConfigLocations;

         }

         public void setCustomConfigLocations(String[] customConfigLocations) {

                   this.customConfigLocations = customConfigLocations;

         }

 

}

 

 

StandAloneOsgiApplicationContextCreator

 

/**

  *

  */

package net.sf.oxmled.springOsgiNoDM;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.osgi.framework.Bundle;

import org.osgi.framework.BundleContext;

import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;

import org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext;

import org.springframework.osgi.extender.OsgiApplicationContextCreator;

import org.springframework.osgi.extender.support.ApplicationContextConfiguration;

import org.springframework.osgi.extender.support.scanning.ConfigurationScanner;

import org.springframework.osgi.util.OsgiStringUtils;

import org.springframework.util.ObjectUtils;

 

 

/**

  * @author 沈东良 [email protected]

  *

  */

public class StandAloneOsgiApplicationContextCreator implements

                   OsgiApplicationContextCreator {

         /** logger */

         private static final Log log = LogFactory.getLog(StandAloneOsgiApplicationContextCreator.class);

        

         private boolean publishContextAsService=true;

         private ConfigurationScanner configurationScanner ;

         private String[] configFiles=null;

        

         public String[] getConfigFiles() {

                   return configFiles;

         }

         public void setConfigFiles(String[] configFiles) {

                   this.configFiles = configFiles;

         }

         /**

           *

           */

         public StandAloneOsgiApplicationContextCreator() {

                  

         }

public StandAloneOsgiApplicationContextCreator(String[] configFiles) {

                   this.setConfigFiles(configFiles);

         }

 

 

         /* (non-Javadoc)

           * @see org.springframework.osgi.extender.OsgiApplicationContextCreator#createApplicationContext(org.osgi.framework.BundleContext)

           */

         public DelegatedExecutionOsgiBundleApplicationContext createApplicationContext(

                            BundleContext bundleContext) throws Exception {

                   // TODO Auto-generated method stub

                   Bundle bundle = bundleContext.getBundle();

                   this.configurationScanner=new ConfigableClassPathDefaultConfigurationScanner(configFiles);

                   ApplicationContextConfiguration config = new ApplicationContextConfiguration(bundle, configurationScanner);

                   if (log.isTraceEnabled())

                            log.trace("Created configuration " + config + " for bundle "

                                               + OsgiStringUtils.nullSafeNameAndSymName(bundle));

 

                   // it's not a spring bundle, ignore it

                  

 

                   log.info("Discovered configurations " + ObjectUtils.nullSafeToString(config.getConfigurationLocations())

                                     + " in bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "]");

 

      String[] strs=config.getConfigurationLocations();

                   DelegatedExecutionOsgiBundleApplicationContext sdoac = new OsgiBundleXmlApplicationContext(

                                     config.getConfigurationLocations());

                   sdoac.setBundleContext(bundleContext);

                   sdoac.setPublishContextAsService(this.isPublishContextAsService());

                   sdoac.refresh();

                   return sdoac;

         }

 

         /**

           * @param args

           */

         public static void main(String[] args) {

                   // TODO Auto-generated method stub

 

         }

 

        

 

         public boolean isPublishContextAsService() {

                   return publishContextAsService;

         }

 

         public void setPublishContextAsService(boolean publishContextAsService) {

                   this.publishContextAsService = publishContextAsService;

         }

 

}

 

 

OSGI 插件中创建 ApplicationContext 的演示

 

/**

  *

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class Activator implements BundleActivator {

    private ClassLoader classLoader = this .getClass().getClassLoader();

    private Log log = LogFactory.getLog ( this .getClass());

    private DelegatedExecutionOsgiBundleApplicationContext applicationContext ;

 

    public DelegatedExecutionOsgiBundleApplicationContext getApplicationContext() {

       return applicationContext ;

    }

 

    /*

      * (non-Javadoc)

      *

      * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)

      */

    public void start(BundleContext context) {

       System. out .println( "com.withub.gis.client.service Startup!!" );

 

       String[] configFiles = new String[] { "spring/bean.xml" ,

              "/spring/client.xml" , "classpath:spring/osgi.xml" };

 

       StandAloneOsgiApplicationContextCreator standAloneOsgiApplicationContextCreator = new StandAloneOsgiApplicationContextCreator(

              configFiles);

       try {

           this . applicationContext = standAloneOsgiApplicationContextCreator

                  .createApplicationContext(context);

           applicationContext .refresh();

       } catch (Exception e1) {

           // TODO Auto-generated catch block

           e1.printStackTrace();

       }

 

       System. out .println( "Spring ApplicationContext  Startup!!" );

 

    }

 

    /*

      * (non-Javadoc)

      *

      * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)

      */

    public void stop(BundleContext context) throws Exception {

       System. out .println( "com.withub.gis.client.service Shutdown!!" );

 

    }

 

    public ClassLoader getClassLoader() {

       return classLoader ;

    }

 

    public void setClassLoader(ClassLoader classLoader) {

       this . classLoader = classLoader;

    }

 

}

 

 

解释

1 ,上面的演示代码中,我是用 spring 目录下的文件作为 Spring 的配置文件。

我不建议你在 META-INF/spring/ 目录下编写 spring 配置文件。否则,如果你的插件的部署环境中有 SPringDM 存在,那么就会创建 2 ApplicationContext


2
,现在,你的 Spring 配置文件中如果使用了 Hibernate ,也仍然是正确无误的!

 

 

 

目前,使用的 spring 配置文件不能使用 Ant 风格的通配符。

如果有哪位朋友对 Spring 源代码有研究,希望能加上这个功能。我还有项目要完成,目前只能做到这个程度了。

 

 

    我的代码使用了 SPringDM 提供的 API 。这是一个简单的办法。因为 Spring 内部的源代码太多,太复杂了。我现在的这个办法是最简单的。

 

由于我使用了 SPringDM 的代码,因此,你应该了解 SPringDM 的一些资源搜索的策略。

 

 

OSGi Search Strategy Prefix Explanation

Class Space classpath: Searches the bundle classloader

(the bundle, all imported packages

and required bundles). Forces the

bundle to be resolved. This method

has similar semantics to

Bundle#getResource(String)

Class Space classpath*: Searches the bundle classloader

(the bundle and all imported

packages and required bundles).

Forces the bundle to be resolved.

This method has similar semantics

to Bundle#getResources(String)

JAR File (or JarSpace) osgibundlejar: Searches only the bundle jar.

Provides low-level access without

Bundles and Application Contexts

Spring Dynamic Modules(1.1.0) 8

OSGi Search Strategy Prefix Explanation

requiring the bundle to be

resolved.

Bundle Space osgibundle: Searches the bundle jar and its

attached fragments (if there are

any). Does not create a class loader

or force the bundle to be resolved.

 

如果提供的 SPring 资源没有前缀,我就会为你增加 classpath: 前缀。

经过试验,只有 classpath: 前缀的资源才可以被正确导入。

 

如,下面的配置

<context:property-placeholder location=" spring/jdbc.properties"/>

在一般的 Spring 程序中是可用的。(当然, spring 目录下有 jdbc.properties 这个文件)

 

    但是在 OSGI 环境下,就会报错,告诉我找不到 spring/jdbc.properties 这个资源。

 

    必须加上 classpath: 前缀,

<context:property-placeholder location="classpath:/spring/jdbc.properties"/>

    现在就正确了!

 

 

结论

一直都很喜欢 Spring ,很喜欢 Spring 团队那帮人。

但是,这次我被 SPringDM 给搞惨了!

 

SpringDM 确实是 Spring 团队里某个鸟人拍拍屁股想出来的歪点子。

 

现在 Spring 还在力推 SpringDM 这个先天畸形的怪胎。哎!!!

 

 

 

OSGI 服务的发布

OSGI 下服务的发布,仍然和 SpringDM 下的相同。

如:

 

<service ref="beanToPublish" interface="com.xyz.MessageService"/>

 

<service id="myServiceRegistration" ref="beanToPublish"

interface="com.xyz.MessageService"/>

 

<osgi:service interface="com.xyz.MessageService">

<bean class="SomeClass">

...

</bean>

</osgi:service>

 

<osgi:service ref="beanToBeExported" interface="com.xyz.MessageService"/>

<bean id="beanToBeExported" scope="bundle" class="com.xyz.MessageServiceImpl"/>

   

    完全相同。不需改变。

 

 

我一向不喜欢自制 Spring 标签,如上面的 osgi:service

自制 Spring 标签,虽然看上去挺酷,很专业。但是确实增加了我们用户的学习强度。你必须看文档才能知道该怎样使用这些千奇百怪的标签。

 

我还是喜欢直接使用 <bean> 标签。实际上, SpringDM 中负责注册服务的类是: org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean 类。

因此,我们可以这样注册 OSGI 服务:

 

1

< bean  id = "userBySessionServiceOsgiService" class = "org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean" >

       < property name = "target" >

          < ref bean = "userBySessionService" />

       </ property >

       < property name = "interfaces" >

          < util:list >

             < value > com.withub.gis.server.login.IUserBySessionService </ value >

          </ util:list >

       </ property >

   </ bean >

 

2

    < bean  id = "loginWSSecurityOsgiService" class = "org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean" >

       < property name = "target" >

          < ref bean = "loginWSSecurity" />

       </ property >

       < property name = "interfaces" >

           < util:list >

             < value > com.withub.gis.server.login.ILoginWSSecurity </ value >

          </ util:list >

       </ property >

   </ bean >

 

 

 

  

 

OSGI 服务的导入

在我的解决方案下,再使用 SpringDM 的导入服务的方式就不行了,会报错。这应该是 SpringDM 内部实现造成的。

SpringDM 下的 org.springframework.osgi.service.importer.support. OsgiServiceProxyFactoryBean 导入一个服务。

OsgiServiceCollectionProxyFactoryBean 导入多个服务。

 

我们需要编写自己的导入 OSGI 服务的类

 

   

导入 OSGI Service 的类

    这是自己独立于 SpringDM OSGI 服务导入类。

 

/**

  *

  */

package com.withub.gis.osgi.util.springSupport.importService;

 

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.Set;

 

import org.osgi.framework.BundleContext;

import org.osgi.framework.Constants;

import org.osgi.framework.ServiceReference;

import org.osgi.util.tracker.ServiceTracker;

 

/**

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class ImportOsgiServiceFactory implements IImportOsgiServiceFactory {

       /* (non-Javadoc)

         * @see com.withub.gis.osgi.util.springSupport.importService.IImportOsgiServiceFactory#getService()

         */

       public Object getService(){

              Object[] services=this.getServices();

              if(services.length>=1){

                     return services[0];

              }else{

                     return null;

              }            

       }

      

       public static int DefaultRetryTimes=1;

       public static int DefaultWaitTime=1000;

      

       private int retryTimes=DefaultRetryTimes;

       private int waitTime=DefaultWaitTime;

       public int getRetryTimes() {

              return retryTimes;

       }

 

       public void setRetryTimes(int retryTimes) {

              this.retryTimes = retryTimes;

       }

       public int getWaitTime() {

              return waitTime;

       }

 

 

       public void setWaitTime(int waitTime) {

              this.waitTime = waitTime;

       }

 

 

       /* (non-Javadoc)

         * @see com.withub.gis.osgi.util.springSupport.importService.IImportOsgiServiceFactory#getServices()

         */

       public Object[] getServices(){

              List<ServiceReference> all=new ArrayList<ServiceReference>();

           String interface0=this.interfaces[0];

                     ServiceTracker serviceTracker=new ServiceTracker(this.bundleContext,interface0 , null);

                     serviceTracker.open();

                    

                     ServiceReference[] serviceReferences=serviceTracker.getServiceReferences();

                    

                     if(serviceReferences==null && this.retryTimes>0){

                            this.retryTimes--;

                            try {

                                   Thread.sleep(this.getWaitTime());

                            } catch (InterruptedException e) {

                                   // TODO Auto-generated catch block

                                   e.printStackTrace();

                            }

                            this.getServices();

                     }

                      

                     for(ServiceReference serviceReference:serviceReferences){

                            String[] objectClasses=(String[]) serviceReference.getProperty(Constants.OBJECTCLASS);

                            if(equal(this.interfaces,objectClasses)){

                                   all.add(serviceReference);

                            }

                     }

                    

                    

                     if(this.map!=null){

                            for(ServiceReference serviceReference:all){

                                   boolean upToGrade=true;

                                     Set<Map.Entry<String,Object>> entrySet=map.entrySet();

                                     for(Map.Entry<String,Object> entry:entrySet){

                                            if(serviceReference.getProperty(entry.getKey())==null){

                                                   if(entry.getValue()!=null){

                                                          upToGrade=false;

                                                          break;

                                                   }

                                            }else{

                                           

                                              if(!serviceReference.getProperty(entry.getKey()).equals(entry.getValue())){

                                                     upToGrade=false;

                                                     break;

                                              }

                                            }

                                           

                                     }

                                   if(upToGrade==false){

                                          all.remove(serviceReference);

                                         

                                   }

                            }

                           

                     }

                    

                     List allServices=new ArrayList();

                for(ServiceReference serviceReference:all){

                       allServices.add(this.bundleContext.getService(serviceReference));

                      

                } 

      

              return allServices.toArray();

       }

      

      

       private boolean equal(String[] interfaces, String[] objectClasses) {

              // TODO Auto-generated method stub

             

              for(String interface0:interfaces){

                     boolean yes=false;

                     for(String clazz:objectClasses){

                            if(interface0.equals(clazz)){

                                   yes=true;

                                   break;

                            }

                           

                     }

                     if(!yes){

                            return false;

                     }

              }

             

             

             

             

              return true;

             

             

             

       }

 

       private BundleContext bundleContext;

       private Map<String,Object> map;

 

      

       /**

         *

         */

       private String[] interfaces;

      

 

       /**

         *

         */

       public ImportOsgiServiceFactory() {

              // TODO Auto-generated constructor stub

       }

       public ImportOsgiServiceFactory(BundleContext bundleContext,String[] interfaces,Map<String,Object> map) {

              // TODO Auto-generated constructor stub

              this.bundleContext=bundleContext;

              this.interfaces=interfaces;

              this.map=map;

       }

 

       /**

         * @param args

         */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

 

       }

 

       public BundleContext getBundleContext() {

              return bundleContext;

       }

 

       public void setBundleContext(BundleContext bundleContext) {

              this.bundleContext = bundleContext;

       }

 

       public String[] getInterfaces() {

              return interfaces;

       }

 

       public void setInterfaces(String[] interfaces) {

              this.interfaces = interfaces;

       }

 

       public Map getMap() {

              return map;

       }

 

       public void setMap(Map map) {

              this.map = map;

       }

 

      

 

}

 

 

使用的方法

下面给出一个使用的例子

 

1 ,使用 Activator

package com.withub.gis.client.app.plugin;

 

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.eclipse.ui.plugin.AbstractUIPlugin;

import org.osgi.framework.BundleContext;

import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;

 

import com.withub.gis.client.app.context.GISApplicationContext;

import com.withub.gis.osgi.util.springSupport.StandAloneOsgiApplicationContextCreator;

import com.withub.gis.server.startDependency.WithubGISServicStartDependency;

 

/**

  * The activator class controls the plug-in life cycle

沈东良

  */

public class Activator extends AbstractUIPlugin {

 

       // The plug-in ID

       public static final String PLUGIN_ID = "com.withub.gis.client.app";

 

       // The shared instance

       private static Activator plugin;

         private DelegatedExecutionOsgiBundleApplicationContext applicationContext;

 

              public DelegatedExecutionOsgiBundleApplicationContext getApplicationContext() {

                     return applicationContext;

              }

       /**

         * The constructor

         */

       public Activator() {

       }

 

       /*

         * (non-Javadoc)

         * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)

         */

       public void start(BundleContext context) throws Exception {

              super.start(context);

              plugin = this;

              /**

                * WithubGISServicStartDependency 这个类是依赖的插件 Service 发布出来的一个类。

                * 如果使用这个类,就意味着 Service 插件必须被启动。

                * 这就使 Service 插件先发布我们需要的 OSGI Service 。保证了使用。

                */

              WithubGISServicStartDependency withubGISServicStartDependency=new WithubGISServicStartDependency();

              System.out.println(Activator.PLUGIN_ID+" Startup!!");

              GISApplicationContext.getInstance().setBundleContext(context);

             

              String[] configFiles = new String[] { "spring/bean.xml", "classpath:spring/osgi.xml" };

 

              StandAloneOsgiApplicationContextCreator standAloneOsgiApplicationContextCreator = new StandAloneOsgiApplicationContextCreator(

                            configFiles);

              try {

                     this.applicationContext = standAloneOsgiApplicationContextCreator.createApplicationContext(context);

                     //applicationContext.refresh();

              } catch (Exception e1) {

                     // TODO Auto-generated catch block

                     e1.printStackTrace();

              }

 

              System.out.println("Spring ApplicationContext  Startup!!");

       }

 

       /*

         * (non-Javadoc)

         * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)

         */

       public void stop(BundleContext context) throws Exception {

              plugin = null;

              super.stop(context);

              System.out.println(Activator.PLUGIN_ID+"  Shutdown!!");

       }

 

       /**

         * Returns the shared instance

         *

         * @return the shared instance

         */

       public static Activator getDefault() {

              return plugin;

       }

       private ClassLoader classLoader = this.getClass().getClassLoader();

       private Log log = LogFactory.getLog(this.getClass());

      

      

 

 

       public ClassLoader getClassLoader() {

              return classLoader;

       }

 

       public void setClassLoader(ClassLoader classLoader) {

              this.classLoader = classLoader;

       }

 

}

 

 

2 ,无关的支持类

/**

  *

  */

package com.withub.gis.client.app.context;

 

import java.util.HashMap;

import java.util.Map;

 

import org.osgi.framework.BundleContext;

 

 

 

/**

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class GISApplicationContext {

 

       /**

         *

         */

       public GISApplicationContext() {

              // TODO Auto-generated constructor stub

       }

 

       /**

         * @param args

         */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

 

       }

       private static GISApplicationContext instance=null;

       private static Object lockInstance=new Object();

    private String  username="";

    private String  password="";

    private String sessionId;

   

   

    private BundleContext  bundleContext;

    private ClassLoader classLoader;

 

    private Map<String,Object> map=new HashMap<String,Object>();

    /**

     *

     * @return

     */

    public static GISApplicationContext getInstance(){

         if(GISApplicationContext.instance==null){

                synchronized(GISApplicationContext.lockInstance){

                       if(GISApplicationContext.instance==null){

                              GISApplicationContext.instance=new GISApplicationContext();

                       }

                      

                }

               

         }

          return GISApplicationContext.instance;

    }

   public String getUsername() {

              return username;

       }

 

       public void setUsername(String username) {

              this.username = username;

       }

 

       public String getPassword() {

              return password;

       }

 

       public void setPassword(String password) {

              this.password = password;

       }

 

       public String getSessionId() {

              return sessionId;

       }

 

       public void setSessionId(String sessionId) {

              this.sessionId = sessionId;

       }

 

       public Map<String, Object> getMap() {

              return map;

       }

 

       public BundleContext getBundleContext() {

              return bundleContext;

       }

 

       public void setBundleContext(BundleContext bundleContext) {

              this.bundleContext = bundleContext;

       }

 

       public ClassLoader getClassLoader() {

              return classLoader;

       }

 

       public void setClassLoader(ClassLoader classLoader) {

              this.classLoader = classLoader;

       }

 

      

}

 

 

3 IApplication 的实现类,这是 RCP 程序的入口点。应该在其他 Activator 启动后执行。这里是 Spring 的消费者。

/**

  *

  */

package com.withub.gis.client.app.eclipse.application;

 

 

import java.awt.Rectangle;

import java.rmi.server.UID;

import java.util.Iterator;

import java.util.Set;

 

import javax.units.SI;

 

import net.refractions.udig.internal.ui.JaiErrorDialog;

import net.refractions.udig.internal.ui.UDIGWorkbenchAdvisor;

import net.refractions.udig.internal.ui.UiPlugin;

 

import org.eclipse.core.runtime.Platform;

import org.eclipse.equinox.app.IApplication;

import org.eclipse.equinox.app.IApplicationContext;

import org.eclipse.jface.dialogs.MessageDialog;

import org.eclipse.swt.widgets.Display;

import org.eclipse.ui.IWorkbench;

import org.eclipse.ui.PlatformUI;

import org.eclipse.ui.application.WorkbenchAdvisor;

import org.geotools.referencing.FactoryFinder;

import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;

 

import com.withub.gis.client.app.context.GISApplicationContext;

import com.withub.gis.client.app.plugin.Activator;

import com.withub.gis.osgi.util.springSupport.StandAloneOsgiApplicationContextCreator;

import com.withub.gis.server.login.ILoginWSSecurity;

import com.withub.gis.ui.dialog.LoginDialog;

 

/**

  * @author 沈东良 Edward Shen [email protected]

  *

  */

public class GISApplication implements IApplication {

    private ILoginWSSecurity loginWSSecurity;

       public ILoginWSSecurity getLoginWSSecurity() {

              return loginWSSecurity;

       }

 

       public void setLoginWSSecurity(ILoginWSSecurity loginWSSecurity) {

              this.loginWSSecurity = loginWSSecurity;

       }

 

       /**

         *

         */

       public GISApplication() {

              // TODO Auto-generated constructor stub

       }

        

    protected WorkbenchAdvisor createWorkbenchAdvisor() {

            return new UDIGWorkbenchAdvisor();

          /*

        return new UDIGWorkbenchAdvisor() {

            @Override

            public String getInitialWindowPerspectiveId() {

                return "net.refractions.udig.tutorials.customapp.perspective";

            }

        };

        */

    }

   

   

    /**

     * Override to perform your own security check.

     *

     * @return true to indicate a successful login

     */

    public  boolean checkLogin() {

        LoginDialog loginDialog = new LoginDialog(

                " 登陆 WithubGIS 系统 ",

                " 请输入用户名和密码: " );

       

        boolean login= false;

      

        while( !login ){

             loginDialog.clearUserNamePassword();

            int result = loginDialog.open();                   

             if( result == MessageDialog.OK){

                try {

                   Activator activator=Activator.getDefault();

                   DelegatedExecutionOsgiBundleApplicationContext applicationContext=activator.getApplicationContext();

                  

                     ILoginWSSecurity loginWSSecurity=       (ILoginWSSecurity)applicationContext.getBean("loginWSSecurityOsgiReference");

                   //loginWSSecurity.login(loginDialog.getUser(), loginDialog.getPassword());

                   //login =loginService.checkLogin( loginDialog.getUser(), loginDialog.getPassword() );

                   String sessionId=loginWSSecurity.login(loginDialog.getUser(), loginDialog.getPassword());

                     GISApplicationContext.getInstance().setSessionId(sessionId);

                    login=true;

                                  

                } catch (Throwable connectionProblem) {

                   System.err.println(connectionProblem.getMessage());

                   MessageDialog.openError(null, " 登录错误 ", " 用户名或者密码错误,请重新登陆! ");

                   // MessageDialog.openInformation(null, " 不能连接服务器,请检查网络 ", connectionProblem.toString() );

                   

                   // return false; // probably should prompt user here?

                }

            }

            else {

                return false; // user cancelled

            }

        }

       // WithubGISApplicationContext.getInstance().getMap().put("loginService", loginService);

      

        return true;

    }

 

 

    /**

     * Starts GIS application with the given context and returns a result. This method must not exit

     * until the application is finished and is ready to exit. The content of the context is

     * unchecked and should conform to the expectations of the application being invoked.

     * <p>

     * Applications can return any object they like. If an <code>Integer</code> is returned it is

     * treated as the program exit code if Eclipse is exiting.

     * <p>

     * Note: This method is called by the platform; it is not intended to be called directly by

     * clients.

     * </p>

     *

     * @return the return value of the application

     * @see #EXIT_OK

     * @see #EXIT_RESTART

     * @see #EXIT_RELAUNCH

     * @param context the application context to pass to the application

     * @exception Exception if there is a problem running this application.

     */

 

    public Object start( IApplicationContext context ) throws Exception {

         

         

         

        WorkbenchAdvisor workbenchAdvisor = createWorkbenchAdvisor();

        Display display = PlatformUI.createDisplay();

 

        boolean isInitalized = init();

        if (!isInitalized) {

            return EXIT_OK;

        }

        int returnCode = EXIT_OK;

        try {

            returnCode = PlatformUI.createAndRunWorkbench(display, workbenchAdvisor);

        } catch (Throwable t) {

            UiPlugin.log("Messages.UDIGApplication_error", t);

        } finally {

            Platform.endSplash();

            display.dispose();

        }

        if (returnCode == PlatformUI.RETURN_RESTART) {

            return EXIT_RESTART;

        }

        return EXIT_OK;

    }

 

  

 

    /**

     * Forces this running application to exit. This method should wait until the running

     * application is ready to exit. The {@link #start(IApplicationContext)} should already have

     * exited or should exit very soon after this method exits

     * <p>

     * This method is only called to force an application to exit. This method will not be called if

     * an application exits normally from the {@link #start(IApplicationContext)} method.

     * <p>

     * Note: This method is called by the platform; it is not intended to be called directly by

     * clients.

     * </p>

     */

    public void stop() {

        final IWorkbench workbench = PlatformUI.getWorkbench();

        if (workbench == null){

            return;

        }

        final Display display = workbench.getDisplay();

        display.syncExec(new Runnable(){

            public void run() {

                if (!display.isDisposed()){

                    workbench.close();

                }

            }

        });

    };

 

    /**

     * Called before the workbench is created.

     * <p>

     * The following checks are performed:

     * <ul>

     * <li>checkForJAI(): optional - Dig will work with reduced functionality if JAI is not

     * available

     * <li>checkForGDI(): required - uDig will not function on WIN_32 if GDI is not present

     * </ul>

     * This method also loads some commonly used objects; subclasses may override this method (say

     * to ask the user to login)

     *

     * @return <code>true </code> on successful startup; false to exit the application with an

     *         error.

     */

    protected boolean init() {

        checkForJAI();

        boolean required = checkForGDI();

 

        if (!required) {

            // we could not meet our requirements; please exit!

            return false;

        }

 

        loadCommonlyUsedObject();

 

        boolean login = checkLogin();

        if (!login) {

            return false;

        }

        return true;

    }

 

    /**

     * Forces the class loader to load several troublesome classes; mostly focused on FactorySPI

     * plugins used by the GeoTools library.

     */

    private void loadCommonlyUsedObject() {

        // potential fix for win32 (occasionally blocks in Drawing.feature(Point/etc) during first

        // render)

        new UID(); // seed the random number generator

 

        load(FactoryFinder.getCoordinateOperationAuthorityFactories());

        load(FactoryFinder.getCoordinateOperationAuthorityFactories());

        load(FactoryFinder.getCRSFactories());

        load(FactoryFinder.getCSFactories());

        load(FactoryFinder.getDatumAuthorityFactories());

        load(FactoryFinder.getDatumFactories());

        load(FactoryFinder.getMathTransformFactories());

        @SuppressWarnings("unused")

        Object o = SI.BIT;

        o = SI.GRAM;

        o = SI.KILOGRAM;

        o = SI.METER;

         o = SI.RADIAN;

        o = SI.SECOND;

        o = SI.STERADIAN;

    }

 

    @SuppressWarnings("unchecked")

    private void load( Set coordinateOperationAuthorityFactories ) {

        for( Iterator iter = coordinateOperationAuthorityFactories.iterator(); iter.hasNext(); ) {

            iter.next();

        }

    }

 

    /**

     * Ensures that GDI is available for the windows inclined. GDI is used by SWT to perform matrix

     * operations; uDIG cannot function without GDI on windows.

     *

     * @return false if GDI is needed and not found.

     */

    public  boolean checkForGDI() {

        if (Platform.getOS().equals(Platform.OS_WIN32)) {

            // test to make sure that GDI+ is installed

               org.eclipse.swt.graphics.Image image = null;

                org.eclipse.swt.graphics.Path path = null;

            try {

                image = new  org.eclipse.swt.graphics.Image(Display.getCurrent(), 10, 10);

                path = net.refractions.udig.ui.graphics.SWTGraphics.convertToPath(new Rectangle(0, 0, 8, 8), Display.getCurrent());

            } catch (Exception e) {

                MessageDialog

                        .openError(

                                Display.getCurrent().getActiveShell(),

                                net.refractions.udig.ui.internal.Messages.UDIGApplication_title,

                                net.refractions.udig.ui.internal.Messages.UDIGApplication_error1

                                        + net.refractions.udig.ui.internal.Messages.UDIGApplication_error2

                                         + "http://www.microsoft.com/downloads/details.aspx?FamilyID=6A63AB9C-DF12-4D41-933C-BE590FEAA05A&displaylang=en"); //$NON-NLS-1$

                return false;

            } finally {

                if (image != null)

                     image.dispose();

                if (path != null)

                    path.dispose();

            }

        }

        return true;

    }

 

    /**

     * Ensures Java Advanced Imaging is installed into the JRE being used.

     * <p>

      * If JAI is not available a dialog will be displayed and false is returned. JAI is not required

     * for everything; currently rasters will not function without JAI; but many simple vector

     * formats will.

     * </p>

     *

     * @return true if JAI is available.

     */

    public  boolean checkForJAI() {

        try {

            Class.forName("javax.media.jai.operator.OrDescriptor"); //$NON-NLS-1$

            return true;

        } catch (Throwable th) {

            JaiErrorDialog.display();

             return false;

        }

    }

 

       /**

         * @param args

         */

       public static void main(String[] args) {

              // TODO Auto-generated method stub

 

       }

 

}

 

 

 

4 Spring 配置

<? xml version = "1.0" encoding = "UTF-8" ?>

< beans xmlns = "http://www.springframework.org/schema/beans"

    xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop = "http://www.springframework.org/schema/aop"

    xmlns:context = "http://www.springframework.org/schema/context"

    xmlns:flow = "http://www.springframework.org/schema/webflow-config"

    xmlns:jee = "http://www.springframework.org/schema/jee"

    xmlns:jms = "http://www.springframework.org/schema/jms"

    xmlns:lang = "http://www.springframework.org/schema/lang"

    xmlns:osgi = "http://www.springframework.org/schema/osgi"

    xmlns:osgi-compendium = "http://www.springframework.org/schema/osgi-compendium"

    xmlns:p = "http://www.springframework.org/schema/p"

    xmlns:tx = "http://www.springframework.org/schema/tx"

    xmlns:util = "http://www.springframework.org/schema/util"

    xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

       http://www.springframework.org/schema/webflow-config http://www.springframework.org/schema/webflow-config/spring-webflow-config-1.0.xsd

       http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd

       http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd

       http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd

       http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd

       http://www.springframework.org/schema/osgi-compendium http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd

       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd" >

 

    < bean id = "gISApplicationContext" class = "com.withub.gis.client.app.context.GISApplicationContext"  factory-method = "getInstance" >

 

  </ bean >

  < bean id = "bundleContext" factory-bean = "gISApplicationContext"  factory-method = "getBundleContext" ></ bean >

 

  < bean id = "userBySessionServiceOsgiReference" factory-bean = "userBySessionServiceOsgiReferenceFactory"  factory-method = "getService" >      

   </ bean >

   < bean id = "userBySessionServiceOsgiReferenceFactory" class = "com.withub.gis.osgi.util.springSupport.importService.ImportOsgiServiceFactory" >

    

      < property name = "bundleContext" >

         < ref  bean = "bundleContext" />

      </ property >

      

      < property name = "interfaces" >

         < list >

            < value > com.withub.gis.server.login.IUserBySessionService </ value >

         </ list >      

      </ property >

     

    

   </ bean >

   

  

   < bean id = "loginWSSecurityOsgiReference" factory-bean = "loginWSSecurityOsgiReferenceFactory"  factory-method = "getService" >      

   </ bean >

   < bean id = "loginWSSecurityOsgiReferenceFactory" class = "com.withub.gis.osgi.util.springSupport.importService.ImportOsgiServiceFactory" >

      

      < property name = "bundleContext" >

          < ref  bean = "bundleContext" />

      </ property >

      

      < property name = "interfaces" >

          < list >

            < value > com.withub.gis.server.login.ILoginWSSecurity </ value >

         </ list >      

      </ property >

     

      < property name = "retryTimes" >

          < value > 5 </ value >

      </ property >

      < property name = "waitTime" >

         < value > 1000 </ value >

      </ property >

   </ bean >

 

</ beans >

 

 

代码说明

 

1 ,已内建 bundleContext 这个 Bean

 

OsgiBundleXmlApplicationContext 创建 Application 时,会创建一个 Bean ,名字叫 bundleContext ,类型是 BundleContext

由于我们的方案中,内部和 SpringDM 一样都使用了 OsgiBundleXmlApplicationContext ,因此,我们的 Spring 内部也有 bundleContext 这个 Bean

因此, Spring 配置中

< bean id = "gISApplicationContext" class = "com.withub.gis.client.app.context.GISApplicationContext"  factory-method = "getInstance" >

 

  </ bean >

  < bean id = "bundleContext" factory-bean = "gISApplicationContext"  factory-method = "getBundleContext" ></ bean >

   2 Bean 可以不用。

   Activator 类的 start() 方法中

GISApplicationContext.getInstance().setBundleContext(context);

             

    这行代码也不需要。

 

 

2 ,外部对象插入 Spring 的技巧

 

这里介绍一个 Spring 的小技巧。

Spring 对于静态类的静态工厂方法,这个类单例在外部也可以使用!是同一的。

这是很好的外部代码插入 Spring 管理的代码的途径!

 

我的代码中使用 GISApplicationContext 类的单例 拿到 BundleContext 的实例,插进 Spring 中就是使用了这个技巧。

 

 

3 ,解决服务注册和导入先后次序的问题的独创方法

 

还有一个问题, B 插件发布服务, A 插件导入服务。就像我的例子那样, A 插件依赖于 B 插件。

但是很有可能 B 插件发布服务的时间远远慢于 A 插件导入服务的时间。

这就会出现问题。特别是我这里的例子, A 插件还是 RCP 程序的入口, IApplicaiton 接口的实现类所在的插件。

这意味着 A 插件会很快启动,一般是比 B 插件先启动的。

 

可以设置 OSGI Start Level ,保证提供服务的插件先启动。

 

 

但我这里推荐的是一种自创的方法,仅在 Eclipse 下测试成功,不保证在所有 OSGI 环境下都能成功。

有使用其他 OSGI 环境经验的朋友可以帮忙测试一下这招是否有效。测好了,把结果告诉我好吗!谢谢!

 

 

首先,在提供 OSGI 服务的插件中发布一个 Package

然后,在导入 OSGI 服务的插件的 Activator 类的 start() 方法中 new 一个提供服务插件的发布 Package 中的类。

然后再创建 ApplicationContext

这可以保证在创建 ApplicationContext 之前,提供 OSGI 服务的插件已启动。

 

    另外, ImportOsgiServiceFactory 类也提供了重试的机制,可以让用户配置重试的次数和每次间隔的时间,以保证 OSGI 服务的引用正确!

 

 

 

 

你可能感兴趣的:(Hibernate)