May 22, 2013

Custom touchpoints in p2

For most people, p2 is simply the Install  Software Dialog  in Eclipse. While this is certainly true (or at least the Install Software Dialog is built with p2), p2 is much more than this. The Eclipse Provisioning Platform (p2) is a general purpose platform for provisioning everything and nothing in particular.

Most of the out-of-the-box p2 tools are related to provisioning OSGi bundles (or Eclipse plug-ins, if you prefer), but there is nothing in the core that limits us to Eclipse or OSGi. In fact, you can extend p2 to install whatever you want. In this example I’ll show you how to create a custom Install Action (touchpoint) that executes an arbitrary binary during installation.

Background

When you use p2 to install software it goes through a number of steps.

  1. A provisioning plan is computed. Based on what you already have installed and the install action you’re taking, p2 computes a plan. The plan includes operands such as: remove foo, upgrade bar, install baz, etc..
  2. Artifacts are fetched and checked. Once a plan is constructed, the plan is executed on the p2 engine. The first step is fetching all the artifacts and the trust checking (checksum validation)
  3. Unconfigure / Uninstall. If there are any units being removed, they are given a chance to ‘unconfigure’ themsevles. Once this is done, they are uninstalled.
  4. Install / Configure. If there are any units being added, they are first installed and then they are given a chance to configure themselves.

For installable units being upgraded, this is essentially an unconfigure / uninstall followed by an instal / configure.

The Problem

p2 has a number of actions you can invoke when an installable unit (IU) is installed, configured, unconfigured or uninstalled. For example, artifacts can be unzippedcopied or the permissions can be set. These actions are called touchpoint actions. Eclipse has two sets of touchpoint actions, native and OSGi. Native ones are general (such as move, copy, mkdir) while the OSGi ones are Eclipse or OSGi specific (install bundle, set VM Args, etc…).

While these are a good start (and enough to provision an Eclipse system), often you need custom actions if you are provisioning a highly specific system. Also, not only do you need custom actions, you need them to be installed before they can be used.

Let’s consider the case where we want to ship and run an arbitrary executable during the installation of a plugin.

Touchpoint Actions

Custom touchpoint actions can be specified using the org.eclipse.equinox.p2.engine.action extension point. Touchpoint Actions are tied to a touchpoint, the controller responsible for executing the action. You can either write your own custom touchpoint, or simply tie your action to an existing one. In our case, we will tie our touchpoint action to the OSGi touchpoint.

Screen Shot 2013 05 22 at 3.43.23 PM Custom touchpoints in p2All touchpoint actions extend ProvisioningAction and provide two methods execute and undo. Execute is used whenever the provisioning action is invoked. Undo is used if the action needs to be rolled back. In our case we will implement execute to read a parameter and call Runtime#exec on the value of that parameter.

public IStatus execute(Map<String, Object> parameters) {
  String artifact = null;
  if ( parameters.containsKey(ARTIFACT) ) {
    // If an artifact is specified, look it up
  }
  String program = (String)parameters.get(PROGRAM);
  String fullPath = computeProgramPath(artifact, program);
  try {
    Runtime.getRuntime().exec(fullPath);
  } catch (IOException e) {
    e.printStackTrace();
    return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot execute " + fullPath);
  }
  return Status.OK_STATUS;
}

The undo method (that is, the inverse of Runtime#exec) has been left as an exercise to the reader icon wink Custom touchpoints in p2 .

MetaRequirements

In order to ensure that  your touchpoints get installed before the components that need them, you can either invoke two updates (update the installer and then update the application) or you can use MetaRequirements. MetaRequirements invoke a two stage plan, ensuring that the touchpoints are fetched and installed before they are invoked.

MetaRequirements can be declared in a p2.inf file as follows.

metaRequirements.0.namespace=org.eclipse.equinox.p2.iu
metaRequirements.0.name=com.ianbull.p2.touchpoint.exec
metaRequirements.0.range=[1,2)

In this case we’ve specified a metaRequirement on the Installable Unit com.ianbull.p2.touchpoint.exec version [1.0,2.0). This Installable Unit must exist before our IU can be installed. For OSGi bundles, the p2.inf file should be placed next to the MANIFEST.MF file. In our case, it’s placed in a bundle called com.ianbull.p2.touchpoint.payload.

To summarize, before ‘payload’ can be installed, the touchpoint ‘exec’ must be installed and available. Since it’s declared as a metaRequirement, p2 will handle this by using a two-stage plan.

Invoking the Touchpoint

Now that we’ve created the touchpoint, all we need to do is invoke it. This is done by the Installable Unit which declares the metaRequirement. In our case, we will ship the executable as part of our ‘payload’ plugin.

We will use two touchpoints, one to set the permission (755) and then our custom touchpoint to launch the application.

instructions.install = \
chmod(targetDir:@artifact,targetFile:payload/someProgram,permissions:755);


instructions.install.import= \
org.eclipse.equinox.p2.touchpoint.eclipse.chmod

instructions.configure = com.ianbull.p2.touchpoint.exec.Execute(artifact:@artifact, program:payload/someProgram);

Putting it all Together

Once we have these two plugins (the ‘exec’ touchpoint and the ‘payload’) we create a repository that contains them both.

With the repository in place, we simply invoke the director, or better yet, the p2 Install Software Dialog to install the payload.

Screen Shot 2013 05 22 at 3.12.34 PM Custom touchpoints in p2

The payload doesn’t actually have any dependency on the ‘exec’ touchpoint, except for the MetaRequirement. This MetaRequirement will cause the ‘exec’ touchpoint to first be installed, and then the touchpoint can be invoked.

And now during installation, our custom application is started. While this particular application doesn’t do anything useful, it could be an installer for another tool, or it may simply invoke an existing service such as Apache HTTPD.

Screen Shot 2013 05 22 at 3.13.54 PM Custom touchpoints in p2

If you’re interested in the code examples I’ve used here, I’ve pushed them all to GitHub.


TwitterGoogle+LinkedInFacebook

Leave a Comment. Tagged with eclipse, p2, eclipse, p2

Eclipse Internationalization Part 2/4 – New Message Extension by Dirk Fauth and Tom Schindl

In my last blog post I explained the current available solutions for translating an Eclipse 4 application and what is wrong with them.

In this blog post I will show you a new solution for translating your Eclipse 4 application. This one is created as an OSGi service and gets rid of all the disadvantages of the existing solutions. The main idea and implementation was created by Tom Schindl, while I extended it to the current state, so we are writing this blog post together.

To use the services you need to install the latest E4 tools into your IDE or at least create a target-platform that contains the location to it. You could for example use the p2 update site for the E4 tools provided by vogella to get the latest version. (see here for more information http://www.vogella.com/blog/2013/03/12/eclipse-4-tools-update-sites-available-from-vogella/)

As the constant based solution has several benefits when used in combination with the tooling, e.g. code completion, refactoring and find references, the solution designed by Tom uses a similar approach. The big difference is, that it is not constant but member based. So using that solution is working on an instance of a message class, which means that it can be collected by the garbage collector if it is not referenced anymore and values can be changed at runtime.

Another advantage over the existing mechanisms is a more technical one. The existing translation mechanism in Eclipse is based on Java 1.4 and has some workaround code to deal with a special use case. What most people are not aware of is that the process on resolving a resource bundle is a little bit different than resolving a key out of a loaded resource bundle. Guess you have a loaded resource bundle for the locale de_DE, the search order for a key in that resource bundle looks like this:

  1. bundle_de_DE.properties
  2. bundle_de.properties
  3. bundle.properties

So it is searching from the most specific to the default bundle for that key. If the default bundle is configured to contain the English translations, if you missed to translate a key to German, the English one will be returned.

For resolving a resource bundle the process differs slightly with a huge impact. Guess you are running on a system that has the German locale set as default locale. Your application contains only the German and the default resource bundle files as shown above. What would you expect to get for translation if you set the application locale to fr_FR? Only knowing about the process above, you would expect to get the English translations out of the default resource bundle. But on resolving and loading the resource bundle, the default locale is used as fallback. So the following search order is used to resolve the bundle for fr_FR:

  1. bundle_fr_FR.properties
  2. bundle_fr.properties
  3. bundle_de_DE.properties
  4. bundle_de.properties
  5. bundle.properties

This means you will get the German translations on a German system if you try to localize your application to fr_FR and there are no such resource bundle files in your application.

In Equinox resolving the resource bundle was implemented like resolving keys without taking the default locale as fallback into account. That was reported and fixed a while ago (https://bugs.eclipse.org/bugs/show_bug.cgi?id=330602). A technical switch was introduced that allows to specify the equinox.root.locale as system property. If it is not set it will be treated as en by default. Now if the value for equinox.root.locale is en and the locale with language code en is requested, the default locale is not used as fallback.

Also the loading of resource bundles out of an OSGi bundle is a bit different because of the fragment support and the classloader per bundle. Using the Java 1.4 API dealing with all of these facts was quite difficult and caused the limitation that only properties based resource bundles are supported.

With Java 1.6 the ResourceBundle.Control class was introduced. It allows you to take control on how resource bundles are loaded, how the candidate locales are determined, e.g. using a fallback locale or not, and how the caching of the loaded resource bundles should be handled. The new message extension is based on Java 1.6 and makes use of this ResourceBundle.Control to deal with the described issues above in a more convenient way. Using the Java default mechanism instead of the custom workaround also enables the usage of class based resource bundles. The advantage of using class based resource bundles will be explained in the last blog post in this series.

To use the new message extension you need to add the org.eclipse.e4.tools.services bundle to the required plugins in your MANIFEST.MF. This bundle contains the OSGi services that are necessary for the new message extension and will give you access to two new annotations, @Message and @Translation.

To explain those annotations and their usage we first need to create the Messages class that will contain our translations.

public class Messages {
	public String label_message;
}

Comparing the implementation with the constant based OSGi NLS solution you will notice that

  • Messages is a POJO and does not implement or extend something
  • There are no constants but member variables
  • There is no static initialization block needed anymore

Now that we have the Messages class, we need to configure where to get the translation values that should be put into those member variables. As different developers have different opinions on where to put their resource bundles to, the new message extension supports a quite flexible approach to specify where the resource bundle files can be found. When processing the initialization, the mechanism searches for the resource bundle files in the following order:

  1. Check for a configuration via @Message annotation
    @Message(contributorURI=”...”)
    public class Messages {
    	public String label_message;
    }
  2. Check for resource bundle files with the same base name and in the same package as the Messages class
    NamedMessages
  3. Check for OSGi resource bundles configured in MANIFEST.MF
    If you placed your resource bundle files in OSGI-INF/l10n and named in bundle, there is nothing else you have to do

@Message annotation

The @Message annotation is optional and only necessary if you want to configure the location of the resource bundle files or the caching behaviour. To configure the location of the resource bundle files, you need to set the contributorURI parameter to the annotation. This parameter supports the following location patterns:

  • platform:/[plugin|fragment]/[Bundle-SymbolicName]
    Load the OSGi resource bundle out of the bundle/fragment named [Bundle-SymbolicName]
    For example:
    @Message(contributorURI="platform:/plugin/com.beone.e4.translation.extension")
    will load the OSGi resource bundle that is configured in the MANIFEST.MF of the com.beone.e4.translation.extension plugin
  • platform:/[plugin|fragment]/[Bundle-SymbolicName]/[Path]/[Basename]
    Load the resource bundle specified by [Path] and [Basename] out of the bundle/fragment named [Bundle-SymbolicName].
    For example:
    @Message(contributorURI="platform:/plugin/com.beone.e4.translation/resources/another")
    will load the resource bundle that is located in the folder resources/other in the com.beone.e4.translation plugin.
  • bundleclass://[plugin|fragment]/[Fully-Qualified-Classname]
    Instantiate the class based resourcebundle specified by [Fully-Qualified-Classname] out of the bundle/fragment named [Bundle-SymbolicName]. Note that the class needs to be a subtype of ResourceBundle.
    For example:
    @Message(contributorURI="bundleclass://com.beone.e4.translation/com.beone.e4.translation.resources.MockBundle")
    will load the class based resource bundle MockBundle in package com.beone.e4.translation.resources in the com.beone.e4.translation plugin.

Using the bundleclass contributorURI enables you to use class based resource bundles. What’s possible by implementing them will be explained in a later post.

While the constant based OSGi NLS translation mechanism had no chance to update the translation values at runtime and kept them in memory forever, you can specify the caching behaviour for messages in the new message extension. This is done by the referenceType parameter of the @Message annotation.

If the same translations are requested for injection at several places, for memory efficiency the same instance will be returned. This is achieved by internally caching the created instances as a SoftReference by default. As they are not hard references, once all requestors are garbage collected (e.g. the parts were closed) the message instance can be garbage collected as well. Using SoftReferences as default has the advantage that e.g. if a part is closed and opened again within a very short time, the message instance can be reused instead of being garbage collected and newly created. In case the default behavior does not suite your needs the @Message annotation provides the following caching/garbage collection strategies:

  • NONE: The message instance is not cached. Each requestor gets its own instance.
  • WEAK: The message instance is cached as a weak reference. If every requestor was garbage collected, the message instance is also discarded at the next garbage collection cycle.
  • SOFT: The message instance is cached as a soft reference. If every requestor was garbage collected, the message instance is not immediately discarded with the next garbage collection cycle, but will retain for a while in memory.

The strategy to use can be specified by setting the annotation parameter referenceType. The following for example will set the strategy to ReferenceType.NONE which means that every requestor will get its own instance of the messages class:

@Message(referenceType=ReferenceType.NONE)

@Translation annotation

After the messages class is created and connected to the resource bundle, it can simply be used by injecting the Messages instance with the @Translation annotation:

@Inject
@Translation
private Messages messages;
...
myLabel.setText(messages.label_message);

Compared with the TranslationService that is already available in Eclipse 4, you are injecting your Messages instance without the need to know the contributionURI of the containing bundle or the key of the translation you are requesting.

@PostConstruct annotation

With the latest implementation it is possibility to add methods annotated with @PostConstruct. Following the dependency injection rules, this method will be executed after the Messages instance is created. This allows you to use placeholders like with using the constant based NLS.bind() solution. Guess you have a property that looks like this:

my.label.message = {0} says {1}

You could initialize this property by adding a method annotated with @PostConstruct to your Messages class.

public class Messages {
	public String my_label_message;

	@PostConstruct
	public void format() {
		my_label_message = MessageFormat.format(
			my_label_message, "Dirk", "Cool");
	}
}

Note that the method annotated with @PostConstruct in the Messages class does not support parameters as it doesn’t support dependency injection at that place. This is quite the default behaviour when thinking of @PostConstruct in the JavaEE context. As @PostConstruct in Eclipse 4 generally supports dependency injection, we need to mention that in this special case it is not supported.

A small but personally huge improvement in properties key handling is the handling of the dot as property key separator. Using the constant based solution you need to use a separator that is valid within a Java variable, e.g. the underscore. This is because the dot is not valid as part of a field name and the binding from key to constant is directly without transformation. The new message extension at least supports the dot as key separator within the properties. So the properties can look like you know them from other Java frameworks. Of course you still can’t use it as separator in the fields. But as you can see in the example above, using the dot in the properties file is possible when using the underscore as separator in the Messages field.

Links:

Buckminster rmap to p2 (composite) repository metadata

ECF has been using an rmap to b3aggr xslt stylesheet for a while now, and today I needed a similar stylesheet to convert to p2 composite repositories (single sourcing repositories feed from Buckminster to the p2 director). It takes an rmap and spits out composite repository metadata referring to all http:// and ftp:// provider uris in the rmap. For an example check out this rmap and the generated metadata. As a generator use
xsltproc --stringparam timestamp $((`date +%s * 1000)) --stringparam repository file:///a/local/p2/repo/ rmap2compositeRepository.xsl my.rmap
In case anybody else needs it, here is the xslt stylesheet:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:bm="http://www.eclipse.org/buckminster/RMap-1.0">
    <xsl:output method="xml" indent="yes" encoding="UTF-8"/>

    <xsl:strip-space elements="*"/>

    <!-- overwrite via parameters -->
    <xsl:param name="repository">file:///dev/null</xsl:param>
    <xsl:param name="title">vogella Packages Repository</xsl:param>
    <xsl:param name="timestamp">13691573970000</xsl:param>
    <xsl:param name="type">org.eclipse.equinox.internal.p2.artifact.repository.CompositeArtifactRepository</xsl:param>

    <xsl:template match="bm:provider">
        <!-- only accept public http|ftp repositories -->
        <xsl:if test="starts-with(bm:uri/@format, 'http://') or starts-with(bm:uri/@format, 'ftp://')">

                <!-- remove dangling ?importType=binary -->
                <xsl:choose>
                        <xsl:when test="contains(bm:uri/@format, '?importType=binary')">
                        	<child location="{substring-before(bm:uri/@format, '?importType=binary')}"/>
                        </xsl:when>
                        <xsl:otherwise>
                        	<child location="{bm:uri/@format}"/>
                        </xsl:otherwise>
                </xsl:choose>
        </xsl:if>
    </xsl:template>

    <xsl:template match="/">

            <repository name='&quot;{$title}&quot;' type='{$type}' version='1.0.0'>

              <properties size='2'>
                <property name='p2.compressed' value='true'/>
                <property name='p2.timestamp' value='{$timestamp}'/>
              </properties>
              <children>
                <child location='{$repository}'/>
                <!-- match all the child nodes of the template match=/ node -->
                <xsl:apply-templates/>
              </children>
            </repository>

    </xsl:template>

</xsl:stylesheet>
May 21, 2013

Remediation support, goodbye cryptic p2 dependency error message.

Thanks to the sponsoring of JBoss, you can now say goodbye to the cryptic dependency error messages. Indeed, starting with Eclipse Kepler, when p2 is not able to complete the requested installation, it proactively searches for alternate ways to help you proceed.This search for alternate solutions is done automatically as soon as p2 realizes that it can't proceed with the installation. No special

What happened at the Eclipse Stammtisch Zurich

Last week we had a great time at the Eclipse Stammtisch Zurich.

Eclipse Scout Talk

Eclipse Scout Talk

Ken Lee and Matthias Zimmermann presented Eclipse Scout. The motto this year: “Write your application once – run on rich, web and mobile”. Since last year Application written on top of the Eclipse Scout Framework can be rendered in a Browser with Eclipse RAP (Remote Application Platform). With Kepler this renderer was extended (model transformation, optimized components, optimized theme) to support mobile and tablets. Scout application can be rendered as web applications for those devices.

Xtext meets Orion Talk

Xtext meets Orion Talk

Holger Schill demonstrated his prototype that allows editing Xtext based DSL in web editors. It is based on Orion (with some modifications) and it uses some help of an Eclipse IDE running as a server (it calculates the editor behavior: quick tip, validations…). It was a really interesting talk, only one slide and a big demo.

Eclipse Orion Talk

Eclipse Orion Talk

Eclipse Orion: “It is not Eclipse in the browser; it is a new editor in the web (using the browser as a platform)”. That is what Mike Milinkovich explained during its talk. The demonstration was really impressive. The primary targeted programming languages are coming from the web (html, javascript, css…). It is really impressive, in comparison to the editor providing only syntax highlighting. You can try it online on orionhub.

Measure for Measure Talk

Measure for Measure Talk

The presentation of Werner Keil was about Eclipse UOMo, a project that ease the correct use of units in a Java project. This is particularly useful when different system communicate which each other. As illustration Werner choose the Shakespeare quote “Say what you will, my false outweighs your true”. Indeed, when not properly handled a system might define 32 liters, where another understands 32 gallons.

Democamp Zurich

We will be happy to meet the Eclipse Community in Zurich for the Kepler Democamp. It will take place on 27th June at the ETH main building.

Meet the Eclipse Scout team at the following other events.

Consuming emf bundles from p2 with maven : b3

Some of the eclipse libraries are not so thightly bound to OSGi and lately I wanted to use EMF in a grails project. It came to me that there are no current maven artifacts available for EMF, so I had to roll my own.

Asking stackoverflow, I first tried to re-jar the bundles with tycho, but that gave me all the OSGI-deps in one jar, what wasn't what I was looking for. After reding through some discussions around maven and p2 in the eclipse bugzilla, I noticed that b3 has the capability to mirror a p2 repository in a maven compliant layout.

As I wanted to use the xsd to ecore converter I created a aggregation for the xsd sdk
notice the maven config at the end of the aggregator tag.

https://gist.github.com/eiswind/5381710

Then I uploaded it to google drive, so that I can consume the repo from heroku build. One major drawback is, that the source bundles are not maven conformant, so that attaching them automagically won't work.

Building a custom RAP Widget based on Select2

This weekend a started to play aorund with new new RAP API introduced in 2.0. It really looks promising.

I started off with the CKEditor Example and created a combo-replacement based on Select2.js. By the way I had more a fight with the select2 api (that I didn't know) than with the rap api thats quite straightforward. I could answer nearly all of my questions from the source.

Here's the example for those who want to try:

It supports markup and (what I always miss on the standard combo) it has "clear selection". When you create it with SWT.MULTI it supports the cute Multiselect feature of select2.

https://gist.github.com/eiswind/5611873 java backend
https://gist.github.com/eiswind/5611866 js client

EMF Dos and Don´ts #10

EMF is a very powerful framework and with power comes…responsibility. You can achieve great things with a minimum of effort using EMF, but if something goes wrong, you can also spend hours trying to find out why. This blog post is part of a series on things you should do and things you should not do when using EMF. You can use the link to the series pilot to navigate to the start and the link below to navigate to the next blog once it is published.

EMF Dos #10: Use EMF ComposedAdapterFactory

In my previous blog post, I discussed AdapterFactoryLabelProvider and AdapterFactoryContentProvider, which can be used to provide labels and children for your entities in viewers. Both require an AdapterFactory in their constructors that will be used to retrieve the actual ItemProviders and their entity-specific implementation.

If you pass in the AdapterFactory of a specific model, the provider will only be able to provide labels for entities of the given model. However, viewers often need to display entities of multiple models. Of course, there is a solution to this with EMF: ComposedAdapterFactory.

A ComposedAdapterFactory is a composition of AdapterFactories, as the name implies. You can construct it with a collection of AdapterFactories, and it will delegate calls to adapt() to the resepective AdapterFactory based on the entity type. If you want to display multiple models in one viewer, you just combine all the models’ AdapterFactories into one ComposedAdapterFactory and pass it to the AdapterFactoryLabelProvider and AdapterFactoryContentProvider.

If you do not want to restrict your viewer to specific models but want to use all models available in your current target platform, you can pass in ComposedAdapterFactory.Descriptor.Registry.INSTANCE, which is the registry of all available AdapterFactories. By default, the  AdapterFactory of a generated Edit-Plugin is registered via the org.eclipse.emf.edit.itemProviderAdapterFactories extension point and thereby added to the aforementioned registry.

If the AdapterFactoryLabelProvider/AdapterFactoryContentProvider and the ComposedAdapterFactory are based on the registry, your viewers can be fully decoupled from the actual models they display while still providing customized icons, labels and content.

Stay tuned for more Dos and Don´ts in my next blog!


TwitterGoogle+LinkedInFacebook

2 Comments. Tagged with eclipse, emf, eclipse, emf

RFP 154 Network Interface Information Service now publicly available

Java standard APIs (i.e. java.net.NetworkInterface, java.net.InetAddress)provide functions that allow IP network interface information, such as the IPaddress and MAC address to be obtained.However, the bundle that wants to get network interface information has tomonitor whether the information has changed or not for a certain period oftime. Changes in network interface can be pushed to the
May 20, 2013

Another Xtend and JavaFX Story

For my spare time JavaFX project, I was looking for a way to export a scene to SVG. Googling a bit I stumbled over a blog entry by Gerrit Grunwald who implemented an JavaFX shape to SVG path converter class for the JFXtras project. It looked good so I decided to give it a try.

 

Java to Xtend

The class is of course written in Java. Even though there is no technical reason - Java and Xtend classes can coexist in the same project without any problems - I wanted to convert it to Xtend as well. I am a lazy guy. Luckily Krzysztof Rzymkowski had recently posted on the Xtend group that he had started to implement a Java to Xtend converter. It even has a web interface, and except for one slight issue with a for-loop it worked like a charm. Great work, Krzysztof!

Of course the resulting code is pretty Java-like, so I wanted to improve on it using more of the cool Xtend features. I found quite a few spots to do so, reducing the amount of code significantly and enhancing readability a lot. The remainder of this post is about this ongoing love of JavaFX and Xtend. The complete source code will likely be made open-source soon.

 

Dispatch Methods

The class starts with a method that only contains an instanceof-cascade to delegate to the conversion method for the specific subclass of Shape the parameter has:
public static String shapeToSvgString(final Shape SHAPE) {
   final StringBuilder fxPath = new StringBuilder();
   if (Line.class.equals(SHAPE.getClass())) {
      fxPath.append(convertLine((Line) SHAPE));
   } else if (Arc.class.equals(SHAPE.getClass())) { 
      fxPath.append(convertArc((Arc) SHAPE)); 
   } else if (QuadCurve.class.equals(SHAPE.getClass())) {
      fxPath.append(convertQuadCurve((QuadCurve) SHAPE));
   } ...
In Xtend I can use dispatch methods to realize this: They must have the same method and number of parameters, but different parameter types. The Xtend compiler then generates the dispatcher method with the instanceof-cascade automatically. So renaming the delegate methods and marking them as dispatch made the dispatcher method obsolete.
def dispatch String toSvgString(Line line) ...
def dispatch String toSvgString(Arc arc) ...
def dispatch String toSvgString(QuadCurve quadCurve) ...
Tip: If you want to delegate from one dispatch case to another, the original methods are available with an underscore preceeding their name.

Templates

An SVG path is kind of a cryptic string. In the Java code it is assembled using a StringBuilder, e.g.
final StringBuilder fxPath = new StringBuilder();
fxPath.append("M ").append(CENTER_X).append(" ")
.append(CENTER_Y - RADIUS).append(" ");
fxPath.append("C ")
.append(CENTER_X + CONTROL_DISTANCE)
.append(" ").append(CENTER_Y - RADIUS).append(" ")
.append(CENTER_X + RADIUS).append(" ")
.append(CENTER_Y - CONTROL_DISTANCE)
.append(" ")
.append(CENTER_X + RADIUS).append(" ")
.append(CENTER_Y).append(" ");
...
That's the best you can do with Java. In Xtend we have template expressions - multiline strings which can be interrupted with values from expressions. Even IF-conditions and FOR-loops are supported. With carefully chosen regular expressions for find/replace and some manual fine-tuning the above becomes nicely readable
'''M «centerX» «centerY - radius»
   C «centerX + controlDistance» «centerY - radius»
   «centerX + radius» «centerY - controlDistance»
   «centerX + radius» «centerY»
...

Switch Expression

I found another finer-grained instanceof-cascade in the convertPath method:
final StringBuilder fxPath = new StringBuilder();
for (PathElement element : PATH.getElements()) {
if (MoveTo.class.equals(element.getClass())) {
fxPath.append("M ")
.append(((MoveTo) element).getX()).append(" ")
.append(((MoveTo) element).getY()).append(" ");
} else if (LineTo.class.equals(element.getClass())) {
fxPath.append("L ")
.append(((LineTo) element).getX()).append(" ")
.append(((LineTo) element).getY()).append(" ");
} else if (CubicCurveTo.class.equals(element.getClass())) {
fxPath.append("C ")
...
As the bodies of the if-statements are so simple, I decided to use Xtend's switch instead. It allows to use type guards for the cases and automatically cast the switch variable to that type inside the case's body:
val it = new StringBuilder
for (element : path.elements) {
   switch element {
      MoveTo: append('''M «element.x» «element.y» ''')
      LineTo: append('''L «element.x» «element.y» ''')
      CubicCurveTo: append('''C «element.controlX1»...
I could further use the operator => instead of the Builder class and the operator <=> replacing Double.compare.

Extension Import (Client Side)

From a client side, the shapeToSvgString method is a utility method for Shapes. In Xtend, you can import such methods using a static extension import. That makes them callable in extension syntax, as if the method was defined in the class of the first parameter. To further improve readability, I renamed the methods to toSvgString such that I can now write
import static extension ...ShapeConverter.*
...
new Rectangle.toSvgString

Moving to a Mac

With my new gig at Talend I have requested a MacBook Pro as a company computer, thinking that since I’m now an architect I will have to do more writing and (god forbid) slides. Of course I will continue to develop software, both for the company and in my open source work. For years, my preferred development platform has been Unix (shows how old I am, I really mean Unix) or Linux. And I have had to work on all three of Windows, Linux and the Mac to test my software. Though I had a Mac, I did not work on it very much. Thanks to Eclipse things just worked there pretty well, I only had a few Mac-specific issues.

So I started to move everything to the Mac and I was shocked at how good it was. Really just good. Everything just feels better than the Ubuntu GUI that I was using (Gnome 3 — I just could not deal with Unity). I spent the money and got the Thunderbolt display and it’s just amazing, so big and clear, and it is essentially a docking station. Everything works fine with my odd keyboard and mouse.

Unfortunately, there is an Eclipse SWT issue (it looks like) which is preventing me from running all 3000 or so of the data transformation unit tests on the Mac, so I have spent a couple of days working on characterizing it, and the thing that I found really shocking about the Mac was Instruments. I could get a ton of profiling information from a running program with an amazing GUI with no effort. By combining this with JProfiler, I should be able to get what I need to characterize (and even possibly fix) this leak problem.

I have never considered having Windows as my daily machine, and now that I have had the Mac, I’m never going back. It’s just that good.


Now at Talend

After many years of leading Oakland Software to develop really good data transformation technology, I have moved to a new phase, having sold these assets to Talend. A little while ago, I joined Talend as a Senior Architect responsible for data transformation as well as other cross product issues and I’m very excited to be part of this team of extremely bright and talented folks.

For quite a while now the Oakland Data Transformer has worked well with the Talend ESB runtime technologies, and now that it’s part of Talend we will work on integration at the Talend Open Studio level for a future Talend release. You will hear more about that as the work progresses.

I am very grateful to the open source community, mostly to my friends at Eclipse on which my product is based. It would not have been possible to make such a high functionality and robust product without the extensive infrastructure provided by Eclipse to do such things. I think however that the most important part of Eclipse is the culture. It’s a culture of openness, mutual respect, and encouragement. People are encouraged to be nice, helpful, and not at all arrogant and this is the case with everyone I have worked with there over the years. It’s been an amazing experience being part of it and I hope to continue my small contributions to Eclipse.


Jenkins EC2 work

About a year ago, I have gotten involved pretty extensively in the Jenkins EC2 plugin where I’m now the maintainer. This was motivated by wanting to move my company’s (Oakland Software — now mostly part of Talend) build process to EC2 and finding out that it was simply not possible unless some significant work was done on the plugin. Seeing that the plugin was missing an active maintainer, I requested the responsibility (which was quickly granted as it the custom in Jenkins).

I have done 3 major releases in the last year with both contributions originated by me and from many members of the community adding significant features like:

  1. Support stopping instead of terminating instances
  2. Allow the use of EC2 spot instances
  3. Proper support for multiple clouds
  4. Greatly increase the accuracy and reliability of starting and stopping instances and adhering to limits
  5. Many bug fixes

I remain active with this project and working closely with several members of the Jenkins community to improve it. With these improvements the plugin has become more popular gaining nearly 200 new installations in the past year.

In addition to coordinating and helping with contributions, I would like to start a conversation about looking the new Google Compute Engine service, which is similar to EC2. We should be able to leverage what we have learned in the EC2 plugin and to the refactoring necessary to cleanly support the Google service in addition to Amazon’s. Ideas and contributions are welcome.


May 17, 2013

Remove “Build path specifies execution environment…” warnings from Problems View

I have often workspaces with projects which specify Java 1.5 as minimal execution environment. On my machine there is no JDK 1.5 installed, and it turns out that getting one for Mac OSX Mountain Lion is not trivial. Actually I don’t need a JDK 1.5, since the standard 1.6 JDK is compatible. However, this raises in the workspace these annoying warnings.

screenshot 2013-05-17 um 10.32.31

In the Execution Environments setting it is possible to mark the Java 1.6 installation as compatible to the J2SE-1.5 Execution Environment:

screenshot 2013-05-17 um 10.52.31

Although the JDK 1.6 is marked compatible now, it is not “strictly compatible”, so the warning message remains.

Next you could try to disable the warning message. There is a setting for this in the preference dialog Java/Compiler/Building:

screenshot 2013-05-17 um 10.35.17

After changing the setting you are asked to rebuild the projects. But again, the warning do not disappear. I suspect this to be a bug and raised Bug#408317 for this.

So the last chance is to filter these warnings. Therefore select the options menu in the Problems View and open the “Configure Contents” dialog. In the “Types” selection tree expand the “Java Build Path Problems” node and uncheck “JRE System Library Problem”.

screenshot 2013-05-17 um 11.03.53

Finally the warning messages disappear from the Problems View. However, they are just filtered from the view, the projects themselves will still have these resource markers, so you will have a warning overlay icon on the project in the Package Explorer View although the Problems View might be empty.

But this raises the next problem: Now the warning disappears and the code is compiled with Java 1.6, and thus against the 1.6 API. This leads to the problem that you could accidently use API from >= 1.6. For example, usage of String#isEmpty() would compile even if the Execution Environment is set to J2SE-1.5 (the Execution Environment anyway just defines the lowest requirement) and also if Java source compatibility is set to 1.6 in the compiler settings.

We need to detect this unwanted use of API that is not 1.5 compatible. Therefore the PDE tooling offers support to install an Execution Environment Description for J2SE-1.5 and set up API tooling. This will finally allow us to detect illegal API use:

screenshot 2013-05-17 um 14.00.15

I like to thank Laurent Goubet and Mikael Barbero for their valuable comments on the potential API problems.


Android Studio: our new, shiny Android IDE

The cat is out of the bag: yesterday, during Google I/O‘s keynote, we announced our new IntelliJ IDEA-based Android IDE, Android Studio. This is what my team has been working on for the past months.

From what I read on the web, Android Studio has been pretty well received. I personally heard nice comments about it, from “awesome!” to “fantastic!” and “really cool.”

Here are some highlights about Android Studio. I’ll be writing more detailed blogs about our IDE soon. Before I continue, let me make this clear:

This is a personal blog. The opinions expressed here represent my own and not those of my employer, Google.

With that out of the way, here we go:

Our goal is to be Gradle-centric

We are working closely with the Gradleware folks on a new Gradle-based build system. Right now, with Android Studio, you can create new Gradle-based Android projects or import existing ones. When you build your project in Android Studio, we disable the default IDEA Java builders and delegate the build to Gradle. You get the same output from building within Android Studio or from the command line. Our goal is to have Gradle build files as the only source of truth.

This is an early access preview

Even though it is possible to create applications with Android Studio, there are still rough corners, bugs and features that we have not implemented yet. It would be great to get as many bug reports and feature requests from early adopters. Here is the list of known issues.

We still support Eclipse

We will be supporting Eclipse ADT as well. In fact, we plan to add similar Gradle support to ADT. The catch here is that given limitations in Eclipse, especially JDT, we cannot guarantee a development experience as smooth as Android Studio. You can, however, export your Eclipse-based Android project as a Gradle project, and then import it into Android Studio.

This is not a fork of IntelliJ IDEA

We have been working, and continue to work, really close with JetBrains, the folks behind the best Java IDE, IDEA. They implemented the changes we needed in their platform in order to develop Android Studio. Eventually, you will be able drop Android Studio as a plug-in into your copy of IDEA.

That’s it for now. Stay tuned for more Android Studio posts :)

Update: Xav‘s and Tor‘s Google I/O talk “What’s New in Android Developer Tools” is on YouTube!

May 16, 2013

EMF Dos and Don´ts #9

EMF is a very powerful framework and with power comes…responsibility. You can achieve great things with a minimum of effort using EMF, but if something goes wrong, you can also spend hours trying to find out why. This blog post is part of a series on things you should do and things you should not do when using EMF. You can use the link to the series pilot to navigate to the start and the link below to navigate to the next blog once it is published.

EMF Dos #9: Use EMF ItemProviders

To display entities in a tree view or in other views, you need a label provider and a content provider. The label provider provides an icon and a text to display each entity type. The content provider defines the children of an entity. Additionally, the label and content providers need to notify their viewers if the label or content changes due to data changes. The viewers will update only if there are data changes. You could implement the label and content providers manually, which is a lot of work, or you can rely on EMF-generated infrastructure.

For every entity defined in your Ecore, EMF will generate an ItemProvider in the Edit-Plugin. The ItemProvider implements methods to get an icon and a text for the entity (getText() and getImage()). Since you want to display all kinds of entities in a tree view, you need to create a label provider that will know the type of your entity and use the matching ItemProvider to retrieve a text and an icon. This label provider needs to implement  the ILabelProvider interface. Similarly, a content provider would have to implement the ITreeContentProvider interface and define the children for all the different kinds of entities. For this purpose, EMF provides a fully functional label and content providers: AdapterFactoryLabelProvider and AdapterFactoryContentProvider. They both work in the same way,  so I will focus on the AdapterFactoryLabelProvider.

Conceptually, an AdapterFactoryLabelProvider is able to provide labels for all supported entities by delegating to the appropriate generated ItemProviders. Technically, the AdapterFactoryLabelProvider requires an AdapterFactory to do this (see the constructor). An AdapterFactory creates adapters (wrappers) for a given entity. For each EMF model, EMF generates an AdapterFactory for the ItemProviders of its entities. Basically, the adapt() method of the AdapterFactory returns the appropriate ItemProvider for a specific  entity.

To construct an AdapterFactoryLabelProvider,  you can pass in the AdapterFactory of your model, and the label provider will be able to display your entities based on their ItemProviders.

If you want to display entities from multiple models you can use a ComposedAdapterFactory, which I will describe in my next blog.

The AdapterFactoryLabelProvider will —  with the help of the ItemProviders — update its viewers if any of the entities it provided a label for change. Furthermore, the AdapterFactoryLabelProvider is very loosely coupled to the ItemProviders that implement entity-specific display behaviour.

Since the ItemProviders are generated (but can be adapted if necessary), the AdapterFactoryLabelProvider and AdapterFactoryContentProvider really are a big productivity boost in displaying EMF-based entities in your viewers.

Stay tuned for more Dos and Don´ts in my next blog!

 


TwitterGoogle+LinkedInFacebook

Leave a Comment. Tagged with eclipse, emf, eclipse, emf

UpdateSite available for eclipse-utils finally

Hey Guys ! Finally created an updateSite for eclipse-utils.
You can now download the utility plugins from the following link

Update Site :: http://www.ancitconsulting.com/eclipse-utils/

Following Plugins will be available in the build
1.gSearch
2.Comment / Remove System.out.println from Selected Resource or Workspace
3.Zip and Email from Project Explorer
4.Show in File Explorer from Project Explorer
May 15, 2013

IntelliJ IDEA for Android

That's an interesting move: Google announced to choose IntelliJ IDEA for the new Android Studio IDE.

The guys are not dumb - if they do such things, they have good reasons. I never had pleasure to use Android plugins for Eclipse, so I can't really judge if it was good or not...

But what does it mean for Eclipse? Is Eclipse not good enough for Google Android developers?

Would be interesting know what are the real reasons to leave Eclipse land for IntelliJ IDEA adventure.

 Here are few candidates from me (just in random order):

  • Lack of "core platform" development since few years (e4 is a fancy toy in a beta stage, sorry)
  • Bugs, bugs, bugs everywhere, feature requests waiting years in bugzilla (word wrapping?)
  • Overcomplicated / error prone installation of 3rd party tools / over-engineered update manager (p2) story
  • Lack of customization options for IDE (according to speakers, extensibility of IntelliJ is one of the reasons to choose it)
  • Lack of standardization, chaos outside of "standard" JDT world (each and every language/VCS/technology stack plugin does things differently)
  • Last but not least, my favorite: with Swing you never see "no more handles" crashes.

Yep Google, I'm disappointed Eclipse user too.

Mongo JEE [step6]

In [step5] we have modified our JAX-RS LogsService to returns List of Pojo by using :

With Pojo Mapper MongoJack, the LogsService looks like this:

@GET
@Path("/all")
@Produces(MediaType.APPLICATION_JSON)
public List<Log> findAll() {
	DB db = MongoHolder.connect().getDB("websites");
	DBCollection dbColl = db.getCollection("logs");
	JacksonDBCollection<Log, String> coll = JacksonDBCollection.wrap(
          dbColl, Log.class, String.class);
	return coll.find().toArray();
}

When I have studied how to use Mongo in JEE Application with JAX-RS, I have read the great article Modern Web Apps using JAX-RS, MongoDB, JSON, and jQuery. After reading this article, I told me "why we need to use a Pojo Mapper with JAX-RS although Mongo manages their data with BSON representation?", on other words why we could not develop our JAX-RS service like this :

@GET
@Path("/all")
@Produces(MediaType.APPLICATION_JSON)
public DBCursor findAll() {
	DB db = MongoHolder.connect().getDB("websites");
	DBCollection coll = db.getCollection("logs");
	return coll.find();
}

Using DBCursor avoids to create a Pojo and avoids to serialize the Pojo to JSON. So I have decided to create Mongo JEE project. In this article we will modify our JAX-RS LogsService to use Mongo DBCursor in the service by using the JAX-RS provider of the Mongo JEE.

Download

You can download step6.zip which hosts the Eclipse Project which contains the explained sources in this article. To use it, unzip it and import this project in your Eclipse workspace. As soon as you will do that, your workspace will look like this :

This Eclipse project contains:

  • LogService is JAX-RS service which returns Mongo DBCursor.
  • MyJaxrsApplication is JAX-RS application which register the JAX-RS LogsService and Mongo JEE JAX-RS providers.
  • web.xml declares the Apache CXF JAX-RS implementation and set MyJaxrsApplication as JAX-RS Application

Download JARs with maven

This project contains the same dependencies than step4 (no need to use MongoJack and Apache CXF JSONProvider).

LogsService – DBCursor

Modify org.samples.mongodb.services.LogsService like this:

package org.samples.mongodb.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.jee.MongoHolder;

@Path("/logs")
public class LogsService {

	@GET
	@Path("/all")
	@Produces(MediaType.APPLICATION_JSON)
	public DBCursor findAll() {
		DB db = MongoHolder.connect().getDB("websites");
		DBCollection coll = db.getCollection("logs");
		return coll.find();
	}

}

Without Mongo JEE

In this step we will not use Mongo JEE to manage DBCursor with JAX-RS. We will see the problem without JAX-RS provider and we will fix it by developping our own JAX-RS provider.

MyJaxrsApplication – with problems

At this step we will use the MyJaxrsApplication of the previous article [step5] :

package org.samples.mongodb.jaxrs;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

import org.samples.mongodb.services.LogsService;

public class MyJaxrsApplication extends Application {

	private final Set<Object> singletons;

	public MyJaxrsApplication() {
		singletons = new HashSet<Object>();
		addSingleton(new LogsService());
	}

	protected void addSingleton(Object singleton) {
		singletons.add(singleton);
	}

	@Override
	public Set<Object> getSingletons() {
		return singletons;
	}
}

Test LogsService – with problem

  • run the StartServer class to start the server.
  • access with your webbrowser to the URL http://localhost:8081/mongo/jaxrs/logs/all/ and you will see the following error :
    No message body writer has been found for response class DBCursor.

This error occurs because Apache CXF doesn’t find JAX-RS provider to serialize the DBCursor to JSON. Mongo JEE provides those JAX-RS provider but before using it, it can be interesting to develop it (with simply mean).

MyDBCursorProvider

To fix the previous problem, create JAX-RS provider org.samples.mongodb.jaxrs.MyDBCursorProvider to serialize DBCursor to JSON like this:

package org.samples.mongodb.jaxrs;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;

import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;

import com.mongodb.DBCursor;
import com.mongodb.jee.util.JSON;

@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class MyDBCursorProvider implements MessageBodyWriter<DBCursor> {

	@Override
	public boolean isWriteable(Class<?> atype, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		if (DBCursor.class.isAssignableFrom(atype)) {
			// the given class type is DBCursor
			return true;
		}
		return false;
	}

	public void writeTo(DBCursor cursor, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType,
			MultivaluedMap<String, Object> httpHeaders,
			OutputStream entityStream) throws IOException,
			WebApplicationException {

		// Write the DBCursor as JSON array stream.
		JSON.serialize(cursor, entityStream);
	}

	@Override
	public long getSize(DBCursor t, Class<?> type, Type genericType,
			Annotation[] annotations, MediaType mediaType) {
		// Return -1 if the content length cannot be determined
		return -1;
	}

}

To use our JAX-RS provider, we need to register it in the JAX-RS Application. To do that modify org.samples.mongodb.jaxrs.MyJaxrsApplication like this:

package org.samples.mongodb.jaxrs;

import java.util.HashSet;
import java.util.Set;

import javax.ws.rs.core.Application;

import org.samples.mongodb.services.LogsService;

public class MyJaxrsApplication extends Application {

	private final Set<Class<?>> classes;
	private final Set<Object> singletons;

	public MyJaxrsApplication() {
		classes = new HashSet<Class<?>>();
		addClass(MyDBCursorProvider.class);
		 
		singletons = new HashSet<Object>();
		addSingleton(new LogsService());
	}

	@Override
	public Set<Class<?>> getClasses() {
		return classes;
	}

	protected void addClass(Class<?> clazz) {
		classes.add(clazz);
	}

	protected void addSingleton(Object singleton) {
		singletons.add(singleton);
	}

	@Override
	public Set<Object> getSingletons() {
		return singletons;
	}
}

Test LogsService – MyDBCursorProvider

Now we can test our JAX-RS provider :

You can check that the JSON array is the same than the Mongo JSON array like (see [step4]).

With Mongo JEE

Mongo JEE provides several JAX-RS providers for Mongo and provides too a JAX-RS Application JaxrsMongoApplication which registers Mongo JAX-RS providers.

MyJaxrsApplication – JaxrsMongoApplication

Modify MyJaxrsApplication to extend JaxrsMongoApplication like this:

package org.samples.mongodb.jaxrs;

import org.samples.mongodb.services.LogsService;

import com.mongodb.jee.jaxrs.JaxrsMongoApplication;

public class MyJaxrsApplication extends JaxrsMongoApplication {

	public MyJaxrsApplication() {
		super.addSingleton(new LogsService());
	}

}

Run the StartServer class to start the server and check that service works again.

Remove _id

The returned JSON stream to the browser, contains the "_id" key. But it’s possible to remove "_id" property .

Modify LogsService like this:

package org.samples.mongodb.services;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.jee.MongoHolder;

@Path("/logs")
public class LogsService {

	@GET
	@Path("/all")
	@Produces(MediaType.APPLICATION_JSON)
	public DBCursor findAll() {
		DB db = MongoHolder.connect().getDB("websites");
		DBCollection coll = db.getCollection("logs");
		BasicDBObject fieldsToIgnore = new BasicDBObject("_id", false);
		return coll.find(null, fieldsToIgnore);
	}

}

Restart the server and test it to check that every thing is working again.

Conclusion

In this article we have seen how to manage Mongo structure with JAX-RS service by using Mongo JEE.

In the next article [step7] we will add a new method service to return paginated logs list.


Inactive Blogs