Wednesday, December 30, 2009

How p2 UI handles with license agreements

P2 install wizard firstly query the repository to find out the root installable unit(as well as top installable). Then p2 recalculate the dependency and try to search the requirements in all available repositories after user submits their installation request. Go to the license agreement page if all the dependencies are satisfied.

P2 agreement page obtains all the units to be installed from the operands of provision plan. The number always is much greater than the number submitted by user. Because the submitted IUs only are the root IUs.

P2 UI would check the unaccepted licenses comparing to before records. The policy class of p2 UI provides the license manager to record the even accepted license. It traverses all the installable units, querying its license whether it has already been accepted if it has. If the license agreement has been accepted, it would be ignored, won't be shown in the agreement page. Otherwise, new record is created to mark it as accepted by the license manager and display it in the agreement wizard page.

The default implementation of license manager would persist the accepted information in the file -- <workspace>/.metadata/.plugins/org.eclipse.equinox.p2.ui.sdk/license.xml.

Tuesday, December 29, 2009

[tip]ssh key

Setup SSH without password.
a) execute "ssh-keygen -t rsa" under your linux/unix login to get the RSA keys.
(press Enter for all)
You will get 2 files uner ~/.ssh/, id_rsa and id_rsa.pub
b) Put the public key id_rsa.pub to your remote host: ~/.ssh/authorized_keys If the remote host share the same nfs, just try " cat id_rsa.pub >> ~/.ssh/authorized_keys"
* Remember to modify hostname or ip info in ~/.ssh/authorized_keys to "*", so that you can login from any host without password in your NIS domain.
For example:
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA4Ri5J0s1BL/+mR7RfAuDW6FY2P6ILc61Zvw1BdDkvHMFrTzaC/AUMw33H7biAMCXuCleakCuSoV8ZDiGHYs4wOVvet5sDmphkwdiC4xTekdl3dRNvGjMVbvFUta/Y5CiayL6YIu47Ro6Vvu4Mutsrv/13pTlifrEz+NTR/+bzMb9nTniCwiryMyYod3E46b8WvS8yE3WK+tH4BZE8bjiCwdvAzSdPyk/OFNrlBNuF1yewwnxv1roRD3UalT2+7O4kfEG9sMvvBHjuX2l7xlUe3stBftYpigBbwGmmadxjRpNIlk88t5xKcQX6nSu7V8HI3GWPHI0D+ISIlbfU5Sunw== kzhu0@*

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.

Wednesday, December 2, 2009

[eclipse]How Equinox load bundles

How Equinox load bundles


Equinox launcher is responsible to start OSGi framework. The system bundle would be created and marked as installed when initializing the framework. Equinox also tries to install the installed bundles if finding them in persistence data during the initializing period. Of course there is no extra bundles would be installed when launching Equinox first time.


Then Equinox launcher would install the bundles specified by vm's system property 'osgi.bundles'. And start the initial bundles that are marked as early start. For example, let's have a look at the configuration/config.ini of Eclipse, you would find a line similar as below,
osgi.bundles=reference\:file\:org.eclipse.equinox.simpleconfigurator_1.0.200.v20090831.jar@1\:start
It means the start level of bundle 'org.eclipse.equinox.simpleconfigurator_1.0.200.v20090831.jar' is 1, and it would be started after installing it.

Here you would ask there are only two bundles are installed(one is system bundle 'org.eclipse.osgi', the other is 'org.eclipse.equinox.simpleconfigurator') when launching Equinox, how the other bundles are installed? It's done by the activate method of 'simpleconfigurator' bundle. The available bundles are recorded in plain text file configuration/org.eclipse.equinox.simpleconfigurator/bundles.info, simpleconfigurator read the file then install those bundles.

It's a new bundle management introduced by p2. P2 also supports the traditional way to install extensions, such as link file, .eclipseproduct file and directly copying features/plugins.
Below table lists the p2 bundles to implement the
compatibility installation feature,


Bundle
Usage
org.eclipse.equinox.p2.directorywatcher
the definition and implementation of directory watcher API
org.eclipse.equinox.p2.updatesite
the implementation of updatesite repository
org.eclipse.equinox.p2.extensionlocation
the implementation of extension repository
org.eclipse.equinox.p2.reconciler.dropins
scan dropin folder and link files; watch the traditional configuration file used by update manager

P2 reconciler would scan the dropin, link folder and legacy configuration file in every Equinox launching. You can disable the capability by marking it not be early start.
org.eclipse.equinox.p2.reconciler.dropins,1.0.100.v20091010,plugins/org.eclipse.equinox.p2.reconciler.dropins_1.0.100.v20091010.jar,4,false

If finding some new bundles in dorpin folder, the reconciler would add the new bundles into a local metadata repository that is stored as OSGi data of Equinox. Then synchronize the bundles into the current p2 profile, then add the new bundles into bundles.info file.

Thursday, November 12, 2009

[Eclipse][P2]Learn p2 step by step

Learn p2 step by step

Kane               

                     

p2 concept





首先来理解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
IU比较好理解,就是对可安装或配置的部分一种描述,并不对应实际要安装的文件。
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。

p2 install

有了这些概念以后,我们来看看如何使用p2 API。以安装为例,
首先需要得到当前安装的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)});
        }
     Operand[]通过ProvisionPlan.getOperands()获得。





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操作。

更改touchpoint type的方法如下。当然也可以为现有的touchpoint type扩展action。内置的touchpoint类型和action的具体命令用法,请参考p2 wiki[3]
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

Wednesday, October 28, 2009

[tip]ssh forward

ssh -qTfnN -D LocalPort remotehost

All the added options are for a ssh session that's used for tunneling.

-q :- be very quite, we are acting only as a tunnel.
-T :- Do not allocate a pseudo tty, we are only acting a tunnel.
-f :- move the ssh process to background, as we don't want to interact with this ssh session directly.
-N :- Do not execute remote command.
-n :- redirect standard input to /dev/null.

In addition on a slow line you can gain performance by enabling compression with the -C option.

Tuesday, October 27, 2009

Simulate p2 self host in Eclipse run

-Dosgi.install.area=<launcher's folder>
-Declipse.p2.profile=<profile id>

Thursday, October 22, 2009

Eclipse/OSGi preference

The IPreferenceStore API of Eclipse is based on OSGi's preferences service. Equinox implements several scope context for different preferences, such DefaultScope, InstanceScope and ConfigurationScope. The IPreferenceStore is the wrapper of instance scope for back-compatibility. It stored the data in workspace(osgi.data.area).
The workspace folder would be created when launching RCP application if it doesn't exist. But we can use argument '-data @none' to suppress the creation of workspace. If that, the instance scope/IPreferenceStore can't store any value any more.
There is a workaround to resolve such issue. Use ConfigurationScope instead of InstanceScope. Both of them are implemented the same interface, so it's easy to migrate to use ConfigurationScope. The data of configuration scope would be stored in @config.dir/.setting folder.

Wednesday, October 21, 2009

The usage of Eclipse's Proxy API

Eclipse platform register an OSGi service 'IProxyService' to manage network connection, which has capability to set proxy setting. There are three types of proxy working mode,
  • Direct(no proxy),
  • Manual(specified by user),
  • Native(using OS's proxy setting, such as gnome-proxy, IE).

There are three types of proxy supported by IProxyService. They're http, https and socks.

It also allows to add/remove ip address from white list, which are accessed without connecting proxy.

End users can manage the proxy setting of Eclipse via Preference - General - Network Connections. Eclipse would do persistence of user's setting. Other components of Eclipse also use those proxy settings to access network, such as ECF.

Below code snippet shows how to use proxy API to manually specify proxy server,

     proxyService.setProxiesEnabled(true);
proxyService.setSystemProxiesEnabled(false);
IProxyData[] datas = proxyService.getProxyData();
IProxyData proxyData = null;
for(IProxyData data : datas) {
// clean old data
((ProxyData)data).setSource("Manual"); //$NON-NLS-1$
data.setUserid(null); //$NON-NLS-1$
data.setPassword(null); //$NON-NLS-1$
if(proxyType == SOCKSPROXY && IProxyData.SOCKS_PROXY_TYPE.equals(data.getType())) {
proxyData = data;
continue;
}else if(proxyType == WEBPROXY && IProxyData.HTTP_PROXY_TYPE.equals(data.getType())){
proxyData = data;
continue;
}
data.setHost(null); //$NON-NLS-1$
data.setPort(0);
}
if(proxyData != null){
proxyData.setHost(proxyServer);
proxyData.setPort(proxyPort);
}
try {
proxyService.setProxyData(datas);
} catch (CoreException e) {
proxyService.setProxiesEnabled(false);
proxyService.setSystemProxiesEnabled(false);
return false;
}

Official API Reference

Monday, February 9, 2009

How to set default input method for GNOME

add below lines in ~/.gnomerc

export XMODIFIERS="@im=fcitx"
export GTK_IM_MODULE="xim"