Our p2 based on installer suffered performance issue when querying IUs from repositories. Though the repositories have a large number of IUs to be queried, but we find the performance of using QL is unacceptable in some special scenarios.
I published several different methods to find the expected IUs. Thomas pointed out the better expression of QL and finally helped us to find out the our repository without IIndexProvider implementation.
IIndexProvider implementation of a repository is quite important to improve the performance of QL, especially use the 'traverse' clause to query something.
And Slicer API is an alternative method when querying the complete dependencies.
Showing posts with label p2. Show all posts
Showing posts with label p2. Show all posts
Monday, October 17, 2011
Friday, April 22, 2011
Eclipse P2's import/export capability
I implemented the replication tool at the end of 2009, then published it to Eclipse Marketplace in May 2010. However it's not pervasively used due to users have to install that plug-in firstly.
I searched a similar request on bugzilla, then I initialized my contribution in the early of this year. Finally it was accepted and will release as part of eclipse itself since Eclipse 3.7 M7! I hope it would benefit the users of Eclipse more and more.
And I was nominated and elected as the committer of Equinox p2, it's a great honor for me. :)
I searched a similar request on bugzilla, then I initialized my contribution in the early of this year. Finally it was accepted and will release as part of eclipse itself since Eclipse 3.7 M7! I hope it would benefit the users of Eclipse more and more.
And I was nominated and elected as the committer of Equinox p2, it's a great honor for me. :)
Tuesday, December 28, 2010
Inside P2's profile (2) - the fragment matches all osgi bundles
Recently our installer met a strange bug, it didn't uninstall all legacy bundles after updating to new version. Finally I found it's due to a magic fragment is missing in the profile due to some causes.
<unit id='tooling.osgi.bundle.default' version='1.0.0' singleton='false'>
<hostRequirements size='1'>
<required namespace='org.eclipse.equinox.p2.eclipse.type' name='bundle' range='0.0.0' multiple='true' greedy='false'/>
</hostRequirements>
<properties size='1'>
<property name='org.eclipse.equinox.p2.type.fragment' value='true'/>
</properties>
<provides size='2'>
<provided namespace='org.eclipse.equinox.p2.iu' name='tooling.osgi.bundle.default' version='1.0.0'/>
<provided namespace='org.eclipse.equinox.p2.flavor' name='tooling' version='1.0.0'/>
</provides>
<requires size='1'>
<required namespace='org.eclipse.equinox.p2.eclipse.type' name='bundle' range='0.0.0' multiple='true' greedy='false'/>
</requires>
<touchpoint id='null' version='0.0.0'/>
<touchpointData size='1'>
<instructions size='4'>
<instruction key='install'>
installBundle(bundle:${artifact})
</instruction>
<instruction key='uninstall'>
uninstallBundle(bundle:${artifact})
</instruction>
<instruction key='unconfigure'>
</instruction>
<instruction key='configure'>
setStartLevel(startLevel:4);
</instruction>
</instructions>
</touchpointData>
</unit>
It has 'hostRequirements' element that represents it's a fragment IU and match all the eclipse's plug-ins in that profile. And this fragment defines the touch point actions for its hosts that will do installBundle action during 'install' phrase and uninstallBundle action during 'uninstall' phrase. It's a very good way to remove the duplicate touch point definitions for all eclipse's plug-ins in the profile.
BTW, p2's engine also doesn't attach this fragment to the eclipse's plug-in IU if the top level IU doesn't have the STRICT rule. I'm not sure the root cause of designing for it, but it's the fact.
<unit id='tooling.osgi.bundle.default' version='1.0.0' singleton='false'>
<hostRequirements size='1'>
<required namespace='org.eclipse.equinox.p2.eclipse.type' name='bundle' range='0.0.0' multiple='true' greedy='false'/>
</hostRequirements>
<properties size='1'>
<property name='org.eclipse.equinox.p2.type.fragment' value='true'/>
</properties>
<provides size='2'>
<provided namespace='org.eclipse.equinox.p2.iu' name='tooling.osgi.bundle.default' version='1.0.0'/>
<provided namespace='org.eclipse.equinox.p2.flavor' name='tooling' version='1.0.0'/>
</provides>
<requires size='1'>
<required namespace='org.eclipse.equinox.p2.eclipse.type' name='bundle' range='0.0.0' multiple='true' greedy='false'/>
</requires>
<touchpoint id='null' version='0.0.0'/>
<touchpointData size='1'>
<instructions size='4'>
<instruction key='install'>
installBundle(bundle:${artifact})
</instruction>
<instruction key='uninstall'>
uninstallBundle(bundle:${artifact})
</instruction>
<instruction key='unconfigure'>
</instruction>
<instruction key='configure'>
setStartLevel(startLevel:4);
</instruction>
</instructions>
</touchpointData>
</unit>
It has 'hostRequirements' element that represents it's a fragment IU and match all the eclipse's plug-ins in that profile. And this fragment defines the touch point actions for its hosts that will do installBundle action during 'install' phrase and uninstallBundle action during 'uninstall' phrase. It's a very good way to remove the duplicate touch point definitions for all eclipse's plug-ins in the profile.
BTW, p2's engine also doesn't attach this fragment to the eclipse's plug-in IU if the top level IU doesn't have the STRICT rule. I'm not sure the root cause of designing for it, but it's the fact.
Inside P2's profile (1) - inclusion rules
You would see some interesting properties at the bottom of eclipse's profile.
For example,
It attaches a property named 'org.eclipse.equinox.p2.internal.inclusion.rules' with value 'STRICT' on the IU 'org.eclipse.sdk.ide' with version 3.6.1.M20100909-0800.
It's a very important property for the p2 engine. It means the IU 'org.eclipse.sdk.ide' has been explicitly installed into the profile, so it's not allowed be implicitly updated or removed.
For example,
We have top feature IU 'org.eclipse.sdk.ide' that represents the Eclipse SDK, 'org.eclipse.pde.feature' that represents the Plug-in Development Tool and 'org.eclipse.jdt.feature' that represents the Java Development Tool. And both JDT and PDT are part of Eclipse SDK, so 'org.eclipse.pde.feature' and 'org.eclipse.jdt.feature' are required by 'org.eclipse.sdk.ide'.
If the profile only has the STRICT rule for 'org.eclipse.sdk.ide', 'org.eclipse.jdt.feature' and 'org.eclipse.pdt.feature' will implicitly be updated to 3.6.2 when updating 'org.eclipse.sdk.ide' from 3.6.1 to 3.6.2.
However the profile has below STRICT rule for PDT feature,
The p2 engine will report errors due to 'org.eclipse.pdt.feature' has STRICT rule for updating. Hence third-party must explicitly update both 'org.eclipse.sdk.ide' and 'org.eclipse.pdt.feature' from 3.6.1 to 3.6.2.
For example,
<iuProperties id='org.eclipse.sdk.ide' version='3.6.1.M20100909-0800'>
<properties size='2'>
<property name='org.eclipse.equinox.p2.internal.inclusion.rules' value='STRICT'/>
</properties>
</iuProperties>
<properties size='2'>
<property name='org.eclipse.equinox.p2.internal.inclusion.rules' value='STRICT'/>
</properties>
</iuProperties>
It attaches a property named 'org.eclipse.equinox.p2.internal.inclusion.rules' with value 'STRICT' on the IU 'org.eclipse.sdk.ide' with version 3.6.1.M20100909-0800.
It's a very important property for the p2 engine. It means the IU 'org.eclipse.sdk.ide' has been explicitly installed into the profile, so it's not allowed be implicitly updated or removed.
For example,
We have top feature IU 'org.eclipse.sdk.ide' that represents the Eclipse SDK, 'org.eclipse.pde.feature' that represents the Plug-in Development Tool and 'org.eclipse.jdt.feature' that represents the Java Development Tool. And both JDT and PDT are part of Eclipse SDK, so 'org.eclipse.pde.feature' and 'org.eclipse.jdt.feature' are required by 'org.eclipse.sdk.ide'.
If the profile only has the STRICT rule for 'org.eclipse.sdk.ide', 'org.eclipse.jdt.feature' and 'org.eclipse.pdt.feature' will implicitly be updated to 3.6.2 when updating 'org.eclipse.sdk.ide' from 3.6.1 to 3.6.2.
However the profile has below STRICT rule for PDT feature,
<iuProperties id='org.eclipse.pdt.feature' version='3.6.1.M20100909-0800'>
<properties size='2'>
<property name='org.eclipse.equinox.p2.internal.inclusion.rules' value='STRICT'/>
</properties>
</iuProperties>
<properties size='2'>
<property name='org.eclipse.equinox.p2.internal.inclusion.rules' value='STRICT'/>
</properties>
</iuProperties>
The p2 engine will report errors due to 'org.eclipse.pdt.feature' has STRICT rule for updating. Hence third-party must explicitly update both 'org.eclipse.sdk.ide' and 'org.eclipse.pdt.feature' from 3.6.1 to 3.6.2.
Friday, December 25, 2009
[Eclipse][P2]P2 replication plug-in
I wrote a plug-in to simplify the process to install the same plug-ins in different platform or different workstation.
Anyone is interested in it, pls follow below guide to freely use it.
http://code.google.com/p/kane-toolkit/wiki/P2Replication
Enjoy it.
Anyone is interested in it, pls follow below guide to freely use it.
http://code.google.com/p/kane-toolkit/wiki/P2Replication
Enjoy it.
Thursday, November 12, 2009
[Eclipse][P2]Learn p2 step by step
Learn p2 step by step
首先来理解p2引入的几个概念[1]
- p2 / Agent
- The provisioning infrastructure on client machines
- Installable Unit (IU)
- Metadata that describes things that can be installed/configured
- Artifact
- The actual content being installed/configured(e.g., bundle JARs)
- Repository
- A store of metadata or artifacts
- Profile
- The target of install/management operations
- Planner
- The decision-making entity in the provisioning system
- Engine
- The mechanism for executing provisioning requests
- Touchpoint
- The part of the engine responsible for integrating the provisioning
system to a particular runtime or management system
Arifact就是来描述实际要安装的文件,bundle类型的jar,feature,binary文件。
这时就有了Repository(仓库)这个概念,是用来保存artifacts信息,以及artifacts的元数据。元数据包括了对artifact的唯一标识符,版本,对外暴露的接口信息,以及它依赖的接口及其版本信息,各个安装阶段需要执行的配置。在p2默认的实现里面,这两个repository用xml文件来描述,同时被压缩为artifacts.jar, content.jar来减小文件大小,缩短传输时间。
从Eclipse 3.4起,当从远程site安装新的软件时,就会看到有个work thread在后台下载content.jar文件。p2在安装时候,首先会根据content.xml(metadata repository)来解析正在安装软件的依赖。在当前runtime里面查找metadata中指定的依赖,如果满足才继续安装。据我个人经验,如果安装的软件比较复杂,那它产生的metadata文件就会比较大(很容易上兆),下载这个文件以及解析它的内容都会比较慢,从而影响用户体验。
比较灵活的是,用户可以实现自己的ArtifactRepository和MetadataRepository,注册到它们各自的Manager里面就可以了。所有这些服务都被实现为OSGi Service.
下一个Profile,是用来管理安装目标里的软件信息。p2在被设计的时候,希望解决多个eclipse实例共享一份安装的某软件。比如为了某种目的我机器上有好几个Eclipse,同时它们都需要CDT,免去为重复安装的麻烦。profile就会记录每次安装的内容,让整个应用程序被管理起来。在Galileo里安装的软件都可以软件管理里面查找到。
Planner和Engine完全就是p2内部的东西。任何p2的操作(安装,删除,配置)都需要Planner实例来描述。有了Planner以后,还需要创建一个Engine对象,通过engine来执行对应的plan。这就是目前调用p2 API来完成安装的一个过程。
最后一个Touchpoint。程序在安装的时候,可能会根据runtime(os, ws, arch等)或阶段(安装,卸载,配置等)执行某些配置,touchpoint就是帮助实现这些配置。具体操作是以IU为单位记录在metadata repository里的。p2默认实现了一些Eclipse touchpoint,比如拷贝,删除文件,执行外部程序等。如果用户有自己特殊的native操作需要执行,可以自己实现自定义的touchpoint。
首先需要得到当前安装的profile。如果是全新安装,通过IProfileRegistry.addProfile创建一个新profile。是更新安装的话,可以通过IProfileRegistry查询到期望更新的profile。创建profile的时候,需要注意设置profile的属性,
Map<String, String> profileProperties = new HashMap<String, String>();
profileProperties.put(IProfile.PROP_INSTALL_FOLDER, installLocation.getAbsolutePath());
profileProperties.put(IProfile.PROP_FLAVOR, "tooling"); //$NON-NLS-1$
profileProperties.put(IProfile.PROP_ENVIRONMENTS, "osgi.os=" + Platform.getOS() + ",osgi.ws=" + Platform.getWS() + ",osgi.arch=" + Platform.getOSArch()); //$NON-NLS-1$;
profileProperties.put(IProfile.PROP_NL, "en_US"); //$NON-NLS-1$
profileProperties.put(IProfile.PROP_INSTALL_FEATURES, "true");
profileProperties.put(IProfile.PROP_CONFIGURATION_FOLDER, new File(installLocation, "configuration").getAbsolutePath());
profileProperties.put(IProfile.PROP_ROAMING, "true");
profileProperties.put(IProfile.PROP_CACHE, installLocation.getAbsolutePath());
currentProfile = registry.addProfile(PROFILE_ID, profileProperties);
PROP_INSTALL_FOLDER设置安装的目录,PROP_CACHE设置保存下载来的Eclipse IU(features/plugins)的目录,如果repository是以feature为单位来发布的话,需要设置PROP_INSTALL_FEATURES为true。如果repository包括native的binary(比如launcher)也需要指定正确的PROP_ENVIROMENTS,包括OS,WS,ARCH或PROCESSOR。
然后需要获得将要安装的IMetadataRepository集合。比如:
ArrayList<IInstallableUnit> ius = new ArrayList<IInstallableUnit>();
IMetadataRepositoryManager repositoryManager = (IMetadataRepositoryManager) ServiceHelper.getService(Activator.getDefault().getBundle().getBundleContext(),
IMetadataRepositoryManager.class.getName());
if (repositoryManager == null)
throw new InterruptedException("Failed to get IMetadataRepositoryManager.");
try {
for (URI uri : uris) {
IMetadataRepository metaRepo = repositoryManager.loadRepository(uri, progress.newChild(50/uris.length));
Collector collector = metaRepo.query(new AccpetQuery(), new LatestNoninstalledIUCollector(currentProfile), progress.newChild(50/uris.length));
ius.addAll(collector.toCollection());
}
} catch (ProvisionException e) {
throw new InterruptedException("Failed to get IMetadataRepository.");
}-同时这里也查找出IMetaRepository中没安装过的IUs。这就需要同当前安装的profile中已经安装过的内容来比较,
Collector collector = metaRepo.query(new AccpetQuery(), new LatestNoninstalledIUCollector(currentProfile), progress.newChild(50/uris.length));
这里需要指出的是,IMetadataRepository实现了IQueryable接口。IQueryable是p2引入的查找接口,返回满足特殊查询条件的集合,同时传入了一个IProgressMonitor对象,可以反应查找进度。这里的AcceptQuery,LatestNoninstalledIUCollector是自定义的Query和Collector对象。p2已经实现了许多有用的Query,经常用到的有InstallableUnitQuery,IUPropertyQuery,RangeQuery。
-接下来生成IEngine所需的ProvisionPlan。首先创建ProfileChangeRequest对象,将先前查找出的要安装的IUs添加进去。 request.addInstallableUnits(ius);
删除的话则与之相反。更新的话也需要通过ProfileChangeRequest.removeInstallableUnits()去掉旧版本的IUs。
调用IPlanner service的getProvisioningPlan(ProfileChangeRequest, ProvisioningContext, IProgressMonitor)得到对应于当前request的plan。
-最后就是调用IEngine.perform(IProfile, PhaseSet, Operand[], ProvisioningContext, IProgressMonitor)来执行provisioning操作。这里的PhaseSet是用来指定Engine将要执行的几个阶段,以及每个阶段的执行时间权重。这些阶段包括了Collect, Unconfigure, Uninstall, Property, CheckTrust, Install, Configure. 如果熟悉Eclipse之前的Installer Handler,对Unconfigure/Uninstall/Install/Configure应该都很熟悉。 在p2里,更是将Collect, CheckTrust这些过程也暴露了出来。下面是p2里默认PhaseSet的实现,
public DefaultPhaseSet() {
this(new Phase[] {new Collect(100), new Unconfigure(10, forcedUninstall), new Uninstall(50, forcedUninstall), new Property(1), new CheckTrust(10), new Install(50), new Configure(10)});
}
p2 install practice
先制作一个可安装的repository,这里的方法是基于Eclipse提供的模版创建一个RCP程序,比如mail template,
然后创建一个feature包含刚才创建出来的plug-in 'com.example.mail'。
基于存在的‘com.example.mail.product’创建product configuration,将其设置为base on features, 同时在dependencies页面添加以下feature。feature的qaulifier id依赖于用到的Eclipsse版本,从下图看到我这里使用的是Eclipse 3.5.1。如果要让RCP程序具有安装插件的能力(包含p2和p2 UI),就需要依赖更多的feature。后面的example里面会实现这部分功能。另外注意:ID不能包括空格字符。
接下来使用Eclipse Product Export Wizard生成repository。记得要勾选上generate metadata repository。
在成功创建了Mail Application的repository后,试用我们自己的p2 installer来安装这个应用程序。安装过程类似下面的截图。然后执行/folk/kzhu0/tmp/mailrcp/mail来运行Mail Application.
p2 repository publish
这一节将会展示如何发布/产生基于p2的repository。在p2最早的版本Eclipse 3.4中将生成repository这个程序称为generator,而3.5对此重构后命名为publisher。重构后的publish过程简单明了。首先需要创建一个IPublishInfo对象,它负责提供将要生成的repository的情况。包括了meta repository, artifact repository的信息,属性,以及提供辅助信息的advice对象。IPublisherAdvice可以看作类似创建RCP窗口时候的WorkbenchAdvice和WorkbenchWindowAdvice等辅助类。它用来提供需要记录在repository中的IU特殊信息。比如IU的属性,touchpoint的类型及各个阶段执行的action,对可执行文件或配置文件IU的处理。此外还需要创建IPublisherAction来处理不同类型的IU发布过程。例如BundlesAction来实现发布bundles到repository,FeaturesAction则是处理feature。此外p2已提供的IPublisherAction还包括product action, config action, launcher action和jre action等等[2]。
有了描述repository情况的publishinfo和发布各种IUs的action后,调用Publisher.publish方法完成repository的发布。
IPublisherInfo info = createPublisherInfo();
IPublisherAction[] actions = createActions();
Publisher publisher = new Publisher(info);
publisher.publish(actions, new NullProgressMonitor());
这里有一点需要注意,publish只是把将要用于部署的features/plugins/binary发布到repository,并不负责编译打包它们。先前我们使用过Eclipse Export功能既编译打包features/plugins同时又生成repository。Export实现的过程首先是调用PDE来编译打包features/plugins,再调用对应的publisher应用程序将编译后的features/plugins/product发布为repository。
customized p2 touchpoint
前面一节已经提过IPublishInfo通过额外的IPublisherAdvise来定制发布到repository的IU信息。这里介绍为自己的IU定制新的touchpoint类型,并且要求在配置阶段在操作系统桌面创建应用程序的启动快捷方式。首先为我们的PublisherInfo添加处理touchpoint data的advice,NativeLauncherTouchPoint实现了ITouchpointAdvice接口,publisher在发布的时候当处理到touchpoint data部分,会查找实现了ITouchpointAdvice接口的advice。如果有advice可用,将会让这些advice处理现有的touchpoint data,并且得到新的touchpoint data,并把结果保存到metadata repository当中。 PublisherInfo result = new PublisherInfo();
result.addAdvice(new NativeLauncherTouchPoint());
NativeLauncherTouchPoint将指定为特定的IU在configure阶段执行createDesktop操作,以及相反的操作,unconfigure阶段执行deleteDesktop操作。result.addAdvice(new NativeLauncherTouchPoint());
iu.setTouchpointType(DesktopTouchpoint.TOUCHPOINT_TYPE);
touchpoint类型和action都是通过extension point来扩展的。通过扩展“org.eclipse.equinox.p2.engine.touchpoints”来添加新的touchpoint类型,扩展”org.eclipse.equinox.p2.engine.actions“将新的action同某个类型关联起来。
p2 repository publish practice
我们创建plug-in 'com.example.p2.touchpoint'来实现桌面快捷方式的扩展,并且创建'com.example.p2.feature'包含touchpoint实现的plug-in。具体实现请参考p2 example源码。然后为Mail Application添加p2相关feature的依赖,重新发布得到支持安装软件的新版本。并且用p2 example installer安装它。p.s: 个人感觉Eclipse在包含第三方plug-in时,层次有些问题。p2作为一个runtime的project(跟equinox, ECF同级),居然需要直接或间接依赖help, rcp.platform这样的上层模块。
接下来创建plug-in 'com.example.mail.desktop' 和 feature 'com.example.mail.desktop.feature',作为提供桌面快捷方式的IU。用Eclipse Export Feature将'com.example.mail.desktop.feature'导出,实际就是用PDE替我们编译打包:)。
运行‘com.example.p2.generator'提供的headless publisher来生成我们定制的repository。’/folk/kzhu0/tmp/mail/desktop-deploy'是先前desktop feature导出后的路径,而'/folk/kzhu0/tmp/mail/desktop'是生成repository的路径。
运行新版本的Mail Application,在Help菜单下面会多出Install New Software选项。将自定义publisher生成的Desktop feature repository添加为新的软件源,安装Mail Desktop Feature。安装完成后,将在桌面找到Mail Application的快捷方式。在Installation Detail里面将会出现这次安装的内容。选中Desktop Feature后选择卸载,桌面的快捷方式文件将会被删除掉。当然也可以使用p2 example installer来为Mail Application安装desktop feature。p.s: example代码里只实现了创建linux/unix桌面快捷方式。
Example Code
Example Code应该只能编译运行在Eclipse 3.5.x。Example Code使用的都是p2 internal API, 而p2 public API将会随Eclipse 3.6首次发布。这些类和方法基本都会保留,但命名,包一定会有重构。http://code.google.com/p/kane-toolkit/source/browse/#svn/trunk/p2-example
Reference
[1] http://wiki.eclipse.org/Equinox/p2/Concepts[2] http://wiki.eclipse.org/Equinox/p2/Publisher
[3] http://wiki.eclipse.org/Equinox/p2/Engine/Touchpoint_Instructions
Tuesday, October 27, 2009
Simulate p2 self host in Eclipse run
-Dosgi.install.area=<launcher's folder>
-Declipse.p2.profile=<profile id>
-Declipse.p2.profile=<profile id>
Subscribe to:
Posts (Atom)