自定制 OSGi Console 进行组建和服务生命周期管理
模块化编程的好处已经很好地被理解了约 40 年,但在 OSGi 以前,开发人员不得不本身发明模块化设计和系统。随着软件应用体系结构的不断发展,OSGi 已经成为了一种很好的软件体系模型。基于 OSGi 框架的应用程序由于其模块化、动态性和面向服务的特性而受到普遍欢迎,然而传统的 OSGi 框架的控制台在适用性和功能扩展性等诸多方面都有不少限制。为此,本文探讨一种自定制 OSGi 控制台来进行服务和组件管理的方法。html
概述
一、OSGi 的现状和发展趋势
模块化编程的好处已经很好地被理解了约 40 年,但在 OSGi 以前,开发人员不得不本身发明模块化设计和系统。OSGi 标准为软件提供了模块化、动态性、面向组件和服务的特性,所以愈来愈受到软件开发商以及开源社区的关注。在网络计算和云计算体系高速发展的带动下,单一节点的传统应用程序正逐渐被分布式多节点应用程序所取代。OSGi 标准正向着分布式、跨虚拟机的方向发展,跨虚拟机的组件和服务访问成为了构建分布式 OSGi 应用程序的重要方式。
二、OSGi 当前控制台的限制和不足
OSGi 控制台对于 OSGi 组件和服务生命周期管理以及模块的调试和诊断的做用是相当重要的。组件和服务的管理者能够经过控制台向 OSGi 框架提交控制命令,由控制台传递到命令解释器,而后解释器再去命令提供者那里查找执行命令,并返回执行结果到控制台,从而完成管理过程的交互。
然而,在 OSGi 应用中使用控制台并非十分方便的,在有些状况下,OSGi 实现的默认控制台是没法使用的,这一点在文中会有详细分析。所以,咱们须要研究探索 OSGi 的控制台的内部机制,寻找更加具备适用性和扩展性的控制台解决方案。
三、本文在 OSGi 控制台方面的研究和探索方向
OSGi 标准仅仅规定了命令定制和解释的接口,命令列表的具体提供和解释,以及控制台的实现,并不在 OSGi 标准的规范范围内。每一个遵循 OSGi 标准的实现均可以提供本身的命令和控制台。这就为咱们自定制 OSGi 控制台提供了方便。
本文使用 Equinox 做为 OSGi 标准的实现来研究和探索控制台。Equinox 是 eclipse 采用的一种 OSGi 标准的实现框架。借助 eclipse 的知名度,Equinox 也获得了很大程度的推广普及,目前不少公司的大型软件产品都是构建在这一框架之上。
Equinox 框架提供了一个默认控制台实现,这个 FrameworkConsole 以经过 System.in 接受用户的命令请求,查询并执行命令,经过 System.out 将结果反馈给用户,完成交互过程。本文在分析在具体应用场景中现有控制台实现的不足的基础上,研究并挖掘了 Equinox 控制台的扩展功能,并对本身实现控制台从而知足更多自定制需求进行了探索,但愿可以抛砖引玉,为更多 OSGi 的使用者提供一个参考。java
OSGi 控制台的研究和探索web
一、Equinox 控制台实如今具体应用环境中的局限性分析
在一般状况下,Equinox 提供的默认控制台能够提供对组件和服务的生命周期控制和管理的功能。但在有些状况下,若是不对 Equinox 系统作任何修改,是不能直接使用控制台进行管理的。
这里先对 Equinox 控制台在具体应用环境中可能遇到的限制和不足作出分析,而后在接下来的部分,会对这些限制和不足提供尝试性解决方案。
场景一,在不作任何修改时 Equinox 控制台没法使用的状况。这种状况在 OSGi 应用程序的平常开发中很常见的。举例来讲:若是使用 equinox servletbridge 经过外部的 servlet 容器来启动 OSGi 应用,若不添加任何启动参数则没法使用 OSGi 控制台;经过 windows 服务来启动的 Websphere Application Server 一样也没法使用默认的控制台;若是用户但愿从远程访问控制台,对系统的组件和服务进行调试和控制,这种需求也是没法知足的。
场景二,Equinox 控制台的默认行为没法知足用户需求的状况。举例来讲:有些状况下,须要对控制台的添加访问控制机制,那么传统的控制台就没法提供这样的附加功能,由于他的行为已经固定了。
场景三,须要控制台服务彻底和应用程序解耦合的状况。在某些状况下,须要对原有系统软件不作任何改变,而同时提供可嵌入式的控制台。控制台和应用程序彻底解耦,具备高度的可复用性。
下面就针对这三种场景,给出通过尝试性解决方案。spring
二、对 Equinox 默认控制台服务内部机制的研究
Equinox 实际上是一个相对完善的 OSGi 实现框架,在充分了解它的内部实现机制以后,场景一所提到的限制就能够很容易的解决。
首先来分析一下 Equinox 框架启动过程当中是如何加载控制台的。
因为 eclipse3.4(3.0 以上版本)是彻底构建在 Equinox 框架之上的,因此能够从 eclipse3.4 的启动过程入手。org.eclipse.osgi_3.4.3.R34x_v20081215-1030.jar 是 eclipse3.4 启动的插件,也是 Equinox 框架的核心所在。这个包里面既包含 Equinox 对于 OSGi 标准的实现,也包含了 eclipse 启动所须要的类。org.eclipse.core.runtime.adaptor.EclipseStarter 这个类是负责启动 eclipse 的,里面有不少和配置相关的参数。其中:编程
public static final String PROP_CONSOLE = "osgi.console"; public static final String PROP_CONSOLE_CLASS = "osgi.consoleClass";
这两个属性是控制台相关的,PROP_CONSOLE_CLASS用来指定控制台的实现类,PROP_CONSOLE 用来指定控制台所使用的 socket 通讯端口。原来 Equinox 默认实现的控制台也是有经过 socket 进行远程通讯的能力的,只不过要受到不少限制,这一点后面会详细分析。而经过设定控制台实现类,Equinox 为开发者提供了一个自定制控制台的切入点。
先从最简单的状况入手。Equinox 能够经过添加系统属性激活基于 socket 通讯的远程控制台。控制台类是在 org.eclipse.osgi.framework.launcher 中被加载的。在 org.eclipse.core.runtime.adaptor.EclipseStart 类中也有相似的 startConsole() 方法,一样用来加载控制台,如 清单 1 所示。windows
清单 1. 加载控制台缓存
1 private void doConsole(OSGi osgi, String[] consoleArgs) { 2 Constructor consoleConstructor; 3 Object osgiconsole; 4 Class[] parameterTypes; 5 Object[] parameters; 6 try { 7 Class osgiconsoleClass = Class.forName(osgiConsoleClazz); 8 if (consolePort.length() == 0) { 9 parameterTypes = new Class[] {OSGi.class, String[].class}; 10 parameters = new Object[] {osgi, consoleArgs}; 11 } else { 12 parameterTypes = new Class[] {OSGi.class, int.class, String[].class}; 13 parameters = new Object[] {osgi, new Integer(consolePort), consoleArgs}; 14 } 15 consoleConstructor = osgiconsoleClass.getConstructor(parameterTypes); 16 osgiconsole = consoleConstructor.newInstance(parameters); 17 Thread t = new Thread(((Runnable) osgiconsole), OSGI_CONSOLE_COMPONENT_NAME); 18 t.start(); 19 } catch (NumberFormatException nfe) { 20 System.err.println(NLS.bind(Msg.LAUNCHER_INVALID_PORT, consolePort)); 21 } catch (Exception ex) { 22 informAboutMissingComponent(OSGI_CONSOLE_COMPONENT_NAME, OSGI_CONSOLE_COMPONENT); 23 } 24 }
在 launcher 类的 doConsole()方法中,先检查启动参数中是否有指定了控制台使用的端口号,构造相应的参数列表,经过反射方式调用控制台类的构造方法来建立控制台对象。若是指定了特定端口号,则构造控制台对象时使用的是 socket 获取的输入输出流;若是没有指定端口号,则使用默认的标准输入输出流来构造控制台。安全
经过指定控制台使用 socket 方式创建通讯,就能够彻底解决场景一提到的问题。指定控制台端口号的方法并不复杂,针对不一样的状况,能够有下面三种种方法:服务器
清单 2. 添加启动参数网络
<init-param> <param-name>commandline</param-name> <param-value>-console 8090</param-value> </init-param>
这种方式不但可让 OSGi 应用的部署本地畅通无阻的访问 OSGi 控制台,还可让远端计算机经过 socket 客户端访问 OSGi 控制台。好比使用 windows 系统的可使用 telnet 命令,例如:telnet 192.168.1.1 8090。
经过上面的尝试,咱们已经能够经过 socket 创建与控制台的通讯,使得本地和远程访问控制台执行 OSGi 命令来管理组件和服务的生命周期变得可行,解决了场景一提出的问题。可是这种方式下,控制台的访问者使用的仍是 equinox 提供的默认控制台的功能,而没法实现必要的自定制功能。所以在实际应用中的做用仍是有限的。这就如同场景二提到的状况。
三、自定义 OSGi 标准的控制台实现类
若是但愿本身实现自定制功能的控制台,解决场景二遇到的问题,须要本身实现一个控制台类,而且添加另一个启动参数。
equinox 提供一个默认的控制台实现,org.eclipse.osgi.framework.internal.core.FrameworkConsole,能够参照这个类来实现本身的控制台。好比你的控制台一般也须要实现 socket 通讯,就能够借用 getSocketStream()方法,但若是你想使用的不是 socket 流,那么也可使其余流或者其余通讯方式。创建通讯以后,控制台的执行过程都在 console() 这个方法中,执行过程也能够自定制。例如 清单 3 就是自定制的控制台过程,在用户接入以后要求用户输入用户名和密码进行认证。经过认证的用户将会看到 OSGi 提示符,并能够执行 OSGi 的相关操做。
清单 3. 自定制的控制台过程
protected void console() { // wait to receive commands from console and handle them BufferedReader br = in; //cache the console prompt String String consolePrompt = "\r\n" + ConsoleMsg.CONSOLE_PROMPT; //$NON-NLS-1$ for(int i=0;i<3;i++) { try { out.print("Input your username:"); out.flush(); String user=br.readLine(); out.print("Input your password:"); out.flush(); String pass=br.readLine(); if(!user.contentEquals("admin") || !pass.contentEquals("admin")) { out.print("Your username or password is invalid.Please input again"); out.flush(); } else { break; } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } while (!disconnected()) { out.print(consolePrompt); out.flush(); String cmdline = null; try { if (blockOnready && !useSocketStream) { // bug 40066: avoid waiting on input stream - apparently generates contention with //other native calls try { while (!br.ready()) Thread.sleep(300); cmdline = br.readLine(); } catch (InterruptedException e) { // do nothing; probably got disconnected } } else cmdline = br.readLine(); } catch (IOException ioe) { if (!shutdown) ioe.printStackTrace(out); } if (cmdline == null) { if (!useSocketStream) // better shutdown; the standard console has failed us. Don't want to get in an //endless loop (bug 212313) (bug 226053) shutdown(); break; } if (!shutdown) docommand(cmdline); } }
控制台类编写好以后,如何告诉 equinox 加载这个类做为控制台的实现呢?能够经过设置 osgi.consoleClass 这个参数来指定控制台类。这个参数的内容就是你自定义的控制台类的路径。这个启动参数的添加方法和前面提到过的 osgi.console 的三种设定方式基本相同,除了不能在 web.xml 文件的 servlet 启动参数中添加外,其余的两种方式均可以用来添加这个参数。
例如: osgi.consoleClass=org.myConsole.myFrameworkConsole
添加这个参数以后,还有最后一个问题。控制台的实现类是须要和 OSGi 的核心实现 bundle 即 org.eclipse.osgi_3.4.0.v20080605-1900.jar启动时一块儿加载到系统中的,当核心 bundle 启动的时候,其余全部 bundle 都没有加载,只有等核心 bundle 启动以后才会去加载其余 bundle。这样一来,若是本身的控制台实现类打包在一个普通的 bundle 中,那核心 bundle 启动的时候是加载不到这个类的。解决方法就是建立一个 fragment 的 bundle,此 bundle 的 Fragment-Host设置为 org.eclipse.osgi;bundle-version="3.4.2",即做为核心 bundle 的附属,这样此 bundle 将和核心 bundle 一块儿启动,这样自定义的类就能够被核心 bundle 加载到了。
注意:须要把包含控制台实现类的包导出以便其余 bundle 来引用。
fragment bundle 的 manifest.mf 文件中重要的几个属性:
Fragment-Host: org.eclipse.osgi;bundle-version="3.4.2"
Export-Package: myconsole
注意,自定义的控制台实现类所在的包须要添加到 Export-Package中,才能在启动过程当中被加载。
对于 fragment bundle 的开发就再也不给出详细步骤了,能够参考本文提供的范例程序,或者联系做者进行讨论。
四、自定义 OSGi 标准的命令执行器
至此咱们已经讨论了场景一和场景二中提到的限制的解决方案。但这些方式使用的控制台也存在很明显的不利。首先,远程管理和控制中相当重要的一个环节是安全方面的访问控制,经过 socket 通讯来进行访问控制将引入附加的安全机制,这种安全机制和系统自己固有的安全机制没法联系,致使系统冗余;其次,不管使用上面描述的任何一种方式,都须要添加 OSGi 启动参数,须要对原有配置作修改,这对于在既有项目中嵌入控制台服务来讲,无疑会形成不便,就如同场景三所描述的状况。
咱们能够换一种角度来看控制台。控制台的本质,就是接受用户的输入,解析输入,执行命令,而后返回操做结果,完成于用户的一次交互。所以咱们须要的仅仅是一个通讯方式和一个命令解析器。
为了不使用 socket 通讯带来的安全问题以及添加启动参数的不便,须要采用其余的远程通讯方式。而 http 协议时目前 web 应用程序中最经常使用的通讯协议,基于通讯 http 协议的 servlet 技术也比较成熟,所以咱们这里使用基于 HTTP 协议的 servlet 请求方式来传递用户请求。用户请求到达以后,servlet 要作的就是解释并执行请求。OSGi 标准提供了两个接口,org.eclipse.osgi.framework.console.CommandInterpreter和 org.eclipse.osgi.framework.console.CommandProvider。实现 CommandProvider 接口的类负责提供 OSGi 可执行的命令,实现这个接口必须实现的方法是 public String getHelp(),此方法用来提供命令帮助,此外还能够自定义一系列如下划线“_”开头的方法,用来提供具体的命令和命令的具体操做,下划线后面即为命令名称。命令解释器 CommandInterpreter 就是把用户输入和这里的名称匹配从而找到命令相对应的方法并执行。
equinox 默认提供了这两个接口的实现类,分别是 org.eclipse.osgi.framework.internal.core.FrameworkCommandProvider以及 org.eclipse.osgi.framework.internal.core.FrameworkCommandInterpreter。 为了实现自定制的功能,用户也能够本身编写这两个实现类。 FrameworkCommandProvider 提供了大部分经常使用的操做,因此通常状况下能够直接使用这个类,若是须要添加自定义操做,能够经过继承这个类来扩展;但因为 FrameworkCommandInterpreter 中命令执行结果输出的流已经绑定为 FrameworkConsole 中的输出流了,因此要本身实现一个 CommandInterpreter。
首先,编写用于和用户交互的 ConsoleServlet,负责接收用户输入和将响应结果返回给用户。在这个应用场景中,用户经过 web 应用程序经行组件和服务的生命周期管理,所提交的一系列命令都会被提交到这个 servlet 中进行处理,如清单 4 所示。
清单 4. 提交命令
1 public class ConsoleServlet extends HttpServlet { 2 BundleContext bc; 3 Bundle[] b; 4 5 public ConsoleServlet() 6 { 7 bc=InternalPlatform.getDefault().getBundleContext(); 8 } 9 public void doGet(HttpServletRequest request, HttpServletResponse response) 10 throws ServletException, IOException 11 { 12 this.doPost(request, response); 13 } 14 public void doPost(HttpServletRequest request, HttpServletResponse response) 15 throws ServletException, IOException 16 { 17 BufferedReader br=request.getReader(); 18 String command=br.readLine(); 19 System.out.println(command); 20 response.setContentType("text/html"); 21 PrintWriter out = response.getWriter(); 22 HttpServletConsole.setOutStream(out);// 设定 HttpServletConsole 使用的输出流 23 HttpServletConsole.docmd(command); 24 for(EventObject eo:Activator.eventInfoMap.keySet()) 25 { 26 out.println(Activator.eventInfoMap.get(eo)+"\n"); 27 System.out.println(Activator.eventInfoMap.get(eo)+"\n"); 28 } 29 Activator.eventInfoMap.clear(); 30 } 31 }
而后,编写控制台类 HttpServletConsole。这个类中最关键的方法是如何将用户的输入交给命令解释器去解释,这个方法就是 docmd(),如清单 5 所示。
清单 5. docmd() 方法
1 /** The OSGi Command Provider */ 2 protected final CommandProvider osgicp = new FrameworkCommandProvider(osgi).intialize(); 3 /** A tracker containing the service object of all registered command providers */ 4 protected static final ServiceTracker cptracker 5 = new ServiceTracker(context, CommandProvider.class.getName(), null); 6 /** 7 * Process the args on the command line. 8 * This method invokes a CommandInterpreter to do the actual work. 9 * 10 * @param cmdline a string containing the command line arguments 11 */ 12 public static void docmd(String cmdline) { 13 cptracker.open(); 14 if (cmdline != null && cmdline.length() > 0) { 15 CommandInterpreter intcp 16 = new ConsoleCommandInterpreter(cmdline, myGetServices(), out); 17 String command = intcp.nextArgument(); 18 if (command != null) { 19 intcp.execute(command); 20 } 21 } 22 } 23 24 /** 25 * Return an array of service objects for all services 26 * being tracked by this ServiceTracker object. 27 * 28 * The array is sorted primarily by descending Service Ranking and 29 * secondarily by ascending Service ID. 30 * 31 * @return Array of service objects; if no service 32 * are being tracked then an empty array is returned 33 */ 34 public static Object[] myGetServices() { 35 ServiceReference[] serviceRefs = cptracker.getServiceReferences(); 36 if (serviceRefs == null) 37 return new Object[0]; 38 Util.dsort(serviceRefs, 0, serviceRefs.length); 39 40 Object[] serviceObjects = new Object[serviceRefs.length]; 41 for (int i = 0; i < serviceRefs.length; i++) 42 serviceObjects[i] = context.getService(serviceRefs[i]); 43 return serviceObjects; 44 } 45 46 // 47 public ConsoleCommandInterpreter(String cmdline, 48 Object[] commandProviders,PrintWriter out) 49 { 50 tok = new StringTokenizer(cmdline); 51 this.commandProviders = commandProviders; 52 this.out = out; 53 }
ConsoleCommandInterpreter 是自定义的 CommandInterpreter 实现,在这个类的构造方法中,须要将输出流做为构造方法的参数传入,这样命令的执行结果才可以返回到用户那里。同时,经过 osgi 的 API 获取到了提供命令的服务的类,这些类都是 CommandProvider 的实现,均可以提供执行特定操做的命令,这些类的对象也做为构造方法的参数传入命令解释器。自定义命令解释器的其余部分与 ConsoleCommandInterpreter 相似,能够参考本文的范例代码。 整个过程的流程能够归纳成:
至此,全部由用户发起的请求均可以获得响应了。可是因为 http 协议的“请求 - 应答”模型的限制,通常来讲服务器端没法主动的吧信息“推送”到客户端呈现给用户。为了保证服务器端的状态能够尽可能及时的反映到客户端上,一般采用 http 请求“轮询”的方法,即每隔固定的时间段就发送一次请求,更新一次状态,形成客户端状态和服务器端实时同步的用户体验。那么若是服务器端系统内部发生了某些事件,这些信息又如何让用户获知呢?咱们能够经过 OSGi 的事件监听机制来获取系统内部事件信息,再经过轮询方式让这些信息尽可能及时的呈现给用户。
OSGi 提供了三种基本 listener 接口,分别是 BundleListener,,FrameworkListener,ServiceListener。当这几种 listener 监听的事件发生时,它们会执行相应的方法来处理事件信息。咱们能够本身编写一个类实现这三个接口,如 清单 6 所示:
清单 6. 实现接口
1 public class Activator implements BundleActivator, 2 BundleListener, FrameworkListener, ServiceListener 3 { 4 public static BundleContext context; 5 static Vector vec; 6 public static boolean isBundleChanged=true; 7 public static Hashtable<EventObject,String> eventInfoMap 8 = new Hashtable<EventObject,String>(); 9 10 public void start(BundleContext context) throws Exception 11 { 12 Activator.context=context; 13 Bundle[] bundles=context.getBundles(); 14 context.addBundleListener(this); 15 context.addFrameworkListener(this); 16 context.addServiceListener(this); 17 18 public void bundleChanged(BundleEvent event) 19 { 20 isBundleChanged=true; 21 int causedBy=event.getType(); 22 String CausedBy=""; 23 switch(causedBy) 24 { 25 case 1: 26 CausedBy="has been installed."; 27 break; 28 case 2: 29 CausedBy="has been started."; 30 break; 31 case 4: 32 CausedBy="has been stopped."; 33 break; 34 case 8: 35 CausedBy="has been updated."; 36 break; 37 case 16: 38 CausedBy="has been uninstalled."; 39 break; 40 case 32: 41 CausedBy="has been resolved."; 42 break; 43 case 64: 44 CausedBy="has been unresolved."; 45 break; 46 case 128: 47 CausedBy="is about to be activated."; 48 break; 49 case 256: 50 CausedBy="is about to deactivated."; 51 break; 52 case 512: 53 CausedBy="will be lazily activated."; 54 break; 55 } 56 Bundle eventBundle=event.getBundle(); 57 String bundleEventInfo=String.valueOf(eventBundle.getBundleId())+": "+ 58 eventBundle.getSymbolicName()+" "+CausedBy; 59 eventInfoMap.put(event , bundleEventInfo); 60 } 61 62 public void frameworkEvent(FrameworkEvent event) 63 { 64 int causedBy=event.getType(); 65 String CausedBy=""; 66 switch(causedBy) 67 { 68 case 1: 69 CausedBy="The Framework has started. "; 70 break; 71 case 2: 72 CausedBy="An error has occurred. "; 73 break; 74 case 4: 75 CausedBy="A PackageAdmin.refreshPackage operation has completed. "; 76 break; 77 case 8: 78 CausedBy="A StartLevel.setStartLevel operation has completed. "; 79 break; 80 case 16: 81 CausedBy="A warning has occurred."; 82 break; 83 case 32: 84 CausedBy="An informational event has occurred. "; 85 break; 86 } 87 Bundle eventBundle=event.getBundle(); 88 String bundleEventInfo=CausedBy+" Caused by bundle "+ 89 String.valueOf(eventBundle.getBundleId())+": "+ 90 eventBundle.getSymbolicName(); 91 eventInfoMap.put(event, event.toString()); 92 } 93 public void serviceChanged(ServiceEvent event) 94 { 95 int causedBy=event.getType(); 96 String CausedBy=""; 97 switch(causedBy) 98 { 99 case 1: 100 CausedBy=" has been registered. "; 101 break; 102 case 2: 103 CausedBy="'s properties have been modified. "; 104 break; 105 case 4: 106 CausedBy=" is in the process of being unregistered. "; 107 break; 108 } 109 String bundleEventInfo=event.toString()+CausedBy; 110 eventInfoMap.put(event, bundleEventInfo); 111 } 112 /* 113 * (non-Javadoc) 114 * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext) 115 */ 116 public void stop(BundleContext context) throws Exception 117 { 118 System.out .print(eventInfoMap); 119 } 120 }
这样,全部的时间的信息都会被缓存在 eventInfoMap 这个 map 中,客户端能够设定间隔固定的时间段发起一次请求,获取此 map 中的信息呈现给客户。
至此,控制台的基本功能就都实现了。
这种方式实现的控制台有不少优良特性:首先,基于 servlet 的通讯机制,有很成熟的安全访问控制机制,好比 spring security 等;其次,servlet 基于 http 协议,能够方便的集成到 web 应用程序中;第三,此控制台自己也是插件,能够不添加任何启动参数,无缝的集成到系统中提供远程控制台访问,有效的管理 OSGi 系统中的组件和服务。这些特性能够很好的解决场景三中提到的限制和不足。
然而,这种方式的控制台也存在明显的不足。受到 HTTP 通讯协议请求应答模型的限制,这种插件式控制台只是一个命令执行器,虽然可以经过各类事件来反应系统信息,但没法把系统的日志信息完整的呈现给用户。这也是这种方式的最大不足,是和原生控制台的最大差距所在
总结
探索自定制 OSGi 控制台的意义
经过上面的探索,咱们针对三种应用场景由浅入深的提出了三种解决方案。三种实现方式中,与控制台原有系统的耦合度是依次递减的:第一种方式直接经过添加启动参数激活系统默认控制台的 socket 通讯功能;第二种方式是经过启动参数添加自定义的控制台实现类;第三种则是干脆抛开系统控制台规则,本身接收、解释和执行命令,并反馈执行结果给用户。
这三种方式中各自有针对的应用场景,都是为了知足某一应用场景的需求,绝非原生控制台的代替品,也不是 OSGi 控制台的最佳实践。所以在选择这三种控制台的实现方式以前须要认真分析系统的应用场景,选择出最合适的解决方案
以上内容引自:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-equnxc/