Dienstag, 24. Mai 2011

Tycho: Creating an Eclipse P2 Mirror

This Blog post covers the creation of a P2 mirror for parts of the central Eclipse update site.

1. Downloading all necessary Eclipse packages

First of all you need to download all necessary Eclipse packages containing the plugins and features to be mirrored.
Download at least the lastest version of following packages:
  • Any Eclipse Platform SDK Build (platform doesn't matter)
  • Eclipse Delta Pack
You may download additonal packages too like:
  • Eclipse Modeling Tools (includes Incubating components) (platform doesn't matter)
  • Eclipse IDE for Java EE Developers
  • Eclipse Webtools Platform SDK (http://download.eclipse.org/webtools/downloads/)
  • Eclipse Babel Translations
  • etc. 
  • etc.
 Important: You always need a feature based version, thus the download have to contain the features and plugins folder.

2. Extract the contents
Extract all packages into a single directory. Finally you should have an Eclipse folder like this:
|- eclipse
  |- features
  |- plugins
  |- site.xml
  |- etc.

3. Create site.xml
After you prepared the eclipse folder you need to create a site.xml containing the list of features of your eclipse folder. This is a very painful work. To make it a bit easier i extended an existing tool from Lars Heinemann. You can grap a copy from https://github.com/sweber/EclipseToMavenTooling.
To run the tool just clone the git repository and run following commands from you favorite shell:
mvn clean install
java -cp target/eclipse2maven-1.0.0-SNAPSHOT.jar de.lhein.tooling.MainWindow
After this choose your eclipse folder from step 2, check the "Generate site.xml" button and git generate. After that you may copy the contents to your site.xml or save it directly into the eclipse folder.
You now have a old style eclips update site.

4. Generate P2 repository from Update Site
Next you have to create the P2 repository from the existing update site.
You can use a built-in Eclipse application to process your update site:

java -jar plugins/org.eclipse.equinox.launcher_*.jar
 -nosplash -application org.eclipse.equinox.p2.publisher.UpdateSitePublisher
 -metadataRepository file:/<location_where_the_repository_will_be_created>
 -artifactRepository file:/<location_where_the_repository_will_be_created>
 -source <the_eclipse_installation_directory_for_which_the_p2_repository_will_be_created>
-compress -publishArtifacts -addJREIU

After executing this command from any Eclipse install location (e.g. your development Eclipse >v3.5) your P2 repository is ready to go.

5. Remove external repository references (optional)
If you do not want that Eclipse or Tycho tries to resolve any other dependecy from the online Eclipse P2 repositories you have to remove the repository references from the generated P2 repository.

To do this open the content.jar located in your generated P2 repository and extract the included content.xml. Edit the content.xml and remove the reference section at the top of the file:

Before:
<?xml version='1.0' encoding='UTF-8'?>
<?metadataRepository version='1.1.0'?>
<repository name='file:/d:/tmp/repository/ - metadata' type='org.eclipse.equinox.internal.p2.metadata.repository.LocalMetadataRepository' version='1'>
  <properties size='2'>
    <property name='p2.timestamp' value='1301562918125'/>
    <property name='p2.compressed' value='true'/>
  </properties>
  <references size='36'>
    <repository uri='http://download.eclipse.org/releases/galileo' url='http://download.eclipse.org/releases/galileo' type='0' options='0'/>
    <repository uri='http://download.eclipse.org/releases/galileo' url='http://download.eclipse.org/releases/galileo' type='1' options='0'/>
    <repository uri='http://download.eclipse.org/dsdp/tm/updates/3.2' url='http://download.eclipse.org/dsdp/tm/updates/3.2' type='1' options='0'/>
<!--(...)-->
    <repository uri='http://download.eclipse.org/technology/epp/updates/1.3/' url='http://download.eclipse.org/technology/epp/updates/1.3/' type='0' options='0'/>
    <repository uri='http://download.eclipse.org/rt/rap/1.3/tooling' url='http://download.eclipse.org/rt/rap/1.3/tooling' type='1' options='0'/>
  </references>
  <units size='1706'>
    <unit id='org.eclipse.emf.compare.diff.edit' version='1.1.2.v20110202-0936'>
      <update id='org.eclipse.emf.compare.diff.edit' range='\[0.0.0,1.1.2.v20110202-0936)' severity='0'/>
      <properties size='5'>
        <property name='df_LT.providerName' value='Eclipse Modeling Project'/>
        <property name='df_LT.pluginName' value='Diff Edit Support'/>
        <property name='org.eclipse.equinox.p2.name' value='%pluginName'/>
        <property name='org.eclipse.equinox.p2.provider' value='%providerName'/>
        <property name='org.eclipse.equinox.p2.bundle.localization' value='plugin'/>
      </properties>
  </unit>
<!--(...)-->
 </units>
</repository>

After:
<?xml version='1.0' encoding='UTF-8'?>
<?metadataRepository version='1.1.0'?>
<repository name='file:/d:/tmp/repository/ - metadata' type='org.eclipse.equinox.internal.p2.metadata.repository.LocalMetadataRepository' version='1'>
  <properties size='2'>
    <property name='p2.timestamp' value='1301562918125'/>
    <property name='p2.compressed' value='true'/>
  </properties>
  <units size='1706'>
    <unit id='org.eclipse.emf.compare.diff.edit' version='1.1.2.v20110202-0936'>
      <update id='org.eclipse.emf.compare.diff.edit' range='\[0.0.0,1.1.2.v20110202-0936)' severity='0'/>
      <properties size='5'>
        <property name='df_LT.providerName' value='Eclipse Modeling Project'/>
        <property name='df_LT.pluginName' value='Diff Edit Support'/>
        <property name='org.eclipse.equinox.p2.name' value='%pluginName'/>
        <property name='org.eclipse.equinox.p2.provider' value='%providerName'/>
        <property name='org.eclipse.equinox.p2.bundle.localization' value='plugin'/>
      </properties>
  </unit>
<!--(...)-->
 </units>
</repository>
Then replace the content.xml inside the content.jar with the modified one.

Et voilà you're finished and have an Eclipse mirror for your Eclipse packages. If you want to share it with your coworkers just put it on your companies internal webserver.

Enjoy!

Montag, 14. März 2011

JAXB2 Primitive Fixer Plugin

Shortly we stumbled on creating a data binding with EclipseLink (Moxy). Moxy is the Eclipse JAXB implementation which can be tightly integrated into the eclipse runtime (keyword OSGI). After quite some work we came to the point were the binding was working fine. Suddenly we had to change the xml schema due to an requesst from another department. So we introduced default values for attributes with native xsd types like xs:int, xs:boolean or xs:float. Nothing you would expect to cause trouble.

Well, we were wrong! JAXB 2.x has a major problem with default values and primitive types. Its an major drawback in every implementation. It points out that the the generate Java classes have inconsistent getter for attributes with default values with primitive types.

Short example for a generated class (omited class overhead):
/** The property name for the value property. */
    public final static String PNAME_VALUE = "value";
    @XmlAttribute
    protected Integer value;
    /**
     * Gets the value of the value property.
     * @return possible object is {@link Integer }
     */
    public int getValue() {
        if (value == null) {
            return  75;
        } else {
            return value;
        }
    }
    /**
     * Sets the value of the value property.
     * @param value allowed object is {@link Integer }
     */
    public void setValue(Integer value) {
         this.value = value;
    }
As you can see, the getter returns an int while the setter expect an Integer. This will result in a not working Eclipse data binding (getter and setter do no match).

There is already an JIRA issue filed for this on the JAXB JIRA.
http://java.net/jira/browse/JAXB-733
However it won't be solved because it would destroy the backward compatibility of JAXB.

A suggestions was to create a JAXB XJC plugin that corrects this issue. So i did, you may grab a copy from
https://github.com/sweber/JAXB2-XJC-Primitive-Fixer-Plugin

The plugin searches for getter/setter pairs and if the setter expects a boxified primitive and the getter returns a primitive it will correct the getter to return the boxified primitive. Additional for boolean it will change the getter name from isXY to getXY (in respect to the Java bean notation).

To use it with maven just deploy the binary jar file to you repository and put it into your maven-jaxb2-plugin configuration like this:

    org.jvnet.jaxb2.maven2
    maven-jaxb2-plugin
    0.7.4
    
        
            
        
    
    
        true
        
            -Xfix-primitives
        
        
            
                de.sweber
                jaxb2-primitive-fixer-plugin
                1.0.0-SNAPSHOT
            
        
    

The argument you have to pass to XJC to get the plugin working is (like above used):
-Xfix-primitives

Hope i could solve a little problem with JAXB out there ;-)

Freitag, 21. Januar 2011

Confluence: Include previous versions of a page inside another page

The company I work for had an demand for a confluence plugin/macro that allows to inline pages of older versions. The standard Atlassian include macro just allows to include the most recent version of a page.
With some digging in the Atlassian confluence documentation and with some helpful comments i figured it out by myself and wrote a macro for it.

The macro is based on the render macro from James Mortimer (thx for it) and a code snippet from Azwandi Mohd Aris from Atlassian.

Below is the source of the macro. Just create a new user macro which outputs HTML and is set to have no body.


## declare render macro
## thanks to James Mortimer
## see http://confluence.atlassian.com/display/DISC/Render+any+text+from+a+user+macro
#macro(trimp $xhtml)
## remove leading and trailing 
#if($xhtml.startsWith("",0) && $xhtml.endsWith("
"))
#set($len=$xhtml.length()-$generalUtil.convertToInteger(4))
#set($xhtml=$xhtml.substring(3,$len))
#end
$xhtml
#end
#macro(render $wiki)
#set($globalHelper=$action.getHelper())
#if($content)## i. e. we render a normal page
#set($renderer=$globalHelper.getWikiStyleRenderer())
#set($context=$content.toPageContext())
#set($xhtml=$renderer.convertWikiToXHtml($context, $wiki).replaceAll("\n|\r",""))
#else## we are e. g. in Global Template Preview
#set($xhtml=$globalHelper.renderConfluenceMacro($wiki).replaceAll("\n|\r",""))
#end
#trimp(${xhtml})
#end


## get pagemanager
## thants to Azwandi Mohd Aris [Atlassian]
## see http://confluence.atlassian.com/display/DOC/Include+Page+Macro?focusedCommentId=218279433#comment-218279433
#set($containerManagerClass=$content.class.forName('com.atlassian.spring.container.ContainerManager'))
#set($getInstanceMethod=$containerManagerClass.getDeclaredMethod('getInstance',null))
#set($containerManager=$getInstanceMethod.invoke(null,null))
#set($containerContext=$containerManager.containerContext)
#set($pageManager=$containerContext.getComponent('pageManager'))

## parse the parameters

#set($spacekey=$space.key)
#if($paramspace)
#set($spacekey=$paramspace)
#end

#set($pageversion='0')
#if($paramversion)
#set($pageversion=$paramversion)
#end

#set($pagename="undefined")
#if($parampage)
#set($pagename=$parampage)
#end

## parse the versionstring to an int
#set($versionInt=$generalUtil.convertToInteger($pageversion))

#set($helper=$action.getHelper())
#set($page=$pageManager.getPage($spacekey,$pagename))


## only advance if we have a page
#if($page)
#set($historypage=$pageManager.getOtherVersion($page,$versionInt))
#end

#if($historypage.getId()==$content.getId())
$helper.renderConfluenceMacro("{warning}The version '$pageversion' of page '$pagename' in space '$spacekey' is the same page as this one! Please correct the version information used for the include macro.{warning}")
#elseif($historypage)
#render($historypage.getContent())
#else
$helper.renderConfluenceMacro("{warning}Version '$pageversion' of page '$pagename' in space '$spacekey' not found!{warning}")
#end

Usage:
{pageinclude:page=name of the page|version=x[|space=spacekey]} 
The spacekey is optional. By default the spacekey of the current space is used.

Example:
If you want to include the page "Demonstration" in version 3 from the space with the key "myspace" use the following macro statment
{pageinclude:page=Demonstration|version=3|space=myspace}

Important:
Do not surround the pagename and the spacekey with quotes!

Have fun with it! :-)


P.S. Do not try to include the same page and version as the page that includes the macro. I took this into account! ;-)

Dienstag, 4. Januar 2011

Forked EclipseToMavenTooling

At my company we currently build our GUI clients based on Eclipse RCP. To create and deploy a RCP for our products we use maven combined with the assembly plugin and install4j. This requires that every Eclipse dependency is being copied to the assembly. Therefore a list of dependencies for the Eclipse RCP must be maintained.
A former collegue of me wrote such a tool which he has made available under
https://github.com/lhein/EclipseToMavenTooling

This tool is very handy but doesn't take features into account. So I forked his Git project and added the support for collecting feature dependencies. You can get it from here:
https://github.com/sweber/EclipseToMavenTooling


Stefan