Tuesday, June 22, 2010

How to Deploy Xtext to a Headless Plain-Java Environment?

Yesterday, Sven and I were at the Eclipse Democamp in Hannover which was organized by brox IT-Solutions and Bredex - thanks for the nice event. I met interesting people and we discussed various topics. One particular question about Xtext was raised several times from different sides: How do I use my language in an environment without Eclipse, e.g. on a build server? As this was already possible from the first minute even with the former oAW version of Xtext and with Xtext 0.7.0, too, I was kind of suprised. In fact, any execution of the generator workflow uses Xtext in a standalone environment - without OSGi, Equinox or any dependency on Eclipse UI. The problem seems to be another one: How can I determine all the required libraries to deploy my Xtext-driven generator?

It turns out that this task is suprisingly easy with Eclipse. We'll use a minor trick to simplify the necessary steps. First of all, we have to create a new Run Configuration for a Java Application. Actually this one will never be executed, but we need to refer to this one in the next step. Just create a new configuration for your generator-project and use the org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher as the main class:
Now that we have a run configuration for the generator project, let's export the project as a runnable jar. Choose Export... -> Runnable JAR file from the context menu of the generator project. The wizard will ask for a launch configuration and that's where we use the one that we created a second ago:
Select the options of your choice, point to a target file and hit the Finish-button. Eclipse will collect required libraries and add the necessary entries to the MANIFEST.MF in the runnable JAR file. As I already said, it's suprisingly simple.

Last but not least, I recommend to create a batch file or shell script that starts a virtual machine, points to the exported archive and refers to the module name of the generator workflow, e.g.:
java -jar exported.jar workflow.MyDslGenerator
It's probably a good idea to wrap the invocation of the Mwe2Launcher in a custom main class to encapsulate the name of the generator workflow and pass additional arguments to the workflow.

10 comments:

Christoph said...

Cool, thx for sharing!

Richard said...

Hi,

Thanks a lot for your blog post.

I have long mileage on Maven, how to create single jar files, details of MANIFEST.MF, etc but near zero on tricky details of Eclipse platform.

The problem I had when I executed the passes you pointed out is that there's no mainClass in the generated MANIFEST.MF created in the exported JAR file.

Due to this reason, I'm not able to run it with the "-jar" option, like you pointed out. It 'works' if I employ "-cp" option and I mention the Mwe2Launcher class name explicitly.

Do you have any idea of what the cause it would be (for a missing mainClass in the MANIFEST.MF file) ?

I suppose that it would be due to the fact I'm also using m2eclipse, i.e: I have my pom.xml file and I have some dependencies (which are properly packaged by Eclipse!).

Do you have any experience with it?

Any help is much appreciated.

Thanks a lot:)

- Richard Gomes

Richard said...

This is my answer to myself so that more people could benefit.

Seems like Eclipse Exporter integrates somehow with m2eclipse. It means that dependencies specified in your pom.xml file are packaged as long as dependencies specified in the /META_INF/MANIFEST.MF file (which is a resource needed by Eclipse plugins; do not confuse with mundane MANIFEST.MF files).

So, in order to specify what your mainClass is, you can do so in your pom.xml file, as part of configuration of maven-jar-plugin. Below you can see what I have:

<project
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.vaadin</groupId>
<artifactId>pageflow-dsl-generator</artifactId>
<version>0.1.0-SNAPSHOT</version>

<build>
<sourceDirectory>src</sourceDirectory>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<index>true</index>
<manifest>
<mainClass>Launcher</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>

</build>



<dependencies>
<dependency>
<groupId>urbanophile</groupId>
<artifactId>java-getopt</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>

</project>


Thanks

Richard Gomes

Sebastian Zarnekow said...

Thanks for providing the answer as a follow up.

tobias2001 said...

When trying to export the JAR as you described (Helios 64-bit Windows package from the Itemis site) I'm getting the following error:

Could not find main method from given launch configuration.

Accordingly, the generated JAR file doesn't work.

The Mwe2Launcher has been correctly selected in the Run Configuration and I can see that it has a main() method.

Do you have any idea what I might be missing?

Michael Scharf said...

Unfortunately, this procedure does not work (for me). The problem is that some emf code needs some properties form a plugin.properties file: ERROR eclipse.emf.mwe.core.WorkflowRunner - The string resource '_UI_DiagnosticRoot_diagnostic' could not be located

So, the trick is to add a plugin.properties to the generated .jar file that fixes the problem.

I use 7zip to add a .properties file.

I generate the plugin.properties file by:

- do the export above but this time I select
"Copy the required file into a sub-folder next to the generated JAR"
- then I extract all jar files (using 7zip) into one folder but
with auto-rename all duplicate files
- cat all plugin*.properties file into one

I hope this helps

Michael

Sebastian Zarnekow said...

@Tobias I'm afraid I've no idea why this fails. Could you provide a small reproducable example so I can have a look at it?

@Michael Thanks for sharing this.

Ameer said...

I had the same problem as Tobias (Helios on snow leopard). It appears that the export facility in eclipse cannot refer to a main class in a referenced jar, rather than the project jar. I solved the issue by creating a wrapper main class in the generator project that simply delegates to org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher

Karsten Thoms said...

Hi Sebastian!

Thanks for this article. This was exactly what I needed. I faced the same problem when exporting the Jar, but writing a wrapper class solved this. I have added a link to this post here: http://code.google.com/a/eclipselabs.org/p/lwc11-xtext/wiki/Phase_3_HeadlessGenerator

~Karsten

Gregory Krasnow said...

I attempted to use this method, but I ran into some issues.

I first ran into the issue mentioned already about the main class not being accessible and needing to use java -cp instead of java -jar.

The other issue I ran into was this:
1 ERROR Mwe2Launcher - Problems instantiating module com.foo.GenerateWildFire: java.lang.reflect.InvocationTargetException
java.lang.RuntimeException: Problems instantiating module com.foo.GenerateWildFire: java.lang.reflect.InvocationTargetException
...
Caused by: org.eclipse.emf.mwe.core.ConfigurationException: The platformUri location '../com.foo.wildfire/..' does not exist
at org.eclipse.emf.mwe.utils.StandaloneSetup.setPlatformUri(StandaloneSetup.java:148)
... 34 more

Is there something which I missed?