EGGG - Eclipse, GWT, GAE, and Gradle

PreethumTue, 2015-05-05 23:16

I've been a long-time developer on GWT and in recent years, that development has been supported by GAE. With the movement of Android to rely on Gradle for it's dependency management, it made sense to start migrating our GWT/GAE projects to the same concept. Since I still use Eclipse, it was important to be able to maintain the benefits of debugging all within a single IDE when possible. That said, there was very little unified information out there about using all of these technologies together and making it work. This article will give you some guidance into how I finally made it work (at least for the straight-forward cases). The final process is not the most user-friendly in terms of starting up the testing modes, but it is functional. I have created a sample project implementing these details: https://bitbucket.org/blue-esoteric/eggg/, so feel free to check it out.

Assumptions

For this article, I make the following assumptions. You have an Eclipse project that uses both Google App Engine (https://cloud.google.com/appengine/docs) for Java, enabled with GWT 2.7.0 (and you have some familiarity using SuperDevMode: http://www.gwtproject.org/articles/superdevmode.html), and at least a basic understanding of Gradle scripts. There are of course a few Eclipse plugin's that you will need:

Step 1: Setup the Gradle Files

First create the build.gradle file using the below template. This file will set you up for dependencies for GWT and GAE against the versions specified in the gradle.properties file. It will also setup your system for debugging both the client and server side.

buildscript {
	// Dependency Versions
	ext{
		gaeVer = '1.9.19'
		gwtVer = '2.7.0'
	}
	
	repositories {
		mavenCentral()
		jcenter()
	}

	dependencies {
		classpath "com.google.appengine:gradle-appengine-plugin:$gaeVer"
		classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6'
	}
}

repositories{
	mavenCentral()
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'war'
apply plugin: 'appengine'
apply plugin: 'gwt'

dependencies {
	compile "com.google.gwt:gwt-user:$gwtVer"
	compile "com.google.gwt:gwt-dev:$gwtVer"
	compile "com.google.appengine:appengine-api-1.0-sdk:$gaeVer"
	appengineSdk "com.google.appengine:appengine-java-sdk:$gaeVer"

	testCompile 'junit:junit:4.11'
	testCompile "com.google.appengine:appengine-testing:$gaeVer"
	testCompile "com.google.appengine:appengine-api-labs:$gaeVer"
	testCompile "com.google.appengine:appengine-api-stubs:$gaeVer"
}

gwt{
	//gwtVersion='2.7.0' // Including this seems to remove -user and -dev, along with several app engine dependencies from the gradle listing in Eclipse
	modules 'com.blueesoteric.eggg.EGGG'
	devModules 'com.blueesoteric.eggg.EGGG'
}

appengine {
	downloadSdk = true
	jvmFlags = ['-Xdebug', '-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000']
	httpPort = 8085
	appcfg{
		oauth2 = true
	}
}

Step 2: Convert the Project and Layout

In Eclipse, right click the project, Configure -> Convert to Gradle Project. Once this is complete, in the project Properties, Google->App Engine, unclick "Use Google App Engine". In the same properties, Google->Web Toolkit, unclick "Use Google Web Toolkit". Now make the following new directories:

  • src/main/java
  • src/test/java
  • src/main/webapp

Now move your original files from your src path to "src/main/java". Move files from your test path into "src/test/java". Finally move your configuration files from "war" into "src/main/webapp". This should include the config files "web.xml", "appengine-web.xml", etc. into "src/main/webapp/WEB-INF".

Step 3: Setup the Debug Profiles

In Eclipse, create a new Debug Configuration. The first configuration will be to allow debugging of the AppEngine service within Eclipse. In the sample app, I call this EGGG_Remote. It is a configuration of type Remote Java Application (1) and should have the following fields:

  • Project - Select your Project
  • Host: localhost
  • Port: 8000 (see line 43 of the above build.gradle script for the source of this port number)

Save that configuration and create a new Debug Configuration of type Launch Chrome (this comes from the SDBG Plugin...It is only available as a debug configuration, not a runtime configuration). I call this EGGG_SDBG. This is an optional action, but it helps to debug GWT client within Eclipse. It should be populated with the following values:

  • URL - http://localhost:8085/EGGG.html (See line 44 of the above build.gradle script for the source of this port number, use your specific .html page)
  • Project - Select your project

Save this configuration. Now you should be all set to run and debug your GWT/GAE app in a local dev environment with SuperDevMode.

Step 4: Test it Out

This is the part that is somewhat annoying. In order to get the testing environment setup correctly, you need to run a couple commands. Within Eclipse, open the Gradle View and select your Project. Assuming you have no errors in your gradle file, you can do a search for Gradle tasks. First run:

appengineRun

This command will start up the app engine server and get it ready to receive commands. Next run 

gwtSuperDev

This command creates the client GWT SuperDevMode connection that will be utilized. 

Next, run the Debug Configuration EGGG_SDBG. This will popup a new Chrome window. If you've never used SuperDevMode, be sure to review creating the bookmarklets for turning SDM on. Once Chrome has loaded your page, it will probably report and error that the *.js file is not available. Ignore it, and within the chrome window, press the Dev Mode On bookmarklet and click compile next to your module. This may take a few moments depending on the complexity of your app.

Finally, run the Debug Configuration EGGG_Remote. This will connect Eclipse to the jvm running your GAE servlets and allow you to debug them.

Now create a few breakpoints in the client code and server side code and start testing!

Optional: Single Eclipse Launcher

To make things a little bit easier, you can create an Eclipse Launcher that will get all of these going simultaneously so that you don't have to type in each command and such. However, because the gradle commands are continuously running, Eclipse can't wait for completion to execute the next task, so you'll have to be both creative with the timing and patient.

First, make sure you've run each gradle command at least once from within Eclipse. Next, create a new Launcher of Type Launch Group. On the right side, hit the Add button. Gradle Build will appear as one of the types to select from, so go down and select your ProjectName appengineRun command. Set the post launch action to delay, and enter 15 in the seconds field. The reason for this is to give the server a chance to start before we get the remote server debugger EGGG_Remote started. If the server has not started, it will die in exception. Adjust this time field as needed for your environment. Then hit Ok. Repeat this process for the ProjectName gwtSuperDev gradle command. I used 30 seconds for this command since it has to do a GWT compile, but realistically, you could ignore the delay for this field. You'll just have to wait until it's done before you hit the Dev Mode On button in Chrome. Next, hit Add again. At the top, change the launch mode to "debug". Then select the EGGG_SDBG launcher from under Launch Chrome. Hit Done. Then hit add again, make sure launch mode is still in "debug", then under Remote Java Application, select EGGG_Remote. Hit done. Finally, use the Up and Down buttons to order the launchers: appEngineRun, EGGG_Remote, gwtSuperDev, EGGG_SDBG.

Optional: Publishing to Local Respository

See a sample project implementing these details: https://bitbucket.org/blue-esoteric/eggg/. This project also provides a sample script publishing.gradle that gives a quick and easy method to push a jar of the client API to the local maven repository. To utilize this, copy the publishing.gradle file from the sample project into your own:

/*****************GWT API Jar to Local Maven Repo*********/
apply plugin: 'maven-publish' 
task clientApiJar(type: Jar){
	baseName = 'clientApi'
	from sourceSets.main.output
	include '**/client/**'
	include '**/shared/**'
}

task clientApiSourcesJar(type: Jar, dependsOn:classes) {
    from sourceSets.main.output
	include '**/client/**'
	include '**/shared/**'
}

task clientApiJavadoc(type: Javadoc){
	source = sourceSets.main.allJava
	include '**/client/**'
	include '**/shared/**'
	destinationDir new File(rootProject.buildDir,'apiJavaDoc')
	destinationDir.mkdirs()
}

task clientApiJavadocJar(type: Jar, dependsOn:clientApiJavadoc) {
    classifier = 'javadoc'
    from clientApiJavadoc.destinationDir
}

publishing{
	publications{
		egggApi(MavenPublication){
			groupId 'com.blueesoteric.eggg'
			artifactId 'eggg-api'
			version '0.1.1'
			
			artifact clientApiJar
			artifact clientApiSourcesJar{
				classifier "sources"
			}
			artifact clientApiJavadocJar{
				classifier 'javadoc'
			}
		}
	}
}

Note on line 31, the name egggApi. Now add the following to the build.gradle script: apply from: 'publishing.gradle'. Now simple run the gradle task publishEgggApiPublicationToMavenLocal. (2

Sources:
1. http://stackoverflow.com/questions/24941620/how-to-debug-the-appengine-l...
2. https://gradle.org/docs/current/userguide/publishing_maven.html

Updates:
2015-05-07: Altered build.gradle script for better variable defintiions, added Optional section on Publishing, and added links to sample EGGG project on Bitbucket
2015-05-22: Added optional item for single launch command within Eclipse.