Monday, December 20, 2010

Eclipse RAP Single Sourcing Awesomeness (with EMF Editor and Teneo+Hibernate as bonus!)

Eclipse Rich Client Platform has come a looong way since it was first introduced (and used in Eclipse IDE). The new Eclipse RAP (Rich Application Platform) is also becoming more and more attractive for deploying existing or new Eclipse RCP applications to the web.

One of my the projects I'm working on is developed on top of Eclipse RCP. It uses additional plugins such as EMF (Eclipse Modeling Framework) including EMF Editor UI, Teneo (EMF Persistence for Relational Databases), and Hibernate.

After some work, I managed to run the whole application on both Eclipse RCP (desktop) and Eclipse RAP (web-based). See the screenshots for proof.

Thanks to the recently released EMF Support for RAP I don't have to let go any of the nice EMF generated editor UIs for the web-based RAP version.

What's amazing is how little the work I have to do to port the RCP app to RAP.

The changes I needed to do is not changing code, but juggling dependencies to plugins and/or packages. Also creating a few platform-specific plugins (different based on whether I deploy on RCP or RAP).

It boils down to:

  1. Do not hard-depend on org.eclipse.ui plugin. Either depend on both org.eclipse.ui and org.eclipse.rap.ui plugins as optional dependencies, or import the specific packages. I prefer optional dependency on both plugins because it's much faster and easier.
  2. Be aware that there will be multiple sessions at once.
  3. 2D Drawing functions are not yet fully available. (and I guess will never be available)

See the Eclipse RAP FAQ on Single Sourcing for more information.

Fixing error: The type org.eclipse.core.runtime.IAdaptable cannot be resolved. It is indirectly referenced from required .class files.

If you get one of the following errors :

The type org.eclipse.core.runtime.IAdaptable cannot be resolved. It is indirectly referenced from required .class files.

The type org.eclipse.core.runtime.CoreException cannot be resolved. It is indirectly referenced from required .class files.

First of all, check that your plugins depend on org.eclipse.core.runtime plugin.

The classes above are located in org.eclipse.equinox.common plugin, and should be included (along with org.eclipse.core.runtime plugin) in your target platform.

If it still occurs, most likely you get your target platform plugins mixed up. I got this error because I tried to mix plugins from my Eclipse IDE installation (Helios 3.6-SR-1) with Eclipse 3.7M4 Platform plugins.

Simply unchecking the plugins from target platform Contents tab is not enough. I have to actually remove the location. If you notice duplicate plugins in your Target Platform Definition's Contents tab, then you're getting this problem.

Remove the offending plugins location from the target platform definition. If you must add plugins from Eclipse IDE location, cherry pick each plugin from the Locations UI, instead of just adding the whole SDK and unchecking them from the Contents tab.

Sunday, December 19, 2010

Creating an About Dialog for your Eclipse RCP Application

Displaying an About box in your Eclipse RCP Application/Product is actually very simple. However as is typical of a framework ("don't call us, we'll call you" principle), there are conventions you must follow.

Before you do this, you must already create an Eclipse RCP Application class that implements IApplication and register it as an Eclipse extension in your plugin.xml.

Adding the Help > About Menu Item


First, edit your ApplicationActionBarAdvisor class (which extends org.eclipse.ui.application.ActionBarAdvisor base class), as follows:

    protected void makeActions(IWorkbenchWindow window) {
        aboutAction = ActionFactory.ABOUT.create(window);
        register(aboutAction);
    }

That will register the About action. You will also need to add the menu action itself to the menu bar:

    protected void fillMenuBar(IMenuManager menuBar) {
        MenuManager helpMenu = new MenuManager("&Help", IWorkbenchActionConstants.M_HELP);
       
        menuBar.add(helpMenu);
        helpMenu.add(aboutAction);
    }

Launch your Eclipse RCP application and you can display the About dialog.

Customizing the About Dialog Box


To customize the About dialog box's contents, first you must create an Eclipse RCP Product Configuration.
Click File > New > Product Configuration and define your product.

This should also add your product to extension point 'org.eclipse.core.runtime.products'.

Edit your project's Launch Configuration to use the your Product Configuration instead of Eclipse Application. Verify that it works well.

Now you can customize the About box contents by editing your Product Configuration (.product file), go to Branding tab and About Dialog section. Specify the About Image and About Text there.

To specify the image, import an image resource (PNG, GIF, JPG) to your plugin project (e.g. inside an /icons folder). Important: Your image will need to be included inside your resulting plugin binary. Edit your plugin's manifest, go to Build tab, and make sure your images/icons are included (checked) for the binary build and source build.

After editing your Product Configuration, you must synchronize it with the plugin manifest. Go to the Product's Overview tab and click Synchronize (under Testing).

That action will update several properties in the org.eclipse.core.runtime.products extension, for example:

   <extension
         id="abispulsa_rcp"
         point="org.eclipse.core.runtime.products">
      <product
            application="com.abispulsa.bisnis.rcp.application"
            description="Layanan bagi korporasi untuk dapat dengan mudah mengisi pulsa."
            name="AbisPulsa Bisnis RCP">
         <property
               name="appName"
               value="AbisPulsa Bisnis RCP">
         </property>
         <property
               name="aboutText"
               value="AbisPulsa Bisnis merupakan layanan bagi korporasi untuk dapat dengan mudah mengisi pulsa.">
         </property>
         <property
               name="aboutImage"
               value="icons/AbisPulsa_icon_75x75.png">
         </property>
      </product>
   </extension>

For your information: Other properties you can use include:

  • windowImages
  • aboutImage
  • aboutText
  • appName
  • welcomePage
  • preferenceCustomization

References:
  1. http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/guide/product_def_extpt.htm
  2. http://help.eclipse.org/helios/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/extension-points/org_eclipse_core_runtime_products.html

Fixing Eclipse RCP Launch Error: Application "org.eclipse.ui.ide.workbench" could not be found in the registry.

If you encounter the following error message when launching your Eclipse RCP application/plugin :

!ENTRY org.eclipse.osgi 4 0 2010-12-20 00:49:08.433
!MESSAGE Application error
!STACK 1
java.lang.RuntimeException: Application "org.eclipse.ui.ide.workbench" could not be found in the registry. The applications available are: org.eclipse.ant.core.antRunner, org.eclipse.jdt.core.JavaCodeFormatter, org.eclipse.help.base.infocenterApplication, org.eclipse.help.base.helpApplication, org.eclipse.help.base.indexTool, com.abispulsa.bisnis.rcp.application, org.eclipse.equinox.app.error.
at org.eclipse.equinox.internal.app.EclipseAppContainer.startDefaultApp(EclipseAppContainer.java:248)
at org.eclipse.equinox.internal.app.MainApplicationLauncher.run(MainApplicationLauncher.java:29)
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:369)
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:619)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:574)
at org.eclipse.equinox.launcher.Main.run(Main.java:1407)
at org.eclipse.equinox.launcher.Main.main(Main.java:1383)

It means that you haven't added the following plugin to your target platform / enabled plugins:

org.eclipse.ui.ide.application

That means you'll use the default "workbench" application as used by Eclipse IDE.

When you've created your own application class which implements org.eclipse.equinox.app.IApplication interface, you need to register an Eclipse extension to org.eclipse.core.runtime.applications in your plugin.xml like the following example:

   <extension id="application"
         point="org.eclipse.core.runtime.applications">
      <application>
         <run class="com.abispulsa.bisnis.rcp.Application">
         </run>
      </application>
   </extension>

Then edit your Eclipse Application launch configuration to use your own application class.

Mirroring an Eclipse Update Site to a Local p2 Repository

Installing or updating Eclipse plugins/features is easy using Eclipse p2 Update Sites. However, since it requires downloading from the Internet the process is often very slow.

Some projects provide an archived update site (.zip file) but the rest do not provide them. When installing or updating features for multiple Eclipse IDE or RCP Application installations, downloading the same files multiple times from the Internet can get annoying. Not to mention it definitely wastes precious time AND bandwidth.

Thankfully there is a way to create a local p2 repository that acts as a mirror site to the original Eclipse p2 Update Sites.

This is useful for making available a full Eclipse release or a set of Eclipse features/plugins to internal corporate users, for example, reducing the bandwidth normally used with dozens of users downloading the same bits from external Eclipse p2 Update sites.

Documentation


First, you need to mirror the site (or a particular feature), so take a look at the mirror command described here:
Running Update Manager from Command Line

then create a site policy (a type of redirection) as described here:
Controlling the Eclipse Update Policy

Command Examples


You can start the update manager in a standalone mode to create a mirror of an update site by using
this command:

java -Dhttp.proxyHost=yourProxy -Dhttp.proxyPort=yourProxyPort \
  -jar plugins/org.eclipse.equinox.launcher_<version>.jar \
  -application org.eclipse.update.core.standaloneUpdate -command mirror \
  -from %updateSiteToMirror% -mirrorUrl %urlOfYourUpdateSite% \
  -to %fileLocationToMirrorTo%

Run this command from Eclipse install directory (i.e. where startup.jar is).
Replace %fileLocationToMirrorTo% with the local directory where the update site contents will be copied to, and %urlOfYourUpdateSite% with a URL which will point to the directory you informed.

Of course you will need to install a local web server, like Apache HTTPD and configure it according the directory/URL you specified before.

It even supports to create one mirror-site of multiple sites if you specify the same location for multiple sites it will append them to the site.xml giving you one big (and messy) update site.

An easy way to use this is use a dos or bash scipt ofcourse. For example the following script to mirror the relevant update sites:

set LAUNCHER=C:\opt\springsource-2.1\sts-2.1.0.RELEASE\plugins/plugins/org.eclipse.equinox.launcher_1.0.200.v20090520.jar

call updateSite http://subclipse.tigris.org/update_1.6.x subclipse
call updateSite http://pmd.sourceforge.net/eclipse pmd
call updateSite http://m2eclipse.sonatype.org/update/ m2eclipse
call updateSite http://findbugs.cs.umd.edu/eclipse/  findbugs
call updateSite http://moreunit.sourceforge.net/org.moreunit.updatesite/  moreunit
call updateSite http://www.springsource.com/update/e3.5 sprinsource-e35
call updateSite http://eclipse-cs.sf.net/update checkstyle
call updateSite http://update.atlassian.com/atlassian-eclipse-plugin atlassian
call updateSite http://commonclipse.sourceforge.net commonclipse
call updateSite https://ajax.dev.java.net/eclipse glassfish
call updateSite http://andrei.gmxhome.de/eclipse/ gmx-plugins
call updateSite http://regex-util.sourceforge.net/update/ regex
call updateSite http://ucdetector.sourceforge.net/update/ ucdetector

goto:eof

:updateSite
java -Dhttp.proxyHost=yourProxy -Dhttp.proxyPort=yourProxyPort -jar %LAUNCHER% -application org.eclipse.update.core.standaloneUpdate -command mirror -from %1 -mirrorUrl http://server/eclipseupdatesite/%2 -to Y:\%2 &goto:eof
goto:eof
This gives us multiple update sites under http://server/eclipseupdatesite/ like http://server/eclipseupdatesite/m2eclipse etc. Of course you still need 1 computer to have unrestricted/fast internet access, but you can always create those sites at home.

Aggregating Specific Features

You can also aggregate several features from other update sites to your own, using either p2.mirror, p2 Composite Repositories, b3, or Nexus Pro.

See http://stackoverflow.com/questions/4378112/p2-repositories-aggregator


Sources:

  1. http://dev.eclipse.org/newslists/news.eclipse.platform/msg29529.html
  2. http://stackoverflow.com/questions/4378112/p2-repositories-aggregator
  3. http://www.willianmitsuda.com/2007/03/09/mirroring-callisto-update-site/
  4. http://www.denoo.info/2009/09/mirroring-eclipse-update-sites/

Mirroring an Eclipse Update Site to a Local p2 Repository

Installing or updating Eclipse plugins/features is easy using Eclipse p2 Update Sites. However, since it requires downloading from the Internet the process is often very slow.

Some projects provide an archived update site (.zip file) but the rest do not provide them. When installing or updating features for multiple Eclipse IDE or RCP Application installations, downloading the same files multiple times from the Internet can get annoying. Not to mention it definitely wastes precious time AND bandwidth.

Thankfully there is a way to create a local p2 repository that acts as a mirror site to the original Eclipse p2 Update Sites.

This is useful for making available a full Eclipse release or a set of Eclipse features/plugins to internal corporate users, for example, reducing the bandwidth normally used with dozens of users downloading the same bits from external Eclipse p2 Update sites.

Documentation


First, you need to mirror the site (or a particular feature), so take a look at the mirror command described here:
Running Update Manager from Command Line

then create a site policy (a type of redirection) as described here:
Controlling the Eclipse Update Policy

Command Examples


You can start the update manager in a standalone mode to create a mirror of an update site by using
this command:

java -Dhttp.proxyHost=yourProxy -Dhttp.proxyPort=yourProxyPort \
  -jar plugins/org.eclipse.equinox.launcher_<version>.jar \
  -application org.eclipse.update.core.standaloneUpdate -command mirror \
  -from %updateSiteToMirror% -mirrorUrl %urlOfYourUpdateSite% \
  -to %fileLocationToMirrorTo%

Run this command from Eclipse install directory (i.e. where startup.jar is).
Replace %fileLocationToMirrorTo% with the local directory where the update site contents will be copied to, and %urlOfYourUpdateSite% with a URL which will point to the directory you informed.

Of course you will need to install a local web server, like Apache HTTPD and configure it according the directory/URL you specified before.

It even supports to create one mirror-site of multiple sites if you specify the same location for multiple sites it will append them to the site.xml giving you one big (and messy) update site.

An easy way to use this is use a dos or bash scipt ofcourse. For example the following script to mirror the relevant update sites:

set LAUNCHER=C:\opt\springsource-2.1\sts-2.1.0.RELEASE\plugins/plugins/org.eclipse.equinox.launcher_1.0.200.v20090520.jar

call updateSite http://subclipse.tigris.org/update_1.6.x subclipse
call updateSite http://pmd.sourceforge.net/eclipse pmd
call updateSite http://m2eclipse.sonatype.org/update/ m2eclipse
call updateSite http://findbugs.cs.umd.edu/eclipse/  findbugs
call updateSite http://moreunit.sourceforge.net/org.moreunit.updatesite/  moreunit
call updateSite http://www.springsource.com/update/e3.5 sprinsource-e35
call updateSite http://eclipse-cs.sf.net/update checkstyle
call updateSite http://update.atlassian.com/atlassian-eclipse-plugin atlassian
call updateSite http://commonclipse.sourceforge.net commonclipse
call updateSite https://ajax.dev.java.net/eclipse glassfish
call updateSite http://andrei.gmxhome.de/eclipse/ gmx-plugins
call updateSite http://regex-util.sourceforge.net/update/ regex
call updateSite http://ucdetector.sourceforge.net/update/ ucdetector

goto:eof

:updateSite
java -Dhttp.proxyHost=yourProxy -Dhttp.proxyPort=yourProxyPort -jar %LAUNCHER% -application org.eclipse.update.core.standaloneUpdate -command mirror -from %1 -mirrorUrl http://server/eclipseupdatesite/%2 -to Y:\%2 &goto:eof
goto:eof
This gives us multiple update sites under http://server/eclipseupdatesite/ like http://server/eclipseupdatesite/m2eclipse etc. Of course you still need 1 computer to have unrestricted/fast internet access, but you can always create those sites at home.

Aggregating Specific Features

You can also aggregate several features from other update sites to your own, using either p2.mirror, p2 Composite Repositories, b3, or Nexus Pro.

See http://stackoverflow.com/questions/4378112/p2-repositories-aggregator


Sources:

  1. http://dev.eclipse.org/newslists/news.eclipse.platform/msg29529.html
  2. http://stackoverflow.com/questions/4378112/p2-repositories-aggregator
  3. http://www.willianmitsuda.com/2007/03/09/mirroring-callisto-update-site/
  4. http://www.denoo.info/2009/09/mirroring-eclipse-update-sites/

Making Software Literate: The Parser, The Interpreter, and The Literal

In my last post, the title suggests "teaching code to read itself". However all I wrote was about Interpreter.

For code to read itself, it must be able to generate a model from itself. Therefore, you need the metamodel of the programming lanaguage the software is written in, and the grammar of that language. By using those two ingredients and the proper tools, you can generate a model from the software.

An example of a comprehensive tool for doing this is Eclipse MoDisco. It comes with a complete tooling for discovering / reflecting / introspecting a Java project.

However, for software to understand the model, it must also have an Interpreter/Evaluator, which can do something with the model.

In a way, a generator is a specialized kind of interpreter which simply outputs the concrete representation ("artifact") of the model being processed.

To not only read a model but also to make changes to it, we need a Manipulator (what a scary name), which is a kind of interpreter that performs actions on a model. Sample action: delete an EClass node named 'Car'.

After making changes, the resulting model can be generated back to file artifacts using generator. The project can then be rebuilt, only the changed artifacts are needed.

To rebuild the project from scratch though, we need a complete set of project files.

A typical software project, not only consists of a single language (hence metamodel) but several other artifacts including:
- build.xml (Ant build)
- plugin.xml, MANIFEST.MF (PDE project)
- pom.xml (Maven project)
- build.gradle (Gradle build)
- .project, .classpath, build.properties (Eclipse JDT project)

Depending on requirements, it may not be needed (sometimes not even desirable) to model all of those artifacts properly. Sometimes, it's enough to model a file as a 'Literal':

File: EClass
-----------------------
name: EString
directory: EString
contents: EString

Which in practice means, that these artifacts are not part of the model-driven lifecycle at all. (i.e. You can actually ignore it and it won't evenmatter)

Model-driven is all about transformation, or processing, or shapeshifting, or (meta)morphing. If an artifact or model stays the same throughout the lifecycle, and it's not being used as a metamodel for transformation of its instances, then it's the same thing as if modeling is not used at all.

When all project artifacts are understood, 'literalled', or generated, the project can be rebuilt from scratch using the model. With a good headless build system such as Maven or Gradle, this should be simple.

The other part to include is the information "in programmer's head". We deal with this everyday that it seldom occurs to us that it *is* information.

Things like:
- the directory location of the project
- project name
- location and name of the generated binaries
- SCM user, password, URL
- SCM tool
- test status (whether the tests pass, how many tests, how many fails, how many passes)

These information should be modeled, and a specialized interpreter can be created to act on the project.

A final behavior is 'replaceSelf', which requires the following information:
1. self source location
2. self binary location
3. self descriptor model
4. location of *this* self descriptor model
5. prototype source location
6. prototype binary location
7. prototype descriptor model

where 'prototype' is the project that we've discussed and built above.

The replaceSelf behavior, given the 'self descriptor model' will update/replace itself using the prototype locations, and also update the self descriptor model (e.g. Update the version number).

If the software runs continuously (as a server/daemon), it can then fork a new version of itself, then terminate its own instance.

I guess now the lifecycle is complete. ;-)