Build a JavaFX + LeapMotion application with gradle

For SlideshowFX (which is an app I’m developing in JavaFX and using the LeapMotion controller) I am trying to use gradle as build and dependencies management system instead of the very well-known  maven. There are few steps I had to fight with to successfully build a self-contained application (aka native bundle). For those don’t knowing what that means take a look at the Oracle Documentation.

Disclaimer : I’m new in the gradle’s world so maybe a lot of things could be improved.

Prerequisites

  • Having a JDK 8 up and running
  • Having the JAVA_HOME environment variable defined to point the the JDK 8 installation
  • Having gradle installed properly (version 1.11 used at the time this article was written)
  • Knowing gradle a little bit

 

The javafxpackager

In order to package a JavaFX application you can use the javafxpackager tool provided with the JDK. It allows you to create the application JAR and then the native bundle for your application.

To create the jar you will use the following command:

javafxpackager -createjar ...

You will also need to define some attributes according your application, like -classpath for the list of JARs and libraries your application is using, -appclass for indicating what is the Application class of your application, -srcdir and -srcfiles for indicating which files should be included in your JAR, -outdir for indicating where the JAR file will be created and -outfile for the name of the JAR file.

To create the native bundle you will use the following command:

javafxpackager -deploy -native ...

Building with gradle

In order to build your application with gradle, we will invoke the javafxpackager tool from gradle itself. A JavaFX plugin for gradle exists but I didn’t succeed in using it for my purpose. Indeed for LeapMotion you need some DLL and dylib files that weren’t included in the final JavaFX bundle so I decided to do it myself using gradle tasks. And this also allowed to have a better understanding of the javafxpackager tool instead of just using an IDE to generate my bundle.

Configuration

In the build.gradle file, I configured all projects with the Java plugin and the source and target compatibility:

subprojects {
  apply plugin: 'java'
  sourceCompatibility = 1.8
  targetCompatibility= 1.8
}

Dependencies and libraries

For SlideshowFX I decided to use file dependencies. So first of all I declared the dependencies at the beginning of the build.gradle file:

ext {
  allLibs = new File(rootDir, '/lib')
  felix = fileTree(dir: allLibs, include: 'Felix/*.jar')
  json = fileTree(dir: allLibs, include: 'json/*.jar')
  jsoup = fileTree(dir: allLibs, include: 'jsoup-1.7.3.jar')
  junit = fileTree(dir: allLibs, include: 'junit-4.11.jar')
  leapmotion = fileTree(dir: allLibs, include: 'Leap/*')
  markdown = fileTree(dir: allLibs, include: 'markdown/*.jar')
  scribe = fileTree(dir: allLibs, include: 'Scribe/*.jar')
  textile = fileTree(dir: allLibs, include: 'WikiText/*.jar')
  velocity = fileTree(dir: allLibs, include: 'Velocity/*.jar')
  vertx = fileTree(dir: allLibs, include: 'Vert.x/*.jar')
  zxing = fileTree(dir: allLibs, include: 'ZXing/*.jar')

  jdk = System.env.'JAVA_HOME'
}

Those files collections will be used further for declaring dependencies for each project. For LeapMotion, all files are included: JARs, DLL, and dylib.

Build the jar

To build the JAR I decided to overwrite the default JAR task present in the Java plugin of gradle. We are going to follow these steps:

  • Copy all dependencies of the project to the libsDir folder
  • Build the -classpath attribute for the javafxpackager tool. Each JAR used by the application is adde to the classpath
  • Build the -srcdir/-srcfiles attributes for the javafxpackager tool. There will be many of these attributes because there are classes and resources
  • Build the javafxpackager command to execute
  • Execute the command

Knowing that here is the task definition:

task jar(overwrite: true) << {

  if (jdk != null && !jdk.isEmpty()) {

    if(!libsDir.exists()) libsDir.mkdirs()

    // Copying libs
    copy {
      from(new File(project(':SlideshowFX-markup').libsDir, project(':SlideshowFX-markup').archivesBaseName + ".jar"))
      from(felix.files)
      from(json.files)
      from(jsoup.files)
      from(leapmotion.files)
      from(scribe.files)
      from(velocity.files)
      from(vertx.files)
      from(zxing.files)

      into(libsDir)
    }

    def classpath = ""

    fileTree(dir: libsDir, include: '*.jar', exclude: archivesBaseName + ".jar").each {
      f ->
      classpath += f.name + ","
    }

    classpath += "."

    def javafxpackager = exec {
      workingDir "${project.projectDir.absolutePath}"

      commandLine "${jdk}/bin/javafxpackager",
                  "-createjar", "-v",
                  "-appclass", "com.twasyl.slideshowfx.app.SlideshowFX",
                  "-classpath", "${classpath}",
                  "-outdir", "${buildDir}${File.separator}${libsDir.name}",
                  "-outfile", "${project.archivesBaseName}",
                  "-srcdir", "${buildDir.name}/classes/main", "-srcfiles", "com",
                  "-srcdir", "${buildDir.name}/resources/main", "-srcfiles", "com"
    }
  }
}

Build the native bundle

Now that we have the JAR, let’s make the native bundle. For this we will also create a task for achieving this.

task buildJavaFXBundle << {

  if (jdk != null && !jdk.isEmpty()) {

    def javafxpackager = exec {
      workingDir "${project.projectDir.absolutePath}"

      commandLine "${jdk}/bin/javafxpackager",
                  "-deploy",
                  "-native",
                  "-name", "SlideshowFX",
                  "-outdir", "${buildDir.name}${File.separator}dist",
                  "-outfile", "SlideshowFX",
                  "-srcdir", "${buildDir.name}${File.separator}${libsDir.name}",
                  "-appclass", "com.twasyl.slideshowfx.app.SlideshowFX"
    }
  }
}

Tasks’ dependencies

Now that we have the needed tasks, let’s add some dependencies between them in order to include everything in the gradle lifecycle:

tasks['jar'].dependsOn 'classes'
tasks['jar'].dependsOn ':SlideshowFX-markup:jar'
tasks['buildJavaFXBundle'].dependsOn 'jar'
tasks['assemble'].dependsOn 'buildJavaFXBundle'

Now calling the assemble task makes it.

Why being a Rebel is hot and spicy?

I usually write technical articles about what is hot and spicy in software development, or at least what I think is hot and spicy. Well, I will make a little exception this time and write about why it is also hot and spicy being a RebelLabs author. If you’re reading this, you probably know what RebelLabs is: a house of geeky ninjas that like to write technical content about hot topics in our geeky development world. According Oliver White, head of RebelLabs:

Developers enjoy the additional “spice” of RebelLabs–we’ve managed to create a reliable content machine producing challenging, opinionated and humorous technical content for all software engineers to enjoy. Recently I saw this quote about RebelLabs by Rafael Winterhalter, in a recent Developer of the Week interview by DZone, and I think it sums things up very nicely: “I like RebelLabs a lot, especially since they manage to be both competent and humorous which is a rather rare combination.” —

Finding passion in the geeky world of development

As a geek, I’m passionate about software development, especially about Java and more recently, JavaFX. This passion leads me to go to people and say “Hey look, I discovered some pretty awesome new stuff”. But you can’t do this with your wife or your grandma, right? You won’t be understood … So frustrating …

But as passionate geek, I still need to tell somebody what I have discovered, tested and developed because I’m proud of it. Yeah you read right, proud of it. It is like being a kid having the latest trendy toy who shows everybody he has it. It is the same for me with software development. RebelLabs brings me the possibility of doing it, with a lot of fun.

I don’t need to write about old tools, APIs, languages and so on. I need to find a subject, find a way of making it attractive and interesting, and write. And when it’s published it’s like telling the world how proud you are. It is like your work being concrete and not only stored on your computer, right? Haven’t you ever felt that feeling of pride when you’ve developed the next most awesome API and nobody is using it?

The open source writer

We all know open source projects, developed by passionate ninjas to help other ninjas (or not) do their jobs better and make them easier by bringing them tools, APIs and other shiny stuff. I know a couple of those. One of them, Guillaume Scheibel (@g_scheibel and Hibernate OGM contributor) told me some time ago, that in the beginning it was not that easy to code in open source projects because you show the way you code to everyone and you have to accept remarks from others giving you advices of how doing things better. You have to accept remarks and sometimes it is not that easy. Writing is almost the same because you show the world what you’ve done and how you write. Double pressure.

I accept both because what you learn is much greater than this. Why is it? Because I work with guys that are as passionate as me and who like to teach you things about writing, tools and more. They don’t want to take you down in what you’re writing, but raising you up. And remarks are always constructive. It helps you do things better.

I also think this needs a little bit of courage. You, and I, have to be brave before publishing your first article. I needed some to accept the opinions of others, I needed some to accept the remarks and comments about my opinion, way of writing and way of thinking. I needed some to accept that other ninjas will take a look and possibly prove me wrong. But at the end the discussion will bring you a lot of point of view, a lot of ideas and you will always have the chance to be better next time.

Being visible

There is a really nice side effect of writing for RebelLabs: you are visible and you’re getting more and more known. People are reading your articles, looking at your Twitter profile, reading your blog and so on. It is a nice recognition to have people with more experience than you reading your articles, commenting on them and sometimes offering you their point of view.

You start to grow much more, and very quickly. And people can see that you are passionate, up to date, open minded and full of resources. It is a very positive image you’re giving of yourself.

You also benefit of the RebelLabs’ image, so you are more considered as experimented because if you’re not, you’re going to know it very quickly. So it is good for RebelLabs and it is good for you as well.

Hot and spicy instead of glory

To conclude, I write for fun. I write because I like to share my knowledge. I write because I’m passionate. I write because I like to write. I don’t write for any glory. I’m humble enough to admit there are plenty of ninjas out there that are thousands times better than me. But I am always looking for new challenges and I like to share them because maybe it could help other ninjas like you.

It’s a great opportunity to write for RebelLabs, and I like it very much. I can only encourage you to think about it, because being a Rebel is freakin’ awesome, and it’s hot and spicy!

Partial HTML generation from textile using eclipse Mylyn standalone

The title is pretty much explicit. For SlideshowFX, I needed to generate HTML content from textile, in order to define slide’s content. Looking for a lib, I found eclipse Mylyn that can be used in a standalone way. The library is pretty powerful but I have to look a lot at the sources in order to get the things done how I wanted to (#LukeAtTheSource power). Generally to convert a markup string into a HTML one, you create a MarkupLanguage as well as a MarkupParser and do the following:

final MarkupLanguage language = new TextileLanguage();
final MarkupParser parser = new MarkupParser(language);
parser.parseToHTML("h1. My little String");

The thing is that this code will create a whole HTML document (with html, head, body and so on tags). Me needs were to just get the HTML code corresponding to my string, e.g.

<h1>My little String</h1>

So the code should be changed to use a DocumentBuilder, like the following one:

final StringWriter writer = new StringWriter();
final DocumentBuilder builder = new HtmlDocumentBuilder(writer);
final MarkupLanguage language = new TextileLanguage();
final MarkupParser parser = new MarkupParser(language, builder);

// false indicates to not produce a whole HTML document
parser.parse("h1. My little String", false);

writer.flush();
writer.close();

String htmlContent = writer.toString();

This is almost done except that Mylyn generates IDs (which is kind of normal right?) by using the content of the markup. In short, I wanted to avoid IDs’ generation but I could’t find a right and efficient way to do it. So I decided to ensure uniqueness of IDs by always getting the current timestamp. Maybe it’s not a wonderful solution, but for the purpose of this little tutorial it will be perfect. The main idea is to change the ID generation and for doing this, you have to override some classes, because the default ID generation strategy is stored as a static and final variable, and no setters are available. The following example demonstrates how to do it:

final StringWriter writer = new StringWriter();

// The generation strategy generates IDs using the current timestamp
final IdGenerationStrategy idGenerationStrategy = new IdGenerationStrategy() {
  @Override
  public String generateId(String s) {
    return System.currentTimeMillis() + "";
  }
};

final IdGenerator idGenerator = new IdGenerator();
idGenerator.setGenerationStrategy(idGenerationStrategy);

final TextileContentState contentState = new TextileContentState() {
  @Override
  public IdGenerator getIdGenerator() {
    return idGenerator;
  }
};

// Override the language to return the created contentState used for the ID generation
final MarkupLanguage language = new TextileLanguage() {
  @Override
  protected ContentState createState() {
    return contentState;
  }
};

final DocumentBuilder builder = new HtmlDocumentBuilder(writer);

final MarkupParser parser = new MarkupParser(language, builder);

parser.parse(markupString, false);

writer.flush();
writer.close();

final String htmlContent = writer.toString();

This is it. Enjoy.

Working with NetBeans 7.3 and JavaFX 8

Maybe you want to work with the latest version of NetBeans, currently 7.3. But also with the early access of JDK 8 in order to play with the latest features of JavaFX. The thing is that NetBeans will not launch your JavaFX application, even if you are using JDK 7. Indeed you will get an exception dealing with Nashorn.

java.lang.VerifyError: Code generation bug in "runScript": likely stack misaligned: java.lang.ArrayIndexOutOfBoundsException: 0 <eval>
	at jdk.nashorn.internal.codegen.CodeGenerator.leave(CodeGenerator.java:1003)
	at jdk.nashorn.internal.ir.FunctionNode.accept(FunctionNode.java:339)
	at jdk.nashorn.internal.codegen.CompilationPhase$7.transform(CompilationPhase.java:239)
	at jdk.nashorn.internal.codegen.CompilationPhase.apply(CompilationPhase.java:372)
	at jdk.nashorn.internal.codegen.Compiler.compile(Compiler.java:263)
	at jdk.nashorn.internal.runtime.Context.compile(Context.java:758)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:720)
	at jdk.nashorn.internal.runtime.Context.compileScript(Context.java:358)
	at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:463)
	at jdk.nashorn.api.scripting.NashornScriptEngine.compileImpl(NashornScriptEngine.java:451)
	at jdk.nashorn.api.scripting.NashornScriptEngine.evalImpl(NashornScriptEngine.java:379)
	at jdk.nashorn.api.scripting.NashornScriptEngine.eval(NashornScriptEngine.java:134)
	at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.util.ReflectUtil.invoke(ReflectUtil.java:108)
	at org.apache.tools.ant.util.ReflectWrapper.invoke(ReflectWrapper.java:81)
	at org.apache.tools.ant.util.optional.JavaxScriptRunner.evaluateScript(JavaxScriptRunner.java:103)
	at org.apache.tools.ant.util.optional.JavaxScriptRunner.executeScript(JavaxScriptRunner.java:67)
	at org.apache.tools.ant.taskdefs.optional.Script.execute(Script.java:52)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.taskdefs.Sequential.execute(Sequential.java:68)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.taskdefs.MacroInstance.execute(MacroInstance.java:398)
	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
	at sun.reflect.GeneratedMethodAccessor74.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:487)
	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
	at org.apache.tools.ant.Task.perform(Task.java:348)
	at org.apache.tools.ant.Target.execute(Target.java:392)
	at org.apache.tools.ant.Target.performTasks(Target.java:413)
	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
	at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
	at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
	at org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:283)
	at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:541)
	at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:153)
Caused by: java.lang.ArrayIndexOutOfBoundsException: 0
	at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1408)
	at jdk.internal.org.objectweb.asm.Frame.merge(Frame.java:1364)
	at jdk.internal.org.objectweb.asm.MethodWriter.visitMaxs(MethodWriter.java:1382)
	at jdk.nashorn.internal.codegen.MethodEmitter.end(MethodEmitter.java:198)
	at jdk.nashorn.internal.codegen.CodeGenerator.leave(CodeGenerator.java:1000)
	... 50 more

The problem is that NetBeans uses the most recent JDK installed on your machine to run itself. And if you look in the About NetBeans menu entry, you will see that it uses the JDK 8 if it is installed, even if your JAVA_HOME points to your JDK 7 installation.

NB_JDK7

But there is a little workaround to get things working again: you can specify, in a configuration file of NetBeans, which JDK it should use. On OSX, show the package content of NetBeans 7.3.app and go to Content > Resources > NetBeans &gt etc and open the file netbeans.cnf. In this file, locate the line containing netbeans_jdkhome and uncomment it and specify the JDK home you want NetBeans to use. For example:

netbeans_jdkhome="/Library/Java/JavaVirtualMachines/Current/Contents/Home"

Then just restart NetBeans and go the About NetBeans menu entry. You should see that your JDK 7 installation instead of JDK 8.

NB_JDK8

Now if you try to launch your JavaFX application, everything is working again.

I JUG-FX, feedback on my JavaFX presentation at the ElsassJUG

It’s not very common I’m writing a post that is not about code. But today is an exception to give you a feedback on my first JavaFX presentation I gave at the ElsassJUG, the JUG located in Strasbourg, Alsace, France.

Back on stage

JavaFX_ElsassJUG_03
First of all, it feels good to be back on stage. As a former Java teacher, I really enjoy sharing my knowledges and experiences with others, having a discussion about a specific topic (essentially Java). Having others’ point of view, confront them make you grow. Having the opportunity to share them again was really great, especially when it is about a topic you really believe in: JavaFX. I really missed those feelings when you’re preparing your talk: you train a lot, ask yourself what you’re going to say and do, you have to be concise but not too much, “should I code something?”, and so on. So many feelings and questions that cheer you up. And then just before to start, in front of your attendance, you’re just a bit nervous, and now time for show! If you like challenges, it’s a good one. Back when I was a teacher, I remember almost every single moment. That was great. And yesterday at the JUG, it was great too!

Show time

JavaFX_ElsassJUG_02 My presentation was about JavaFX 2. If you’re following this blog, you won’t be surprised. Why JavaFX? Because since I discovered JavaFX 2, I’m very excited about it, and really enthusiast. I knew JavaFX 1 and I have to be honest, I was disappointed from it. I know Swing for many years and I think it suits no more the way we do UI today, in 2012. I know JSF (1 & 2) and some aspects are really great. I’ve always been someone who believes in desktop apps, despite of the great progress done in web, and all the great stuff you can do, I still believe in desktop apps. And JavaFX 2 is for me an absolutely great platform for developing desk apps in Java. Up to date in terms of development process (in comparison to Swing), easier, more powerful, UI & UX oriented, it takes nice concepts from the entire Java ecosystem. And I really wanted (still want) to share my enthusiast about it. So I did. My presentation was about an introduction to JavaFX 2, but I decided to give an overview of really important concepts like properties and bindings, as well as FXML, which for me are some really key features of JavaFX. And with which you do crazy things, believe me! And during the talk we switched from the slides to the code some times, in order to create a simple browser without pretensions, just to explore our key features. I think it’s important to explain important concepts but also to give a “real life” example. I could tell you many things about the properties during 30 minutes, but you won’t remember anything. So I kept it simple with a demo. And it’s fun to see Google and LinkedIn in our little browser. At the end we had a Q&A session which was really interesting because I felt some enthusiasm in the attendance about JavaFX and that was great! I really enjoyed the evening.

Community

As a Java developer I feel lucky. Lucky because the Java ecosystem has a great community. When you go to Devoxx, you have such a spirit that when you come back you just want to say “I wanna go back to Devoxx”. You meet people like you and I, people that love coding, love to try new things, enjoy to talk with you about the latest thing they’ve tried. Lucky because I have a JUG. A JUG is a place when you meet people that know what problems you’re facing when you’re trying to connect using a REST webservice, trying to understand why your pom.xml doesn’t work, how you could overcome a JPA problem and so on. A place where you discover so much topics from people with passion. Lucky because I know people all around the world that are as must passionate as I am, working in various companies (even at Oracle), on various projects and to who I can ask “Hey I don’t know how to do this, you have an idea?” and get an answer. That is what community is all about. The community spirit, the Java spirit!

And now?

I have another presentation about JavaFX planned in March 2013 at the MarsJUG (the JUG in Marseille, France) just before Devoxx France. Of course if you would like a JavaFX presentation, feel free to contact me. I think about submitting a paper at Devoxx France 2013, I just need to continue to think about a nice subject (I already have one in mind). After this first talk I am more than ever enthusiast about JavaFX, and spread the word about it. I wish I could *-FX instead of “just” JUG-FX. I’m working on that.

And also I’ll try get involved in the OpenJFX project because it’s a nice place for JavaFX lovers (and I’m definitively one of them!) and to learn a lot of things.

In conclusion, keep coding, keep loving that and do it with passion! (And do some JavaFX, it rocks)
See you around.

PS: I really would like to thank some guys at Oracle for the great support and also the team at the ElsassJUG for letting me present JavaFX. And thanks to the folks who came at my presentation!