getInitialContext
最近3个星期一直在研究OSGi框架与现有项目的结合,由于该方案可能进度问题,不能够用得上,但研究过程艰辛,该经验需要记录下来。研究方向如下:
1. 与现有的SOA接口集成,包括EJB, WebServie的调用
2. 与J2EE容器集成, 包括开发环境Tomcat 6 和 生产环境 IBM Websphere 6.0
3. 与数据库接口的集成,包括JDBC 和 JNDI 形式调用
开发OSGI与传统的开发中不同,首先开发的环境不同,其次是Classloader。
- 1.传统开发中,项目都集中在同一个Java Project/Dynamic Web Project,而且所有类都在同一个classloader中加载,即Web容器或者Application容器
- 2.OSGi开发中,每个bundle都有各自的classloader, 参考<<OSGi 实战.pdf>>。由于classloader的问题,常常会引起ClassNotFoundException,尤其是当你把OSGi嵌入到Web容器的时候,当bundle中的类需要调用容器中的资源的时候,如用JNDI查找数据源,往往会遇到ClassNotFoundException和ClassCastException问题。
请看以下的代码:
以下代码是摘自org.springframework.jndi.JndiTemplate。
public class JndiTemplate
protected Context createInitialContext() throws NamingException { Hashtable icEnv = null; Properties env = getEnvironment(); if (env != null) { icEnv = new Hashtable(env.size()); CollectionUtils.mergePropertiesIntoMap(env, icEnv); } return new InitialContext(icEnv); }
DAO代码
public class UserDao extends SqlMapClientDaoSupport implements IUserDao
以下是bundle中Spring-DM的配置文件
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${jndi.url}"/> </bean> <!-- Transaction manager for a single JDBC DataSource --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- SqlMap setup for iBATIS Database Layer --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocations" value="META-INF/sql-map-config*.xml"/> <property name="dataSource" ref="dataSource"/> </bean> <!-- ========================= DAO DEFINITIONS: IBATIS IMPLEMENTATIONS ========================= --> <bean id="userDao" class="org.amway.osgi.internal.dao.UserDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> <!-- ========================= OSGI SERVICE DEFINITIONS ========================= --> <osgi:service ref="userDao" interface="com.amway.osgi.dao.test.IUserDao" />
这里使用Spring-DM注册DAO服务,在spring-dm启动时候会扫描BundleContext中所有bundle的META-INF/spring/*.xml, 并且创建OsgiApplicationContext。
注意JndiTemplate中createInitialContext方法中new InitialContext(icEnv)。追踪源码,会看到Context是通过ContextFactory生成的,而这个ContextFactory由TCCL(Thread Context Class Loader)加载的。
public static Context getInitialContext(Hashtable<?,?> env) throws NamingException { InitialContextFactory factory; InitialContextFactoryBuilder builder = getInitialContextFactoryBuilder(); if (builder == null) { // No factory installed, use property // Get initial context factory class name String className = env != null ? (String)env.get(Context.INITIAL_CONTEXT_FACTORY) : null; if (className == null) { NoInitialContextException ne = new NoInitialContextException( "Need to specify class name in environment or system " + "property, or as an applet parameter, or in an " + "application resource file: " + Context.INITIAL_CONTEXT_FACTORY); throw ne; } try { factory = (InitialContextFactory) helper.loadClass(className).newInstance(); } catch(Exception e) { NoInitialContextException ne = new NoInitialContextException( "Cannot instantiate class: " + className); ne.setRootCause(e); throw ne; } } else { factory = builder.createInitialContextFactory(env); } return factory.getInitialContext(env); }
final class VersionHelper12 extends VersionHelper { ClassLoader getContextClassLoader() { return (ClassLoader) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return Thread.currentThread().getContextClassLoader(); } } ); }
ClassLoader oldTCCL = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(newTCCL); ... } catch (Throwable e) { ... } finally { Thread.currentThread().setContextClassLoader(oldTCCL); }bi
@Override protected Context createInitialContext() throws NamingException { InitialContext context = null; // 1 Get current class loader(osgi bundle loader) Thread current = Thread.currentThread(); ClassLoader old = current.getContextClassLoader(); try { // 2 Get BridgeServlet class loader(web container class loader) ClassLoader newCl = BridgeServlet.class.getClassLoader(); // 3 set current class loader to web ctx class loader current.setContextClassLoader(newCl); context = new InitialContext(); } catch (NamingException e) { e.printStackTrace(); } finally { current.setContextClassLoader(old); } return context; }
public Object lookup(final String name) throws NamingException { if (logger.isDebugEnabled()) { logger.debug("Looking up JNDI object with name [" + name + "]"); } return JndiUtil.execute(new JndiCallback<Object>() { public Object doInContext(Context ctx) throws NamingException { Object located = ctx.lookup(name); if (located == null) { throw new NameNotFoundException( "JNDI object with [" + name + "] not found: JNDI implementation returned null"); } return located; } }); }
public static <T> T execute(Hashtable environment,JndiCallback<T> contextCallback) throws NamingException { T result = null; InitialContext context = null; // 1 Get current class loader(osgi bundle loader) Thread current = Thread.currentThread(); ClassLoader old = current.getContextClassLoader(); try { // 2 Get BridgeServlet class loader(web container class loader) ClassLoader newCl = BridgeServlet.class.getClassLoader(); // 3 set current class loader to web ctx class loader current.setContextClassLoader(newCl); context = new InitialContext(environment ); result = contextCallback.doInContext(context); } catch (NamingException e) { e.printStackTrace(); } finally { current.setContextClassLoader(old); } return result; }
org.eclipse.equinox.servletbridge.extensionbundle的MANIFEST.MF文件 Fragment-Host: system.bundle; extension:=framework Export-Package: javax.servlet;version="2.5",javax.servlet.htt p;version ="2.5",javax.servlet.resources;version="2.5",org.eclipse.eq uinox.servletbridge;version="1.1"====================================================================================
ClassLoader original = Thread.currentThread().getContextClassLoader(); try { System.setProperty("osgi.framework.useSystemProperties", "false"); //$NON-NLS-1$ //$NON-NLS-2$ URL[] frameworkURLs = findFrameworkURLs(initialPropertyMap); frameworkClassLoader = new ChildFirstURLClassLoader(frameworkURLs, this.getClass().getClassLoader()); Class clazz = frameworkClassLoader.loadClass(STARTER); Method setInitialProperties = clazz.getMethod("setInitialProperties", new Class[] {Map.class}); //$NON-NLS-1$ setInitialProperties.invoke(null, new Object[] {initialPropertyMap}); registerRestartHandler(clazz); Method runMethod = clazz.getMethod("startup", new Class[] {String[].class, Runnable.class}); //$NON-NLS-1$ runMethod.invoke(null, new Object[] {args, null}); Method getSystemBundleContext = clazz.getMethod("getSystemBundleContext", new Class[0]); //$NON-NLS-1$ Object bundleContext = getSystemBundleContext.invoke(null, new Object[0] ); frameworkContextClassLoader = Thread.currentThread().getContextClassLoader(); } catch (InvocationTargetException ite) { Throwable t = ite.getTargetException(); if (t == null) t = ite; context.log("Error while starting Framework", t); //$NON-NLS-1$ throw new RuntimeException(t.getMessage()); } catch (Exception e) { context.log("Error while starting Framework", e); //$NON-NLS-1$ throw new RuntimeException(e.getMessage()); } finally { Thread.currentThread().setContextClassLoader(original); }这里受到了启发。 该处代码是容器里面启动Equinox OSGi的关键地方,该处ChildFirstURLClassLoader是在配置文件config.ini中的osgi.framework中指定的equinox的jar的class loader。之后通过反射去调用该class loader加载的 framework的startup 方法。注意,这是2个不同class loader交互的桥梁.
public interface CrossClassloaderObjectWrapper{ public void setDelegate(Object object); public void setOutsideClassloader(ClassLoader classloader); public void setInsideClassloader(ClassLoader classloader); }
public class DataSourceWrapper implements CrossClassloaderObjectWrapper, DataSource { private Object dataSource; private ClassLoader outsideClassloader; private Class clazz; public DataSourceWrapper() { } public void setDelegate(Object t) { dataSource = t; } private Class getOutsideClass() { if (clazz == null) { try { if (this.outsideClassloader != null) { clazz = (Class<DataSource>) this.outsideClassloader .loadClass("javax.sql.DataSource"); } } catch (ClassNotFoundException e) { e.printStackTrace(); } } return clazz; } public Connection getConnection() throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "getConnection"); Connection connect = (Connection) ReflectionUtils.invokeMethod(m, dataSource); return connect; } public Connection getConnection(String username, String password) throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "getConnection", String.class, String.class); Connection connect = (Connection) ReflectionUtils.invokeMethod(m, dataSource, username, password); return connect; } public PrintWriter getLogWriter() throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "getLogWriter"); return (PrintWriter) ReflectionUtils.invokeMethod(m, dataSource); } public void setLogWriter(PrintWriter out) throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "setLogWriter", PrintWriter.class); ReflectionUtils.invokeMethod(m, dataSource, out); } public void setLoginTimeout(int seconds) throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "setLoginTimeout", Integer.class); ReflectionUtils.invokeMethod(m, dataSource, seconds); } public int getLoginTimeout() throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "getLoginTimeout"); return (Integer) ReflectionUtils.invokeMethod(m, dataSource); } public <T> T unwrap(Class<T> iface) throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "unwrap", Class.class); return (T) ReflectionUtils.invokeMethod(m, dataSource, iface); } public boolean isWrapperFor(Class<?> iface) throws SQLException { Method m = ReflectionUtils.findMethod(getOutsideClass(), "isWrapperFor", Class.class); return (Boolean) ReflectionUtils.invokeMethod(m, dataSource, iface); } public void setOutsideClassloader(ClassLoader classloader) { outsideClassloader = classloader; } public void setInsideClassloader(ClassLoader classloader) { // TODO Auto-generated method stub } }
public class ServletJndiTemplate extends JndiTemplate { @Override public Object lookup(final String name) throws NamingException { if (logger.isDebugEnabled()) { logger.debug("Looking up JNDI object with name [" + name + "]"); } final DataSourceWrapper wrapper = new DataSourceWrapper(); Object object = JndiUtil.execute(new JndiCallback<Object>() { public Object doInContext(Context ctx) throws NamingException { Object located = ctx.lookup(name); if (located == null) { throw new NameNotFoundException( "JNDI object with [" + name + "] not found: JNDI implementation returned null"); } wrapper.setDelegate(located); ClassLoader cl = Thread.currentThread().getContextClassLoader(); System.out.println("JndiUtil.execute: current class loader:" + cl); wrapper.setOutsideClassloader(cl); return located; } }); return wrapper; } }
<?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:context="http://www.springframework.org/schema/context" xmlns:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd"> <!-- ========================= RESOURCE DEFINITIONS ========================= --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>META-INF/datasource.properties</value> </list> </property> </bean> <!-- ========================= Datasource: jdbc ========================= --> <!-- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </bean> --> <bean id="osgiJndiTemplate" class="org.amway.osgi.jndi.internal.ServletJndiTemplate"> </bean> <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiTemplate" ref="osgiJndiTemplate"/> <property name="jndiName" value="${jndi.url}"/> </bean> <!-- <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName" value="${jndi.url}"/> </bean> --> <!-- Transaction manager for a single JDBC DataSource --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- SqlMap setup for iBATIS Database Layer --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocations" value="META-INF/sql-map-config*.xml"/> <property name="dataSource" ref="dataSource"/> </bean> <!-- ========================= DAO DEFINITIONS: IBATIS IMPLEMENTATIONS ========================= --> <bean id="userDao" class="org.amway.osgi.internal.dao.UserDao"> <property name="sqlMapClient" ref="sqlMapClient"/> </bean> <!-- ========================= OSGI SERVICE DEFINITIONS ========================= --> <osgi:service ref="userDao" interface="com.amway.osgi.dao.test.IUserDao" /> </beans>
參考:
http://www.eclipse.org/forums/index.php/t/28276/
http://www.ibm.com/developerworks/cn/opensource/os-cn-eclosgisb/
http://www.eclipse.org/equinox/documents/quickstart.php
http://www.eclipse.org/equinox/server/http_in_container.php
http://www.ibm.com/developerworks/cn/java/j-lo-classloader/
<<OSGi and Equinox - Creating Highly Modular Java System>>
<<OSGi 实战.pdf>>
<<Manning.OSGi.in.Action.2011>>
相关推荐
maven-osgi-plugin-launcher-framework-equinox-1.0.15.jar
赠送jar包:osgi-resource-locator-1.0.1.jar; 赠送原API文档:osgi-resource-locator-1.0.1-javadoc.jar; 赠送源代码:osgi-resource-locator-1.0.1-sources.jar; 赠送Maven依赖信息文件:osgi-resource-locator...
maven-osgi-plugin-launcher-framework-equinox-1.0.19.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.18.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.17.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.16.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.14.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.13.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.12.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.11.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.10.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.9.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.8.jar
maven-osgi-plugin-launcher-framework-equinox-1.0.8b.jar
赠送jar包:osgi-resource-locator-1.0.1.jar; 赠送原API文档:osgi-resource-locator-1.0.1-javadoc.jar; 赠送源代码:osgi-resource-locator-1.0.1-sources.jar; 赠送Maven依赖信息文件:osgi-resource-locator...
spring-osgi-1.2.1-with-dependencies.zip spring-osgi-1.2.1-with-dependencies.zip spring-osgi-1.2.1-with-dependencies.zip
本文将介绍 Equinox 的 ServletBridge 项目,提供一个示例来说明如何使用 ServletBridge,并将简要分析 它的实现方法。 读者将首先了解到如何在 Servlet Container 中嵌入 OSGI,并从文章提供的例子中了了解其工作...
总结,Spring OSGi 1.2.0-rc1是Spring框架与OSGi技术结合的重要里程碑,它为开发者提供了在模块化环境中使用Spring的强大工具。理解并熟练掌握Spring OSGi,能帮助开发者构建更加灵活、可扩展的Java应用程序。
它允许开发者使用Spring的XML配置或者基于注解的方式来定义和管理Bean,同时,这些Bean能够在OSGi环境中被正确地激活、销毁和更新,确保了服务生命周期的管理。 再者,Spring OSGi提供了事件模型,使得组件之间可以...