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 * Commercial use is permitted to the extent that the code/component(s) 011 * do NOT become part of another Open Source or Commercially developed 012 * licensed development library or toolkit without explicit permission. 013 * 014 * DirectJNgine is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with DirectJNgine. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * This software uses the ExtJs library (http://extjs.com), which is 023 * distributed under the GPL v3 license (see http://extjs.com/license). 024 */ 025 026 package com.softwarementors.extjs.djn.servlet; 027 028 import java.io.IOException; 029 import java.util.ArrayList; 030 import java.util.List; 031 032 import javax.servlet.ServletConfig; 033 import javax.servlet.ServletException; 034 import javax.servlet.http.HttpServlet; 035 import javax.servlet.http.HttpServletRequest; 036 import javax.servlet.http.HttpServletResponse; 037 038 import org.apache.commons.fileupload.FileItem; 039 import org.apache.commons.fileupload.FileUploadException; 040 import org.apache.commons.fileupload.servlet.ServletFileUpload; 041 import org.apache.log4j.Logger; 042 import org.apache.log4j.NDC; 043 044 import com.softwarementors.extjs.djn.EncodingUtils; 045 import com.softwarementors.extjs.djn.ServletUtils; 046 import com.softwarementors.extjs.djn.StringUtils; 047 import com.softwarementors.extjs.djn.Timer; 048 import com.softwarementors.extjs.djn.api.Registry; 049 import com.softwarementors.extjs.djn.config.ApiConfiguration; 050 import com.softwarementors.extjs.djn.config.GlobalConfiguration; 051 import com.softwarementors.extjs.djn.gson.GsonBuilderConfigurator; 052 import com.softwarementors.extjs.djn.jscodegen.CodeFileGenerator; 053 import com.softwarementors.extjs.djn.router.RequestRouter; 054 import com.softwarementors.extjs.djn.router.RequestType; 055 import com.softwarementors.extjs.djn.router.dispatcher.Dispatcher; 056 import com.softwarementors.extjs.djn.router.processor.RequestException; 057 import com.softwarementors.extjs.djn.router.processor.poll.PollRequestProcessor; 058 import com.softwarementors.extjs.djn.router.processor.standard.form.upload.UploadFormPostRequestProcessor; 059 import com.softwarementors.extjs.djn.scanner.Scanner; 060 061 public class DirectJNgineServlet extends HttpServlet { 062 063 private static final Logger logger = Logger.getLogger( DirectJNgineServlet.class); 064 065 /********************************************************* 066 * GlobalParameters and configuration 067 *********************************************************/ 068 private static final String VALUES_SEPARATOR = ","; 069 070 public static class GlobalParameters { 071 public static final String PROVIDERS_URL = "providersUrl"; 072 public static final String DEBUG = "debug"; 073 public static final String GSON_BUILDER_CONFIGURATOR_CLASS = "gsonBuilderConfiguratorClass"; 074 public static final String REGISTRY_CONFIGURATOR_CLASS = "registryConfiguratorClass"; 075 076 private static final String APIS_PARAMETER = "apis"; 077 private static final String MINIFY = "minify"; 078 079 public static final String BATCH_REQUESTS_MULTITHREADING_ENABLED = "batchRequestsMultithreadingEnabled"; 080 public static final String BATCH_REQUESTS_MIN_THREADS_POOOL_SIZE = "batchRequestsMinThreadsPoolSize"; 081 public static final String BATCH_REQUESTS_MAX_THREADS_POOOL_SIZE = "batchRequestsMaxThreadsPoolSize"; 082 public static final String BATCH_REQUESTS_THREAD_KEEP_ALIVE_SECONDS = "batchRequestsMaxThreadKeepAliveSeconds"; 083 public static final String BATCH_REQUESTS_MAX_THREADS_PER_REQUEST = "batchRequestsMaxThreadsPerRequest"; 084 } 085 086 public static class ApiParameters { 087 public static final String API_FILE = "apiFile"; 088 public static final String API_NAMESPACE = "apiNamespace"; 089 public static final String ACTIONS_NAMESPACE = "actionsNamespace"; 090 public static final String CLASSES = "classes"; 091 } 092 093 private RequestRouter processor; 094 private ServletFileUpload uploader; 095 private long id = 1000; // It is good we will get lots of ids with the same number of digits... 096 private synchronized long getUniqueRequestId() { 097 return this.id++; 098 } 099 100 @Override 101 public void init(ServletConfig configuration) throws ServletException 102 { 103 assert configuration != null; 104 super.init(configuration); 105 106 Timer timer = new Timer(); 107 createDirectJNgineRouter(configuration); 108 timer.stop(); 109 timer.logDebugTimeInMilliseconds("Djn initialization: total DirectJNgine initialization time"); 110 } 111 112 protected void createDirectJNgineRouter(ServletConfig configuration) throws ServletException { 113 assert configuration != null; 114 115 116 Timer subtaskTimer = new Timer(); 117 GlobalConfiguration globalConfiguration = createGlobalConfiguration(configuration); 118 List<ApiConfiguration> apiConfigurations = createApiConfigurationsFromServletConfigurationApi(configuration); 119 subtaskTimer.stop(); 120 subtaskTimer.logDebugTimeInMilliseconds("Djn initialization: Servlet Configuration Load time"); 121 122 subtaskTimer.restart(); 123 Registry registry = new Registry( globalConfiguration ); 124 Scanner scanner = new Scanner(registry); 125 scanner.scanAndRegisterApiConfigurations( apiConfigurations ); 126 subtaskTimer.stop(); 127 subtaskTimer.logDebugTimeInMilliseconds("Djn initialization: Standard Api processing time"); 128 129 if( globalConfiguration.hasCustomRegistryConfigurationClass() ) { 130 subtaskTimer.restart(); 131 performCustomRegistryConfiguration( configuration, registry ); 132 subtaskTimer.stop(); 133 subtaskTimer.logDebugTimeInMilliseconds("Djn initialization: Custom Registry processing time"); 134 } 135 136 subtaskTimer.restart(); 137 try { 138 CodeFileGenerator.updateApiFiles(registry); 139 subtaskTimer.stop(); 140 subtaskTimer.logDebugTimeInMilliseconds("Djn initialization: Api Files creation time"); 141 } 142 catch( IOException ex ) { 143 ServletException e = new ServletException( "Unable to create DirectJNgine API files", ex ); 144 logger.fatal( e.getMessage(), e ); 145 throw e; 146 } 147 148 subtaskTimer.restart(); 149 this.uploader = UploadFormPostRequestProcessor.createFileUploader(); 150 this.processor = createRequestRouter(registry, globalConfiguration); 151 subtaskTimer.stop(); 152 subtaskTimer.logDebugTimeInMilliseconds("Djn initialization: Request Processor initialization time"); 153 } 154 155 protected RequestRouter createRequestRouter(Registry registry, GlobalConfiguration globalConfiguration) { 156 assert registry != null; 157 assert globalConfiguration != null; 158 159 return new RequestRouter( registry, globalConfiguration, new Dispatcher() ); 160 } 161 162 protected void performCustomRegistryConfiguration(ServletConfig configuration, Registry registry) { 163 assert configuration != null; 164 assert registry != null; 165 assert registry.getGlobalConfiguration().hasCustomRegistryConfigurationClass(); 166 167 createCustomRegistryConfigurator( registry.getGlobalConfiguration().getRegistryConfiguratorClass() ).configure( registry, configuration); 168 } 169 170 private RegistryConfigurator createCustomRegistryConfigurator( Class<? extends RegistryConfigurator> configuratorClass ) { 171 assert configuratorClass != null; 172 173 try { 174 return configuratorClass.newInstance(); 175 } 176 catch (InstantiationException e) { 177 RegistryConfiguratorException ex = RegistryConfiguratorException.forUnableToInstantiateRegistryConfigurator(configuratorClass, e); 178 logger.fatal( ex.getMessage(), ex); 179 throw ex; 180 } 181 catch (IllegalAccessException e) { 182 RegistryConfiguratorException ex = RegistryConfiguratorException.forUnableToInstantiateRegistryConfigurator(configuratorClass, e); 183 logger.fatal( ex.getMessage(), ex); 184 throw ex; 185 } 186 } 187 188 189 190 protected GlobalConfiguration createGlobalConfiguration(ServletConfig configuration) { 191 assert configuration != null; 192 193 ServletUtils.checkRequiredParameters(configuration, GlobalParameters.PROVIDERS_URL); 194 195 boolean isDebug = ServletUtils.getBooleanParameter( configuration, GlobalParameters.DEBUG, GlobalConfiguration.DEFAULT_DEBUG_VALUE); 196 String providersUrl = ServletUtils.getRequiredParameter(configuration, GlobalParameters.PROVIDERS_URL); 197 String gsonConfiguratorClassName = ServletUtils.getParameter(configuration, GlobalParameters.GSON_BUILDER_CONFIGURATOR_CLASS, GlobalConfiguration.DEFAULT_GSON_BUILDER_CONFIGURATOR_CLASS.getName()); 198 Class<? extends GsonBuilderConfigurator> gsonConfiguratorClass = getGsonBuilderConfiguratorClass(gsonConfiguratorClassName); 199 String registryConfiguratorClassName = ServletUtils.getParameter(configuration, GlobalParameters.REGISTRY_CONFIGURATOR_CLASS, null); 200 Class<? extends RegistryConfigurator> registryConfiguratorClass = getRegistryConfiguratorClass(registryConfiguratorClassName); 201 202 // Global multithreaded-batched requests support parameters 203 boolean isBatchRequestsMultithreadingEnabled = ServletUtils.getBooleanParameter( configuration, GlobalParameters.BATCH_REQUESTS_MULTITHREADING_ENABLED, GlobalConfiguration.DEFAULT_BATCH_REQUESTS_MULTITHREADING_ENABLED_VALUE); 204 boolean minifyEnabled = ServletUtils.getBooleanParameter( configuration, GlobalParameters.MINIFY, GlobalConfiguration.DEFAULT_MINIFY_VALUE); 205 206 int batchRequestsMinThreadsPoolSize = ServletUtils.getIntParameterGreaterOrEqualToValue( 207 configuration, GlobalParameters.BATCH_REQUESTS_MIN_THREADS_POOOL_SIZE, 208 GlobalConfiguration.MIN_BATCH_REQUESTS_MIN_THREAD_POOL_SIZE, GlobalConfiguration.DEFAULT_BATCH_REQUESTS_MIN_THREAD_POOL_SIZE); 209 int batchRequestsMaxThreadsPoolSize = ServletUtils.getIntParameterGreaterOrEqualToValue( 210 configuration, GlobalParameters.BATCH_REQUESTS_MAX_THREADS_POOOL_SIZE, 211 GlobalConfiguration.MIN_BATCH_REQUESTS_MAX_THREAD_POOL_SIZE, GlobalConfiguration.DEFAULT_BATCH_REQUESTS_MAX_THREAD_POOL_SIZE); 212 int batchRequestsThreadKeepAliveSeconds = ServletUtils.getIntParameterGreaterOrEqualToValue( 213 configuration, GlobalParameters.BATCH_REQUESTS_THREAD_KEEP_ALIVE_SECONDS, 214 GlobalConfiguration.MIN_BATCH_REQUESTS_THREAD_KEEP_ALIVE_SECONDS, GlobalConfiguration.DEFAULT_BATCH_REQUESTS_THREAD_KEEP_ALIVE_SECONDS); 215 int batchRequestsMaxThreadsPerRequest = ServletUtils.getIntParameterGreaterOrEqualToValue( 216 configuration, GlobalParameters.BATCH_REQUESTS_MAX_THREADS_PER_REQUEST, 217 GlobalConfiguration.MIN_BATCH_REQUESTS_MAX_THREADS_PER_REQUEST, GlobalConfiguration.DEFAULT_BATCH_REQUESTS_MAX_THREADS_PER_REQUEST); 218 219 if( batchRequestsMinThreadsPoolSize > batchRequestsMaxThreadsPoolSize ) { 220 ServletConfigurationException ex = ServletConfigurationException.forMaxThreadPoolSizeMustBeEqualOrGreaterThanMinThreadPoolSize(batchRequestsMinThreadsPoolSize, batchRequestsMaxThreadsPoolSize); 221 logger.fatal( ex.getMessage(), ex ); 222 throw ex; 223 } 224 225 if( logger.isInfoEnabled() ) { 226 logger.info( "Servlet GLOBAL configuration: " + 227 GlobalParameters.DEBUG + "=" + isDebug + ", " + 228 GlobalParameters.PROVIDERS_URL + "=" + providersUrl + ", " + 229 GlobalParameters.GSON_BUILDER_CONFIGURATOR_CLASS + "=" + gsonConfiguratorClassName + ", " + 230 GlobalParameters.REGISTRY_CONFIGURATOR_CLASS + "=" + registryConfiguratorClassName + ", " + 231 GlobalParameters.MINIFY + "=" + minifyEnabled + ", " + 232 GlobalParameters.BATCH_REQUESTS_MULTITHREADING_ENABLED + "=" + isBatchRequestsMultithreadingEnabled + ", " + 233 GlobalParameters.BATCH_REQUESTS_MIN_THREADS_POOOL_SIZE + "=" + batchRequestsMinThreadsPoolSize + ", " + 234 GlobalParameters.BATCH_REQUESTS_MAX_THREADS_POOOL_SIZE + "=" + batchRequestsMaxThreadsPoolSize + ", " + 235 GlobalParameters.BATCH_REQUESTS_MAX_THREADS_PER_REQUEST + "=" + batchRequestsMaxThreadsPerRequest + ", " + 236 GlobalParameters.BATCH_REQUESTS_THREAD_KEEP_ALIVE_SECONDS + "=" + batchRequestsThreadKeepAliveSeconds 237 ); 238 } 239 240 GlobalConfiguration result = new GlobalConfiguration( providersUrl, isDebug, 241 gsonConfiguratorClass, registryConfiguratorClass, 242 minifyEnabled, 243 isBatchRequestsMultithreadingEnabled, batchRequestsMinThreadsPoolSize, batchRequestsMaxThreadsPoolSize, 244 batchRequestsThreadKeepAliveSeconds, batchRequestsMaxThreadsPerRequest ); 245 return result; 246 } 247 248 private Class<? extends GsonBuilderConfigurator> getGsonBuilderConfiguratorClass(String gsonConfiguratorClassName) { 249 assert !StringUtils.isEmpty(gsonConfiguratorClassName); 250 251 Class<? extends GsonBuilderConfigurator> configuratorClass; 252 try { 253 configuratorClass = loadGsonConfiguratorClass(gsonConfiguratorClassName); 254 if( !GsonBuilderConfigurator.class.isAssignableFrom(configuratorClass)) { 255 ServletConfigurationException ex = ServletConfigurationException.forGsonBuilderConfiguratorMustImplementGsonBuilderConfiguratorInterface(gsonConfiguratorClassName ); 256 logger.fatal( ex.getMessage(), ex ); 257 throw ex; 258 } 259 return configuratorClass; 260 } 261 catch( ClassNotFoundException ex ) { 262 ServletConfigurationException e = ServletConfigurationException.forClassNotFound(gsonConfiguratorClassName, ex ); 263 logger.fatal( e.getMessage(), e ); 264 throw e; 265 } 266 } 267 268 private Class<? extends RegistryConfigurator> getRegistryConfiguratorClass(String registryConfiguratorClassName) { 269 if( StringUtils.isEmpty(registryConfiguratorClassName)) { 270 return null; 271 } 272 273 Class<? extends RegistryConfigurator> configuratorClass; 274 try { 275 configuratorClass = loadRegistryConfiguratorClass(registryConfiguratorClassName); 276 if( !RegistryConfigurator.class.isAssignableFrom(configuratorClass)) { 277 ServletConfigurationException ex = ServletConfigurationException.forRegistryConfiguratorMustImplementGsonBuilderConfiguratorInterface(registryConfiguratorClassName ); 278 logger.fatal( ex.getMessage(), ex ); 279 throw ex; 280 } 281 return configuratorClass; 282 } 283 catch( ClassNotFoundException ex ) { 284 ServletConfigurationException e = ServletConfigurationException.forClassNotFound(registryConfiguratorClassName, ex ); 285 logger.fatal( e.getMessage(), e ); 286 throw e; 287 } 288 } 289 290 @SuppressWarnings("unchecked") // Avoid generics typecast warning 291 private Class<GsonBuilderConfigurator> loadGsonConfiguratorClass(String gsonConfiguratorClassName) 292 throws ClassNotFoundException { 293 assert !StringUtils.isEmpty(gsonConfiguratorClassName); 294 295 return (Class<GsonBuilderConfigurator>)Class.forName(gsonConfiguratorClassName); 296 } 297 298 @SuppressWarnings("unchecked") // Avoid generics typecast warning 299 private Class<RegistryConfigurator> loadRegistryConfiguratorClass(String registryConfiguratorClassName) 300 throws ClassNotFoundException { 301 assert !StringUtils.isEmpty(registryConfiguratorClassName); 302 303 return (Class<RegistryConfigurator>)Class.forName(registryConfiguratorClassName); 304 } 305 306 protected List<ApiConfiguration> createApiConfigurationsFromServletConfigurationApi(ServletConfig configuration) { 307 assert configuration != null; 308 309 List<ApiConfiguration> result = new ArrayList<ApiConfiguration>(); 310 String apisParameter = ServletUtils.getRequiredParameter(configuration, GlobalParameters.APIS_PARAMETER); 311 List<String> apis = StringUtils.getNonBlankValues(apisParameter, VALUES_SEPARATOR); 312 logger.info( "Servlet APIs configuration: " + GlobalParameters.APIS_PARAMETER + "=" + apisParameter ); 313 314 for( String api : apis) { 315 ApiConfiguration apiConfiguration = createApiConfigurationFromServletConfigurationApi( configuration, api ); 316 result.add( apiConfiguration ); 317 } 318 319 if( result.isEmpty() ) { 320 logger.warn( "No apis specified"); 321 } 322 323 return result; 324 } 325 326 private ApiConfiguration createApiConfigurationFromServletConfigurationApi(ServletConfig configuration, String api) { 327 assert configuration != null; 328 assert !StringUtils.isEmpty(api); 329 330 String apiFile = ServletUtils.getParameter( configuration, api + "." + ApiParameters.API_FILE, api + ApiConfiguration.DEFAULT_API_FILE_SUFFIX ); 331 String fullGeneratedApiFile = getServletContext().getRealPath(apiFile); 332 String apiNamespace = ServletUtils.getParameter( configuration, api + "." + ApiParameters.API_NAMESPACE, "" ); 333 String actionsNamespace = ServletUtils.getParameter( configuration, api + "." + ApiParameters.ACTIONS_NAMESPACE, "" ); 334 335 // If apiNamespace is empty, try to use actionsNamespace: if still empty, use the api name itself 336 if( apiNamespace.equals("")) { 337 if( actionsNamespace.equals("")) { 338 apiNamespace = ApiConfiguration.DEFAULT_NAMESPACE_PREFIX + api; 339 if( logger.isDebugEnabled() ) { 340 logger.debug( "Using the api name, prefixed with '" + ApiConfiguration.DEFAULT_NAMESPACE_PREFIX + "' as the value for " + ApiParameters.API_NAMESPACE); 341 } 342 } 343 else { 344 apiNamespace = actionsNamespace; 345 logger.debug( "Using " + ApiParameters.ACTIONS_NAMESPACE + " as the value for " + ApiParameters.API_NAMESPACE); 346 } 347 } 348 349 String classNames = ServletUtils.getParameter( configuration, api + "." + ApiParameters.CLASSES, "" ); 350 List<Class<?>> classes = getClasses( classNames ); 351 352 if( logger.isInfoEnabled() ) { 353 logger.info( "Servlet '" + api + "' Api configuration: " + 354 ApiParameters.API_NAMESPACE + "=" + apiNamespace + ", " + 355 ApiParameters.ACTIONS_NAMESPACE + "=" + actionsNamespace + ", " + 356 ApiParameters.API_FILE + "=" + apiFile + " => Api file: " + fullGeneratedApiFile + ", " + 357 ApiParameters.CLASSES + "=" + classNames); 358 } 359 360 if( classes.isEmpty() ) { 361 logger.warn( "There are no action classes to register for api '" + api + "'"); 362 } 363 ApiConfiguration apiConfiguration = new ApiConfiguration( api, fullGeneratedApiFile, apiNamespace, actionsNamespace, classes ); 364 365 return apiConfiguration; 366 } 367 368 private static List<Class<?>> getClasses( String classes ) { 369 assert classes != null; 370 371 List<Class<?>> result = new ArrayList<Class<?>>(); 372 if( StringUtils.isEmpty(classes) ) { 373 return result; 374 } 375 List<String> classNames = StringUtils.getNonBlankValues( classes, VALUES_SEPARATOR ); 376 377 for( String className : classNames ) { 378 try { 379 Class<?> cls = Class.forName( className ); 380 result.add( cls ); 381 } 382 catch( ClassNotFoundException ex ) { 383 logger.fatal( ex.getMessage(), ex ); 384 ServletConfigurationException e = ServletConfigurationException.forClassNotFound(className, ex ); 385 throw e; 386 } 387 } 388 389 return result; 390 } 391 392 private static RequestType getFromRequestContentType( HttpServletRequest request ) { 393 assert request != null; 394 395 String contentType = request.getContentType(); 396 String contentTypeLowercase = ""; 397 if( contentType != null ) { 398 contentTypeLowercase = contentType.toLowerCase(); 399 } 400 401 String pathInfo = request.getPathInfo(); 402 403 if( !StringUtils.isEmpty(pathInfo) && pathInfo.startsWith( PollRequestProcessor.PATHINFO_POLL_PREFIX)) { 404 return RequestType.POLL; 405 } 406 else if( contentTypeLowercase.startsWith( "application/json")) { 407 return RequestType.JSON; 408 } 409 else if( contentTypeLowercase.startsWith("application/x-www-form-urlencoded") && request.getMethod().toLowerCase().equals("post")) { 410 return RequestType.FORM_SIMPLE_POST; 411 } 412 else if( ServletFileUpload.isMultipartContent(request)) { 413 return RequestType.FORM_UPLOAD_POST; 414 } 415 else { 416 String requestInfo = ServletUtils.getDetailedRequestInformation(request); 417 RequestException ex = RequestException.forRequestFormatNotRecognized(); 418 logger.error( "Error during file uploader: " + ex.getMessage() + "\nAdditional request information: " + requestInfo, ex ); 419 throw ex; 420 } 421 } 422 423 @Override 424 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 425 assert request != null; 426 assert response != null; 427 428 doPost(request, response); 429 } 430 431 @Override 432 public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 433 assert request != null; 434 assert response != null; 435 436 NDC.push( "rid: " + Long.toString(getUniqueRequestId()) ); 437 try { 438 Timer timer = new Timer(); 439 try { 440 if( logger.isTraceEnabled()) { 441 String requestInfo = ServletUtils.getDetailedRequestInformation(request); 442 logger.trace( "Request info: " + requestInfo); 443 } 444 445 response.setContentType("text/html"); // MUST be "text/html" for uploads to work! 446 String requestEncoding = request.getCharacterEncoding(); 447 // If we don't know what the request encoding is, assume it to be UTF-8 448 if( StringUtils.isEmpty(requestEncoding)) { 449 request.setCharacterEncoding(EncodingUtils.UTF8); 450 } 451 response.setCharacterEncoding(EncodingUtils.UTF8); 452 453 RequestType type = getFromRequestContentType(request); 454 switch( type ) { 455 case FORM_SIMPLE_POST: 456 this.processor.processSimpleFormPostRequest( request.getReader(), response.getWriter() ); 457 break; 458 case FORM_UPLOAD_POST: 459 processUploadFormPost(request, response); 460 break; 461 case JSON: 462 this.processor.processJsonRequest( request.getReader(), response.getWriter() ); 463 break; 464 case POLL: 465 this.processor.processPollRequest( request.getReader(), response.getWriter(), request.getPathInfo() ); 466 break; 467 } 468 } 469 finally { 470 timer.stop(); 471 timer.logDebugTimeInMilliseconds("Total servlet processing time"); 472 } 473 } 474 finally { 475 NDC.pop(); 476 } 477 } 478 479 private void processUploadFormPost(HttpServletRequest request, HttpServletResponse response) throws IOException { 480 assert request != null; 481 assert response != null; 482 483 try { 484 this.processor.processUploadFormPostRequest( getFileItems(request), response.getWriter() ); 485 } 486 catch( FileUploadException e ) { 487 this.processor.handleFileUploadException( e ); 488 } 489 } 490 491 @SuppressWarnings("unchecked") // Unfortunately, parseRequest returns List, not List<FileItem>: define this wrapper method to avoid warnings 492 private List<FileItem> getFileItems(HttpServletRequest request) throws FileUploadException { 493 assert request != null; 494 495 return this.uploader.parseRequest(request); 496 } 497 498 }