Appengine with Google Cloud Endpoints and Guice - google-app-engine

So i want to use Guice in Appengine with Cloud Endpoints to inject my services, or daos - pretty common I guess, but I found no tutorial for this.
Official Guice for Appengine documentation seems to be here: https://github.com/google/guice/wiki/GoogleAppEngine
When configuring Guice you set up the com.google.inject.servlet.GuiceFilter to intercept every request "/*". And at some point you must initialize the modules. Like the documentation says a good place to do that is a ServletContextListener.
One special kind of Module are ServletModules, that map request-Paths to Servlet-Classes, instead of doing this in the web.xml you can now do this programmatically.
Pretty straight forward up until here. But how do I configure Guice to also include the Endpoint-Classes?

Turns out there is a GuiceSystemServiceServletModule that handles exactly this.
public class GuiceSSSModule extends GuiceSystemServiceServletModule {
#Override
protected void configureServlets() {
super.configureServlets();
Set<Class<?>> serviceClasses = new HashSet<Class<?>>();
serviceClasses.add(MyEndpoint.class);
serviceClasses.add(AnotherAndpoint.class);
this.serveGuiceSystemServiceServlet("/_ah/spi/*", serviceClasses);
}
}
Include this module in the Injector construction in your ServletContextListener:
public class MyGSCL extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new GuiceSSSModule(), new BaseModule());
}
}
and use this listener in your web.xml:
<listener>
<listener-class>de.mypkg.MyGSCL</listener-class>
</listener>
Also make sure to include the Guice filter in your web.xml:
<!-- GUICE -->
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Your endpoints will be available under /_ah/api/... again and you can use #Inject in your endpoint classes.

Related

Migrating to Cloud Endpoints 2.0 - Working on localhost but not on App Engine

I'm migration my GAE Java app to Google Cloud Endpoints 2.0. I followed the migration guide (https://cloud.google.com/endpoints/docs/frameworks/legacy/v1/java/migrating) to migrate to Endpoints v2.0.
The endpoints service calls are working on localhost but returning 404 (Not Found) when uploaded to the App Engine. I'm using Guice.
The relevant section from my web.xml looks similar to this:
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>com.xyz.myapp.server.guice.MyAppGuiceServletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>EndpointsServlet</servlet-name>
<servlet-class>com.google.api.server.spi.EndpointsServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>com.xyz.myapp.server.endpoints.MyEP1,com.xyz.myapp.server.endpoints.MyEP2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>EndpointsServlet</servlet-name>
<url-pattern>/_ah/api/*</url-pattern>
</servlet-mapping>
MyAppGuiceServletContextListener.java
public class MyAppGuiceServletContextListener extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new GuiceServletModule(), new EPServletModule());
}
}
EPServletModule.java
public class EPServletModule extends EndpointsModule {
#Override
protected void configureServlets() {
super.configureServlets();
Set<Class<?>> serviceClasses = new HashSet<Class<?>>();
serviceClasses.add(MyEP1.class);
serviceClasses.add(MyEP2.class);
configureEndpoints("/_ah/api/*", serviceClasses);
}
}
GuiceServletModule:
public class GuiceServletModule extends ServletModule {
#Override
protected void configureServlets() {
super.configureServlets();
serve("/myapp/servlet1").with(Servlet1.class);
serve("/myapp/servlet2").with(Servlet2.class);
}
}
I'm able to invoke the regular servlets at the paths:
https://[version]-dot-myapp-id.appspot.com/myapp/servlet1
https://[version]-dot-myapp-id.appspot.com/myapp/servlet2
But I'm not able to access the endpoints. It always returns 404 error code. I tried via my Javascript client and also via the APIs Explorer, and get the same error.
I also checked the logs and strangely the logs show the POST request like this:
"POST /_ah/spi/com.xyz.myapp.server.endpoints.MyEP1.myEPMethod HTTP/1.1" 404
Why does it start with /_ah/spi/ when my client is invoking it via /_ah/api/ ?
NOTE: I'm able to invoke the Endpoints 1.0, which are deployed on a different version of the same app, without any issue. But the Endpoints 2.0 version is not working.
Am I missing something?
I also have a very basic question. My client is Javascript based. Does it really make use of the Discovery Document?
I fixed this by Updating Android Studio to the latest version and updating the SDK.

App Engine: Empty referrer error from cron Job when calling Endpoint with restricted API key

I have an App engine app + Cloud endpoints. I have configured a cron task in the task queue to call one of the endpoints. The cron has an auth-constraint to admin.
All of this is working, however when I restrict the api key to certain domains, I get the following error when the cron is run:
Failed
check_errors {
code: REFERER_BLOCKED
detail: "Requests from referer <empty> are blocked."
}
It doesn't seem like I can add a referee header to the cron.yaml
apparently Google App Engine issues cron requests from the IP address 0.1.0.1.
so I could potentially allow that ip, but I want to restrict api key by domain not i.p. and it doesn't seem like I can do both
Does anyone know a workaround to allow the cron job access to an api key restricted by domain?
I found a work around for this:
Note: I see people referencing this in the docs:
"Calling Google Cloud Endpoints
You cannot call a Google Cloud Endpoint from a cron job. Instead, you should issue a request to a target that is served by a handler that's specified in your app's configuration file or in a dispatch file. That handler then calls the appropriate endpoint class and method."
https://cloud.google.com/appengine/docs/standard/java/config/cron#
Without further explanation or example that I could see.
I am however able to call my endpoints from a cron job, and it was working fine, other than the api key restraint issue.
I read several comments on other posts that mention doing a servlet mapping, but without providing an example, so here is the workaround I found and example code for the servlet mapping.
Java Class
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// call your Endpoint Method here, or whatever you want
resp.setContentType("text/plain");
resp.getWriter().println("Hello, world");
}
}
web.xml
<servlet>
<servlet-name>cronServlet</servlet-name>
<servlet-class>com.example.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>cronServlet</servlet-name>
<url-pattern>/cronServlet</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>cronServletConstraint</web-resource-name>
<url-pattern>/cronServlet</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
cron.yaml
cron:
- description: myCron
url: /cronServlet
schedule: every 12 hours
https://cloud.google.com/appengine/docs/flexible/java/how-requests-are-handled

Servlet-Filter is not honoured for welcome file

I am using a Filter do generate dynamicly content to be visible for webcrawlers (https://developers.google.com/webmasters/ajax-crawling/docs/specification). This filter is working fine if the incoming url contains a path (http://www.unclestock.com/app.jsp#!s=GOOG). If the incoming url contains just my domain (and a fragment), say http://www.unclestock.com#!s=GOOG, the welcome file (app.jsp) is returned, but the filter is not honnoured.
My web.xml contains the following filter map:
<filter-mapping>
<filter-name>crawler</filter-name>
<url-pattern>/app.jsp</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>app.jsp</welcome-file>
</welcome-file-list>
I have tried to use an index.html welcome file instead, which redirects to app.jsp. The filter is then executed. However, this does not solve my problem: A client side redirect is not followed by the crawlers (which is the idea), and with server side redirect, I would loose my url fragment (which I also need).
Do you see any alternative solution?
I'm using Google Appengine.
I solved it by using a welcome servlet which does a RequestDispatcher forward. Note that the dispatcher FORWARD must be added to the filter-mapping in order to have the filter working during the foward.
web.xml:
<filter-mapping>
<filter-name>crawler</filter-name>
<url-pattern>*.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>Welcome</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>welcome</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
Welcome.java:
public class Welcome extends RemoteServiceServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("app.jsp");
rd.forward(req, resp);
}
}

UriMatcher equivalent helper for web apps?

I have coded a lot of Android App and many times, I have used the UriMatcher class to match both conetent uris and http urls.
Now I am working on a little web app using Java and Gae. I have little servlet that need to match calls agains DYNAMIC info. Not static url patterns, but runtime parse data.
In Android, UriMatcher would have been my chioce, but what is available in the Java Se World?
You can simply use Java Servlet API on GAE and register it to receive a wildcard Uris, then resolve and map Uris:
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
String uri = req.getRequestURI();
// do something with the uri here
// generate a response
resp.setContentType("text/plain");
resp.getWriter().println("Hello, world");
}
}
and register it:
<servlet>
<servlet-name>My servlet</servlet-name>
<servlet-class>com.package.MyServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>My servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>

Trouble getting unit testing of RPC on GWT

I am trying to get RPC testing using GWT. I am using the default StockWatcher project that is mentioned here, I download the project, I import it, everything works fine.
I then run junitcreator in the StockWatcher project:
/Users/stephen/Work/gwt/gwt-mac-1.6.4/junitCreator -junit /Users/stephen/Applications/eclipse/plugins/org.junit_3.8.2.v20080602-1318/junit.jar -module stockwatcher -eclipse StockWatcher com.google.gwt.sample.stockwatcher.StockWatcherTest
this creates the StockWatcherTest.java in the appropriate test directory, and gives me some hosted and web mode launch files.
I then also added junit.jar to the classpath for this project.
I then modify StockWatcherTest.java to test whether I am capable of making a asynchronous request to the server. Everything looks fine, but when I try to run StockWatcherTest.java in hosted mode, I get the following error:
Starting HTTP on port 0 HTTP
listening on port 49569
The development shell servlet received a
request for 'greet' in module
'com.google.gwt.sample.stockwatcher.StockWatcher.JUnit.gwt.xml'
[WARN] Resource not found: greet;
(could a file be missing from the
public path or a tag
misconfigured in module
com.google.gwt.sample.stockwatcher.StockWatcher.JUnit.gwt.xml
?)
com.google.gwt.user.client.rpc.StatusCodeException:
Cannot find resource 'greet' in the
public path of module
'com.google.gwt.sample.stockwatcher.StockWatcher.JUnit'
Here is my StockWatcherTest.java class
package com.google.gwt.sample.stockwatcher.client;
import com.google.gwt.core.client.GWT;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* GWT JUnit tests must extend GWTTestCase.
*/
public class StockWatcherTest extends GWTTestCase {
/**
* Must refer to a valid module that sources this class.
*/
public String getModuleName() {
return "com.google.gwt.sample.stockwatcher.StockWatcher";
}
/**
* Add as many tests as you like.
*/
public void testSimple() {
GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
greetingService.greetServer("Bob",
new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
// Show the RPC error message to the user
System.out.println(caught);
fail("big time failure");
finishTest();
}
public void onSuccess(String result) {
System.out.println("success, biatch");
assertTrue(true);
}
});
delayTestFinish(1000);
}
}
Here is com/google/gwt/sample/stockwatcher/StockWatcher.gwt.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.2//EN" "http://google-web-toolkit.googlecode.com/svn/tags/1.6.2/distro-source/core/src/gwt-module.dtd">
<module rename-to='stockwatcher'>
<!-- Inherit the core Web Toolkit stuff. -->
<inherits name='com.google.gwt.user.User'/>
<!-- Inherit the default GWT style sheet. You can change -->
<!-- the theme of your GWT application by uncommenting -->
<!-- any one of the following lines. -->
<inherits name='com.google.gwt.user.theme.standard.Standard'/>
<!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
<!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/> -->
<!-- Other module inherits -->
<!-- Specify the app entry point class. -->
<entry-point class='com.google.gwt.sample.stockwatcher.client.StockWatcher'/>
</module>
and here is web.xml in my generated war
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<!-- Default page to serve -->
<welcome-file-list>
<welcome-file>StockWatcher.html</welcome-file>
</welcome-file-list>
<!-- Servlets -->
<servlet>
<servlet-name>greetServlet</servlet-name>
<servlet-class>com.google.gwt.sample.stockwatcher.server.GreetingServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>greetServlet</servlet-name>
<url-pattern>/stockwatcher/greet</url-pattern>
</servlet-mapping>
</web-app>
So what am I doing wrong? Any help is appreciated. Thank you.
1-you need to add "finishTest();" at the end of the "onSuccess" method.
2-And to resolve the exeption you got : add in your StockWatcher.gwt.xml the path to your servlet greet.
servlet path='/greet' class='com.google.gwt.sample.stockwatcher.server.GreetingServiceImpl'/
Another solution is using GWT SyncProxy (support both sync & async) to test GWT RPC services in JRE
See the post at http://www.gdevelop.com/w/blog/2010/01/10/testing-gwt-rpc-services/ for details
i made some simple tests for the stock watcher. you can see them at: http://tayek.com/StockWatcher.zip

Resources