DeepnightTwo
posts - 15, comments - 11, trackbacks - 0, articles - 0
BlogJava
首页
新随笔
联系
管理
聚合
2010年5月17日
OSGi(Equinox)类加载的问题——使用ClassLoader突破bundle的访问限制
例子下载在此
最近搞了个小实验,发现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方法时发生的类加载行为。
或者是我有什么地方没设置好?求解答。
例子下载在此
。
posted @
2010-05-17 12:09
深夜两点 阅读(4779) |
评论 (8)
|
编辑
收藏
<
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)