KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > web > portlet > FrameworkPortlet


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.web.portlet;
18
19 import java.io.IOException JavaDoc;
20 import java.security.Principal JavaDoc;
21 import java.util.Map JavaDoc;
22
23 import javax.portlet.ActionRequest;
24 import javax.portlet.ActionResponse;
25 import javax.portlet.PortletException;
26 import javax.portlet.PortletRequest;
27 import javax.portlet.PortletResponse;
28 import javax.portlet.RenderRequest;
29 import javax.portlet.RenderResponse;
30
31 import org.springframework.beans.BeanUtils;
32 import org.springframework.beans.BeansException;
33 import org.springframework.context.ApplicationContext;
34 import org.springframework.context.ApplicationContextException;
35 import org.springframework.context.ConfigurableApplicationContext;
36 import org.springframework.util.StringUtils;
37 import org.springframework.web.portlet.context.ConfigurablePortletApplicationContext;
38 import org.springframework.web.portlet.context.PortletApplicationContextUtils;
39 import org.springframework.web.portlet.context.PortletRequestHandledEvent;
40 import org.springframework.web.portlet.context.XmlPortletApplicationContext;
41
42 /**
43  * Base portlet for portlets within the portlet framework. Allows integration
44  * with an application context, in a JavaBean-based overall solution.
45  *
46  * <p>This class offers the following functionality:
47  * <ul>
48  * <li>Uses a Portlet ApplicationContext to access a BeanFactory. The portlet's
49  * configuration is determined by beans in the portlet's namespace.
50  * <li>Publishes events on request processing, whether or not a request is
51  * successfully handled.
52  * </ul>
53  *
54  * <p>Subclasses must implement <code>doActionService</code> and <code>doRenderService</code>
55  * to handle action and render requests. Because this extends GenericPortletBean rather
56  * than Portlet directly, bean properties are mapped onto it. Subclasses can override
57  * initFrameworkPortlet() for custom initialization.
58  *
59  * <p>Regards a "contextClass" parameter at the portlet init-param level,
60  * falling back to the default context class (XmlPortletApplicationContext) if not found.
61  * With the default FrameworkPortlet, a context class needs to implement
62  * ConfigurablePortletApplicationContext.
63  *
64  * <p>Passes a "contextConfigLocation" portlet init-param to the context instance,
65  * parsing it into potentially multiple file paths which can be separated by any
66  * number of commas and spaces, like "test-portlet.xml, myPortlet.xml".
67  * If not explicitly specified, the context implementation is supposed to build a
68  * default location from the namespace of the portlet.
69  *
70  * <p>Note: In case of multiple config locations, later bean definitions will
71  * override ones defined in earlier loaded files, at least when using one of
72  * Spring's default ApplicationContext implementations. This can be leveraged
73  * to deliberately override certain bean definitions via an extra XML file.
74  *
75  * <p>The default namespace is "'portlet-name'-portlet", e.g. "test-portlet" for a
76  * portlet-name "test" (leading to a "/WEB-INF/test-portlet.xml" default location
77  * with XmlPortletApplicationContext). The namespace can also be set explicitly via
78  * the "namespace" portlet init-param.
79  *
80  * @author William G. Thompson, Jr.
81  * @author John A. Lewis
82  * @author Juergen Hoeller
83  * @since 2.0
84  * @see #doActionService
85  * @see #doRenderService
86  * @see #initFrameworkPortlet
87  * @see #setContextClass
88  * @see #setContextConfigLocation
89  * @see #setNamespace
90  */

91 public abstract class FrameworkPortlet extends GenericPortletBean {
92
93     /**
94      * Default context class for FrameworkPortlet.
95      * @see org.springframework.web.portlet.context.XmlPortletApplicationContext
96      */

97     public static final Class JavaDoc DEFAULT_CONTEXT_CLASS = XmlPortletApplicationContext.class;
98
99     /**
100      * Suffix for Portlet ApplicationContext namespaces. If a portlet of this class is
101      * given the name "test" in a context, the namespace used by the portlet will
102      * resolve to "test-portlet".
103      */

104     public static final String JavaDoc DEFAULT_NAMESPACE_SUFFIX = "-portlet";
105
106     /**
107      * Prefix for the PortletContext attribute for the Portlet ApplicationContext.
108      * The completion is the portlet name.
109      */

110     public static final String JavaDoc PORTLET_CONTEXT_PREFIX = FrameworkPortlet.class.getName() + ".CONTEXT.";
111
112     /**
113      * Default USER_INFO attribute names to search for the current username:
114      * "user.login.id", "user.name".
115      */

116     public static final String JavaDoc[] DEFAULT_USERINFO_ATTRIBUTE_NAMES = {"user.login.id", "user.name"};
117
118
119     /**
120      * Portlet ApplicationContext implementation class to use
121      */

122     private Class JavaDoc contextClass = DEFAULT_CONTEXT_CLASS;
123
124     /**
125      * Namespace for this portlet
126      */

127     private String JavaDoc namespace;
128
129     /**
130      * Explicit context config location
131      */

132     private String JavaDoc contextConfigLocation;
133
134     /**
135      * Should we publish the context as a PortletContext attribute?
136      */

137     private boolean publishContext = true;
138
139     /**
140      * Should we publish a PortletRequestHandledEvent at the end of each request?
141      */

142     private boolean publishEvents = true;
143
144     /**
145      * USER_INFO attributes that may contain the username of the current user
146      */

147     private String JavaDoc[] userinfoUsernameAttributes = DEFAULT_USERINFO_ATTRIBUTE_NAMES;
148
149     /**
150      * ApplicationContext for this portlet
151      */

152     private ApplicationContext portletApplicationContext;
153
154
155     /**
156      * Set a custom context class. This class must be of type ApplicationContext;
157      * when using the default FrameworkPortlet implementation, the context class
158      * must also implement ConfigurablePortletApplicationContext.
159      * @see #createPortletApplicationContext
160      */

161     public void setContextClass(Class JavaDoc contextClass) {
162         this.contextClass = contextClass;
163     }
164
165     /**
166      * Return the custom context class.
167      */

168     public Class JavaDoc getContextClass() {
169         return contextClass;
170     }
171
172     /**
173      * Set a custom namespace for this portlet,
174      * to be used for building a default context config location.
175      */

176     public void setNamespace(String JavaDoc namespace) {
177         this.namespace = namespace;
178     }
179
180     /**
181      * Return the namespace for this portlet, falling back to default scheme if
182      * no custom namespace was set. (e.g. "test-portlet" for a portlet named "test")
183      */

184     public String JavaDoc getNamespace() {
185         return (this.namespace != null) ? this.namespace : getPortletName() + DEFAULT_NAMESPACE_SUFFIX;
186     }
187
188     /**
189      * Set the context config location explicitly, instead of relying on the default
190      * location built from the namespace. This location string can consist of
191      * multiple locations separated by any number of commas and spaces.
192      */

193     public void setContextConfigLocation(String JavaDoc contextConfigLocation) {
194         this.contextConfigLocation = contextConfigLocation;
195     }
196
197     /**
198      * Return the explicit context config location, if any.
199      */

200     public String JavaDoc getContextConfigLocation() {
201         return contextConfigLocation;
202     }
203
204     /**
205      * Set whether to publish this portlet's context as a PortletContext attribute,
206      * available to all objects in the web container. Default is true.
207      * <p>This is especially handy during testing, although it is debatable whether
208      * it's good practice to let other application objects access the context this way.
209      */

210     public void setPublishContext(boolean publishContext) {
211         this.publishContext = publishContext;
212     }
213
214     /**
215      * Return whether to publish this portlet's context as a PortletContext attribute.
216      */

217     public boolean isPublishContext() {
218         return publishContext;
219     }
220
221     /**
222      * Set whether this portlet should publish a PortletRequestHandledEvent at the end
223      * of each request. Default is true; can be turned off for a slight performance
224      * improvement, provided that no ApplicationListeners rely on such events.
225      * @see org.springframework.web.portlet.context.PortletRequestHandledEvent
226      */

227     public void setPublishEvents(boolean publishEvents) {
228         this.publishEvents = publishEvents;
229     }
230
231     /**
232      * Return whether this portlet should publish a PortletRequestHandledEvent at the end
233      * of each request.
234      */

235     public boolean isPublishEvents() {
236         return publishEvents;
237     }
238
239     /**
240      * Set the list of attributes to search in the USER_INFO map when trying
241      * to find the username of the current user.
242      * @see #getUsernameForRequest
243      */

244     public void setUserinfoUsernameAttributes(String JavaDoc[] userinfoUsernameAttributes) {
245         this.userinfoUsernameAttributes = userinfoUsernameAttributes;
246     }
247
248     /**
249      * Returns the list of attributes that will be searched in the USER_INFO map
250      * when trying to find the username of the current user
251      * @see #getUsernameForRequest
252      */

253     public String JavaDoc[] getUserinfoUsernameAttributes() {
254         return 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     protected final void initPortletBean() throws PortletException, BeansException {
263         long startTime = System.currentTimeMillis();
264         if (logger.isInfoEnabled()) {
265             logger.info("FrameworkPortlet '" + getPortletName() + "': initialization started");
266         }
267
268         try {
269             this.portletApplicationContext = initPortletApplicationContext();
270             initFrameworkPortlet();
271         }
272         catch (PortletException ex) {
273             logger.error("Context initialization failed", ex);
274             throw ex;
275         }
276         catch (BeansException ex) {
277             logger.error("Context initialization failed", ex);
278             throw ex;
279         }
280
281         if (logger.isInfoEnabled()) {
282             long elapsedTime = System.currentTimeMillis() - startTime;
283             logger.info("FrameworkPortlet '" + getPortletName() + "': initialization completed in " + elapsedTime + " ms");
284         }
285     }
286
287     /**
288      * Initialize and publish the PortletApplicationContext for this portlet.
289      * Delegates to createPortletApplicationContext for actual creation.
290      * Can be overridden in subclasses.
291      * @return the Portlet ApplicationContext for this portlet
292      * @throws BeansException if the context couldn't be initialized
293      * @see #createPortletApplicationContext
294      */

295     protected ApplicationContext initPortletApplicationContext() throws BeansException {
296         getPortletContext().log("Loading PortletApplicationContext for Spring FrameworkPortlet '" + getPortletName() + "'");
297
298         ApplicationContext parent = PortletApplicationContextUtils.getWebApplicationContext(getPortletContext());
299         ApplicationContext pac = createPortletApplicationContext(parent);
300         if (logger.isInfoEnabled()) {
301             logger.info("Using context class '" + pac.getClass().getName() + "' for portlet '" +
302                     getPortletName() + "'");
303         }
304
305         if (isPublishContext()) {
306             // publish the context as a portlet context attribute
307
String JavaDoc attName = getPortletContextAttributeName();
308             getPortletContext().setAttribute(attName, pac);
309             if (logger.isDebugEnabled()) {
310                 logger.debug("Published PortletApplicationContext of portlet '" + getPortletName() +
311                         "' as PortletContext attribute with name [" + attName + "]");
312             }
313         }
314         return pac;
315     }
316
317     /**
318      * Instantiate the PortletApplicationContext for this portlet, either a default
319      * XmlPortletApplicationContext or a custom context class if set. This implementation
320      * expects custom contexts to implement ConfigurablePortletApplicationContext.
321      * 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      * @throws BeansException if the context couldn't be initialized
325      * @see #setContextClass
326      * @see org.springframework.web.portlet.context.XmlPortletApplicationContext
327      */

328     protected ApplicationContext createPortletApplicationContext(ApplicationContext parent)
329             throws BeansException {
330
331         if (logger.isDebugEnabled()) {
332             logger.debug("Portlet with name '" + getPortletName() +
333                     "' will try to create custom PortletApplicationContext context of class '" +
334                     getContextClass().getName() + "'" + ", using parent context [" + parent + "]");
335         }
336         if (!ConfigurablePortletApplicationContext.class.isAssignableFrom(getContextClass())) {
337             throw new ApplicationContextException("Fatal initialization error in portlet with name '" + getPortletName() +
338                     "': custom PortletApplicationContext class [" + getContextClass().getName() +
339                     "] is not of type ConfigurablePortletApplicationContext");
340         }
341
342         ConfigurablePortletApplicationContext pac =
343                 (ConfigurablePortletApplicationContext) BeanUtils.instantiateClass(getContextClass());
344         pac.setParent(parent);
345         pac.setPortletContext(getPortletContext());
346         pac.setPortletConfig(getPortletConfig());
347         pac.setNamespace(getNamespace());
348         if (getContextConfigLocation() != null) {
349             pac.setConfigLocations(StringUtils.tokenizeToStringArray(getContextConfigLocation(),
350                     ConfigurablePortletApplicationContext.CONFIG_LOCATION_DELIMITERS));
351         }
352         pac.refresh();
353         return pac;
354     }
355
356     /**
357      * Return the PortletContext attribute name for this portlets's PortletApplicationContext.
358      * Default implementation returns PORTLET_CONTEXT_PREFIX + portlet name.
359      * @see #PORTLET_CONTEXT_PREFIX
360      * @see #getPortletName
361      */

362     public String JavaDoc getPortletContextAttributeName() {
363         return PORTLET_CONTEXT_PREFIX + getPortletName();
364     }
365
366     /**
367      * Return this portlet's ApplicationContext.
368      */

369     public final ApplicationContext getPortletApplicationContext() {
370         return portletApplicationContext;
371     }
372
373     /**
374      * This method will be invoked after any bean properties have been set and
375      * the PortletApplicationContext has been loaded. The default implementation is empty;
376      * subclasses may override this method to perform any initialization they require.
377      *
378      * @throws PortletException in case of an initialization exception
379      * @throws BeansException if thrown by ApplicationContext methods
380      */

381     protected void initFrameworkPortlet() throws PortletException, BeansException {
382     }
383
384
385     /**
386      * Delegate render requests to processRequest/doRenderService.
387      */

388     protected final void doDispatch(RenderRequest request, RenderResponse response)
389             throws PortletException, IOException JavaDoc {
390
391         processRequest(request, response);
392     }
393
394     /**
395      * Delegate action requests to processRequest/doActionService.
396      */

397     public final void processAction(ActionRequest request, ActionResponse response)
398             throws PortletException, IOException JavaDoc {
399
400         processRequest(request, response);
401     }
402     
403     /**
404      * Process this request, publishing an event regardless of the outcome.
405      * The actual event handling is performed by the abstract
406      * <code>doActionService()</code> and <code>doRenderService()</code> template methods.
407      * @see #doActionService
408      * @see #doRenderService
409      */

410     protected final void processRequest(PortletRequest request, PortletResponse response)
411             throws PortletException, IOException JavaDoc {
412
413         long startTime = System.currentTimeMillis();
414         Throwable JavaDoc failureCause = null;
415
416         try {
417             if (request instanceof ActionRequest) {
418                 doActionService((ActionRequest) request, (ActionResponse) response);
419             }
420             else {
421                 doRenderService((RenderRequest) request, (RenderResponse) response);
422             }
423         }
424         catch (PortletException ex) {
425             failureCause = ex;
426             throw ex;
427         }
428         catch (IOException JavaDoc ex) {
429             failureCause = ex;
430             throw ex;
431         }
432         catch (Throwable JavaDoc ex) {
433             failureCause = ex;
434             throw new PortletException("Request processing failed", ex);
435         }
436
437         finally {
438             if (failureCause != null) {
439                 logger.error("Could not complete request", failureCause);
440             }
441             else {
442                 logger.debug("Successfully completed request");
443             }
444             if (isPublishEvents()) {
445                 // Whether or not we succeeded, publish an event.
446
long processingTime = System.currentTimeMillis() - startTime;
447                 this.portletApplicationContext.publishEvent(
448                         new PortletRequestHandledEvent(this,
449                                 getPortletConfig().getPortletName(), request.getPortletMode().toString(),
450                                 (request instanceof ActionRequest ? "action" : "render"),
451                                 request.getRequestedSessionId(), getUsernameForRequest(request),
452                                 processingTime, failureCause));
453             }
454         }
455     }
456
457     /**
458      * Determine the username for the given request.
459      * Default implementation first tries the UserPrincipal.
460      * If that does not exist, then it checks the USER_INFO map.
461      * Can be overridden in subclasses.
462      * @param request current portlet request
463      * @return the username, or null if none
464      * @see javax.portlet.PortletRequest#getUserPrincipal
465      * @see javax.portlet.PortletRequest#getRemoteUser
466      * @see javax.portlet.PortletRequest#USER_INFO
467      * @see #setUserinfoUsernameAttributes(String[])
468      */

469     protected String JavaDoc getUsernameForRequest(PortletRequest request) {
470         // Try the Principal.
471
Principal JavaDoc userPrincipal = request.getUserPrincipal();
472         if (userPrincipal != null) {
473             return userPrincipal.getName();
474         }
475
476         // Try the remote user name.
477
String JavaDoc userName = request.getRemoteUser();
478         if (userName != null) {
479             return userName;
480         }
481         
482         // Try the Portlet USER_INFO map.
483
Map JavaDoc userInfo = (Map JavaDoc) request.getAttribute(PortletRequest.USER_INFO);
484         if (userInfo != null) {
485             for (int i = 0, n = this.userinfoUsernameAttributes.length; i < n; i++) {
486                 userName = (String JavaDoc) userInfo.get(this.userinfoUsernameAttributes[i]);
487                 if (userName != null) {
488                     return userName;
489                 }
490             }
491         }
492         
493         // Nothing worked...
494
return null;
495     }
496
497     /**
498      * Subclasses must implement this method to do the work of render request handling.
499      * <p>The contract is essentially the same as that for the <code>doDispatch</code>
500      * method of GenericPortlet.
501      * <p>This class intercepts calls to ensure that exception handling and
502      * event publication takes place.
503      * @param request current render request
504      * @param response current render response
505      * @throws Exception in case of any kind of processing failure
506      * @see javax.portlet.GenericPortlet#doDispatch
507      */

508     protected abstract void doRenderService(RenderRequest request, RenderResponse response)
509             throws Exception JavaDoc;
510
511     /**
512      * Subclasses must implement this method to do the work of action request handling.
513      * <p>The contract is essentially the same as that for the <code>processAction</code>
514      * method of GenericPortlet.
515      * <p>This class intercepts calls to ensure that exception handling and
516      * event publication takes place.
517      * @param request current action request
518      * @param response current action response
519      * @throws Exception in case of any kind of processing failure
520      * @see javax.portlet.GenericPortlet#processAction
521      */

522     protected abstract void doActionService(ActionRequest request, ActionResponse response)
523             throws Exception JavaDoc;
524
525
526     /**
527      * Close the PortletApplicationContext of this portlet.
528      * @see org.springframework.context.ConfigurableApplicationContext#close
529      */

530     public void destroy() {
531         // Close the portlet application context of this portlet.
532
getPortletContext().log("Closing PortletApplicationContext of Spring FrameworkPortlet '" + getPortletName() + "'");
533         if (this.portletApplicationContext instanceof ConfigurableApplicationContext) {
534             ((ConfigurableApplicationContext) this.portletApplicationContext).close();
535         }
536     }
537
538 }
539
Popular Tags