DeepnightTwo
posts - 15, comments - 11, trackbacks - 0, articles - 0
BlogJava
首页
新随笔
联系
管理
聚合
OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
Posted on 2010-05-17 12:09
深夜两点
阅读(4779)
评论(8)
编辑
收藏
例子下载在此
最近搞了个小实验,发现Eclipse 插件的类加载的一个问题。Eclipse使用
Equinox
实现OSGi的框架,可以在插件的配置中确定哪些类expose出去,哪些类不能为外部所见。我发现的问题是,可以通过ClassLoader绕过这个限制,在外部插件中加载到插件里那些不为外部所见的类,并且能够创建类的实例,可以通过反射调用其方法(当然,如果被加载的类实现了某些接口,也可以通过接口的引用直接调用相应的方法)。
为了演示这个问题,先在eclipse中创建一个插件
UtilityLibrary
:
其中u
tilitylibrary.expose包中的类会暴露给外部,而utilitylibrary.hide包中的类不会暴露给外部。在MANIFEST.MF中增加这个设置:
VisiableClass
和
VisiableClass
类的内容很简单:
package
utilitylibrary.expose;
public
class
VisiableClass {
public
VisiableClass() {
System.out.println(
"
This is VisiableClass
"
);
}
public
String getMessage() {
return
"
From VisiableClass:\r\n
"
+
this
.getClass().getClassLoader().toString()
+
"
\t
"
;
}
}
package
utilitylibrary.hide;
public
class
InvisiableClass {
public
InvisiableClass() {
System.out.println(
"
InvisiableClass
"
);
}
public
String getMessage() {
return
"
From InvisiableClass:\r\n
"
+
this
.getClass().getClassLoader().toString()
+
"
\t
"
;
}
}
其实主要就是打印出相应的信息。类代码几乎是一样的。
下面创建另一个插件UsePlugin,依赖并使用UtilityLibrary中的类。插件其实就是Eclipse自带的Hello World程序,它会在eclipse 的toolbar上增加一个按钮,点击后会弹出一个MessageBox。好,MessageBox上显示的就是从UtilityLibrary中类的方法的返回值。首先增加插件依赖关系:
在SampleAction中的Run方法里,如果直接使用InvisiableClass,插件完全找不到这个类,修改建议里面建议expose这个类:
当然,使用VisiableClass是没问题的。下面通过VisiableClass来将InvisiableClass拽出来,SampleAction类的源代码如下,只要关心run方法就可以了:
package
useplugin.actions;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Method;
import
org.eclipse.jface.action.IAction;
import
org.eclipse.jface.dialogs.MessageDialog;
import
org.eclipse.jface.viewers.ISelection;
import
org.eclipse.ui.IWorkbenchWindow;
import
org.eclipse.ui.IWorkbenchWindowActionDelegate;
import
utilitylibrary.expose.VisiableClass;
/**
* Our sample action implements workbench action delegate. The action proxy will
* be created by the workbench and shown in the UI. When the user tries to use
* the action, this delegate will be created and execution will be delegated to
* it.
*
*
@see
IWorkbenchWindowActionDelegate
*/
public
class
SampleAction
implements
IWorkbenchWindowActionDelegate {
private
IWorkbenchWindow window;
/**
* The constructor.
*/
public
SampleAction() {
}
/**
* The action has been activated. The argument of the method represents the
* 'real' action sitting in the workbench UI.
*
*
@see
IWorkbenchWindowActionDelegate#run
*/
public
void
run(IAction action) {
try
{
Class
<?>
clazz
=
VisiableClass.
class
.getClassLoader().loadClass(
"
utilitylibrary.hide.InvisiableClass
"
);
Object obj
=
clazz.newInstance();
Method method
=
clazz.getMethod(
"
getMessage
"
);
Object ret
=
method.invoke(obj,
new
Object[] {});
System.out.println(ret);
MessageDialog.openInformation(window.getShell(),
"
UsePlugin
"
, ret
.toString());
}
catch
(ClassNotFoundException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(InstantiationException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(IllegalAccessException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(SecurityException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(NoSuchMethodException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(IllegalArgumentException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
catch
(InvocationTargetException e) {
//
TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Selection in the workbench has been changed. We can change the state of
* the 'real' action here if we want, but this can only happen after the
* delegate has been created.
*
*
@see
IWorkbenchWindowActionDelegate#selectionChanged
*/
public
void
selectionChanged(IAction action, ISelection selection) {
}
/**
* We can use this method to dispose of any system resources we previously
* allocated.
*
*
@see
IWorkbenchWindowActionDelegate#dispose
*/
public
void
dispose() {
}
/**
* We will cache window object in order to be able to provide parent shell
* for the message dialog.
*
*
@see
IWorkbenchWindowActionDelegate#init
*/
public
void
init(IWorkbenchWindow window) {
this
.window
=
window;
}
}
在run方法里面,直接使用VisiableClass.class.getClassLoader().loadClass("utilitylibrary.hide.InvisiableClass");来加载本不应该被外部所见的Invisiable类。因为在Eclipse中,每个插件使用一个ClassLoader,所以用来加载VisiableClass类的ClassLoader也同样负责加载在同一个插件中的InvisiableClass类。这样InvisiableClass就在插件外部被加载成功了。类加载成功后,剩下的事情就是顺水推舟了,创建个实例然后使用反射调用相应的方法。
程序运行的时候,点击toolbar上那个button,会弹出如下对话框:
程序运行也没啥错误。
问题分析:
其实我觉得这个问题是很难绕过去的。对于同一个插件,因为内部的类需要互相引用和互相使用,所以必须使用同一个类加载器来加载。所以,这个插件只要expose出来一个包,那么外部的插件就可以通过包中的任何一个类来得到加载这个插件中的类的类加载器,然后就可以通过reflect爱做啥做啥了。
换一个角度可能更好理解这个问题为什么难以绕过去。假设VisiableClass需要用到InvisiableClass,虽然InvisiableClass没有暴露出来,但是在正常的使用VisiableClass的时候,需要先加载VisiableClass类,而加载VisiableClass的时候JVM就会隐式的加载InvisiableClass。这个过程和例子里现式的加载InvisiableClass没啥本质不同。也就是说,从ClassLoader的角度,很难判断一个类的加载是正常的代码还是为了突破bundle的访问限制——它们都是在执行run方法时发生的类加载行为。
或者是我有什么地方没设置好?求解答。
例子下载在此
。
Feedback
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 12:30 by
Jet Geng
你好,拜读你的文章我发现classloader的另外一种用法。谢谢。只是有一点不太明白。你为什么要绕过osgi的这种限制。
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 12:40 by
Johnny.Liang
从设计的角度来看,通过了解一些底层机制,绕过OSGi的类加载策略来直接访问不对外公开的类,不见得是一件好的事情,作为技术研究,了解这些底层机制有助于更熟悉一个框架,以更灵活的运用它,但作为软件开发,这些做法可能会导致很多隐患和风险,个人认为不值得推崇。
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 12:46 by
三人行,必有我师焉
一般隐藏起来的,都是internal的。里面的内容可以随意改变,你用Class.ForName来初始化一个类,一旦class name改变,你的代码就报废了。
这么做有什么意义呢?
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 12:57 by
深夜两点
@Jet Geng
@三人行,必有我师焉
@Johnny.Liang
谢谢大家的关注~。 我这也只是一种尝试,并不是在实际中要这么使用,也无意去绕过OSGi的限制,只是为了说明“可以绕过这种限制”。这么使用定然不好。所以我才觉得Equinox应该把这条路封掉。现在的疑问是Equinox没有封掉这条路,期待的答案是Equinox能不能封掉这条路。
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 13:20 by
临远
其实没有这么麻烦,大家可以在bundle里看到有loadClass和getResource的方法,直接调用,就可以通过类名和资源名获得对应的资源。实际上内部也是利用了classLoader。
就像暴力反射一样,虽然一般不会这样用,但是也确实提供了让你为所欲为的可能。
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 13:41 by
深夜两点
@临远
其实没有这么麻烦,大家可以在bundle里看到有loadClass和getResource的方法,直接调用,就可以通过类名和资源名获得对应的资源。实际上内部也是利用了classLoader。
请问boudle里指的是什么?
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 13:52 by
表现表达
2010-05-17 12:40 by Johnny.Liang
从设计的角度来看,通过了解一些底层机制,绕过OSGi的类加载策略来直接访问不对外公开的类,不见得是一件好的事情,作为技术研究,了解这些底层机制有助于更熟悉一个框架,以更灵活的运用它,但作为软件开发,这些做法可能会导致很多隐患和风险,个人认为不值得推崇。
同意,真的不见得是好事情。弊大于利
#
re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
回复
更多评论
2010-05-17 18:03 by
临远
@深夜两点
你看一下org.osgi.framework.Bundle的api javadoc。里边有你需要的方法。
新用户注册
刷新评论列表
只有注册用户
登录
后才能发表评论。
网站导航:
博客园
IT新闻
知识库
C++博客
博问
管理
<
2010年5月
>
日
一
二
三
四
五
六
25
26
27
28
29
30
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
1
2
3
4
5
常用链接
我的随笔
我的评论
我的参与
最新评论
留言簿
给我留言
查看公开留言
查看私人留言
随笔档案
(12)
2010年5月 (1)
2010年4月 (3)
2010年3月 (2)
2009年12月 (5)
2009年11月 (1)
文章档案
(2)
2009年10月 (2)
搜索
最新评论
1. re: 玩一下技术宅,数学之美——Julia Set分形
密集恐惧症 看了 给跪
--yurenchen
2. re: 玩一下技术宅,数学之美——Julia Set分形
是不是 有点卡..
--yurenchen
3. re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
@深夜两点
你看一下org.osgi.framework.Bundle的api javadoc。里边有你需要的方法。
--临远
4. re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
评论内容较长,点击标题查看
--表现表达
5. re: OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
评论内容较长,点击标题查看
--深夜两点
阅读排行榜
1. OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制(4779)
2. 玩一下技术宅,数学之美——Julia Set分形(2616)
3. 我的书出版了(1660)
4. Java中的认证(1108)
5. 计算机中的加密和认证。(778)
评论排行榜
1. OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制(8)
2. 玩一下技术宅,数学之美——Julia Set分形(2)
3. 我的书出版了(1)
4. 线程模型设计(0)
5. Java之父离开Oracle(0)