001: /*
002: * Copyright 2002-2006 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: * http://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: package org.springframework.web.portlet;
017:
018: import java.util.Enumeration;
019: import java.util.HashSet;
020: import java.util.Set;
021:
022: import javax.portlet.GenericPortlet;
023: import javax.portlet.PortletConfig;
024: import javax.portlet.PortletContext;
025: import javax.portlet.PortletException;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029:
030: import org.springframework.beans.BeanWrapper;
031: import org.springframework.beans.BeanWrapperImpl;
032: import org.springframework.beans.BeansException;
033: import org.springframework.beans.MutablePropertyValues;
034: import org.springframework.beans.PropertyValue;
035: import org.springframework.beans.PropertyValues;
036: import org.springframework.core.io.Resource;
037: import org.springframework.core.io.ResourceEditor;
038: import org.springframework.core.io.ResourceLoader;
039: import org.springframework.util.StringUtils;
040: import org.springframework.web.portlet.context.PortletContextResourceLoader;
041:
042: /**
043: * Simple extension of <code>javax.portlet.GenericPortlet</code> that treats
044: * its config parameters as bean properties.
045: *
046: * <p>A very handy superclass for any type of portlet. Type conversion is automatic.
047: * It is also possible for subclasses to specify required properties.
048: *
049: * <p>This portlet leaves request handling to subclasses, inheriting the default
050: * behaviour of GenericPortlet (<code>doDispatch</code>, <code>processAction</code>, etc).
051: *
052: * <p>This portlet superclass has no dependency on a Spring application context,
053: * in contrast to the FrameworkPortlet class which loads its own context.
054: *
055: * @author William G. Thompson, Jr.
056: * @author John A. Lewis
057: * @author Juergen Hoeller
058: * @since 2.0
059: * @see #addRequiredProperty
060: * @see #initPortletBean
061: * @see #doDispatch
062: * @see #processAction
063: * @see FrameworkPortlet
064: */
065: public abstract class GenericPortletBean extends GenericPortlet {
066:
067: /** Logger available to subclasses */
068: protected final Log logger = LogFactory.getLog(getClass());
069:
070: /**
071: * Set of required properties (Strings) that must be supplied as
072: * config parameters to this portlet.
073: */
074: private final Set requiredProperties = new HashSet();
075:
076: /**
077: * Subclasses can invoke this method to specify that this property
078: * (which must match a JavaBean property they expose) is mandatory,
079: * and must be supplied as a config parameter. This method would
080: * normally be called from a subclass constructor.
081: * @param property name of the required property
082: */
083: protected final void addRequiredProperty(String property) {
084: this .requiredProperties.add(property);
085: }
086:
087: /**
088: * Map config parameters onto bean properties of this portlet, and
089: * invoke subclass initialization.
090: * @throws PortletException if bean properties are invalid (or required
091: * properties are missing), or if subclass initialization fails.
092: */
093: public final void init() throws PortletException {
094: if (logger.isInfoEnabled()) {
095: logger.info("Initializing portlet '" + getPortletName()
096: + "'");
097: }
098:
099: // Set bean properties from init parameters.
100: try {
101: PropertyValues pvs = new PortletConfigPropertyValues(
102: getPortletConfig(), this .requiredProperties);
103: BeanWrapper bw = new BeanWrapperImpl(this );
104: ResourceLoader resourceLoader = new PortletContextResourceLoader(
105: getPortletContext());
106: bw.registerCustomEditor(Resource.class, new ResourceEditor(
107: resourceLoader));
108: initBeanWrapper(bw);
109: bw.setPropertyValues(pvs, true);
110: } catch (BeansException ex) {
111: logger.error("Failed to set bean properties on portlet '"
112: + getPortletName() + "'", ex);
113: throw ex;
114: }
115:
116: // let subclasses do whatever initialization they like
117: initPortletBean();
118:
119: if (logger.isInfoEnabled()) {
120: logger.info("Portlet '" + getPortletName()
121: + "' configured successfully");
122: }
123: }
124:
125: /**
126: * Initialize the BeanWrapper for this GenericPortletBean,
127: * possibly with custom editors.
128: * @param bw the BeanWrapper to initialize
129: * @throws BeansException if thrown by BeanWrapper methods
130: * @see org.springframework.beans.BeanWrapper#registerCustomEditor
131: */
132: protected void initBeanWrapper(BeanWrapper bw)
133: throws BeansException {
134: }
135:
136: /**
137: * Overridden method that simply returns <code>null</code> when no
138: * PortletConfig set yet.
139: * @see #getPortletConfig()
140: */
141: public final String getPortletName() {
142: return (getPortletConfig() != null ? getPortletConfig()
143: .getPortletName() : null);
144: }
145:
146: /**
147: * Overridden method that simply returns <code>null</code> when no
148: * PortletConfig set yet.
149: * @see #getPortletConfig()
150: */
151: public final PortletContext getPortletContext() {
152: return (getPortletConfig() != null ? getPortletConfig()
153: .getPortletContext() : null);
154: }
155:
156: /**
157: * Subclasses may override this to perform custom initialization.
158: * All bean properties of this portlet will have been set before this
159: * method is invoked. This default implementation does nothing.
160: * @throws PortletException if subclass initialization fails
161: */
162: protected void initPortletBean() throws PortletException {
163: }
164:
165: /**
166: * PropertyValues implementation created from PortletConfig init parameters.
167: */
168: private static class PortletConfigPropertyValues extends
169: MutablePropertyValues {
170:
171: /**
172: * Create new PortletConfigPropertyValues.
173: * @param config PortletConfig we'll use to take PropertyValues from
174: * @param requiredProperties set of property names we need, where
175: * we can't accept default values
176: * @throws PortletException if any required properties are missing
177: */
178: private PortletConfigPropertyValues(PortletConfig config,
179: Set requiredProperties) throws PortletException {
180:
181: Set missingProps = (requiredProperties != null && !requiredProperties
182: .isEmpty()) ? new HashSet(requiredProperties)
183: : null;
184:
185: Enumeration en = config.getInitParameterNames();
186: while (en.hasMoreElements()) {
187: String property = (String) en.nextElement();
188: Object value = config.getInitParameter(property);
189: addPropertyValue(new PropertyValue(property, value));
190: if (missingProps != null) {
191: missingProps.remove(property);
192: }
193: }
194:
195: // fail if we are still missing properties
196: if (missingProps != null && missingProps.size() > 0) {
197: throw new PortletException(
198: "Initialization from PortletConfig for portlet '"
199: + config.getPortletName()
200: + "' failed; the following required properties were missing: "
201: + StringUtils
202: .collectionToDelimitedString(
203: missingProps, ", "));
204: }
205: }
206: }
207:
208: }
|