1 16 17 package org.springframework.web.servlet.mvc.multiaction; 18 19 import java.lang.reflect.InvocationTargetException ; 20 import java.lang.reflect.Method ; 21 import java.util.ArrayList ; 22 import java.util.HashMap ; 23 import java.util.List ; 24 import java.util.Map ; 25 26 import javax.servlet.ServletRequest ; 27 import javax.servlet.http.HttpServletRequest ; 28 import javax.servlet.http.HttpServletResponse ; 29 import javax.servlet.http.HttpSession ; 30 31 import org.apache.commons.logging.Log; 32 import org.apache.commons.logging.LogFactory; 33 34 import org.springframework.beans.BeanUtils; 35 import org.springframework.util.Assert; 36 import org.springframework.validation.ValidationUtils; 37 import org.springframework.validation.Validator; 38 import org.springframework.web.HttpSessionRequiredException; 39 import org.springframework.web.bind.ServletRequestDataBinder; 40 import org.springframework.web.servlet.ModelAndView; 41 import org.springframework.web.servlet.mvc.AbstractController; 42 import org.springframework.web.servlet.mvc.LastModified; 43 import org.springframework.web.util.NestedServletException; 44 45 119 public class MultiActionController extends AbstractController implements LastModified { 120 121 122 public static final String LAST_MODIFIED_METHOD_SUFFIX = "LastModified"; 123 124 125 public static final String DEFAULT_COMMAND_NAME = "command"; 126 127 128 public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound"; 129 130 131 protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY); 132 133 134 138 private MethodNameResolver methodNameResolver = new InternalPathMethodNameResolver(); 139 140 141 private Validator[] validators; 142 143 144 private Object delegate; 145 146 147 private Map handlerMethodMap = new HashMap (); 148 149 150 private Map lastModifiedMethodMap = new HashMap (); 151 152 153 private Map exceptionHandlerMap = new HashMap (); 154 155 156 161 public MultiActionController() { 162 this.delegate = this; 163 registerHandlerMethods(this.delegate); 164 } 166 167 173 public MultiActionController(Object delegate) { 174 setDelegate(delegate); 175 } 176 177 178 182 public final void setMethodNameResolver(MethodNameResolver methodNameResolver) { 183 this.methodNameResolver = methodNameResolver; 184 } 185 186 189 public final MethodNameResolver getMethodNameResolver() { 190 return this.methodNameResolver; 191 } 192 193 197 public final void setValidators(Validator[] validators) { 198 this.validators = validators; 199 } 200 201 204 public final Validator[] getValidators() { 205 return validators; 206 } 207 208 214 public final void setDelegate(Object delegate) { 215 Assert.notNull(delegate, "Delegate must not be null"); 216 this.delegate = delegate; 217 registerHandlerMethods(this.delegate); 218 if (this.handlerMethodMap.isEmpty()) { 220 throw new IllegalStateException ("No handler methods in class [" + this.delegate.getClass() + "]"); 221 } 222 } 223 224 225 228 private void registerHandlerMethods(Object delegate) { 229 this.handlerMethodMap.clear(); 230 this.lastModifiedMethodMap.clear(); 231 this.exceptionHandlerMap.clear(); 232 233 Method [] methods = delegate.getClass().getMethods(); 236 for (int i = 0; i < methods.length; i++) { 237 Method method = methods[i]; 239 if (isHandlerMethod(method)) { 240 registerHandlerMethod(method); 241 registerLastModifiedMethodIfExists(delegate, method); 242 } 243 } 244 245 for (int i = 0; i < methods.length; i++) { 247 Method method = methods[i]; 248 if (isExceptionHandlerMethod(method)) { 249 registerExceptionHandlerMethod(method); 251 } 252 } 253 } 254 255 260 private boolean isHandlerMethod(Method method) { 261 Class returnType = method.getReturnType(); 262 if (ModelAndView.class.equals(returnType) || Map .class.equals(returnType) || void.class.equals(returnType)) { 263 Class [] parameterTypes = method.getParameterTypes(); 264 return (parameterTypes.length >= 2 && 265 HttpServletRequest .class.equals(parameterTypes[0]) && 266 HttpServletResponse .class.equals(parameterTypes[1]) && 267 !("handleRequest".equals(method.getName()) && parameterTypes.length == 2)); 268 } 269 return false; 270 } 271 272 275 private boolean isExceptionHandlerMethod(Method method) { 276 return (isHandlerMethod(method) && 277 method.getParameterTypes().length == 3 && 278 Throwable .class.isAssignableFrom(method.getParameterTypes()[2])); 279 } 280 281 284 private void registerHandlerMethod(Method method) { 285 if (logger.isDebugEnabled()) { 286 logger.debug("Found action method [" + method + "]"); 287 } 288 this.handlerMethodMap.put(method.getName(), method); 289 } 290 291 295 private void registerLastModifiedMethodIfExists(Object delegate, Method method) { 296 try { 298 Method lastModifiedMethod = delegate.getClass().getMethod( 299 method.getName() + LAST_MODIFIED_METHOD_SUFFIX, 300 new Class [] {HttpServletRequest .class}); 301 this.lastModifiedMethodMap.put(method.getName(), lastModifiedMethod); 303 if (logger.isDebugEnabled()) { 304 logger.debug("Found last modified method for action method [" + method + "]"); 305 } 306 } 307 catch (NoSuchMethodException ex) { 308 } 310 } 311 312 315 private void registerExceptionHandlerMethod(Method method) { 316 this.exceptionHandlerMap.put(method.getParameterTypes()[2], method); 317 if (logger.isDebugEnabled()) { 318 logger.debug("Found exception handler method [" + method + "]"); 319 } 320 } 321 322 323 327 332 public long getLastModified(HttpServletRequest request) { 333 try { 334 String handlerMethodName = this.methodNameResolver.getHandlerMethodName(request); 335 Method lastModifiedMethod = (Method ) this.lastModifiedMethodMap.get(handlerMethodName); 336 if (lastModifiedMethod != null) { 337 try { 338 Long wrappedLong = (Long ) lastModifiedMethod.invoke(this.delegate, new Object [] { request }); 340 return wrappedLong.longValue(); 341 } 342 catch (Exception ex) { 343 logger.error("Failed to invoke last-modified method", ex); 346 } 347 } } 349 catch (NoSuchRequestHandlingMethodException ex) { 350 } 354 return -1L; 355 } 356 357 358 362 368 protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) 369 throws Exception { 370 try { 371 String methodName = this.methodNameResolver.getHandlerMethodName(request); 372 return invokeNamedMethod(methodName, request, response); 373 } 374 catch (NoSuchRequestHandlingMethodException ex) { 375 return handleNoSuchRequestHandlingMethod(ex, request, response); 376 } 377 } 378 379 390 protected ModelAndView handleNoSuchRequestHandlingMethod( 391 NoSuchRequestHandlingMethodException ex, HttpServletRequest request, HttpServletResponse response) 392 throws Exception { 393 394 pageNotFoundLogger.warn(ex.getMessage()); 395 response.sendError(HttpServletResponse.SC_NOT_FOUND); 396 return null; 397 } 398 399 404 protected final ModelAndView invokeNamedMethod( 405 String methodName, HttpServletRequest request, HttpServletResponse response) throws Exception { 406 407 Method method = (Method ) this.handlerMethodMap.get(methodName); 408 if (method == null) { 409 throw new NoSuchRequestHandlingMethodException(methodName, getClass()); 410 } 411 412 try { 413 List params = new ArrayList (4); 414 params.add(request); 415 params.add(response); 416 417 if (method.getParameterTypes().length >= 3 && method.getParameterTypes()[2].equals(HttpSession .class) ){ 418 HttpSession session = request.getSession(false); 419 if (session == null) { 420 throw new HttpSessionRequiredException( 421 "Pre-existing session required for handler method '" + methodName + "'"); 422 } 423 params.add(session); 424 } 425 426 if (method.getParameterTypes().length >= 3 && 428 !method.getParameterTypes()[method.getParameterTypes().length - 1].equals(HttpSession .class)) { 429 Object command = newCommandObject(method.getParameterTypes()[method.getParameterTypes().length - 1]); 430 params.add(command); 431 bind(request, command); 432 } 433 434 Object returnValue = method.invoke(this.delegate, params.toArray(new Object [params.size()])); 435 return massageReturnValueIfNecessary(returnValue); 436 } 437 catch (InvocationTargetException ex) { 438 return handleException(request, response, ex.getTargetException()); 440 } 441 catch (Exception ex) { 442 return handleException(request, response, ex); 444 } 445 } 446 447 452 private ModelAndView massageReturnValueIfNecessary(Object returnValue) { 453 if (returnValue instanceof ModelAndView) { 454 return (ModelAndView) returnValue; 455 } 456 else if (returnValue instanceof Map ) { 457 return new ModelAndView().addAllObjects((Map ) returnValue); 458 } 459 else { 460 return null; 463 } 464 } 465 466 467 475 protected Object newCommandObject(Class clazz) throws Exception { 476 if (logger.isDebugEnabled()) { 477 logger.debug("Must create new command of class [" + clazz.getName() + "]"); 478 } 479 return BeanUtils.instantiateClass(clazz); 480 } 481 482 488 protected void bind(HttpServletRequest request, Object command) throws Exception { 489 logger.debug("Binding request parameters onto MultiActionController command"); 490 ServletRequestDataBinder binder = createBinder(request, command); 491 binder.bind(request); 492 if (this.validators != null) { 493 for (int i = 0; i < this.validators.length; i++) { 494 if (this.validators[i].supports(command.getClass())) { 495 ValidationUtils.invokeValidator(this.validators[i], command, binder.getBindingResult()); 496 } 497 } 498 } 499 binder.closeNoCatch(); 500 } 501 502 516 protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object command) 517 throws Exception { 518 519 ServletRequestDataBinder binder = new ServletRequestDataBinder(command, getCommandName(command)); 520 initBinder(request, binder); 521 return binder; 522 } 523 524 531 protected String getCommandName(Object command) { 532 return DEFAULT_COMMAND_NAME; 533 } 534 535 552 protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) 553 throws Exception { 554 555 initBinder((ServletRequest ) request, binder); 556 } 557 558 563 protected void initBinder(ServletRequest request, ServletRequestDataBinder binder) 564 throws Exception { 565 } 566 567 568 574 protected Method getExceptionHandler(Throwable exception) { 575 Class exceptionClass = exception.getClass(); 576 if (logger.isDebugEnabled()) { 577 logger.debug("Trying to find handler for exception class [" + exceptionClass.getName() + "]"); 578 } 579 Method handler = (Method ) this.exceptionHandlerMap.get(exceptionClass); 580 while (handler == null && !exceptionClass.equals(Throwable .class)) { 581 if (logger.isDebugEnabled()) { 582 logger.debug("Trying to find handler for exception superclass [" + exceptionClass.getName() + "]"); 583 } 584 exceptionClass = exceptionClass.getSuperclass(); 585 handler = (Method ) this.exceptionHandlerMap.get(exceptionClass); 586 } 587 return handler; 588 } 589 590 599 private ModelAndView handleException(HttpServletRequest request, HttpServletResponse response, Throwable ex) 600 throws Exception { 601 602 Method handler = getExceptionHandler(ex); 603 if (handler != null) { 604 return invokeExceptionHandler(handler, request, response, ex); 605 } 606 if (ex instanceof Exception ) { 608 throw (Exception ) ex; 609 } 610 if (ex instanceof Error ) { 611 throw (Error ) ex; 612 } 613 throw new NestedServletException("Unknown Throwable type encountered", ex); 615 } 616 617 621 private ModelAndView invokeExceptionHandler( 622 Method handler, HttpServletRequest request, HttpServletResponse response, Throwable ex) 623 throws Exception { 624 625 if (handler == null) { 626 throw new NestedServletException("No handler for exception", ex); 627 } 628 629 if (logger.isDebugEnabled()) { 631 logger.debug("Invoking exception handler [" + handler + "] for exception [" + ex + "]"); 632 } 633 try { 634 Object returnValue = handler.invoke(this.delegate, new Object [] {request, response, ex}); 635 return massageReturnValueIfNecessary(returnValue); 636 } 637 catch (InvocationTargetException ex2) { 638 Throwable targetEx = ex2.getTargetException(); 639 if (targetEx instanceof Exception ) { 640 throw (Exception ) targetEx; 641 } 642 if (targetEx instanceof Error ) { 643 throw (Error ) targetEx; 644 } 645 throw new NestedServletException("Unknown Throwable type encountered", targetEx); 647 } 648 } 649 650 } 651 | Popular Tags |