/******************************************************************************* * Copyright (c) 2010, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.server.servlets; import java.io.IOException; import java.io.StringWriter; import java.net.URI; import java.util.Collection; import java.util.Collections; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.orion.internal.server.servlets.Activator; import org.eclipse.orion.internal.server.servlets.ServletResourceHandler; import org.eclipse.orion.internal.server.servlets.ServletStatusHandler; import org.eclipse.orion.server.core.IOUtilities; import org.eclipse.orion.server.core.IWebResourceDecorator; import org.eclipse.orion.server.core.ProtocolConstants; import org.eclipse.orion.server.core.ServerStatus; import org.eclipse.orion.server.core.tasks.IURIUnqualificationStrategy; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.slf4j.LoggerFactory; /** * Common base class for servlets that defines convenience API for (de)serialization * of requests and responses. */ public abstract class OrionServlet extends HttpServlet { /** * Global flag for enabling detailed tracing of the protocol. */ protected static final boolean DEBUG_VEBOSE = false; private static final long serialVersionUID = 1L; private static final ServletResourceHandler<IStatus> statusHandler = new ServletStatusHandler(); private static String prettyPrint(Object result) { try { if (result instanceof JSONObject) return ((JSONObject) result).toString(2); } catch (JSONException e) { //fall through below } return result.toString(); } public static void writeJSONResponse(HttpServletRequest req, HttpServletResponse resp, Object result) throws IOException { writeJSONResponse(req, resp, result, JsonURIUnqualificationStrategy.ALL); } public static void writeJSONResponse(HttpServletRequest req, HttpServletResponse resp, Object result, IURIUnqualificationStrategy strategy) throws IOException { Assert.isLegal(result instanceof JSONObject || result instanceof JSONArray); resp.setCharacterEncoding("UTF-8"); //$NON-NLS-1$ resp.setStatus(HttpServletResponse.SC_OK); resp.setHeader("Cache-Control", "no-cache"); //$NON-NLS-1$ //$NON-NLS-2$ resp.setHeader("Cache-Control", "no-store"); //$NON-NLS-1$ //$NON-NLS-2$ if (result instanceof JSONObject) { decorateResponse(req, (JSONObject) result); } if (strategy == null) { strategy = JsonURIUnqualificationStrategy.ALL; } strategy.run(req, result); //TODO look at accept header and chose appropriate response representation resp.setContentType(ProtocolConstants.CONTENT_TYPE_JSON); String response = prettyPrint(result); resp.getWriter().print(response); LoggerFactory.getLogger(OrionServlet.class).debug(response); } /** * Decorates a JSON response and rewrites URLs found in it. * @param req * @param result * @param strategy */ public static void decorateResponse(HttpServletRequest req, JSONObject result, IURIUnqualificationStrategy strategy) { decorateResponse(req, result); strategy.run(req, result); //if ("XMLHttpRequest".equals(req.getHeader("X-Requested-With"))) { //$NON-NLS-1$ //$NON-NLS-2$ // In JSON that is sent to in-Browser clients, remove scheme/userInfo/port information from URLs. //strategy.run(req, result); //} } /** * If there is a search provider for this request resource, then add the search * service location to the result object. */ public static void decorateResponse(HttpServletRequest req, JSONObject result) { decorateResponse(req, result, (IWebResourceDecorator)null); } /** * If there is a search provider for this request resource, then add the search * service location to the result object. */ public static void decorateResponse(HttpServletRequest req, JSONObject result, IWebResourceDecorator skip) { Collection<IWebResourceDecorator> decorators = Activator.getDefault().getWebResourceDecorators(); URI requestURI = ServletResourceHandler.getURI(req); for (IWebResourceDecorator decorator : decorators) { if (decorator == skip) continue; decorator.addAtributesFor(req, requestURI, result); } } /** * Returns the JSON object that is serialized in the request stream. Returns an * empty JSON object if the request body is empty. Never returns null. */ public static JSONObject readJSONRequest(HttpServletRequest request) throws IOException, JSONException { JSONObject jsonRequest = (JSONObject) request.getAttribute("JSONRequest"); if (jsonRequest == null) { StringWriter writer = new StringWriter(); IOUtilities.pipe(request.getReader(), writer, false, false); String resultString = writer.toString(); jsonRequest = (resultString.length() == 0) ? new JSONObject() : new JSONObject(resultString); request.setAttribute("JSONRequest", jsonRequest); } return jsonRequest; } protected ServletResourceHandler<IStatus> getStatusHandler() { return statusHandler; } /** * Generic handler for exceptions. */ protected void handleException(HttpServletResponse response, IStatus status) throws ServletException { statusHandler.handleRequest(null, response, status); } /** * Generic handler for exceptions. */ protected void handleException(HttpServletResponse response, IStatus status, int httpCode) throws ServletException { handleException(response, new ServerStatus(status, httpCode)); } /** * Generic handler for exceptions. */ protected void handleException(HttpServletResponse resp, String msg, Exception e) throws ServletException { handleException(resp, msg, e, HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } /** * Generic handler for exceptions. */ protected void handleException(HttpServletResponse resp, String msg, Exception e, int httpCode) throws ServletException { handleException(resp, new Status(IStatus.ERROR, Activator.PI_SERVER_SERVLETS, msg, e), httpCode); } protected void printHeaders(HttpServletRequest req, StringBuffer out) { for (String header : Collections.<String> list(req.getHeaderNames())) out.append(header + ": " + req.getHeader(header) + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ } /** * Helper method to print the request when debugging. */ protected void traceRequest(HttpServletRequest req) { StringBuffer result = new StringBuffer(req.getMethod()); result.append(' '); result.append(req.getRequestURI()); String query = req.getQueryString(); if (query != null) result.append('?').append(query); if (DEBUG_VEBOSE) printHeaders(req, result); LoggerFactory.getLogger(OrionServlet.class).debug(result.toString()); } }