3
votes

I am attempting to migrate a Java AppEngine from Cloud Endpoints to Cloud Endpoints Framework. Ie v1 to v2.

Migration goes fine, the endpoints are all accessible on the new instance. And discovery shows all the methods https://myapp.appspot.com/_ah/api/discovery/v1/apis

But when I use https://apis-explorer.appspot.com/apis-explorer for my appengine domain, while I can still see the API methods, when I drill down on them I can no longer provide params and execute them.

Looking at the AppEngine log I am seeing the following ClassCastException

Uncaught exception from servlet
javax.servlet.UnavailableException: java.lang.ClassCastException: com.google.api.server.spi.config.AnnotationBoolean cannot be cast to com.google.api.server.spi.config.AnnotationBoolean
    at org.mortbay.jetty.servlet.ServletHolder.makeUnavailable(ServletHolder.java:415)
    at org.mortbay.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:458)
    at org.mortbay.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:339)
    at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166)
    at com.googlecode.objectify.cache.AsyncCacheFilter.doFilter(AsyncCacheFilter.java:58)
    at com.googlecode.objectify.ObjectifyFilter.doFilter(ObjectifyFilter.java:48)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.ParseBlobUploadFilter.doFilter(ParseBlobUploadFilter.java:125)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.runtime.jetty.SaveSessionFilter.doFilter(SaveSessionFilter.java:37)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.JdbcMySqlConnectionCleanupFilter.doFilter(JdbcMySqlConnectionCleanupFilter.java:60)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:48)
    at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
    at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
    at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
    at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
    at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
    at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
    at com.google.apphosting.runtime.jetty.AppVersionHandlerMap.handle(AppVersionHandlerMap.java:257)
    at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
    at org.mortbay.jetty.Server.handle(Server.java:326)
    at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)
    at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923)
    at com.google.apphosting.runtime.jetty.RpcRequestParser.parseAvailable(RpcRequestParser.java:76)
    at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
    at com.google.apphosting.runtime.jetty.JettyServletEngineAdapter.serviceRequest(JettyServletEngineAdapter.java:145)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchServletRequest(JavaRuntime.java:559)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.dispatchRequest(JavaRuntime.java:519)
    at com.google.apphosting.runtime.JavaRuntime$RequestRunnable.run(JavaRuntime.java:489)
    at com.google.tracing.TraceContext$TraceContextRunnable.runInContext(TraceContext.java:453)
    at com.google.tracing.TraceContext$TraceContextRunnable$1.run(TraceContext.java:460)
    at com.google.tracing.CurrentContext.runInContext(CurrentContext.java:293)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContextNoUnref(TraceContext.java:319)
    at com.google.tracing.TraceContext$AbstractTraceContextCallback.runInInheritedContext(TraceContext.java:311)
    at com.google.tracing.TraceContext$TraceContextRunnable.run(TraceContext.java:457)
    at com.google.apphosting.runtime.ThreadGroupPool$PoolEntry.run(ThreadGroupPool.java:238)
    at java.lang.Thread.run(Thread.java:745)

Which seems to match up to the attempts to drill down on the methods in API Explorer. These errors don't occur with v1 of Cloud Endpoints.

The ClassCast implies that the AnnotationBoolen has been loaded into 2 different classloaders. I'm not playing with classloaders myself, so I presume this is something new for Endpoints v2. What do I change to avoid the ClassCast?

Does API Explorer not fully support Cloud Endpoints Framework?

Is there some extra step I need to take to enable method execution via API Explorer for Cloud Endpoints Framework?

2
This error looks unrelated to API Explorer. Are you using the quickstart or your own app?saiyr
My own app. I don't really need to migrate an old quick-start. I suspect that the migration instructions are not correct, that it is not an API Explorer issueWilliam
Is there an inner exception?saiyr
That's all that is available in the AppEngine logs (and from api explorer). It doesn't pass through any of my code, so I can't see how to inject any exception handling to dig deeper.William
Willian, I am running in the same issue, did you figure it out?Julien

2 Answers

3
votes

According to the Cloud EndPoints Frameworks migration guide these maven goals are no longer supported.

                      <goal>endpoints_get_discovery_doc</goal>
                      <goal>endpoints_get_client_lib</goal>

See https://cloud.google.com/appengine/docs/standard/java/endpoints/migrating

It also looks like you need to

  1. Add Cloud Endpoints API management to your app. See https://cloud.google.com/endpoints/docs/frameworks/java/adding-api-management
  2. Generate an API configuration file and deploy it PRIOR to deploying the app. See https://cloud.google.com/endpoints/docs/frameworks/java/test-deploy

  3. Generate client libs using ...

See https://cloud.google.com/endpoints/docs/frameworks/java/quickstart-frameworks-java

Step 1 means adding something like the following to your web.xml:

<!--
    EndPoints management API. See https://cloud.google.com/endpoints/docs/frameworks/java/adding-api-management
-->
<filter>
    <filter-name>endpoints-api-configuration</filter-name>
    <filter-class>com.google.api.control.ServiceManagementConfigFilter</filter-class>
</filter>

<!--
    EndPoints management API. See https://cloud.google.com/endpoints/docs/frameworks/java/adding-api-management
-->
<filter>
    <filter-name>endpoints-api-controller</filter-name>
    <filter-class>com.google.api.control.extensions.appengine.GoogleAppEngineControlFilter</filter-class>
    <init-param>
        <param-name>endpoints.projectId</param-name>
        <param-value>your-appengine-projectid</param-value>
    </init-param>
    <init-param>
        <param-name>endpoints.serviceName</param-name>
        <param-value>your-appengine-project-url.appspot.com</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>endpoints-api-configuration</filter-name>
    <servlet-name>EndpointsServlet</servlet-name>
</filter-mapping>
<filter-mapping>
    <filter-name>endpoints-api-controller</filter-name>
    <servlet-name>EndpointsServlet</servlet-name>
</filter-mapping>
0
votes

The ClassCastException that you experienced is due to loading the old SystemServiceServlet instead of the new EndpointsServlet. Configure your web.xml to replace

<servlet>
    <servlet-name>SystemServiceServlet</servlet-name>
    <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
    ...
</servlet>
<servlet-mapping>
    <servlet-name>SystemServiceServlet</servlet-name>
    <url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>

with

<servlet>
    <servlet-name>EndpointsServlet</servlet-name>
    <servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
    ...
</servlet>
<servlet-mapping>
    <servlet-name>EndpointsServlet</servlet-name>
    <url-pattern>/_ah/api/*</url-pattern>
</servlet-mapping>