Showing posts with label eclipse. Show all posts
Showing posts with label eclipse. Show all posts

Fix for ClassNotFoundException in PDE JUnit Test Runner

When trying to run a PDE JUnit test suite in Eclipse, the requested unit test is not executed, the hosted workbench terminates immediately with the following error to the console (where org.epic.perleditor.editors.TestEditorAssociation is the name of the unit test). The plug-in which contains the allegedly missing class is present in the workspace. (This error also falls into the "it used to work before" category.)

Class not found org.epic.perleditor.editors.TestEditorAssociation
java.lang.ClassNotFoundException: org.epic.perleditor.editors.TestEditorAssociation
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:481)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:397)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:385)
 at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:87)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:313)
 at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
 at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadClass(AbstractBundle.java:1274)
 at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner$BundleClassLoader.findClass(RemotePluginTestRunner.java:38)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:425)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:445)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
 at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
 at org.eclipse.pde.internal.junit.runtime.UITestApplication$1.run(UITestApplication.java:114)
 at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
 at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
 at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3378)
 at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3036)
 at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2382)
 at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2346)
 at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2198)
 at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:493)
 at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:288)
 at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:488)
 at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
 at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:113)
 at org.eclipse.pde.internal.junit.runtime.UITestApplication.start(UITestApplication.java:46)
 at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:193)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:386)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:549)
 at org.eclipse.equinox.launcher.Main.basicRun(Main.java:504)
 at org.eclipse.equinox.launcher.Main.run(Main.java:1236)
 at org.eclipse.equinox.launcher.Main.main(Main.java:1212)

Solution: edit META-INF/MANIFEST.MF of the plug-in containing the not found class and add the class folder (i.e. bin) to the Bundle-ClassPath: directive, like so:

Bundle-ClassPath: cglib-full-2.0.2.jar,
 easymock.jar,
 bin/

If you need to debug this problem in more detail, the method responsible for finding the class (which works in a good configuration, and fails otherwise) is org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass.

Fix for "Cannot complete the install because of a conflicting dependency."

When plug-in installation fails in newer versions of Eclipse (such as "e4"), you may have to deal with cryptic error messages such as this one:

Cannot complete the install because of a conflicting dependency.
 Software being installed: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
 Software currently installed: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
 Only one of the following can be installed at once:
   Core Runtime 3.6.100.v20101122 (org.eclipse.core.runtime 3.6.100.v20101122)
   Core Runtime 3.7.0.v20110110 (org.eclipse.core.runtime 3.7.0.v20110110)
 Only one of the following can be installed at once:
   Equinox Launcher Linux X86 Fragment 1.1.100.v20110321 (org.eclipse.equinox.launcher.gtk.linux.x86 1.1.100.v20110321)
   Equinox Launcher Linux X86 Fragment 1.1.100.v20101018 (org.eclipse.equinox.launcher.gtk.linux.x86 1.1.100.v20101018)
 Only one of the following can be installed at once:
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20110411 (org.eclipse.equinox.security 1.1.0.v20110411)
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20101004 (org.eclipse.equinox.security 1.1.0.v20101004)
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20110124-0830 (org.eclipse.equinox.security 1.1.0.v20110124-0830)
 Only one of the following can be installed at once:
   Refactoring UI 3.5.100.v20101012-0800 (org.eclipse.ltk.ui.refactoring 3.5.100.v20101012-0800)
   Refactoring UI 3.6.0.v20110307-2000a (org.eclipse.ltk.ui.refactoring 3.6.0.v20110307-2000a)
   Refactoring UI 3.5.100.v20110111-0800 (org.eclipse.ltk.ui.refactoring 3.5.100.v20110111-0800)
 Only one of the following can be installed at once:
   Eclipse Platform 4.1.0.v20110420-1420 (org.eclipse.platform 4.1.0.v20110420-1420)
   Eclipse Platform 4.1.0.v20110415-1401 (org.eclipse.platform 4.1.0.v20110415-1401)
   Eclipse Platform 3.7.0.v201104210100 (org.eclipse.platform 3.7.0.v201104210100)
   Eclipse Platform 3.7.0.v201103101119 (org.eclipse.platform 3.7.0.v201103101119)
   Eclipse Platform 4.1.0.v20110407-2200 (org.eclipse.platform 4.1.0.v20110407-2200)
   Eclipse Platform 4.1.0.v20110415-1030 (org.eclipse.platform 4.1.0.v20110415-1030)
   Eclipse Platform 3.7.0.v201103220800 (org.eclipse.platform 3.7.0.v201103220800)
   Eclipse Platform 3.7.0.v201104061223 (org.eclipse.platform 3.7.0.v201104061223)
   Eclipse Platform 4.1.0.v20110412-2200 (org.eclipse.platform 4.1.0.v20110412-2200)
   Eclipse Platform 3.7.0.v201104121532 (org.eclipse.platform 3.7.0.v201104121532)
   Eclipse Platform 3.7.0.v201104211800 (org.eclipse.platform 3.7.0.v201104211800)
   Eclipse Platform 4.1.0.v20110127-2200 (org.eclipse.platform 4.1.0.v20110127-2200)
   Eclipse Platform 3.7.0.v201102150800 (org.eclipse.platform 3.7.0.v201102150800)
   Eclipse Platform 4.1.0.v20101202-1530 (org.eclipse.platform 4.1.0.v20101202-1530)
   Eclipse Platform 4.1.0.v20110312-1300 (org.eclipse.platform 4.1.0.v20110312-1300)
   Eclipse Platform 4.1.0.v20110325-1411 (org.eclipse.platform 4.1.0.v20110325-1411)
   Eclipse Platform 4.1.0.v20110419-2200 (org.eclipse.platform 4.1.0.v20110419-2200)
   Eclipse Platform 3.7.0.v201012081300 (org.eclipse.platform 3.7.0.v201012081300)
   Eclipse Platform 4.1.0.v20110422-0200 (org.eclipse.platform 4.1.0.v20110422-0200)
   Eclipse Platform 4.1.0.v20110421-0500 (org.eclipse.platform 4.1.0.v20110421-0500)
   Eclipse Platform 4.1.0.v20110414-2200 (org.eclipse.platform 4.1.0.v20110414-2200)
   Eclipse Platform 3.7.0.v201104191004 (org.eclipse.platform 3.7.0.v201104191004)
 Cannot satisfy dependency:
   From: Equinox p2 Provisioning for IDEs 2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT (org.eclipse.equinox.p2.user.ui.feature.group 2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT)
   To: org.eclipse.equinox.security [1.1.0.v20110124-0830]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.r20110302-9gF7SHCIFt6ms-lIjrC6vK_XO-IabJMKu (org.eclipse.platform.feature.group 3.7.0.r20110302-9gF7SHCIFt6ms-lIjrC6vK_XO-IabJMKu)
   To: org.eclipse.platform [3.7.0.v201103101119]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20100923-9fF7MHDqFsAkplGz0n61z-yU57WHoz0JdMfaI (org.eclipse.platform.feature.group 3.7.0.v20100923-9fF7MHDqFsAkplGz0n61z-yU57WHoz0JdMfaI)
   To: org.eclipse.rcp.feature.group [3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110209-9fF7QHFQFsAlpyfO4rKw4lNW200gZcvGr (org.eclipse.platform.feature.group 3.7.0.v20110209-9fF7QHFQFsAlpyfO4rKw4lNW200gZcvGr)
   To: org.eclipse.ltk.ui.refactoring [3.5.100.v20110111-0800]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHCJFt3cwDesZ5LrhrcdfooveV1uWqcqPF (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHCJFt3cwDesZ5LrhrcdfooveV1uWqcqPF)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHIZFt3cwDktE9WrW9XEj1iOiW9fgbhxME (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHIZFt3cwDktE9WrW9XEj1iOiW9fgbhxME)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHLgFt4cwDmoWF-DvhVMEUrQfXBVSritR5 (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHLgFt4cwDmoWF-DvhVMEUrQfXBVSritR5)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDooaDrDsaVdEWi0oMLWlh6mbT (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDooaDrDsaVdEWi0oMLWlh6mbT)
   To: org.eclipse.equinox.security [1.1.0.v20110411]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDqmhLPCtURdWWmWmO0Plh6mbT (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDqmhLPCtURdWWmWmO0Plh6mbT)
   To: org.eclipse.equinox.security [1.1.0.v20110411]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDrmlStE-Ufz0RVqQoc8RS8f2gQ (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDrmlStE-Ufz0RVqQoc8RS8f2gQ)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ]
 Cannot satisfy dependency:
   From: Eclipse Platform 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU (org.eclipse.platform.feature.group 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU)
   To: org.eclipse.ltk.ui.refactoring [3.6.0.v20110307-2000a]
 Cannot satisfy dependency:
   From: Eclipse Platform 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU (org.eclipse.platform.feature.group 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU)
   To: org.eclipse.platform [4.1.0.v20110312-1300]
 Cannot satisfy dependency:
   From: Eclipse Product Configuration 1.0.0.I20110310-1119 (org.eclipse.rcp.configuration.feature.group 1.0.0.I20110310-1119)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20101018]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR (org.eclipse.rcp.feature.group 3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR)
   To: org.eclipse.core.runtime [3.6.100.v20101122]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse Project SDK 4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu (org.eclipse.sdk.feature.group 4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu)
   To: org.eclipse.platform.feature.group [4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.equinox.p2.user.ui.feature.group [2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.rcp.configuration.feature.group [1.0.0.I20110310-1119]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.sdk.feature.group [4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: toolingorg.eclipse.sdk.ide.configuration [4.1.0.I20110312-1300]
 Cannot satisfy dependency:
   From: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
   To: org.eclipse.platform.feature.group [3.2.0,4.0.0)
 Cannot satisfy dependency:
   From: toolinggtk.linux.x86org.eclipse.core.runtime 4.1.0.I20110312-1300
   To: bundle org.eclipse.core.runtime 3.7.0.v20110110
 Cannot satisfy dependency:
   From: toolingorg.eclipse.sdk.ide.configuration 4.1.0.I20110312-1300
   To: toolinggtk.linux.x86org.eclipse.core.runtime [4.1.0.I20110312-1300]

The usability problems with this error message are evident: excessive length, lack of focus, little clue for the developer about possible solutions (and even less for a mere user who is just trying to install a plug-in). Paradoxically, the best way to even know which parts of the error message are relevant at all is to have already solved the problem at hand. Having done just this, here are some possibly helpful inferences:

  • Chances that you are going to solve a problem of this sort as a normal user are slight. The error likely lies in dependency declarations contained in feature.xml descriptors that are being processed by the installer and you cannot/are not supposed to manually edit those. It's safe to assume that it's the developers' job to sort out this mess. The remaining bullet points address how.
  • The "Only one of the following can be installed at once" parts of the message are misleading and should be ignored as noise.
  • The "Cannot satisfy dependency" parts might be relevant, however most of them are bogus, resulting from error propagation, and should be ignored as noise as well.
  • The root cause is likely contained in the feature.xml descriptor of whatever it is that you are trying to install. In the above example my installation attempt concerns the EPIC IDE, and only the following short snippet from the bottom of the error message actually matters:
     Cannot satisfy dependency:
       From: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
       To: org.eclipse.platform.feature.group [3.2.0,4.0.0)
    
  • The above dependency comes from a line in content.xml, a file which is found within content.jar downloaded by the installer from the update site. In my case the relevant line of content.xml looked like so:
    <required namespace='org.eclipse.equinox.p2.iu'
             name='org.eclipse.platform.feature.group'
             range='[3.2.0,4.0.0)'/>
    
  • The file content.xml is a generated one, so to fix the problem (by removing the line) the developer must track back to the input files that are under his control. In my case it was feature.xml, which contained the following "offending" snippet:
      <requires>
         <import feature="org.eclipse.platform"
                    version="3.2.0"
                    match="compatible"/>
      </requires>
    

Eventually, changing match="compatible" to match="greaterOrEqual" in feature.xml and rebuilding content.jar fixed the cause of the original messy error message, as was to be expected ("compatible" means only compatible within the major version, i.e. "3.x.x"). After the fix content.xml contains range="3.2.0", which is fine for installation in 4.1.0.

Migrating Eclipse update sites to P2

This article describes how to upgrade an Eclipse update site from the classic, pre-P2 layout (site.xml together with plugins and features directories) to the "new and improved" (read: unnecessarily complicated) P2 layout. Considering that P2, the new update manager introduced in Eclipse 3.4, is backwards-compatible with the old layout of update sites (more or less), why bother at all? In my experience, browsing update sites with many plug-ins and features used to be somewhat slow before, but with P2 it has become excruciatingly slow. Fortunately, P2 also supports a new update site format designed to alleviate this problem, that is, to speed up update site browsing. However, as far as I know, no comprehensive documentation of the upgrade path for those of us still using the classic update site layout exists. More confusingly, the article Update Site Optimization (coming from the source?) only tells half of the story. As I found out, following the somewhat outdated instructions listed in there won't leave you with a working update site. Information from different sources had to be pieced together. Hopefully, no longer, if you read on.

Classic update site vs. P2 update site

Let's start by comparing the classic update site layout with the newer layout tailored for P2. I suppose you have a working update site organized in the classic way and want to switch to the new P2 one for reasons mentioned above:

Classic update siteP2 update site
update site = a world-accessible directory on a web serversame as before
update site contains site.xml, which contains a list of versioned features installable from this site update site contains content.jar and artifacts.jar, which together supersede site.xml
subdirectory features contains one JAR file per versioned feature, referenced from site.xml same as before, but the references now originate from content.jar and artifacts.jar
subdirectory plugins contains one JAR file per versioned plug-in, referenced from feature.xml files contained in feature JARs subdirectory plugins contains one .jar.pack.gz file per versioned plug-in, referenced from feature.xml as before, but also from content.jar and artifacts.jar
  update site (optionally?) contains digest.zip - see description below

While the syntax of feature.xml and site.xml is easy to understand and these files are easy to generate/process by your own tools if need be, the new content.jar and artifacts.jar leave no such hopes. You are advised to treat them as P2's private mess (found inside: fat, obscure XML documents). As you will see next, both these JAR files can and should be generated from a slightly modified version of site.xml.

The plug-in .jar.pack.gz files are also generated - each from the original plug-in JAR found of the classic update site. They are essentially JARs recursively compressed with the pack200 tool first introduced in Java 1.5.

The old site.xml and old plug-in JAR files are not ever accessed by the P2 update manager, but if you wish to stay backwards-compatible, you should keep them around in your update site as well.

If you read the Update Site Optimization article, you might be wondering whether a "digest" file (digest.zip) is also needed, where to put it, and what for. I have never observed P2 trying to access this file and suspect that it has been superseded by the content.jar and artifacts.jar duo (which the original article fails to mention). However, it is worth noting that the official Eclipse Ganymede update site does contain a digest.zip in the update site directory and an attribute digestURL="http://download.eclipse.org/releases/ganymede/" on the site element in site.xml (the value of this attribute points to the update site, i.e. location of digest.zip). We'll see next how to generate digest.zip just in case.

How to upgrade to a P2 update site

In essence, the following steps are required:

  1. Add a new attribute pack200="true" to the site element in site.xml.
  2. Add a new attribute digestURL="http://your/update/site/url/" to the site element in site.xml.
  3. Ensure that each of your feature JARs in the features directory contains a feature.properties file (which may be empty). (Here is why.)
  4. Generate a .jar.pack.gz file from each plug-in JAR file in the plugins subdirectory.
  5. Generate digest.zip based on the classic update site (including site.xml).
  6. Generate content.jar and artifacts.jar based on the classic update site (including site.xml).

Steps 1, 2, 3 do not require any sophistication and thus won't be further elaborated. Steps 4 and 5 are (surprisingly) intertwined, as described below. Step 6 is also described in the last section.

How to generate .jar.pack.gz files (Step 4) and digest.zip (Step 5)

The generating .jar.pack.gz files step actually consists of two parts:

  1. For each JAR file in question, "condition" or "repack" the JAR file to prepare it for the second part.
  2. Generate .jar.pack.gz files from a set of conditioned JAR files. Also generate digest.zip

In order to condition (repack) a JAR file, run:

$JAVA_HOME/bin/java \
    -jar $launcher \
    -application org.eclipse.update.core.siteOptimizer \
    -jarProcessor -verbose -processAll -repack -outputDir $output_dir/plugins \
    $input_jar_file

The above invocation contains some variables, to be replaced as follows:

JAVA_HOMEpath to where Java 1.5 (or newer) is installed
launcherpath to plugins/org.eclipse.equinox.launcher_1.0.101.R34x_v20080819.jar from your Eclipse 3.4 (or newer) installation (the version number in the JAR file name may vary)
output_dirpath to where the conditioned JAR file should be written - the plugins directory of the upgraded update site
input_filepath to the input plug-in JAR file

Sadly, you will have to run the above tool for each JAR file individually. Observe that the conditioned JARs contain META-INF/eclipse.inf, absent in unconditioned ones. Finally, copy site.xml and the features subdirectory of your original update site to $output_dir. This completes part 1.

For part 2, run the following command. Unlike part 1, this processes all the conditioned JARs it can find through site.xml:

$JAVA_HOME/bin/java \
    -jar $launcher \
    -application org.eclipse.update.core.siteOptimizer \
    -digestBuilder \
    -digestOutputDir=$output_dir \
    -siteXML=$output_dir/site.xml \\
    -jarProcessor -pack -outputDir $output_dir $output_dir

You should now have $output_dir/plugins full of conditioned JARs and a corresponding .pack.jar.gz for each of them. You should also have $output_dir/digest.zip. If not, maybe you forgot to take care of feature.properties in Step 3 mentioned earlier.

How to generate content.jar and artifacts.jar (Step 6)

Here is how (set a human readable $project_name first):

$JAVA_HOME/bin/java -jar $launcher \
    -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator \
    -updateSite ${output_dir}/ \
    -site file:${output_dir}/site.xml \
    -metadataRepository file:${output_dir}/ \
    -metadataRepositoryName "${project_name} Update Site" \
    -artifactRepository file:${output_dir}/ \
    -artifactRepositoryName "${project_name} Artifacts" \
    -compress \
    -reusePack200Files \
    -noDefaultIUs \
    -vmargs -Xmx256M

The final result

Here is an example P2-upgraded, backwards compatible update site with 1 feature version:

.
|-- site.xml
|-- artifacts.jar
|-- content.jar
|-- digest.zip
|-- features
|   `-- org.epic.feature.main_0.6.34.jar
`-- plugins
    |-- org.epic.debug_0.6.27.jar
    |-- org.epic.debug_0.6.27.jar.pack.gz
    |-- org.epic.doc_0.6.2.jar
    |-- org.epic.doc_0.6.2.jar.pack.gz
    |-- org.epic.lib_0.6.1.jar
    |-- org.epic.lib_0.6.1.jar.pack.gz
    |-- org.epic.perleditor_0.6.23.jar
    |-- org.epic.perleditor_0.6.23.jar.pack.gz
    |-- org.epic.regexp_0.6.1.jar
    |-- org.epic.regexp_0.6.1.jar.pack.gz
    |-- org.epic.source_0.6.34.jar
    `-- org.epic.source_0.6.34.jar.pack.gz

site.xml contains the two new attributes, pack200 and digestURL.

P2 will access content.jar, artifacts.jar, org.epic.feature.main_0.6.34.jar and the plug-in .jar.pack.gz files.

The classic update manager will access site.xml, org.epic.feature.main_0.6.34.jar and the plug-in .jar files.

Automation of the above process using Ant scripts, predefined Eclipse tasks or some such is left as an exercise for the reader. Also note another post with hints on how to debug helper applications started by the Eclipse launcher.

Debugging Eclipse helper applications

Eclipse includes some command-line tools for platform developers, which can be launched by providing the -application option to the Equinox launcher JAR (formerly startup.jar). A need may arise to debug one of those helper applications. Proceed as follows:

  1. Import the plug-in org.eclipse.equinox.launcher into workspace (with source code).
  2. Import the plug-in containing the helper application into workspace (with source code). For example: org.eclipse.update.core contains the siteOptimizer application.
  3. Create a debug configuration for the Java class org.eclipse.equinox.launcher.Main. Specify -Dosgi.install.area=/path/to/eclipse/installation/dir as a VM argument. Specify the remaining command-line arguments (e.g. -application ...).
  4. Set a breakpoint at the entry point to the helper application (likely a method called run or start).
  5. If the debugger asks for a source code location, point it to the workspace project.

Solution for missing startup.jar in Eclipse

Some articles, for example Update Site Optimization, mention a file startup.jar used for launching Eclipse's helper command-line applications. However, this file no longer exists in newer versions of Eclipse (such as 3.4).

Solution: just replace references to the missing startup.jar with references to $ECLIPSE_HOME/plugins/org.eclipse.equinox.launcher_version.jar (Insert the actual version found in your plugins directory).

Solution for no output from digestBuilder in Eclipse siteOptimizer

The article Update Site Optimization provides instructions for creating optimized update sites in Eclipse to speed up downloads. While following those instructions, you may run into a problem that the digestBuilder component seems to run (displaying lines such as "Extracting locales from feature jar" and "Processing... feature jar"), but no output is ever written to the specified digestOutputDir. Alternatively, digest.zip might be created, but missing some features.

Solution: make sure that each of your feature JAR files contains a file named feature.properties (even an empty one). As of Eclipse 3.4, features without this file are skipped altogether by the digestBuilder.

Further reading: Debugging Eclipse helper applications and the tutorial on how to optimize an Eclipse update site for P2.

Solution for Eclipse CVS merge conflicts

Problem: Eclipse's CVS merge feature (despite all your efforts) keeps showing tons of pseudo-conflicts (red conflict symbol with a superimposed white plus or cross sign, whatever it might mean). Solution: don't use Eclipse (for merging). Instead, fall back on the good old command-line CVS client; it's not that difficult.

Change to your project directory and issue a command like:

cvs -q update -d \
    -j "VERSION_2_2:2008-11-24" \
    -j VERSION_2_2 &> /tmp/merge.log

Here VERSION_2_2 is your source branch, from which the merging should occur. The first -j also contains a start tag (or date).

Inspect merge.log for mentions of "warning" and "conflict". Inspect the reported files and merge manually around the standard CVS conflict markers, i.e. >>>>, ==== and <<<<). Finally, delete all files whose names match the pattern .#* (created by the CVS client to indicate which files were affected by merge).

There is one catch here - the extssh access mode routinely used by Eclipse is not recognized by the command-line CVS client, so you will have to replace :extssh: strings by :ext: in all CVS/Root files within your project and set the environment variable CVS_RSH=/usr/bin/ssh to emulate Eclipse's behavior (after you're done revert the replacements).