当我们在开发基于OSGi的应用时,不可避免的要通过它的控制台进行相关调试。在开发阶段,调试可以直接在eclipse控制台输入相关命令,而当我们把程序部署到web服务器上后就不能再采用这样的方式了。但幸运的是,我们可以扩console以满足自己的需要,因此可以实现一个通过web访问console的方式。扩展conso可以参考基于 Equinox 的 OSGi Console 的研究和探索。
由于equinox 3.4和3.6在代码结构上的差异,要扩展一个通用的通过web控制的console,用反射是个好的选择,eg. FrameworkConsole的构造函数不一致,3.6已经没有OSGi这个类...以下是详细实现:
WebConsoleServlet.java,发布该servlet让客户端访问,并通过参数cmd传输相关的OSGi命令:
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
response.setContentType("text/html;charset=utf-8");
response.setCharacterEncoding("utf-8");
try {
String command = request.getParameter("cmd");
try {
final CommandServiceFacade facade = CommandServiceFacade.INSTANCE;
facade.setContext(Activator.bundleContext);
facade.setOut(response);
facade.execute(command);
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
CommandServiceFacade.java,封装具体实现:
public class CommandServiceFacade {
private static final String OSGI_VERION_3_4 = "3.4";
private static final String OSGI_VERION_3_6 = "3.6";
/**
* OSGi版本号.
*/
private String osgiVersion = null;
/**
* bundle上下文容器.
*/
private BundleContext context = null;
/**
* FrameworkConsole输出流,适用于OSGi3.4.
*/
private PrintWriter out = null;
/**
* FrameworkConsole输出流,适用于OSGi3.6.
*/
private OutputStream outputStream = null;
/**
* 单例.
*/
public static final CommandServiceFacade INSTANCE = new CommandServiceFacade();
/**
* 私有构造函数.
*/
private CommandServiceFacade() {
}
/**
* 设置bundle上下文环境.
*
* @param context
* bundle上下文容器.
*/
public void setContext(final BundleContext context) {
this.context = context;
}
/**
* 设置Console输出流.
*/
public void setOut(final HttpServletResponse response) {
try {
if (OSGI_VERION_3_6.equals(getOSGiVersion())) {
this.outputStream = response.getOutputStream();
} else if (OSGI_VERION_3_4.equals(getOSGiVersion())) {
this.out = response.getWriter();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 设置控制台输出流.
*/
public void setOut(final OutputStream os) {
try {
if (OSGI_VERION_3_6.equals(getOSGiVersion())) {
this.outputStream = os;
} else if (OSGI_VERION_3_4.equals(getOSGiVersion())) {
this.out = new PrintWriter(new OutputStreamWriter(os, "GB2312"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* <pre>
* 执行控制台命令.
* 由于OSGi3.4和3.6在代码结构上的差异,要扩展一个通用的通过web控制的console,用反射是个好的选择.
* eg. FrameworkConsole的构造函数不一致,3.6已经没有OSGi这个类...
* </pre>
*/
public void execute(final String command) {
if (null == command || command.length() == 0) {
return;
}
final ClassLoader systemBundleCL = context.getBundle(0).getClass().getClassLoader();
if (OSGI_VERION_3_6.equals(getOSGiVersion())) {
try {
final ServiceTracker cpTracker = new ServiceTracker(context, CommandProvider.class.getName(), null);
cpTracker.open();
final Class conSessClass = systemBundleCL
.loadClass("org.eclipse.osgi.framework.internal.core.FrameworkConsoleSession");
final Constructor conSessConstructor = conSessClass.getConstructor(new Class[] { InputStream.class,
OutputStream.class, Socket.class });
// Class FrameworkConsoleSession
final Object conSess = conSessConstructor.newInstance(new Object[] {
new ByteArrayInputStream(command.getBytes()), outputStream, null });
final Class fcClass = systemBundleCL
.loadClass("org.eclipse.osgi.framework.internal.core.FrameworkConsole");
final Class csClass = systemBundleCL.loadClass("org.eclipse.osgi.framework.console.ConsoleSession");
final Constructor fcConstructor = fcClass.getConstructor(new Class[] { BundleContext.class, csClass,
Boolean.TYPE, ServiceTracker.class });
// Class FrameworkConsole
final Object fc = fcConstructor
.newInstance(new Object[] { context, conSess, Boolean.FALSE, cpTracker });
fcClass.getMethod("run", null).invoke(fc, null);
} catch (Exception e) {
e.printStackTrace();
}
} else if (OSGI_VERION_3_4.equals(getOSGiVersion())) {
try {
final Class fcClass = systemBundleCL
.loadClass("org.eclipse.osgi.framework.internal.core.FrameworkConsole");
final Class osgiClass = systemBundleCL.loadClass("org.eclipse.osgi.framework.internal.core.OSGi");
final Constructor fcConstructor = fcClass.getConstructor(new Class[] { osgiClass, String[].class });
// Class FrameworkConsole
final Object fc = fcConstructor.newInstance(new Object[] { getOsgi(), new String[0] });
final Class intpClass = systemBundleCL
.loadClass("org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter");
final Constructor intpConstructor = intpClass.getConstructor(new Class[] { String.class,
Object[].class, fcClass });
// Class FrameworkCommandInterpreter
final Object interpreter = intpConstructor.newInstance(new Object[] { command, getServices(), fc });
final Field out = intpClass.getDeclaredField("out");
out.setAccessible(true);
out.set(interpreter, this.out);
final Method nextArgument = intpClass.getMethod("nextArgument", null);
final Method execute = intpClass.getMethod("execute", String.class);
execute.invoke(interpreter, (String) nextArgument.invoke(interpreter, null));
} catch (Exception e) {
e.printStackTrace();
}
} else {
// 暂不处理其他版本
}
}
/**
* 获取OSGi的版本,只取前2位.
*
* @return OSGi的版本
*/
private String getOSGiVersion() {
if (null == this.osgiVersion && context != null) {
this.osgiVersion = context.getBundle(0).getHeaders().get(Constants.BUNDLE_VERSION).toString()
.substring(0, 3);
}
return this.osgiVersion;
}
/**
* OSGi framework对象.
*
* @return OSGi framework.
*/
private static Object getOsgi() {
Object osgi = null;
try {
Field osgiField = EclipseStarter.class.getDeclaredField("osgi");
osgiField.setAccessible(true);
osgi = osgiField.get(null);
} catch (Exception e) {
throw new RuntimeException(e);
}
return osgi;
}
/**
* @return service对象数组.
*/
private Object[] getServices() {
final ServiceTracker cptracker = new ServiceTracker(context, CommandProvider.class.getName(), null);
cptracker.open();
final ServiceReference[] serviceRefs = cptracker.getServiceReferences();
if (serviceRefs == null) {
return new Object[0];
}
Util.dsort(serviceRefs, 0, serviceRefs.length);
final Object[] serviceObjects = new Object[serviceRefs.length];
for (int i = 0; i < serviceRefs.length; i++) {
serviceObjects[i] = context.getService(serviceRefs[i]);
}
return serviceObjects;
}
}