Thursday, October 29, 2009

Developing Java applications with GAE SDK 1.2.6 and javaagent turned off

Since version 1.2.6 Google AppEngine SDK in development mode requires to run JVM with javaagent.

If you don't do this you'll get the following exception:



java.lang.RuntimeException: Unable to locate the App Engine agent.
Please use dev_appserver, KickStart, or set the jvm flag:
"-javaagent:<sdk_root>/lib/agent/appengine-agent.jar"
at com.google.appengine.tools.development.DevAppServerFactory.testAgentIsInstalled(102)
at com.google.appengine.tools.development.DevAppServerFactory.createDevAppServer(77)
at com.google.appengine.tools.development.DevAppServerFactory.createDevAppServer(38)
at com.google.appengine.tools.development.DevAppServerMain$StartAction.apply(153)
at com.google.appengine.tools.util.Parser$ParseResult.applyArgs(Parser.java:48)
at com.google.appengine.tools.development.DevAppServerMain.(DevAppServerMain.java:113)
at com.google.appengine.tools.development.DevAppServerMain.main(89)
Caused by: java.lang.NoClassDefFoundError: com/google/appengine/tools/development/agent/AppEngineDevAgent
at com.google.appengine.tools.development.DevAppServerFactory.testAgentIsInstalled(98)
... 6 more



But if you do you may get very strange behaviour of your app. Here's a few of mines I get developing with Tapestry 5.2.0.0-SNAPSHOT:


java.lang.ClassFormatError: Invalid length 65050 in LocalVariableTable
in class file org/apache/tapestry5/corelib/components/Form
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.lang.ClassLoader.defineClass(Unknown Source)
at javassist.Loader.findClass(Loader.java:379)
at org.apache.tapestry5.internal.services.ComponentInstantiatorSourceImpl$PackageAwareLoader.findClass(94)
...



I've found a discussion where similiar problem was caused by multiple versions of javassist on CLASSPATH. I tried to find them, but I had only one of it.

The other exception is due to previous.



org.apache.tapestry5.ioc.internal.util.TapestryException:
Failure creating embedded component 'sendInvite' of dmitrygusev.ping.pages.Index:
java.lang.ClassNotFoundException: caught an exception while obtaining a class
file for org.apache.tapestry5.corelib.components.Form"
[at context:Index.tml, line 23]
at org.apache.tapestry5.internal.pageload.ComponentAssemblerImpl.createEmbeddedAssembler(316)
at org.apache.tapestry5.internal.pageload.PageLoaderImpl.startComponent(740)

...

Caused by: java.lang.RuntimeException: Class org.apache.tapestry5.corelib.components.Form contains field(s)
(_$bindingSource, _$environment, _$onActionInfo, _$resources, _$type, _$type_0, _$type_1)
that are not private. You should change these fields to private, and add accessor methods if needed.
at org.apache.tapestry5.internal.services.InternalClassTransformationImpl.verifyFields(293)
at org.apache.tapestry5.internal.services.InternalClassTransformationImpl.preloadMemberNames(255)
at org.apache.tapestry5.internal.services.InternalClassTransformationImpl.(151)
at org.apache.tapestry5.internal.services.ComponentClassTransformerImpl.transformComponentClass(163)
at $ComponentClassTransformer_1249f7f3976.transformComponentClass(...)
at org.apache.tapestry5.internal.services.ComponentInstantiatorSourceImpl.onLoad(205)
at javassist.Loader.findClass(Loader.java:340)
... 95 more



This all very strange, because when deployed to Google AppEgine there is no such errors, and this is very similiar to problem with a Security Manager I wrote about in a previous post.

To fix it I had to not specify javaagent on JVM start, but to avoid that "Unable to locate the App Engine agent" exception, I created the following class:


package com.google.appengine.tools.development.agent;

public class AppEngineDevAgent {

public static Object getAgent()
{
return null;
}

}


and putted it to my project:



Now I can run my project without javaagent. Please note, that you may still need to apply patch to Security Manager (its compatible with 1.2.2-6 GAE SDKs) to avoid other potential issues developing with GAE SDK.

Its also okay to deploy your app with this class to GAE, since it won't conflict with GAE environment.

6 comments:

  1. Dmitriy, thanks for solution, but how to make javaagent off?
    I'm using Intellij IDEA with GAE plugin, but your solution is not working in it...

    ReplyDelete
  2. TopaZ,

    Javaagent should be off by default unless you add jvm flag I noted above.

    Not sure if IDEA plugin added the option for you. Check your project's launch configuration and remove the option if it's in there.

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. Dmitry, thanks, I've fixed my issue with javaagent.
    I use GAE SDK 1.3.1 and my code looks so:
    package com.google.appengine.tools.development.agent;

    public class AppEngineDevAgent {

    public AppEngineDevAgent() { }

    public static void premain(java.lang.String agentArgs, java.lang.instrument.Instrumentation inst) { }

    public static java.lang.Object getAgent() { return null; }

    private static java.net.URL findAgentImplLib() { return null; }

    static interface AgentImplStruct {

    void run(java.lang.instrument.Instrumentation instrumentation);

    java.lang.Object getInstance();
    }

    }

    Unfortunately, IDEA GAE Plugin uses -javaagent option by default, and there is no ability to disable this option. I can just add another JVM options. So my solution is to build appengine-agent.jar from code listed above and to replace original .jar file with new created. One more issue is that this jar file should contain manifest file with cpecified string:
    Premain-Class: com.google.appengine.tools.development.agent.AppEngineDevAgent
    Maven havn't add it ot manifest on build, so I made this manually.
    (Maybe there is some ability to add this string from pom - I do not know Maven very well :-) )
    After all actions everything works fine.
    Thank you for idea about how to fix.

    ReplyDelete
  5. Everything works now by adding the AppEngineDevAgent to my project. But now I get an error in Eclipse that says:

    java.lang.instrument.Instrumentation is not supported by Google App Engine's Java runtime environment

    Is there a way to skip or ignore this error?





    package com.google.appengine.tools.development.agent;

    import java.lang.instrument.Instrumentation;

    public class AppEngineDevAgent {

    public static void premain(String s, Instrumentation instrumentation) {

    }

    public static Object getAgent()
    {
    return null;
    }

    }

    ReplyDelete
  6. THANK YOU SO MUCH FOR POSTING THIS! You are my hero, I was tearing my hair out for an hour, but my limited knowledge of the JAVA GAE SDK had killed me. :D

    ReplyDelete