001 /* 002 * Copyright © 2008, 2009 Pedro Agulló Soliveres. 003 * 004 * This file is part of DirectJNgine. 005 * 006 * DirectJNgine is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License. 009 * 010 * DirectJNgine is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License 016 * along with DirectJNgine. If not, see <http://www.gnu.org/licenses/>. 017 * 018 * This software uses the ExtJs library (http://extjs.com), which is 019 * distributed under the GPL v3 license (see http://extjs.com/license). 020 */ 021 022 package com.softwarementors.extjs.djn.servlet; 023 024 import java.io.IOException; 025 import java.util.ArrayList; 026 import java.util.List; 027 028 import javax.servlet.ServletConfig; 029 import javax.servlet.ServletException; 030 import javax.servlet.http.HttpServlet; 031 import javax.servlet.http.HttpServletRequest; 032 import javax.servlet.http.HttpServletResponse; 033 034 import org.apache.commons.fileupload.FileItem; 035 import org.apache.commons.fileupload.FileUploadException; 036 import org.apache.commons.fileupload.servlet.ServletFileUpload; 037 import org.apache.log4j.Logger; 038 039 import com.softwarementors.extjs.djn.ServletUtils; 040 import com.softwarementors.extjs.djn.StringUtils; 041 import com.softwarementors.extjs.djn.config.ApiConfiguration; 042 import com.softwarementors.extjs.djn.config.GlobalConfiguration; 043 import com.softwarementors.extjs.djn.config.Registry; 044 import com.softwarementors.extjs.djn.router.PollRequestProcessor; 045 import com.softwarementors.extjs.djn.router.RequestException; 046 import com.softwarementors.extjs.djn.router.RequestRouter; 047 import com.softwarementors.extjs.djn.router.RequestType; 048 import com.softwarementors.extjs.djn.router.UploadFormPostRequestProcessor; 049 050 public class DirectJNgineServlet extends HttpServlet { 051 052 private static final Logger logger = Logger.getLogger( DirectJNgineServlet.class); 053 054 /********************************************************* 055 * GlobalParameters and configuration 056 *********************************************************/ 057 private static final String VALUES_SEPARATOR = ","; 058 059 private Registry registry; 060 private List<String> apis; 061 private List<ApiConfiguration> apiConfigurations = new ArrayList<ApiConfiguration>(); 062 private GlobalConfiguration globalConfiguration; 063 private RequestRouter processor; 064 private ServletFileUpload upload; 065 066 067 @Override 068 public void init(ServletConfig configuration) throws ServletException 069 { 070 assert configuration != null; 071 super.init(configuration); 072 073 retrieveGlobalConfiguration(configuration); 074 retrieveApiConfigurations(configuration); 075 076 this.registry = new Registry( this.apiConfigurations); 077 try { 078 this.registry.updateJavascriptApiFiles(this.globalConfiguration); 079 } 080 catch( IOException ex ) { 081 throw new ServletException( "Unable to create DirectJNgine API files", ex ); 082 } 083 084 this.upload = UploadFormPostRequestProcessor.createFileUploader(); 085 this.processor = initializeRequestProcessor(); 086 } 087 088 private RequestRouter initializeRequestProcessor() { 089 return new RequestRouter( this.registry, this.globalConfiguration.getDebug() ); 090 } 091 092 private void retrieveGlobalConfiguration(ServletConfig configuration) { 093 assert configuration != null; 094 095 ServletUtils.checkRequiredParameters(configuration, GlobalConfiguration.Parameters.APIS, GlobalConfiguration.Parameters.PROVIDERS_URL); 096 097 String debug = ServletUtils.getParameter( configuration, GlobalConfiguration.Parameters.DEBUG, "false"); 098 boolean isDebug = debug.equalsIgnoreCase("true") || debug.equals("1"); 099 String apisParameter = ServletUtils.getRequiredParameter(configuration, GlobalConfiguration.Parameters.APIS); 100 this.apis = StringUtils.getNonBlankValues(apisParameter, VALUES_SEPARATOR); 101 String providersUrl = ServletUtils.getRequiredParameter(configuration, GlobalConfiguration.Parameters.PROVIDERS_URL); 102 103 logger.info( "Servlet configuration: apis=" + apisParameter); 104 logger.info( "Servlet configuration: debug=" + isDebug); 105 logger.info( "Servlet configuration: providersUrl=" + providersUrl); 106 107 this.globalConfiguration = new GlobalConfiguration( providersUrl, isDebug); 108 } 109 110 private void retrieveApiConfigurations(ServletConfig configuration) { 111 assert configuration != null; 112 113 for( String api : this.apis) { 114 createApiConfiguration( configuration, api ); 115 } 116 } 117 118 private ApiConfiguration createApiConfiguration(ServletConfig configuration, String api) { 119 assert configuration != null; 120 assert !StringUtils.isEmpty(api); 121 122 ServletUtils.checkRequiredParameters(configuration, 123 api + "." + ApiConfiguration.Parameters.API_FILE, 124 api + "." + ApiConfiguration.Parameters.NAMESPACE, 125 api + "." + ApiConfiguration.Parameters.CLASSES ); 126 127 String apiFile = ServletUtils.getRequiredParameter( configuration, api + "." + ApiConfiguration.Parameters.API_FILE ); 128 String fullGeneratedApiFile = getServletContext().getRealPath(apiFile); 129 String namespace = ServletUtils.getRequiredParameter( configuration, api + "." + ApiConfiguration.Parameters.NAMESPACE ); 130 String classNames = ServletUtils.getRequiredParameter( configuration, api + "." + ApiConfiguration.Parameters.CLASSES ); 131 List<Class<?>> classes = getClasses( classNames ); 132 133 logger.info( "Servlet configuration: " + api + ".apiFile=" + apiFile + " => Api file: " + fullGeneratedApiFile); 134 logger.info( "Servlet configuration: " + api + ".namespace=" + namespace); 135 logger.info( "Servlet configuration: " + api + ".classes=" + classNames); 136 137 ApiConfiguration apiConfiguration = new ApiConfiguration( api, fullGeneratedApiFile, namespace, classes ); 138 this.apiConfigurations.add( apiConfiguration ); 139 140 return apiConfiguration; 141 } 142 143 private static List<Class<?>> getClasses( String classes ) { 144 assert !StringUtils.isEmpty(classes); 145 146 List<String> classNames = StringUtils.getNonBlankValues( classes, VALUES_SEPARATOR ); 147 List<Class<?>> result = new ArrayList<Class<?>>(); 148 149 for( String className : classNames ) { 150 logger.trace( "Looking for class '" + className + "'"); 151 try { 152 Class<?> cls = Class.forName( className ); 153 result.add( cls ); 154 } 155 catch( ClassNotFoundException ex ) { 156 logger.fatal( ex.getMessage(), ex ); 157 ServletConfigurationException e = ServletConfigurationException.forClassNotFound(className, ex ); 158 throw e; 159 } 160 } 161 162 return result; 163 } 164 165 private static RequestType getFromRequestContentType( HttpServletRequest request ) { 166 assert request != null; 167 168 String contentType = request.getContentType(); 169 String contentTypeLowercase = ""; 170 if( contentType != null ) { 171 contentTypeLowercase = contentType.toLowerCase(); 172 } 173 174 String pathInfo = request.getPathInfo(); 175 176 if( !StringUtils.isEmpty(pathInfo) && pathInfo.startsWith( PollRequestProcessor.PATHINFO_POLL_PREFIX)) { 177 return RequestType.POLL; 178 } 179 else if( contentTypeLowercase.startsWith( "application/json")) { 180 return RequestType.JSON; 181 } 182 else if( contentTypeLowercase.startsWith("application/x-www-form-urlencoded") && request.getMethod().toLowerCase().equals("post")) { 183 return RequestType.FORM_SIMPLE_POST; 184 } 185 else if( ServletFileUpload.isMultipartContent(request)) { 186 return RequestType.FORM_UPLOAD_POST; 187 } 188 else { 189 ServletUtils.logTraceDetailedRequestInformation(request); 190 191 RequestException ex = RequestException.forRequestFormatNotRecognized(); 192 logger.error( ex.getMessage(), ex ); 193 throw ex; 194 } 195 } 196 197 @Override 198 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 199 doPost(request, response); 200 } 201 202 @Override 203 public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 204 RequestType type = getFromRequestContentType(request); 205 206 response.setContentType("text/html"); // MUST be "text/html" for uploads to work! 207 208 switch( type ) { 209 case FORM_SIMPLE_POST: 210 this.processor.processSimpleFormPostRequest( request.getReader(), response.getWriter() ); 211 break; 212 case FORM_UPLOAD_POST: 213 processUploadFormPost(request, response); 214 break; 215 case JSON: 216 this.processor.processJsonRequest( request.getReader(), response.getWriter() ); 217 break; 218 case POLL: 219 this.processor.processPollRequest( request.getReader(), response.getWriter(), request.getPathInfo() ); 220 break; 221 } 222 } 223 224 private void processUploadFormPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 225 try { 226 this.processor.processUploadFormPostRequest( getFileItems(request), response.getWriter() ); 227 } 228 catch( FileUploadException e ) { 229 this.processor.handleFileUploadException( e ); 230 } 231 } 232 233 @SuppressWarnings("unchecked") // Unfortunately, parseRequest returns List, not List<FileItem> 234 private List<FileItem> getFileItems(HttpServletRequest request) throws FileUploadException { 235 return this.upload.parseRequest(request); 236 } 237 238 }