Eclipse 运行时概述

Eclipse 运行时(Runtime)概述

        Eclipse 运行时定义了所有其他插件所依赖的插件(org.eclipse.osgi 和 org.eclipse.core.runtime),运行时负责定义插件的结构及其背后的实现细节(bundles和类加载器),运行时也负责发现和执行Eclipse 主程序,维护一个包含插件和它们所实现的扩展与扩展点的注册表。同时,运行时提供了各种各样的工具,例如日志、调试跟踪、适配器、首选项存储和一个并发基础工具。

 

1. 运行时插件模型

        当运行Eclipse程序时,Eclipse运行时会随着运行。运行时实现了Eclipse的插件模型和基础结构,维护所有的插件及插件所提供的功能。

 

        插件就是一个向Eclipse 系统贡献代码(或者是文档,或者两者都包含)的具有组织结构、通过一种结构化方式描述的组件,插件可以定义扩展点,供其他的插件扩展。当一个插件创建了一个扩展点的实现时,我们把这叫做向平台添加了一个扩展,扩展和扩展点是通过插件的清单文件(plugin.cml)声明的。

 

        通过一个通用扩展模型为插件提供了一个的结构化的方法,描述插件如果被扩展和如果扩展其他插件。定义一个扩展点如果顶任何其他的API,不同的是扩展点是通过XML而不是代码来声明的。同样,客户插件也使用XML来描述所实现的扩展。

 

        运行时的一个重要机制是,如果没有使用到安装到平台中的插件,插件就不会占用内存和产生性能损耗。平台扩展模型的声明式的特性使得运行时能够在不运行插件的同时获得插件所提供的扩展点和扩展。这样,可以安装很多插件,但是除非用户的操作请求到插件所提供的功能,插件就不会被激活。对于提供一个可伸缩的、健壮的平台来说这是一个重要的特性。

 

1.1 插件和束(bundles)

        插件的实现机制是用OSGI框架实现的,从这个角度来说,一个插件其实就是个OSGI bundle,bundle和所关联的类定义和实现了类加载过程、先决条件管理和bundle的生命周期。在接下来的讨论中,我们会交换使用plug-in 和bundle,除非是讨论框架中的特殊类。

 

1.1.1 plug-in

        Plugin 类表示一个运行在Eclipse平台中的插件,方便于集中管理插件的生命周期和整体语义。插件可以在其生命周期的startstop 阶段实现特定的功能。每一个生命周期方法都包含一个BundleContext 的引用,BundleContext 提供了插件的上下文信息。

 

        插件生命周期的start部分值得特别讨论。我们已经看到,不需要运行插件的任何代码,就可以通过插件的清单文件获得插件的信息。通常用户在工作台中的一些操作会引起一个能够触发插件启动的事件链。从实现的角度来看,除非需要加载插件中的类,插件就不会被启动。

 

        start 方法方便于实现插件的初始化和注册工作,但是必须认识到,插件能够在许多不同的环境下被启动,一些例如获得一个图片来装饰一个对象这样简单的过程都会导致加载插件的类,以致引起插件的启动。过早的初始化操作会导致插件在实际需要很久之前就会加载代码和数据。这样的话,就有必要仔细检查一下插件的初始化工作,考虑初始化的代替方法。

  • 如果可以快速执行,注册的工作,例如在插件启动期间注册监听器,或者启动后台线程就是比较合适的,但是,如果注册工作有副作用,例如要初始化大的数据结构或者运行不相关的操作,建议访问插件的数据是才触发这些注册行为。
  • 当第一次访问数据时,数据的初始化最好是延迟进行的,而不是在启动的代码中执行。这能够确保在真正使用之前不会创建大的数据结构。
1.1.2 Bundle 上下文
        生命周期管理是OSGI “bundle” 术语和平台“plug-in” 数据相交汇的地方。当插件启动时,会获得一个包含插件相关信息的BundleContext 引用,BundleContext也可以用来获得系统中其他的bundle/插件。

        BundleContext.getBundles() 可以用来获取一个包含系统中所有bundle的数组,可以注册  BundleEvent 事件监听器,当前插件能够获悉其他的bundle在其生命周期状态中发生的变化。更多信息请查看 BundleContext 和  BundleEvent 类的javadoc。

1.1.3 Bundle Activator
        BundleActivator 接口定义了在 Plugin 类中实现的start 和 stop 行为。尽管 Plugin 类方便与实现插件功能,插件开发者可以完全自由地使用任何适合插件设计的类实现 BundleActivator 接口。事实上,如果没有特别的生命周期管理的需要,完全没有必要去实现这个接口。

1.1.4 Bundles
        每一个插件都对应一个Eclipse 平台管理的OSGI bundle, Bundle 是OSGI 的模块单位。从根本上来说,一个bundle 是一个安装到平台中的文件(资源和代码)集合,每一个bundle 都它自己的Java 类加载器,包含启动、停止和卸载bundle自己的协议。从Eclipse 平台的角度来看,Bundle仅仅是了一个实现类。插件开发者不用扩展bundle 类,而是使用 Plugin,或者其他 BundleActivator 实现类来表示一个插件。

1.2 扩展点和注册表
        从运行时插件和运行时工具的角度来看,插件具有OSGI ‘bungle’ 的概念,通常,一个插件涉及到插件定义了什么扩展点和插件贡献了什么扩展。Eclipse 平台的注册表——  IExtensionRegistry —— 提供了这些数据。

        插件是能够获知那些扩展是可用的,一个具体的例子可以帮助我们展示了获取扩展点和扩展信息的需要以及获取的方式。打开工作台 “Show View” 对话框,它可以显示所有安装到平台中的现有视图。

Eclipse 运行时概述

        我们知道,一个插件扩展了 org.eclipse.ui.views,目录名称和所有视图的名称是在plugin.xml 文件中定义的,但是工作台是怎么发现这些信息的呢?是从平台扩展注册表。下面的代码片段来自于工作台 ‘Show View’ 对话框实现:
...
IExtensionRegistry registry = Platform.getExtensionRegistry();
IExtensionPoint point = registry.getExtensionPoint("org.eclipse.ui.views");
if (point == null) return;
IExtension[] extensions = point.getExtensions();
for (int i = 0; i < extensions.length; i++)
	readExtension(extensions[i]);  //get the information about each extension
...
        从上面可以看出注册表对象可以从 Platform 类中获得,  IExtensionRegistry 提供了获取‘ org.eclipse.ui.views’ 扩展点的方法。可以通过 IExtensionRegistry, IExtensionPoint, 和 IExtension 提供的方法获取特定扩展点和扩展的注册表信息。这些类的Java文档提供注册方法表详细的信息。一旦获得所需要的扩展的定义对象, IConfigurationElement 提供的方法可以用来获取扩展的属性数据。

2. 运行时组件
Eclipse 运行时分为几个组件:
  • org.eclipse.core.contenttype         - Content type mechanism
  • org.eclipse.core.jobs                     - Concurrency infrastructure
  • org.eclipse.core.net                       - Network and proxy management infrastructure
  • org.eclipse.equinox.app                - Application Model
  • org.eclipse.equinox.common         - Common basic functionality
  • org.eclipse.equinox.preferences   - Runtime preferences
  • org.eclipse.equinox.registry          - Extension registry
  • org.eclipse.equinox.security          - Security infrastructure
        如果插件依赖 org.eclipse.core.runtime,不做任何修改就可以运行,但是如果希望最小化以来的数量,就可以仅吧所需要使用的运行时bundle挑选出来。如果要使用Import-Package 指令(而不是 Require-bundle),需要知道 org.eclipse.core.runtime 包是分为几个不同的bundle的。

导入  org.eclipse.equinox.common bundle 时,在清单文件中使用下面这行代码:
        Import-Package: org.eclipse.core.runtime; common="split"
导入 org.eclipse.equinox.registry 和 org.eclipse.equinox.common  bundles 时,使用:
Import-Package: org.eclipse.core.runtime; registry="split"
使用完整的包时:
Import-Package: org.eclipse.core.runtime

3. 运行时首选项
        org.eclipse.core.runtime.preferences 包提供了存储插件首选项数据的基础工具,首选项一般对应于用户在首选项页面进行的设置。插件首选项就是键/值对,键描述首选项的名字,值可以是几种不同的类(boolean, double, float, int, long, 或 string),平台可以存储并从文件系统重新读取首选项数据。所保存首选项的存储地址依赖于首选的范围(scope)。

3.1 首选项范围(scope)
        首选项范围和首选项的存储位置紧密相关,插件开发者可以决定使用哪一个符合程序要求的标准首选项范围,也可以定义满去需求的新的首选项范围,先看看平台运行时定义的首选项范围:
  • Instance (实例) 范围首选项存储在工作空间,或者平台的运行实例的位置
  • Configuration (配置) 范围首选项存储在平台的安装位置,在不同的工作空中共享。例如,如果用户安装了一个单一的Eclipse 平台,但是运行了几个不同的工作空间,configuration 级别范围的首选项可以在不同工作空间中共享。
  • Default (默认) 范围首选项表示首选项的默认值,平台不会修改或存储它们。
        你可以把所有的首选项当作一个由节点组成层级结构,层级结构的每一个主要分支表示一个特殊的范围,每一个分支节点的子节点取决于主分支上首选项范围是如果定义的,对于实例和配置范围,其子节点就是一个特定插件的首选项,这些首选项都被指定了一个首选项标识,通常就是插件的ID。

        可能这些听起来让人费解,别担心。如果不关心首选项范围和节点,不要考虑首选项的范围或者首选项层级上的哪一个节点包含着首选项的值,首选项API 将会自动排序首选项数据( instance, configuration, default),帮助我们查询一个首选项的值,使用所提供的标识和首选项名称获取包含首选项值的首选项节点。

使用  IPreferencesService 可以访问首选项。可以使用 Platform 类调用平台默认的首选项服务。
        IPreferencesService service = Platform.getPreferencesService();

         获取首选项服务之后,就可以使用  IPreferencesService 提供的get... 方法并传入名称查询首选项的值,例如,下面的代码片段查询了插件  "com.example.myplugin" 的首选项 "MyPreference"的值:
...
IPreferencesService service = Platform.getPreferencesService();
boolean value = service.getBoolean("com.example.myplugin", "MyPreference", true, null);
//do something with the value.
...
        查询方法中的最后一个参数是一个范围上下文数组,如果这个数组为 null,平台会假设使用默认的范围顺序搜索,推测合适的首选项对象。

3.2 使用首选项范围和节点
        如果插件需要精细控制首选项搜索范围的顺序,可以通过表示首选项范围的类访问特定范围内的首选项节点,因此,可以创建一个搜索范围的节点数组。下面的代码使用和上面相同的式获取首选项服务,但是首先搜索插件的配置范围,后搜索插件的实例范围,默认的首选项范围没有包含其中,也就是说,平台将会只在已提供的特殊的范围中进行搜索。
...
IPreferencesService service = Platform.getPreferencesService();
Preferences configurationNode = new ConfigurationScope().getNode("com.example.myplugin");
Preferences instanceNode = new InstanceScope().getNode("com.example.myplugin");
Preferences[] nodes = new Preferences[] {configurationNode, instanceNode};
stringValue = service.get("MyPreference", "true", nodes);
//do something with the value.
...
 插件也可以自己实现对首选项树的节点的遍历。可以从首选项服务获取首选项树的根节点,首选项范围类可以用来继续便利首选项节点树,下面的代码通过遍历获得一个节点,并从节点中获取了首选项的值:
...
IPreferencesService service = Platform.getPreferencesService();
Preferences root = service.getRootNode();
Preferences myInstanceNode = root.node(InstanceScope.SCOPE).node("com.example.myplugin");
if (myInstanceNode != null) {
	value = node.getBoolean("MyPreference", "true");
	//do something with the value.
}
...
 
3.3 扩展首选项范围
       插件可以使用  org.eclipse.core.runtime.preferences 定义自己的首选项范围。具体请查阅  Project-scoped preferences

4. 运行时应用模型 (Runtime application model)
        Elicpse 平台运行时提供了一个应用容器(application container),用来控制和执行应用(application),运行时应用容器实现了OSGI R4 规范中的应用管理服务标准。应用容器负责发现所有可用的应用(application),为每一个可用的应用(application)注册一个 ApplicationDescriptor OSGi service。 ApplicationDescriptor OSGi service 可以用来启动应用(application) 。当应用(application) 启动时,会注册一个表示正在运行的应用(application) 实例的 ApplicationDescriptor OSGi service。 ApplicationHandle  service 用来关闭应用(application) org.osgi.service.application 包和 OSGi R4.1 specification 提供了更多信息。

4.1 默认应用(default application)
        一个给定的Eclipse 配置可能包含多个产品和应用,Eclipse 配置定义了一个默认的应用,平台运行时启动并运行时,应用容器会启动这个默认的应用。可以通过任何一个下面的配置选项定义默认应用:
  • eclipse.product - 标识和平台运行时一同运行的产品,产品提供了商标信息(窗口小图片、标题栏等),定义了一个默认的应用。
  • eclipse.application - 标识和平台运行时一同运行的应用。这个选项覆盖了产品定义的默认应用。
4.2 定义一个应用(defining an application)
        插件可以通过  org.eclipse.core.runtime.applications 定义自己的应用,插件在这个扩展中定义应用的名称和ID,并且定义一个实现了这个应用的类。实现应用的这个类用来启动和关闭应用实例。
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI;

/**
 * This class controls all aspects of the application's execution
 */
public class Application implements IApplication {

	/* (non-Javadoc)
	 * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.IApplicationContext)
	 */
	public Object start(IApplicationContext context) throws Exception {
		Display display = PlatformUI.createDisplay();
		try {
			int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
			if (returnCode == PlatformUI.RETURN_RESTART)
				return IApplication.EXIT_RESTART;
			else
				return IApplication.EXIT_OK;
		} finally {
			display.dispose();
		}
		
	}

	/* (non-Javadoc)
	 * @see org.eclipse.equinox.app.IApplication#stop()
	 */
	public void stop() {
		if (!PlatformUI.isWorkbenchRunning())
			return;
		final IWorkbench workbench = PlatformUI.getWorkbench();
		final Display display = workbench.getDisplay();
		display.syncExec(new Runnable() {
			public void run() {
				if (!display.isDisposed())
					workbench.close();
			}
		});
	}
}

5. 网络支持
网络支持作为平台的一部分包括:
  • 插件org.eclipse.core.net 的包 org.eclipse.core.net.proxy 提供的代理支持
  • com.jcraft.jsch 插件提供的SSH2 支持。客户程序应该利用 org.eclipse.jsch.core 包中的API 来确保能够遵守首选项页面中的设置。

你可能感兴趣的:(eclipse,数据结构,应用服务器,网络应用,osgi)