/******************************************************************************* * Signavio Core Components * Copyright (C) 2012 Signavio GmbH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package com.signavio.platform.handler; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URLDecoder; import java.util.Collection; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.util.RequestUtil; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import com.signavio.platform.annotations.HandlerConfiguration; import com.signavio.platform.core.Platform; import com.signavio.platform.exceptions.IORequestException; import com.signavio.platform.exceptions.JSONRequestException; import com.signavio.platform.exceptions.RequestException; import com.signavio.platform.security.business.FsAccessToken; import com.signavio.platform.security.business.FsSecureBusinessObject; import com.signavio.platform.servlets.DispatcherServlet; /** * AbstractHandler describes the highlevel class of an Handler. * @author Willi * */ public abstract class AbstractHandler { /** * Define some variables */ private ServletContext servletContext; protected final String SERVER_URL; protected final String PLATFORM_URI; /** * Constructor * @param servletContext */ public AbstractHandler(ServletContext servletContext) { this.servletContext = servletContext; SERVER_URL = Platform.getInstance().getPlatformProperties().getServerName(); PLATFORM_URI = servletContext.getContextPath() + "/p"; } /** * Get the servlet context * @return */ protected ServletContext getServletContext() { return this.servletContext; } /** * Returns the server url * @param req * @return <scheme>://<server name>(:<port>)/ (Port only if it is not the default port) */ protected String getServerURL() { return SERVER_URL; } /** * Returns the absolute path to the root directory of the webapps folder * @return Directory string of the root folder */ protected String getRootDirectory() { return this.getServletContext().getRealPath(""); } /** * Returns the full path to the server's root directory * (for tomcat, it's the webapps folder). * @return The full path to the server's root directory. */ protected String getServerRootPath() { String realPath = this.getServletContext().getRealPath(""); File backendDir = new File(realPath); return backendDir.getParent(); } /** * Get a representation from the resource * @param id * @param params * @return */ public <T extends FsSecureBusinessObject> Object getRepresentation(T sbo, Object params, FsAccessToken token) { return null; } /** * Get the representation from not a particular resource * @param params * @return */ public Object getRepresentation(Object params, FsAccessToken token){ return null; } /** * Creates a new representation and returns the initial resources back * @param params * @return * @throws InvalidIdentifierException */ public Object postRepresentation(Object params, FsAccessToken token) { return null; } /** * Set the representation for a resource * @param id * @param params */ public <T extends FsSecureBusinessObject> Object putRepresentation(T sbo, Object params, FsAccessToken token) { return null; } /** * Set the representation for a resource * @param id * @param params */ public <T extends FsSecureBusinessObject> void deleteRepresentation(T sbo, Object params, FsAccessToken token) { } /** * Implementation of a GET request * @param req * @param res * @param identifier * @throws Exception */ public <T extends FsSecureBusinessObject> void doGet(HttpServletRequest req, HttpServletResponse res, FsAccessToken token, T sbo) { // Check if getRepresentation is activated if( isRepresenationActivated("get", sbo != null) ) { // Set status code and write the representation res.setStatus(200); res.setContentType("application/json"); if( sbo != null ){ try{ writeOutput( this.getRepresentation(sbo, req.getAttribute("params"), token), res); } catch (JSONException e) { throw new JSONRequestException(e); } catch (IOException e) { throw new IORequestException(e); } } else { try { writeOutput( this.getRepresentation(req.getAttribute("params"), token), res ); } catch (JSONException e) { throw new JSONRequestException(e); } catch (IOException e) { throw new IORequestException(e); } } } else { // If it is not activated, 405 - Method Not Allowed res.setStatus(405); } } /** * Implementation of a POST request * @param req * @param res * @param identifier * @throws Exception */ public <T extends FsSecureBusinessObject> void doPost(HttpServletRequest req, HttpServletResponse res, FsAccessToken token, T sbo) { // Get the parameter list JSONObject parameter = (JSONObject)req.getAttribute("params"); // FORM-HTML-PUT-HACK - Check if the parameter method is put try { if( (parameter.has("method") && parameter.getString("method").equals("put")) || (parameter.has("_method") && parameter.getString("_method").equals("put"))){ // If so DO PUT this.doPut(req, res, token, sbo); return; } } catch (JSONException e) { throw new RequestException("platform.jsonexception", e); } // Check if getRepresentation is activated if( sbo == null && isRepresenationActivated("post", false) ) { // Set status code and write the representation res.setStatus(200); try { writeOutput(this.postRepresentation(parameter, token), res); } catch (JSONException e) { throw new JSONRequestException(e); } catch (IOException e) { throw new IORequestException(e); } } else { // Post with an identifier is not implemented throw new RequestException("platform.abstractHanlder.postWithIdNotAllowed"); } } /** * Implementation of a PUT request * @param req * @param res * @param identifier * @throws Exception */ public <T extends FsSecureBusinessObject> void doPut(HttpServletRequest req, HttpServletResponse res, FsAccessToken token, T sbo) { // Check if getRepresentation is activated if( sbo != null && isRepresenationActivated("put", true) ) { // Set status code and write the representation try{ res.setStatus(200); writeOutput(this.putRepresentation(sbo, req.getAttribute("params"), token), res); } catch (JSONException e) { throw new JSONRequestException(e); } catch (IOException e) { throw new IORequestException(e); } } else { // If it is not activated, 405 - Method Not Allowed res.setStatus(405); } } /** * Implementation of a DELETE request * @param req * @param res * @param identifier * @throws Exception */ public <T extends FsSecureBusinessObject> void doDelete(HttpServletRequest req, HttpServletResponse res, FsAccessToken token, T sbo) { // Check if getRepresentation is activated if( sbo != null && isRepresenationActivated("delete", true) ) { // Set status code and write the representation try{ res.setStatus(200); this.deleteRepresentation(sbo, req.getAttribute("params"), token); writeOutput(new JSONObject("{\"success\":true}"), res); } catch (JSONException e) { throw new JSONRequestException(e); } catch (IOException e) { throw new IORequestException(e); } } else { // If it is not activated, 405 - Method Not Allowed res.setStatus(405); } } /** * Check if the given method name is activated with the annotation HandlerMethodActivation * @param method * @param includesIndentifier * @return * @throws SecurityException * @throws NoSuchMethodException */ @SuppressWarnings("unchecked") private Boolean isMethodActivated( String method, Class[] args ) throws SecurityException, NoSuchMethodException{ // return this.getClass().getMethod( method, args).getAnnotation(HandlerMethodActivation.class) != null; return true; } /** * Checks if the representation is activated * @param type Type of the representation function * @param includesIdentifier Specify if the identifier is included in the request * @return True if its activate */ protected Boolean isRepresenationActivated( String type, Boolean includesIdentifier ){ try { return isMethodActivated(type + "Representation", (includesIdentifier ? new Class[]{ FsSecureBusinessObject.class, Object.class, FsAccessToken.class} : new Class[]{ Object.class, FsAccessToken.class})); } catch (Exception e) { return false; } } /** * Writes an object to the writer of the response * @param o * @param res * @throws JSONException * @throws IOException */ private void writeOutput(Object o, HttpServletResponse res) throws JSONException, IOException{ if( o instanceof JSONObject ){ writeOutput((JSONObject)o, res); } else if( o instanceof JSONArray ){ writeOutput((JSONArray)o, res); } } /** * Writes an JSONObject to the writer of the response * @param o * @param res * @throws JSONException * @throws IOException */ private void writeOutput(JSONObject o, HttpServletResponse res) throws JSONException, IOException{ o.write( res.getWriter() ); } /** * Writes an JSONArray to the writer of the response * @param o * @param res * @throws JSONException * @throws IOException */ private void writeOutput(JSONArray o, HttpServletResponse res) throws JSONException, IOException{ o.write( res.getWriter() ); } /** * Parses all the request parameter out and creates a JSONObject. * @param req The HttpRequest * @deprecated implementation moved to parseParameters filter * @return */ @Deprecated @SuppressWarnings({ "unchecked", "deprecation" }) protected Object parseParam(HttpServletRequest req){ Object p = req.getAttribute("signavio_request_params"); if(p != null) { return (JSONObject)p; } JSONObject j = new JSONObject(); try { // Get the preceding string after the specifies URL String[] path = DispatcherServlet.parseURL( req.getRequestURI() ); if( path[3] != null && !path[3].equals("") ){ j.put( "suffix", URLDecoder.decode( path[3], "utf-8")); } // Try to convert the content of the request to a JSONObject String content = this.convertStreamToString(req.getInputStream()); if( content != null && content.length() >= 1 ){ Map<String, String> ps = new HashMap<String, String>(); RequestUtil.parseParameters(ps, content, "UTF-8"); JSONObject jc = new JSONObject(ps); Iterator<String> keys = jc.keys(); while( keys.hasNext() ){ String key = keys.next(); j.put(key, ((String[])jc.get(key)).length == 1 ? ((String[])jc.get(key))[0] : jc.get(key)); } } // Get all parameter and add those to the list Enumeration<String> paramNames = req.getParameterNames(); while( paramNames.hasMoreElements() ){ String key = paramNames.nextElement(); String[] values = req.getParameterValues(key); if( values.length == 1 ){ j.put( key, values[0] ); } else if( values.length > 1 ){ j.put( key, new JSONArray(values) ); } } } catch (Exception e) {} req.setAttribute("signavio_request_params", j); return j; } @Deprecated private String convertStreamToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; try { String separator = ""; while ((line = reader.readLine()) != null) { sb.append(separator + line); separator = "\n"; } is.close(); } catch (IOException e) { try { is.close(); } catch (IOException e2) {} throw new RequestException("platform.stream2StringFailed", e); } return sb.toString(); } /** * Get the HandlerConfiguration Annotation * @return Annotation */ protected HandlerConfiguration getHandlerConfiguration(){ return this.getClass().getAnnotation(HandlerConfiguration.class); } /** * Get the URI out of the Annotation * @return URI from Annotation */ protected String getHandlerURI(){ return getHandlerConfiguration().uri(); } protected JSONObject generateResource( String rel, String url, Object rep){ JSONObject j = new JSONObject(); try { j.put("rel", rel); j.put("href", url); j.put("rep", rep); } catch (JSONException e) {} return j; } protected void writeFileToResponse(File file, HttpServletResponse res) throws IOException { PrintWriter out = null; BufferedReader reader = null; try { out = res.getWriter(); reader = new BufferedReader(new FileReader(file)); String line = null; while (( line = reader.readLine()) != null){ out.append(line); //TODO @jan-felix: wieso nicht \n ? out.append(System.getProperty("line.separator")); } } finally { try { out.close(); reader.close(); } catch(IOException e) { } } } /** * Adds default objects as attributes to the request, that are usually * necessary in our jsp files. * @param req */ protected void addJSPAttributes(HttpServletRequest req) { req.setAttribute("explorer_url", Platform.getInstance().getPlatformProperties().getExplorerUri()); req.setAttribute("editor_url", Platform.getInstance().getPlatformProperties().getEditorUri()); req.setAttribute("libs_url", Platform.getInstance().getPlatformProperties().getLibsUri()); // req.setAttribute("translation", TranslationFactory.getTranslation(req)); } @SuppressWarnings("unchecked") public static String getParameter(HttpServletRequest req, String key) { Map<String, Collection<String>> params = (Map<String, Collection<String>>) req.getAttribute("javaParams"); Collection<String> values = params.get(key); if (values != null) { return values.iterator().next(); } else { return null; } } /** * Returns a Map containing all parameters of the request created by the ParametersFilter * @param req * @return */ @SuppressWarnings("unchecked") protected static Map<String, Collection<String>> getJavaParams(HttpServletRequest req) { Map<String, Collection<String>> params = (Map<String, Collection<String>>) req.getAttribute("javaParams"); return params; } /** * Renders a jsp with the given name from the WEB-INF/jsp directory and sends it to the user. * The JSP is initialized with default attributes like e.g. translation * @param pageName * @param req * @param res */ protected void renderJsp(String pageName, HttpServletRequest req, HttpServletResponse res) { try { addJSPAttributes(req); req.getRequestDispatcher("/WEB-INF/jsp/" + pageName).include(req, res); } catch (ServletException e) { throw new RequestException("servletException", e); } catch (IOException e) { throw new IORequestException(e); } } }