com.google.web.bindery.requestfactory.server.RequestFactoryServlet Maven / Gradle / Ivy
/*
* Copyright 2010 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.web.bindery.requestfactory.server;
import com.google.gwt.core.server.StackTraceDeobfuscator;
import com.google.gwt.user.server.rpc.RPCServletUtils;
import com.google.web.bindery.requestfactory.shared.RequestFactory;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Handles GWT RequestFactory JSON requests.
*/
@SuppressWarnings("serial")
public class RequestFactoryServlet extends HttpServlet {
private static final boolean DUMP_PAYLOAD = Boolean.getBoolean("gwt.rpc.dumpPayload");
private static final String JSON_CHARSET = "UTF-8";
private static final String JSON_CONTENT_TYPE = "application/json";
private static final Logger log = Logger
.getLogger(RequestFactoryServlet.class.getCanonicalName());
/**
* These ThreadLocals are used to allow service objects to obtain access to
* the HTTP transaction.
*/
private static final ThreadLocal perThreadContext =
new ThreadLocal();
private static final ThreadLocal perThreadRequest =
new ThreadLocal();
private static final ThreadLocal perThreadResponse =
new ThreadLocal();
/**
* Returns the thread-local {@link HttpServletRequest}.
*
* @return an {@link HttpServletRequest} instance
*/
public static HttpServletRequest getThreadLocalRequest() {
return perThreadRequest.get();
}
/**
* Returns the thread-local {@link HttpServletResponse}.
*
* @return an {@link HttpServletResponse} instance
*/
public static HttpServletResponse getThreadLocalResponse() {
return perThreadResponse.get();
}
/**
* Returns the thread-local {@link ServletContext}
*
* @return the {@link ServletContext} associated with this servlet
*/
public static ServletContext getThreadLocalServletContext() {
return perThreadContext.get();
}
private final SimpleRequestProcessor processor;
/**
* Constructs a new {@link RequestFactoryServlet} with a
* {@code DefaultExceptionHandler}.
*/
public RequestFactoryServlet() {
this(new DefaultExceptionHandler());
}
/**
* Use this constructor in subclasses to provide a custom
* {@link ExceptionHandler}.
*
* @param exceptionHandler an {@link ExceptionHandler} instance
* @param serviceDecorators an array of ServiceLayerDecorators that change how
* the RequestFactory request processor interact with the domain
* objects
*/
public RequestFactoryServlet(ExceptionHandler exceptionHandler,
ServiceLayerDecorator... serviceDecorators) {
processor = new SimpleRequestProcessor(ServiceLayer.create(serviceDecorators));
processor.setExceptionHandler(exceptionHandler);
}
/**
* Processes a POST to the server.
*
* @param request an {@link HttpServletRequest} instance
* @param response an {@link HttpServletResponse} instance
* @throws IOException if an internal I/O error occurs
* @throws ServletException if an error occurs in the servlet
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
perThreadContext.set(getServletContext());
perThreadRequest.set(request);
perThreadResponse.set(response);
// No new code should be placed outside of this try block.
try {
ensureConfig();
String jsonRequestString =
RPCServletUtils.readContent(request, JSON_CONTENT_TYPE, JSON_CHARSET);
if (DUMP_PAYLOAD) {
System.out.println(">>> " + jsonRequestString);
}
try {
String payload = processor.process(jsonRequestString);
if (DUMP_PAYLOAD) {
System.out.println("<<< " + payload);
}
response.setStatus(HttpServletResponse.SC_OK);
response.setContentType(RequestFactory.JSON_CONTENT_TYPE_UTF8);
// The Writer must be obtained after setting the content type
PrintWriter writer = response.getWriter();
writer.print(payload);
writer.flush();
} catch (RuntimeException e) {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
log.log(Level.SEVERE, "Unexpected error", e);
}
} finally {
perThreadContext.set(null);
perThreadRequest.set(null);
perThreadResponse.set(null);
}
}
private void ensureConfig() {
String symbolMapsDirectory = getServletConfig().getInitParameter("symbolMapsDirectory");
String symbolMapsResourcePath = getServletConfig().getInitParameter("symbolMapsResourcePath");
if (symbolMapsResourcePath != null) {
try {
Logging.setStackTraceDeobfuscator(
StackTraceDeobfuscator.fromUrl(getServletContext().getResource(symbolMapsResourcePath)));
} catch (MalformedURLException e) {
log.log(Level.WARNING, "Failed to get the symbolMaps resource", e);
}
} else if (symbolMapsDirectory != null) {
Logging.setSymbolMapsDirectory(symbolMapsDirectory);
}
}
}