Chinaunix首页 | 论坛 | 博客
  • 博客访问: 151561
  • 博文数量: 27
  • 博客积分: 531
  • 博客等级: 一等列兵
  • 技术积分: 332
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-25 18:31
文章分类

全部博文(27)

文章存档

2015年(4)

2014年(3)

2013年(6)

2012年(14)

我的朋友

分类: Java

2015-04-12 16:40:32

这两天在工作中遇到点问题,熬了两周左右,终于解决,觉得有一部分实践结果又点价值,所有写这篇文字作为记录

1. 问题背景
使用 osgi 的方式,加载WebSphere客户端类,连接WebSphere。
由于WebSphere自己实现了SocketFactory,故该类的jar被打进osgi 的 bundle。

2. 在使用过程中出现如下问题

WebSphere的client实现中存在如下代码:javax.net.ssl.SSLSocketFactory.getDefault()。
该函数直接从系统的classloader中加载 ssl.ScoketFactory.provider 指定的实现类。
由于该了再bundle的classloader中,故加载失败,导致程序错误。

javax.net.ssl.SSLSocketFactory的源代码如下:


getDefault实现摘录如下:

点击(此处)折叠或打开

  1. String clsName = getSecurityProperty("ssl.SocketFactory.provider");

  2. if (clsName != null) {
  3.     log("setting up default SSLSocketFactory");

  4.     try {
  5.         Class cls = null;
  6.         try {
  7.             cls = Class.forName(clsName);
  8.         } catch (ClassNotFoundException e) {
  9.             ClassLoader cl = ClassLoader.getSystemClassLoader();
  10.             if (cl != null) {
  11.                 cls = cl.loadClass(clsName);
  12.             }
  13.         }

  14.        log("class " + clsName + " is loaded");

  15.         SSLSocketFactory fac = (SSLSocketFactory)cls.newInstance();

  16.         log("instantiated an instance of class " + clsName);

  17.         theFactory = fac;
  18.         return fac;
  19.     } catch (Exception e) {
  20.         log("SSLSocketFactory instantiation failed: " + e.toString());
  21.         theFactory = new DefaultSSLSocketFactory(e);
  22.         return theFactory;
  23.     }
  24. }


3. 问题抽象
该问题的本质原因在于,java的factory实现中加载实际的factory实现时是使用系统的classloader。
但是产品中不能将相关的jar文件放置在classpath中,而必须使用osgi的方式。

4. 解决的原则
打通java的factory实现中加载类的classloader(系统的classloader)和osgi的classloader。

5. 解决方案
作为软件实践的一个指导方针,即任何问题都可以通过加入一个中间层进行解决。
所以计划实现一个socketfactory的代理类,该类放置在classpath中,然后再该类构造中,从thread的classloader中加载真正的factory实现,进而代理真正的factory。

6. 思考
如何让java的SSLSocketFactory加载代理的factory?注意到其实现中有这样一句话:
String clsName = getSecurityProperty("ssl.SocketFactory.provider");

即设置该属性则可以指导其加载我们自己实现的proxy

7. 实现
下面的代码使用urlclassloader模拟实现osgi 的 bundle实现:

7.1 代码结构


7.2
Test.java实现如下

点击(此处)折叠或打开

  1. public class Test {

  2.     public static void main(String[] args) throws Exception {
  3.         File f1 = new File("conn.factory.jar");
  4.         File f2 = new File("conn.impl.jar");
  5.         File f3 = new File("socket.factory.jar");
  6.         File f4 = new File("socket.factory.proxy.jar");

  7.         URL[] urls = new URL[] { f1.toURI().toURL(), f2.toURI().toURL(), f3.toURI().toURL(), f4.toURI().toURL() };

  8.         Security.setProperty("ssl.SocketFactory.provider", "socket.factory.proxy.MySocketFactoryProxy");

  9.         // 创建自己的classloader
  10.         URLClassLoader cl = new URLClassLoader(urls, ClassLoader.getSystemClassLoader());

  11.         // 有自己的classloader加载指定的类
  12.         Connector connector = (Connector) cl.loadClass("conn.impl.ConnectorImpl").newInstance();
  13.         connector.createConnector();
  14.     }

  15. }

7.3 不同的命令行classpath,运行结果如下


解释:
java -Djavax.net.debug=ssl -cp conn.jar conn.Test
在classpath中没有包含proxy的jar文件,故sslsocketfactory在加载时出现问题

java -Djavax.net.debug=ssl -cp socket.factory.proxy.jar;conn.jar conn.Test
在classpath中包含了proxy的jar,故成功加载

7.4 proxy的实现

点击(此处)折叠或打开

  1. public class MySocketFactoryProxy extends SSLSocketFactory {
  2.     private static final String className = "socket.factory.MySocketFactory";
  3.     private SSLSocketFactory mySSLSocketFactory = null;

  4.     public MySocketFactoryProxy() {
  5.         System.out.println("in MySocketFactoryProxy.");
  6.         try {
  7.             ClassLoader cl = Thread.currentThread().getContextClassLoader();
  8.             mySSLSocketFactory = (SSLSocketFactory) cl.loadClass(className).newInstance();
  9.         } catch (Exception e) {
  10.             e.printStackTrace();
  11.         }
  12.         System.out.println("out MySocketFactoryProxy.");
  13.     }

  14.     @Override
  15.     public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
  16.         return mySSLSocketFactory.createSocket(s, host, port, autoClose);
  17.     }

  18.     @Override
  19.     public String[] getDefaultCipherSuites() {
  20.         return getDefaultCipherSuites();
  21.     }

  22.     @Override
  23.     public String[] getSupportedCipherSuites() {
  24.         return getSupportedCipherSuites();
  25.     }

  26.     @Override
  27.     public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
  28.         return createSocket(host, port);
  29.     }

  30.     @Override
  31.     public Socket createSocket(InetAddress host, int port) throws IOException {
  32.         return createSocket(host, port);
  33.     }

  34.     @Override
  35.     public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException,
  36.             UnknownHostException {
  37.         return createSocket(host, port, localHost, localPort);
  38.     }

  39.     @Override
  40.     public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort)
  41.             throws IOException {
  42.         return createSocket(address, port, localAddress, localPort);
  43.     }

  44. }
即从当前线程的classloader中load实际的proxy

8. 最后
所有源码实现:

阅读(4398) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~