001/* 002 * Copyright 2002-2015 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.portlet; 018 019import java.io.IOException; 020import java.security.Principal; 021import java.util.Map; 022import javax.portlet.ActionRequest; 023import javax.portlet.ActionResponse; 024import javax.portlet.EventRequest; 025import javax.portlet.EventResponse; 026import javax.portlet.PortletException; 027import javax.portlet.PortletRequest; 028import javax.portlet.PortletResponse; 029import javax.portlet.RenderRequest; 030import javax.portlet.RenderResponse; 031import javax.portlet.ResourceRequest; 032import javax.portlet.ResourceResponse; 033 034import org.springframework.beans.BeanUtils; 035import org.springframework.context.ApplicationContext; 036import org.springframework.context.ApplicationContextException; 037import org.springframework.context.ApplicationListener; 038import org.springframework.context.ConfigurableApplicationContext; 039import org.springframework.context.event.ContextRefreshedEvent; 040import org.springframework.context.event.SourceFilteringListener; 041import org.springframework.context.i18n.LocaleContext; 042import org.springframework.context.i18n.LocaleContextHolder; 043import org.springframework.context.i18n.SimpleLocaleContext; 044import org.springframework.core.env.ConfigurableEnvironment; 045import org.springframework.web.context.request.RequestAttributes; 046import org.springframework.web.context.request.RequestContextHolder; 047import org.springframework.web.context.request.ServletRequestAttributes; 048import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext; 049import org.springframework.web.portlet.context.PortletApplicationContextUtils; 050import org.springframework.web.portlet.context.PortletRequestAttributes; 051import org.springframework.web.portlet.context.PortletRequestHandledEvent; 052import org.springframework.web.portlet.context.StandardPortletEnvironment; 053import org.springframework.web.portlet.context.XmlPortletApplicationContext; 054 055/** 056 * Base portlet for Spring's portlet framework. Provides integration with 057 * a Spring application context, in a JavaBean-based overall solution. 058 * 059 * <p>This class offers the following functionality: 060 * <ul> 061 * <li>Manages a Portlet {@link org.springframework.context.ApplicationContext} 062 * instance per portlet. The portlet's configuration is determined by beans 063 * in the portlet's namespace. 064 * <li>Publishes events on request processing, whether or not a request is 065 * successfully handled. 066 * </ul> 067 * 068 * <p>Subclasses must implement {@link #doActionService} and {@link #doRenderService} 069 * to handle action and render requests. Because this extends {@link GenericPortletBean} 070 * rather than Portlet directly, bean properties are mapped onto it. Subclasses can 071 * override {@link #initFrameworkPortlet()} for custom initialization. 072 * 073 * <p>Regards a "contextClass" parameter at the portlet init-param level, 074 * falling back to the default context class 075 * ({@link org.springframework.web.portlet.context.XmlPortletApplicationContext}) 076 * if not found. Note that, with the default FrameworkPortlet, 077 * a context class needs to implement the 078 * {@link org.springframework.web.portlet.context.ConfigurablePortletApplicationContext} SPI. 079 * 080 * <p>Passes a "contextConfigLocation" portlet init-param to the context instance, 081 * parsing it into potentially multiple file paths which can be separated by any 082 * number of commas and spaces, like "test-portlet.xml, myPortlet.xml". 083 * If not explicitly specified, the context implementation is supposed to build a 084 * default location from the namespace of the portlet. 085 * 086 * <p>Note: In case of multiple config locations, later bean definitions will 087 * override ones defined in earlier loaded files, at least when using one of 088 * Spring's default ApplicationContext implementations. This can be leveraged 089 * to deliberately override certain bean definitions via an extra XML file. 090 * 091 * <p>The default namespace is "'portlet-name'-portlet", e.g. "test-portlet" for a 092 * portlet-name "test" (leading to a "/WEB-INF/test-portlet.xml" default location 093 * with XmlPortletApplicationContext). The namespace can also be set explicitly via 094 * the "namespace" portlet init-param. 095 * 096 * @author William G. Thompson, Jr. 097 * @author John A. Lewis 098 * @author Juergen Hoeller 099 * @since 2.0 100 * @see #doActionService 101 * @see #doRenderService 102 * @see #setContextClass 103 * @see #setContextConfigLocation 104 * @see #setNamespace 105 */ 106public abstract class FrameworkPortlet extends GenericPortletBean 107 implements ApplicationListener<ContextRefreshedEvent> { 108 109 /** 110 * Default context class for FrameworkPortlet. 111 * @see org.springframework.web.portlet.context.XmlPortletApplicationContext 112 */ 113 public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlPortletApplicationContext.class; 114 115 /** 116 * Suffix for Portlet ApplicationContext namespaces. If a portlet of this class is 117 * given the name "test" in a context, the namespace used by the portlet will 118 * resolve to "test-portlet". 119 */ 120 public static final String DEFAULT_NAMESPACE_SUFFIX = "-portlet"; 121 122 /** 123 * Prefix for the PortletContext attribute for the Portlet ApplicationContext. 124 * The completion is the portlet name. 125 */ 126 public static final String PORTLET_CONTEXT_PREFIX = FrameworkPortlet.class.getName() + ".CONTEXT."; 127 128 /** 129 * Default USER_INFO attribute names to search for the current username: 130 * "user.login.id", "user.name". 131 */ 132 public static final String[] DEFAULT_USERINFO_ATTRIBUTE_NAMES = {"user.login.id", "user.name"}; 133 134 135 /** Portlet ApplicationContext implementation class to use */ 136 private Class<?> contextClass = DEFAULT_CONTEXT_CLASS; 137 138 /** Namespace for this portlet */ 139 private String namespace; 140 141 /** Explicit context config location */ 142 private String contextConfigLocation; 143 144 /** Should we publish the context as a PortletContext attribute? */ 145 private boolean publishContext = true; 146 147 /** Should we publish a PortletRequestHandledEvent at the end of each request? */ 148 private boolean publishEvents = true; 149 150 /** Expose LocaleContext and RequestAttributes as inheritable for child threads? */ 151 private boolean threadContextInheritable = false; 152 153 /** USER_INFO attributes that may contain the username of the current user */ 154 private String[] userinfoUsernameAttributes = DEFAULT_USERINFO_ATTRIBUTE_NAMES; 155 156 /** ApplicationContext for this portlet */ 157 private ApplicationContext portletApplicationContext; 158 159 /** Flag used to detect whether onRefresh has already been called */ 160 private boolean refreshEventReceived = false; 161 162 163 /** 164 * Set a custom context class. This class must be of type ApplicationContext; 165 * when using the default FrameworkPortlet implementation, the context class 166 * must also implement ConfigurablePortletApplicationContext. 167 * @see #createPortletApplicationContext 168 */ 169 public void setContextClass(Class<?> contextClass) { 170 this.contextClass = contextClass; 171 } 172 173 /** 174 * Return the custom context class. 175 */ 176 public Class<?> getContextClass() { 177 return this.contextClass; 178 } 179 180 /** 181 * Set a custom namespace for this portlet, 182 * to be used for building a default context config location. 183 */ 184 public void setNamespace(String namespace) { 185 this.namespace = namespace; 186 } 187 188 /** 189 * Return the namespace for this portlet, falling back to default scheme if 190 * no custom namespace was set. (e.g. "test-portlet" for a portlet named "test") 191 */ 192 public String getNamespace() { 193 return (this.namespace != null) ? this.namespace : getPortletName() + DEFAULT_NAMESPACE_SUFFIX; 194 } 195 196 /** 197 * Set the context config location explicitly, instead of relying on the default 198 * location built from the namespace. This location string can consist of 199 * multiple locations separated by any number of commas and spaces. 200 */ 201 public void setContextConfigLocation(String contextConfigLocation) { 202 this.contextConfigLocation = contextConfigLocation; 203 } 204 205 /** 206 * Return the explicit context config location, if any. 207 */ 208 public String getContextConfigLocation() { 209 return this.contextConfigLocation; 210 } 211 212 /** 213 * Set whether to publish this portlet's context as a PortletContext attribute, 214 * available to all objects in the web container. Default is true. 215 * <p>This is especially handy during testing, although it is debatable whether 216 * it's good practice to let other application objects access the context this way. 217 */ 218 public void setPublishContext(boolean publishContext) { 219 this.publishContext = publishContext; 220 } 221 222 /** 223 * Set whether this portlet should publish a PortletRequestHandledEvent at the end 224 * of each request. Default is true; can be turned off for a slight performance 225 * improvement, provided that no ApplicationListeners rely on such events. 226 * @see org.springframework.web.portlet.context.PortletRequestHandledEvent 227 */ 228 public void setPublishEvents(boolean publishEvents) { 229 this.publishEvents = publishEvents; 230 } 231 232 /** 233 * Set whether to expose the LocaleContext and RequestAttributes as inheritable 234 * for child threads (using an {@link java.lang.InheritableThreadLocal}). 235 * <p>Default is "false", to avoid side effects on spawned background threads. 236 * Switch this to "true" to enable inheritance for custom child threads which 237 * are spawned during request processing and only used for this request 238 * (that is, ending after their initial task, without reuse of the thread). 239 * <p><b>WARNING:</b> Do not use inheritance for child threads if you are 240 * accessing a thread pool which is configured to potentially add new threads 241 * on demand (e.g. a JDK {@link java.util.concurrent.ThreadPoolExecutor}), 242 * since this will expose the inherited context to such a pooled thread. 243 */ 244 public void setThreadContextInheritable(boolean threadContextInheritable) { 245 this.threadContextInheritable = threadContextInheritable; 246 } 247 248 /** 249 * Set the list of attributes to search in the USER_INFO map when trying 250 * to find the username of the current user. 251 * @see #getUsernameForRequest 252 */ 253 public void setUserinfoUsernameAttributes(String[] userinfoUsernameAttributes) { 254 this.userinfoUsernameAttributes = userinfoUsernameAttributes; 255 } 256 257 258 /** 259 * Overridden method of GenericPortletBean, invoked after any bean properties 260 * have been set. Creates this portlet's ApplicationContext. 261 */ 262 @Override 263 protected final void initPortletBean() throws PortletException { 264 getPortletContext().log("Initializing Spring FrameworkPortlet '" + getPortletName() + "'"); 265 if (logger.isInfoEnabled()) { 266 logger.info("FrameworkPortlet '" + getPortletName() + "': initialization started"); 267 } 268 long startTime = System.currentTimeMillis(); 269 270 try { 271 this.portletApplicationContext = initPortletApplicationContext(); 272 initFrameworkPortlet(); 273 } 274 catch (PortletException ex) { 275 logger.error("Context initialization failed", ex); 276 throw ex; 277 } 278 catch (RuntimeException ex) { 279 logger.error("Context initialization failed", ex); 280 throw ex; 281 } 282 283 if (logger.isInfoEnabled()) { 284 long elapsedTime = System.currentTimeMillis() - startTime; 285 logger.info("FrameworkPortlet '" + getPortletName() + "': initialization completed in " + elapsedTime + " ms"); 286 } 287 } 288 289 /** 290 * Initialize and publish the Portlet ApplicationContext for this portlet. 291 * <p>Delegates to {@link #createPortletApplicationContext} for actual creation. 292 * Can be overridden in subclasses. 293 * @return the ApplicationContext for this portlet 294 */ 295 protected ApplicationContext initPortletApplicationContext() { 296 ApplicationContext parent = PortletApplicationContextUtils.getWebApplicationContext(getPortletContext()); 297 ApplicationContext pac = createPortletApplicationContext(parent); 298 299 if (!this.refreshEventReceived) { 300 // Apparently not a ConfigurableApplicationContext with refresh support: 301 // triggering initial onRefresh manually here. 302 onRefresh(pac); 303 } 304 305 if (this.publishContext) { 306 // publish the context as a portlet context attribute 307 String attName = getPortletContextAttributeName(); 308 getPortletContext().setAttribute(attName, pac); 309 if (logger.isDebugEnabled()) { 310 logger.debug("Published ApplicationContext of portlet '" + getPortletName() + 311 "' as PortletContext attribute with name [" + attName + "]"); 312 } 313 } 314 return pac; 315 } 316 317 /** 318 * Instantiate the Portlet ApplicationContext for this portlet, either a default 319 * XmlPortletApplicationContext or a custom context class if set. 320 * <p>This implementation expects custom contexts to implement 321 * ConfigurablePortletApplicationContext. Can be overridden in subclasses. 322 * @param parent the parent ApplicationContext to use, or null if none 323 * @return the Portlet ApplicationContext for this portlet 324 * @see #setContextClass 325 * @see org.springframework.web.portlet.context.XmlPortletApplicationContext 326 */ 327 protected ApplicationContext createPortletApplicationContext(ApplicationContext parent) { 328 Class<?> contextClass = getContextClass(); 329 if (logger.isDebugEnabled()) { 330 logger.debug("Portlet with name '" + getPortletName() + 331 "' will try to create custom ApplicationContext context of class '" + 332 contextClass.getName() + "'" + ", using parent context [" + parent + "]"); 333 } 334 if (!ConfigurablePortletApplicationContext.class.isAssignableFrom(contextClass)) { 335 throw new ApplicationContextException("Fatal initialization error in portlet with name '" + getPortletName() + 336 "': custom ApplicationContext class [" + contextClass.getName() + 337 "] is not of type ConfigurablePortletApplicationContext"); 338 } 339 ConfigurablePortletApplicationContext pac = 340 (ConfigurablePortletApplicationContext) BeanUtils.instantiateClass(contextClass); 341 342 // Assign the best possible id value. 343 String portletContextName = getPortletContext().getPortletContextName(); 344 if (portletContextName != null) { 345 pac.setId(ConfigurablePortletApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + portletContextName + "." + getPortletName()); 346 } 347 else { 348 pac.setId(ConfigurablePortletApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + getPortletName()); 349 } 350 351 pac.setEnvironment(getEnvironment()); 352 pac.setParent(parent); 353 pac.setPortletContext(getPortletContext()); 354 pac.setPortletConfig(getPortletConfig()); 355 pac.setNamespace(getNamespace()); 356 pac.setConfigLocation(getContextConfigLocation()); 357 pac.addApplicationListener(new SourceFilteringListener(pac, this)); 358 359 // The wac environment's #initPropertySources will be called in any case when the context 360 // is refreshed; do it eagerly here to ensure portlet property sources are in place for 361 // use in any post-processing or initialization that occurs below prior to #refresh 362 ConfigurableEnvironment env = pac.getEnvironment(); 363 if (env instanceof StandardPortletEnvironment) { 364 ((StandardPortletEnvironment) env).initPropertySources(pac.getServletContext(), getPortletContext(), getPortletConfig()); 365 } 366 367 postProcessPortletApplicationContext(pac); 368 pac.refresh(); 369 370 return pac; 371 } 372 373 /** 374 * Post-process the given Portlet ApplicationContext before it is refreshed 375 * and activated as context for this portlet. 376 * <p>The default implementation is empty. {@code refresh()} will 377 * be called automatically after this method returns. 378 * @param pac the configured Portlet ApplicationContext (not refreshed yet) 379 * @see #createPortletApplicationContext 380 * @see ConfigurableApplicationContext#refresh() 381 */ 382 protected void postProcessPortletApplicationContext(ConfigurableApplicationContext pac) { 383 } 384 385 /** 386 * Return the PortletContext attribute name for this portlets's ApplicationContext. 387 * <p>The default implementation returns PORTLET_CONTEXT_PREFIX + portlet name. 388 * @see #PORTLET_CONTEXT_PREFIX 389 * @see #getPortletName 390 */ 391 public String getPortletContextAttributeName() { 392 return PORTLET_CONTEXT_PREFIX + getPortletName(); 393 } 394 395 /** 396 * Return this portlet's ApplicationContext. 397 */ 398 public final ApplicationContext getPortletApplicationContext() { 399 return this.portletApplicationContext; 400 } 401 402 403 /** 404 * This method will be invoked after any bean properties have been set and 405 * the ApplicationContext has been loaded. 406 * <p>The default implementation is empty; subclasses may override this method 407 * to perform any initialization they require. 408 * @throws PortletException in case of an initialization exception 409 */ 410 protected void initFrameworkPortlet() throws PortletException { 411 } 412 413 /** 414 * Refresh this portlet's application context, as well as the 415 * dependent state of the portlet. 416 * @see #getPortletApplicationContext() 417 * @see org.springframework.context.ConfigurableApplicationContext#refresh() 418 */ 419 public void refresh() { 420 ApplicationContext pac = getPortletApplicationContext(); 421 if (!(pac instanceof ConfigurableApplicationContext)) { 422 throw new IllegalStateException("Portlet ApplicationContext does not support refresh: " + pac); 423 } 424 ((ConfigurableApplicationContext) pac).refresh(); 425 } 426 427 /** 428 * ApplicationListener endpoint that receives events from this servlet's 429 * WebApplicationContext. 430 * <p>The default implementation calls {@link #onRefresh} in case of a 431 * {@link org.springframework.context.event.ContextRefreshedEvent}, 432 * triggering a refresh of this servlet's context-dependent state. 433 * @param event the incoming ApplicationContext event 434 */ 435 @Override 436 public void onApplicationEvent(ContextRefreshedEvent event) { 437 this.refreshEventReceived = true; 438 onRefresh(event.getApplicationContext()); 439 } 440 441 /** 442 * Template method which can be overridden to add portlet-specific refresh work. 443 * Called after successful context refresh. 444 * <p>This implementation is empty. 445 * @param context the current Portlet ApplicationContext 446 * @see #refresh() 447 */ 448 protected void onRefresh(ApplicationContext context) { 449 // For subclasses: do nothing by default. 450 } 451 452 453 /** 454 * Overridden for friendlier behavior in unit tests. 455 */ 456 @Override 457 protected String getTitle(RenderRequest renderRequest) { 458 try { 459 return super.getTitle(renderRequest); 460 } 461 catch (NullPointerException ex) { 462 return getPortletName(); 463 } 464 } 465 466 /** 467 * Delegate action requests to processRequest/doActionService. 468 */ 469 @Override 470 public final void processAction(ActionRequest request, ActionResponse response) 471 throws PortletException, IOException { 472 473 processRequest(request, response); 474 } 475 476 /** 477 * Delegate render requests to processRequest/doRenderService. 478 */ 479 @Override 480 protected final void doDispatch(RenderRequest request, RenderResponse response) 481 throws PortletException, IOException { 482 483 processRequest(request, response); 484 } 485 486 @Override 487 public void serveResource(ResourceRequest request, ResourceResponse response) 488 throws PortletException, IOException { 489 490 processRequest(request, response); 491 } 492 493 @Override 494 public void processEvent(EventRequest request, EventResponse response) 495 throws PortletException, IOException { 496 497 processRequest(request, response); 498 } 499 500 /** 501 * Process this request, publishing an event regardless of the outcome. 502 * The actual event handling is performed by the abstract 503 * {@code doActionService()} and {@code doRenderService()} template methods. 504 * @see #doActionService 505 * @see #doRenderService 506 */ 507 protected final void processRequest(PortletRequest request, PortletResponse response) 508 throws PortletException, IOException { 509 510 long startTime = System.currentTimeMillis(); 511 Throwable failureCause = null; 512 513 // Expose current LocaleResolver and request as LocaleContext. 514 LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 515 LocaleContextHolder.setLocaleContext(buildLocaleContext(request), this.threadContextInheritable); 516 517 // Expose current RequestAttributes to current thread. 518 RequestAttributes previousRequestAttributes = RequestContextHolder.getRequestAttributes(); 519 PortletRequestAttributes requestAttributes = null; 520 if (previousRequestAttributes == null || 521 PortletRequestAttributes.class == previousRequestAttributes.getClass() || 522 ServletRequestAttributes.class == previousRequestAttributes.getClass()) { 523 requestAttributes = new PortletRequestAttributes(request, response); 524 RequestContextHolder.setRequestAttributes(requestAttributes, this.threadContextInheritable); 525 } 526 527 if (logger.isTraceEnabled()) { 528 logger.trace("Bound request context to thread: " + request); 529 } 530 531 try { 532 String phase = (String) request.getAttribute(PortletRequest.LIFECYCLE_PHASE); 533 if (PortletRequest.ACTION_PHASE.equals(phase)) { 534 doActionService((ActionRequest) request, (ActionResponse) response); 535 } 536 else if (PortletRequest.RENDER_PHASE.equals(phase)) { 537 doRenderService((RenderRequest) request, (RenderResponse) response); 538 } 539 else if (PortletRequest.RESOURCE_PHASE.equals(phase)) { 540 doResourceService((ResourceRequest) request, (ResourceResponse) response); 541 } 542 else if (PortletRequest.EVENT_PHASE.equals(phase)) { 543 doEventService((EventRequest) request, (EventResponse) response); 544 } 545 else { 546 throw new IllegalStateException("Invalid portlet request phase: " + phase); 547 } 548 } 549 catch (PortletException ex) { 550 failureCause = ex; 551 throw ex; 552 } 553 catch (IOException ex) { 554 failureCause = ex; 555 throw ex; 556 } 557 catch (Throwable ex) { 558 failureCause = ex; 559 throw new PortletException("Request processing failed", ex); 560 } 561 562 finally { 563 // Clear request attributes and reset thread-bound context. 564 LocaleContextHolder.setLocaleContext(previousLocaleContext, this.threadContextInheritable); 565 if (requestAttributes != null) { 566 RequestContextHolder.setRequestAttributes(previousRequestAttributes, this.threadContextInheritable); 567 requestAttributes.requestCompleted(); 568 } 569 if (logger.isTraceEnabled()) { 570 logger.trace("Cleared thread-bound resource request context: " + request); 571 } 572 573 if (failureCause != null) { 574 logger.error("Could not complete request", failureCause); 575 } 576 else { 577 logger.debug("Successfully completed request"); 578 } 579 if (this.publishEvents) { 580 // Whether or not we succeeded, publish an event. 581 long processingTime = System.currentTimeMillis() - startTime; 582 this.portletApplicationContext.publishEvent( 583 new PortletRequestHandledEvent(this, 584 getPortletConfig().getPortletName(), request.getPortletMode().toString(), 585 (request instanceof ActionRequest ? "action" : "render"), 586 request.getRequestedSessionId(), getUsernameForRequest(request), 587 processingTime, failureCause)); 588 } 589 } 590 } 591 592 /** 593 * Build a LocaleContext for the given request, exposing the request's 594 * primary locale as current locale. 595 * @param request current HTTP request 596 * @return the corresponding LocaleContext 597 */ 598 protected LocaleContext buildLocaleContext(PortletRequest request) { 599 return new SimpleLocaleContext(request.getLocale()); 600 } 601 602 /** 603 * Determine the username for the given request. 604 * <p>The default implementation first tries the UserPrincipal. 605 * If that does not exist, then it checks the USER_INFO map. 606 * Can be overridden in subclasses. 607 * @param request current portlet request 608 * @return the username, or {@code null} if none found 609 * @see javax.portlet.PortletRequest#getUserPrincipal() 610 * @see javax.portlet.PortletRequest#getRemoteUser() 611 * @see javax.portlet.PortletRequest#USER_INFO 612 * @see #setUserinfoUsernameAttributes 613 */ 614 protected String getUsernameForRequest(PortletRequest request) { 615 // Try the principal. 616 Principal userPrincipal = request.getUserPrincipal(); 617 if (userPrincipal != null) { 618 return userPrincipal.getName(); 619 } 620 621 // Try the remote user name. 622 String userName = request.getRemoteUser(); 623 if (userName != null) { 624 return userName; 625 } 626 627 // Try the Portlet USER_INFO map. 628 Map<?, ?> userInfo = (Map<?, ?>) request.getAttribute(PortletRequest.USER_INFO); 629 if (userInfo != null) { 630 for (int i = 0, n = this.userinfoUsernameAttributes.length; i < n; i++) { 631 userName = (String) userInfo.get(this.userinfoUsernameAttributes[i]); 632 if (userName != null) { 633 return userName; 634 } 635 } 636 } 637 638 // Nothing worked... 639 return null; 640 } 641 642 643 /** 644 * Subclasses must implement this method to do the work of action request handling. 645 * <p>The contract is essentially the same as that for the {@code processAction} 646 * method of GenericPortlet. 647 * <p>This class intercepts calls to ensure that exception handling and 648 * event publication takes place. 649 * @param request current action request 650 * @param response current action response 651 * @throws Exception in case of any kind of processing failure 652 * @see javax.portlet.GenericPortlet#processAction 653 */ 654 protected abstract void doActionService(ActionRequest request, ActionResponse response) 655 throws Exception; 656 657 /** 658 * Subclasses must implement this method to do the work of render request handling. 659 * <p>The contract is essentially the same as that for the {@code doDispatch} 660 * method of GenericPortlet. 661 * <p>This class intercepts calls to ensure that exception handling and 662 * event publication takes place. 663 * @param request current render request 664 * @param response current render response 665 * @throws Exception in case of any kind of processing failure 666 * @see javax.portlet.GenericPortlet#doDispatch 667 */ 668 protected abstract void doRenderService(RenderRequest request, RenderResponse response) 669 throws Exception; 670 671 /** 672 * Subclasses must implement this method to do the work of resource request handling. 673 * <p>The contract is essentially the same as that for the {@code serveResource} 674 * method of GenericPortlet. 675 * <p>This class intercepts calls to ensure that exception handling and 676 * event publication takes place. 677 * @param request current resource request 678 * @param response current resource response 679 * @throws Exception in case of any kind of processing failure 680 * @see javax.portlet.GenericPortlet#serveResource 681 */ 682 protected abstract void doResourceService(ResourceRequest request, ResourceResponse response) 683 throws Exception; 684 685 /** 686 * Subclasses must implement this method to do the work of event request handling. 687 * <p>The contract is essentially the same as that for the {@code processEvent} 688 * method of GenericPortlet. 689 * <p>This class intercepts calls to ensure that exception handling and 690 * event publication takes place. 691 * @param request current event request 692 * @param response current event response 693 * @throws Exception in case of any kind of processing failure 694 * @see javax.portlet.GenericPortlet#processEvent 695 */ 696 protected abstract void doEventService(EventRequest request, EventResponse response) 697 throws Exception; 698 699 700 /** 701 * Close the ApplicationContext of this portlet. 702 * @see org.springframework.context.ConfigurableApplicationContext#close() 703 */ 704 @Override 705 public void destroy() { 706 getPortletContext().log("Destroying Spring FrameworkPortlet '" + getPortletName() + "'"); 707 if (this.portletApplicationContext instanceof ConfigurableApplicationContext) { 708 ((ConfigurableApplicationContext) this.portletApplicationContext).close(); 709 } 710 } 711 712}