vrijdag 8 mei 2015

Using JYZ3D (and JOGL) in Eclipse RCP

As part of evaluating several 3D charting packages for use in Eclipse RCP Applications I needed to get JYZ3D (http://www.jyz3d.org) working on OSX.

Jzy3d is an open source java library that allows to easily draw 3d scientific data: surfaces, scatter plots, bar charts, and lot of other 3d primitives. The API provides support for rich interactive charts, with colorbars, tooltips and overlays. Axis and chart layout can be fully customized and enhanced. Relying on JOGL 2, you can easily deploy native OpenGL charts on Windows, Unix, MacOs (...) and integrate into Swing, AWT, or SWT. Various contributions have also made Jzy3d available for other languages/platforms such as Scala, Groovy, and Matlab.

JYZ3D is described as suitable for RCP but it requires some setup. I describe the process below in order to help others get results quicker.

First attempt

Using libraries in RCP requires packaging them in bundles and adding an OSGi MANIFEST so that they can be properly located as dependencies. As JYZ3D requires JOGL (http://jogamp.org/jogl/www/) I looked for ways to install JOGL easily on RCP, by converting it to a bundle. I found the tutorial by Wade Walker from 2010 that can easily be adapted to the latest version of JOGL meaning 2.3.1.

The first attempt resulted in a Exception: java.lang.UnsatisfiedLinkError: Can't load library: /System/Library/Frameworks/gluegen-rt.Framework/gluegen-rt

UnsatisfiedLinkError

As JOGL uses some interesting class loader tricks, the main library requires an Activator to insert some extra logic on start up using JarUtil.setResolver().

The resulting Activator.java is as follows:

package jogamp.osgi;

import java.io.IOException;
import java.net.URL;

import jogamp.nativewindow.Debug;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

import com.jogamp.common.util.JarUtil;

/**
 * The activator class controls the plug-in life cycle
 */
public class Activator extends AbstractUIPlugin {

 // The shared instance
 private static Activator plugin;

 /**
  * Returns the shared instance
  *
  * @return the shared instance
  */
 public static Activator getDefault() {
  return plugin;
 }

 /**
  * The constructor
  */
 public Activator() {
 }
 @Override
 public void start(BundleContext context) throws Exception {
  super.start(context);
  JarUtil.setResolver(new JarUtil.Resolver() {
   @Override
   public URL resolve(URL url) {
    try {
     // System.out.println("before resolution: " + url.toString());
     URL after = FileLocator.resolve(url);
     // System.out.println("after  resolution: " + after.toString());
     return (after);
    } catch (IOException ioexception) {
     return (url);
    }
   }
  });
  plugin = this;
}

 @Override
 public void stop(BundleContext context) throws Exception {
  plugin = null;
  super.stop(context);
 }

}

There was a change in the native code library naming conventions from Java 6 to Java 7 so you must unpack the *macosx-universal jars (jogl and gluegen), duplicate al *.jnilib files to *.dylib files and repack into the jars.

Once you have done this you can run Wade Walker's example view code.

JOGL Demo Wade Walker I then downloaded jars for JZY3D 0.9.1 from Maven and created a bundle using Create Bundle from jar.

The result was a lot of errors about package javax.media.opengl not being found. JXY3D relies on a much older version of JOGL and despite it being a 2.x.x. version there is definitely a compatibility break here. So much for the adoption of proper semantic versioning.

Second attempt

I downloaded the source for JXY3D from GitHub from https://github.com/jzy3d/jzy3d-api importing them as Maven projects (important step) and built the jars as Maven projects.

These depend on a newer version of JOGL (2.1.5-01) but having learned my lesson about version compatibility I created new JOGL Library plugin using the jars for version 2.1.5-01. I downloaded these from Maven Central. Again fix the native library naming issue for macosx-universal versions.

The mechanism for finding the natives changes between JOGL versions, so here the solution is to put all native jars into the same bundle as the main library. Again add the above bundle activator, and, as this is a bundle with native jars in the root, add the bin/ folder with the Activator to the class path.

Another problem (noted by Alexis Drogoul) is a bug in FileLocator that occurs when the path contains spaces. This was also fixed on 2015-06-03

classpath

The final error purely on OSX was org.eclipse.swt.SWTError: Not implemented java.lang.ClassNotFoundException: apple.awt.CEmbeddedFrame> This can be solved using the magic found on stackoverflow: SWT_AWT.embeddedFrameClass = "sun.lwawt.macosx.CViewEmbeddedFrame";

The final Activator.java is as follows:

package jogamp.osgi;

import java.io.IOException;
import java.net.URL;

import jogamp.nativewindow.Debug;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.swt.awt.SWT_AWT;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;

import com.jogamp.common.util.JarUtil;

/**
 * The activator class controls the plug-in life cycle
 */
public class Activator extends AbstractUIPlugin {

 // The shared instance
 private static Activator plugin;

 /**
  * Returns the shared instance
  *
  * @return the shared instance
  */
 public static Activator getDefault() {
  return plugin;
 }

 /**
  * The constructor
  */
 public Activator() {
 }
 @Override
 public void start(BundleContext context) throws Exception {
  super.start(context);
  if ("Mac OS X".equals(System.getProperty("os.name"))) {
   System.out.println("Set SWT_AWT.embeddedFrameClass");
   SWT_AWT.embeddedFrameClass = "sun.lwawt.macosx.CViewEmbeddedFrame";
  }
  JarUtil.setResolver(new JarUtil.Resolver() {
   @Override
   public URL resolve(URL url) {
    try {
      // System.out.println("before resolution: " + url.toString());
      URL urlUnresolved = FileLocator.resolve(url);
      URL urlResolved = new URI(urlUnresolved.getProtocol(), urlUnresolved.getPath(), null)
       .toURL();
      // System.out.println("after resolution: " + urlResolved.toString());
      return (urlResolved);
     } catch (IOException ioexception) {
      return (url);
     } catch (URISyntaxException e) {
      return (url);
     }
   }
  });
  plugin = this;
}

 @Override
 public void stop(BundleContext context) throws Exception {
  plugin = null;
  super.stop(context);
 }

}

And then I can also run the example code for JYZ3D.

JYZ3D demo

Lessons

  • Don't assume that everybody uses semantic versioning.
  • Be grateful for the people who take the time to answer questions on StackOverflow.

dinsdag 10 juni 2014

Workspace Mechanic and Eclipse Arduino Plugin

After reading Wim Jongmans blog post about Managing Eclipse Preferences with Workspace Mechanic I decided to give it a go for the Arduino Eclipse Plug-in by Jantje. The settings for this plug-in are managed as instance settings, so they exist once for the Eclipse install. Now that we have regular upgrades to prepare for the Eclipse Luna release, this means reconfiguring for each new download. I use a Mac so it has to look like this:Screen Shot 2014 06 10 at 21 14 48 To make this happen with Workspace Mechanic install this file in your user_home/.eclipse/mechanic/ folder.
# @title Arduino Mac
# @description Standard Arduino Settings
# @task_type RECONCILE
#
# Copyright 2014 Maarten Meijer
# License EPL: http://www.eclipse.org/legal/epl-v10.html
#
file_export_version=3.0
/instance/it.bayens.arduino/Arduino Path=/Applications/Arduino.app/
/instance/it.bayens.arduino/Private Library Path=/Users/your_name/Documents/Arduino/libraries
/instance/it.bayens.arduino/Private hardware Path=/Users/your_name/Arduino/hardware
/instance/it.bayens.arduino/Arduino DisAbleRXTX=false
Every new release you will; get a nice Workspace Mechanic warning and you can fix all!