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 126 String apiFile = ServletUtils.getRequiredParameter( configuration, api + "." + ApiConfiguration.Parameters.API_FILE ); 127 String fullGeneratedApiFile = getServletContext().getRealPath(apiFile); 128 String namespace = ServletUtils.getRequiredParameter( configuration, api + "." + ApiConfiguration.Parameters.NAMESPACE ); 129 String classNames = ServletUtils.getParameter( configuration, api + "." + ApiConfiguration.Parameters.CLASSES, "" ); 130 List<Class<?>> classes = getClasses( classNames ); 131 132 logger.info( "Servlet configuration: " + api + ".apiFile=" + apiFile + " => Api file: " + fullGeneratedApiFile); 133 logger.info( "Servlet configuration: " + api + ".namespace=" + namespace); 134 logger.info( "Servlet configuration: " + api + ".classes=" + classNames); 135 136 ApiConfiguration apiConfiguration = new ApiConfiguration( api, fullGeneratedApiFile, namespace, classes ); 137 this.apiConfigurations.add( apiConfiguration ); 138 139 return apiConfiguration; 140 } 141 142 private static List<Class<?>> getClasses( String classes ) { 143 144 List<Class<?>> result = new ArrayList<Class<?>>(); 145 if( StringUtils.isEmpty(classes) ) { 146 return result; 147 } 148 List<String> classNames = StringUtils.getNonBlankValues( classes, VALUES_SEPARATOR ); 149 150 for( String className : classNames ) { 151 logger.trace( "Looking for class '" + className + "'"); 152 try { 153 Class<?> cls = Class.forName( className ); 154 result.add( cls ); 155 } 156 catch( ClassNotFoundException ex ) { 157 logger.fatal( ex.getMessage(), ex ); 158 ServletConfigurationException e = ServletConfigurationException.forClassNotFound(className, ex ); 159 throw e; 160 } 161 } 162 163 return result; 164 } 165 166 private static RequestType getFromRequestContentType( HttpServletRequest request ) { 167 assert request != null; 168 169 String contentType = request.getContentType(); 170 String contentTypeLowercase = ""; 171 if( contentType != null ) { 172 contentTypeLowercase = contentType.toLowerCase(); 173 } 174 175 String pathInfo = request.getPathInfo(); 176 177 if( !StringUtils.isEmpty(pathInfo) && pathInfo.startsWith( PollRequestProcessor.PATHINFO_POLL_PREFIX)) { 178 return RequestType.POLL; 179 } 180 else if( contentTypeLowercase.startsWith( "application/json")) { 181 return RequestType.JSON; 182 } 183 else if( contentTypeLowercase.startsWith("application/x-www-form-urlencoded") && request.getMethod().toLowerCase().equals("post")) { 184 return RequestType.FORM_SIMPLE_POST; 185 } 186 else if( ServletFileUpload.isMultipartContent(request)) { 187 return RequestType.FORM_UPLOAD_POST; 188 } 189 else { 190 ServletUtils.logTraceDetailedRequestInformation(request); 191 192 RequestException ex = RequestException.forRequestFormatNotRecognized(); 193 logger.error( ex.getMessage(), ex ); 194 throw ex; 195 } 196 } 197 198 @Override 199 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 200 doPost(request, response); 201 } 202 203 @Override 204 public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 205 RequestType type = getFromRequestContentType(request); 206 207 response.setContentType("text/html"); // MUST be "text/html" for uploads to work! 208 209 switch( type ) { 210 case FORM_SIMPLE_POST: 211 this.processor.processSimpleFormPostRequest( request.getReader(), response.getWriter() ); 212 break; 213 case FORM_UPLOAD_POST: 214 processUploadFormPost(request, response); 215 break; 216 case JSON: 217 this.processor.processJsonRequest( request.getReader(), response.getWriter() ); 218 break; 219 case POLL: 220 this.processor.processPollRequest( request.getReader(), response.getWriter(), request.getPathInfo() ); 221 break; 222 } 223 } 224 225 private void processUploadFormPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 226 try { 227 this.processor.processUploadFormPostRequest( getFileItems(request), response.getWriter() ); 228 } 229 catch( FileUploadException e ) { 230 this.processor.handleFileUploadException( e ); 231 } 232 } 233 234 @SuppressWarnings("unchecked") // Unfortunately, parseRequest returns List, not List<FileItem> 235 private List<FileItem> getFileItems(HttpServletRequest request) throws FileUploadException { 236 return this.upload.parseRequest(request); 237 } 238 239 }