I\'m having this problem with GWT when it\'s behind a reverse proxy. The backend app is deployed within a context - let\'s call it /context.
The GWT app works fine
I'm fairly sure the correct answer here is to patch the source and submit a bug report. Another option would be to run the GWT app at /
on your backend.
I'd prefer the former, but the latter should work too. If you really needed things separated out into multiple contexts, use a different port number?
My goal was to avoid additional header(s) which would make deployment and configuration harder. I solved this problem by overriding RemoteServiceServlet.doGetSerializationPolicy()
:
@Override protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName) { String localServerAddress = "http://127.0.0.1:" + getThreadLocalRequest().getLocalPort(); String localContextPath = getServletConfig().getServletContext().getContextPath(); String moduleName = extractGwtModuleName(moduleBaseURL); String localModuleBaseURL = joinPaths(localServerAddress, localContextPath, moduleName, "/"); return super.doGetSerializationPolicy(request, localModuleBaseURL, strongName); }
In above code:
extractGwtModuleName()
extracts last string prefixed and/or followed by slash
joinPaths()
joins multiple url parts, removes unnecessary slashes
KC's answer is good. For those that do not want to muck around with apache configs, or need a quick and dirty way to test, here is a code only solution.
protected SerializationPolicy doGetSerializationPolicy(final HttpServletRequest request, String moduleBaseURL, final String strongName) {
final String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if (moduleBaseURLHdr != null) {
moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar");
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
The application is on http://server/bar
, the proxy is serving it at http://proxy/foo/bar
Hence moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar"); makes GWT happy.
Likewise if the application is at http://server/bar
and the proxy is serving at http://proxy/
, you need to add bar to the moduleBaseURL (right before the package name).
This can be generalized through the use of getServletContext().getContextPath() etc...
Use restful JSON for your RPC calls instead of GWT-RPC. This solves the reverse-proxy problem since no serialization files are required.
I have the same problem, and I opened a bug report:
http://code.google.com/p/google-web-toolkit/issues/detail?id=4817
The problem is that it was marked "As Design", so I don't think it will be fixed.
I found this solution for me. I extended the class RemoteServiceServlet and I forced GWT to load serialization policy file starting from ContextName instead of URL. Then I extended my service my class instead of RemoteServiceServlet class. In this way the application will be unlinked from the url from where it will be called.
Here there is my custom class:
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
public class MyRemoteServiceServlet extends RemoteServiceServlet
{
@Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName)
{
return MyRemoteServiceServlet.loadSerializationPolicy(this, request, moduleBaseURL, strongName);
}
/**
* Used by HybridServiceServlet.
*/
static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
HttpServletRequest request, String moduleBaseURL, String strongName) {
// The serialization policy path depends only by contraxt path
String contextPath = request.getContextPath();
SerializationPolicy serializationPolicy = null;
String contextRelativePath = contextPath + "/";
String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(contextRelativePath
+ strongName);
// Open the RPC resource file and read its contents.
InputStream is = servlet.getServletContext().getResourceAsStream(
serializationPolicyFilePath);
try {
if (is != null) {
try {
serializationPolicy = SerializationPolicyLoader.loadFromStream(is,
null);
} catch (ParseException e) {
servlet.log("ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
servlet.log("ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else {
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
servlet.log(message);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore this error
}
}
}
return serializationPolicy;
}
}
Michele,
Thank you for the example servlet to handle this problem. However when I tried to use your approach it worked in the reverse proxy environment but not in my dev mode eclipse environment.
I took an approach that would let me seamlessly move between my dev and prod environments.
As you did I overwrote RemoteServiceServlet but I only replaced following...
@Override
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
//get the base url from the header instead of the body this way
//apache reverse proxy with rewrite on the header can work
String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if(moduleBaseURLHdr != null){
moduleBaseURL = moduleBaseURLHdr;
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
In my apache config I added...
ProxyPass /app/ ajp://localhost:8009/App-0.0.1-SNAPSHOT/
RequestHeader edit X-GWT-Module-Base ^(.*)/app/(.*)$ $1/App-0.0.1-SNAPSHOT/$2
This approach works in all scenarios and delegates the url "mucking" to apache's proxy settings which is the approach I've always taken.
Comments on this approach are appreciated