I have a Google Web Toolkit (GWT) project that is to run on Google App Engine (GAE). How do I use Maven to manage dependencies, while still maintaining the functionality that the Google Plugin for Eclipse (GPE) gives me?
Your first question might be "How is this even a problem?". Yeah, well, I thought it would be butt simple when I first looked at it too. If it was that simple, I wouldn't need to write this post. For all I know, there is a much easier way to get everything to play nice, but this is what I got to work.
The main issues I had revolved around launching the application locally. I've done this two ways: through a Maven build (mvn appengine:devserver) and with GPE (Run As - Web Application). Because the app is targeted to GAE and uses GWT, the plugin offers a great deal of convenience. The other issue (which actually triggered this whole exercise) was the build process in Maven. I have this project using a modified version of the process described in my previous post about automatic builds and deployment to GAE with Jenkins. Maven started to choke, and during the troubleshooting process I wiped out my settings, so GPE stopped working right too. My frustration level spiked pretty rapidly. So here I am, telling future me how to set everything up so I don't have to completely relearn it (again).
What starts out broken?
So, as good a place to start as any is what works and what doesn't on a fresh copy of the project. So I cloned the project again into a new folder:
Next, import as a Maven project into Eclipse. For purposes of this example I made a temporary change to the pom (changed the artifactid), otherwise it complained that the project existed already.
The first time I tried to open the project, Eclipse got hung up trying to build the workspace, but after restarting the IDE all seemed well. Run the unit tests and it's a sea of green, all is good and right in the world:
I'd just merged functionality into the "development" branch that connects to a REST service using a service account. The service account uses a credential stored in a p12 file that is included in the folder with the server side classes. Because the ApiService tests and AuthService tests all pass, it's working correctly at this point.
Getting Maven - Project Update to work
Because this is a Maven project, it's possible (required, actually) to sync the pom file with the project configuration. It's a simple enough operation:
But for a long time (even before the major meltdowns I'll describe in a bit that caused me to reevaluate this whole process...) trying to do this would cause Eclipse to complain:
"Where the hell is the error log in Eclipse" I ask myself finally. Google (probably, ultimately, Stack Overflow) gives me the answer: Window -> Show View -> Other -> General -> Error Log. Now I can get some more info on what is breaking:
Well thanks, Captain Obvious, that is super helpful. Again, Google finds the answer on Stack Overflow. Go into the Build Path configuration, and move the Google App Engine library above the Maven Dependencies:
So run Maven -> Update Project again and... still broken. I'm getting a "NullPointerException" from Eclipse, and when I look in the Error Log I see this:
Googled the full exception message and found an answer on the Eclipse bug tracker. Apparently the icon for the "Deployment Descriptor" wasn't found because it was looking for the wrong version. When I check in "Project Facets" in the project properties, the "Dynamic Web Module" is set to version 3.0, but it should be 2.5. Unfortunately, it won't let me change it to 2.5, saying the constraints haven't been met...
I eventually resorted to manually changing the setting in the configuration XML file. KDiff3 has an option to compare directories that came in very handy, as it let me compare the settings in the broken project with the settings from the project I have working. In the .settings folder in the base of the project, there is a file called org.eclipse.wst.common.project.facet.core.xml:
<?xml version="1.0" encoding="UTF-8"?> <faceted-project> <runtime name="Google App Engine 1.9.30"/> <fixed facet="wst.jsdt.web"/> <installed facet="java" version="1.7"/> <installed facet="jpt.jpa" version="2.1"/> <installed facet="wst.jsdt.web" version="1.0"/> <installed facet="jst.web" version="3.0"/> <installed facet="com.google.appengine.facet" version="1"/> </faceted-project>
Change the version on jst.web from "3.0" to "2.5" and suddenly the errors stop. Of course I can't be 100% sure that this is the only thing that fixed it, but it definitely fixed these weird errors when running Maven -> Project Update:
One of the other files in there is com.google.gdt.eclipse.core.prefs which is just a text file with key-value pairs, one of which is lastWarOutDir. By default, this is where the Web Application run config will look for the app. It prompts the first time it's run and it took me forever to figure out where the setting was stored so I could change it later.
Configuring Google Plugin for Eclipse
This particular bit of wonkiness led to a great deal of frustration, and took a ton of trial and error to figure out. In the project's Properties view, there is a menu entry for Google. Now, I was expecting to see entries for "App Engine", "Web Application", and "Web Toolkit", but instead got "Deployment" and "Validation". Searching didn't get me much help, but I eventually stumbled across what was making the change. The Maven Project Update was setting a facet called "Google App Engine (for a single module)", which was changing the menu options. Turning this option off got me the right menus.
Now, this will appear to work just fine when launched as a Web Application, but where it breaks is if you want to deploy from GPE:
So we have to go to the Google settings, and make sure that App Engine is turned on, and that launch and deploy is set to work from where we have the build output going.
Now we should be able to Deploy from GPE:
Jenkins is sad, though
Wrestling with all these configuration issues was really just an exercise in getting me back to zero. For a long time all this stuff just worked... until one day when suddenly everything broke. It started with Jenkins.
First, a bit of background and context. My application talks to a backend service that persists the organization chart entities. The api for this service requires an oauth token for access. I configured the backend to allow a service account tied to my app. The credential for my service account is stored in a p12 file. Because the application lives in App Engine, it has no file system access, so the file lives in the package with the other classes, and is accessed with class.getResourceAsStream("key.p12"). This method worked fine in a previous project and based on the green balls in JUnit, was working just fine here too.
The one thing I hadn't done, though, on this branch was to try and build the project with Maven using this setup. The first time Maven built the project was when I merged the feature branch this code lived on with the development branch. Jenkins detected the change, built the project... and exploded. Digging in the console output gave me this exception:
java.lang.IllegalArgumentException
at com.google.api.client.repackaged.com.google.common.base.Preconditions
.checkArgument(Preconditions.java:111)
at com.google.api.client.util.Preconditions.checkArgument(Preconditions.java:37)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.<init>
(GoogleCredential.java:317)
at com.google.api.client.googleapis.auth.oauth2.GoogleCredential$Builder
.build(GoogleCredential.java:515)
at gov.wyo.ets.orgchart.ui.server.GoogleAuthServiceImpl
.getServiceAccountCredential(GoogleAuthServiceImpl.java:42)
Ooookaaaay... -___- .... What the hell does this mean? The exception told me next to nothing, and the fact that it worked locally just added to the frustration. Well, I thought it was working locally. Tried running Maven locally... same error...
Now, as a bonus, it doesn't work locally either! Try running the unit tests again and they are throwing the exception too! What in the bloody hell!!
This is about the time I start to channel my inner Michael Bolton (Office Space character, not the pop singer). "IllegalArgumentException?? What the fuck does that mean??" Steam coming from my ears, blood shooting out my eyes... rage, boiling over. To make matters worse, this started piling up in the afternoon and I had to go home before I could find a resolution. Leaving it at work was a challenge, but I managed (the beer probably helped haha).
I tried starting from scratch (which killed all my previous configuration tweaks) but that only made matters worse. Also making matters worse was the fact that every once in a while... the tests would mysteriously pass again. What did I change? No idea...
After hours of this, I stumbled upon a reproducible phenomenon. If, in Eclipse, I selected "Project -> clean" and then ran the unit tests, they would pass. If I then ran a mvn build, they would fail again. It was consistent, which made it a valuable clue. I tried deleting the target directory (other things broke). Eventually I thought to "remove" the p12 file (by renaming it with a .BAK extension). Eureka! Where it once worked it was throwing the same IllegalArgumentException. But why was Maven breaking this? The file was in the source folder...
Turns out, Maven wasn't copying it over. I didn't dig in to exactly why not, but I think it is because Maven only copies resource files if they are in the resources folder by default. I found the answer on SO: Maven: how to place resource file together with jar?
So fixing it was just a matter of adding some code to the pom in the <build> section:
<resources> <resource> <directory>src/main/java/gov/wyo/ets/orgchart/ui/server</directory> <includes> <include>service-account-key.p12</include> </includes> <targetPath>gov/wyo/ets/orgchart/ui/server</targetPath> </resource> </resources>
Getting rid of gwt-unitCache
I like to keep generated crap out of my git repo, especially if said crap isn't even necessary for the application to work. I'm sure the unit cache has some legitimate purpose, and may even serve it in some circumstances, but having it on didn't seem to speed up my GWT compiles and it was just more kruft I had to ignore in my repo (and it still showed up in grey in Eclipse). I found the solution in a Google Groups thread: gwt-unitCache.
Simply pass -Dgwt.persistentunitcache=false to the JVM in the run configuration:
No comments:
Post a Comment