当我们在开发基于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; } }