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