001: /*
002: * $Id: BeanSelectionProvider.java 560881 2007-07-30 07:05:55Z rgielen $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts2.config;
022:
023: import java.util.Properties;
024: import java.util.StringTokenizer;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.apache.struts2.StrutsConstants;
029: import org.apache.struts2.dispatcher.mapper.ActionMapper;
030: import org.apache.struts2.dispatcher.multipart.MultiPartRequest;
031: import org.apache.struts2.views.freemarker.FreemarkerManager;
032: import org.apache.struts2.views.velocity.VelocityManager;
033:
034: import com.opensymphony.xwork2.ActionProxyFactory;
035: import com.opensymphony.xwork2.ObjectFactory;
036: import com.opensymphony.xwork2.TextProvider;
037: import com.opensymphony.xwork2.config.Configuration;
038: import com.opensymphony.xwork2.config.ConfigurationException;
039: import com.opensymphony.xwork2.config.ConfigurationProvider;
040: import com.opensymphony.xwork2.inject.Container;
041: import com.opensymphony.xwork2.inject.ContainerBuilder;
042: import com.opensymphony.xwork2.inject.Context;
043: import com.opensymphony.xwork2.inject.Factory;
044: import com.opensymphony.xwork2.inject.Scope;
045: import com.opensymphony.xwork2.util.ClassLoaderUtil;
046: import com.opensymphony.xwork2.util.LocalizedTextUtil;
047: import com.opensymphony.xwork2.util.ObjectTypeDeterminer;
048: import com.opensymphony.xwork2.util.XWorkConverter;
049: import com.opensymphony.xwork2.util.location.LocatableProperties;
050:
051: /**
052: * Selects the implementations of key framework extension points, using the loaded
053: * property constants. The implementations are selected from the container builder
054: * using the name defined in its associated property. The default implementation name will
055: * always be "struts".
056: *
057: * <p>
058: * The following is a list of the allowed extension points:
059: *
060: * <!-- START SNIPPET: extensionPoints -->
061: * <table border="1">
062: * <tr>
063: * <th>Type</th>
064: * <th>Property</th>
065: * <th>Scope</th>
066: * <th>Description</th>
067: * </tr>
068: * <tr>
069: * <td>com.opensymphony.xwork2.ObjectFactory</td>
070: * <td>struts.objectFactory</td>
071: * <td>singleton</td>
072: * <td>Creates actions, results, and interceptors</td>
073: * </tr>
074: * <tr>
075: * <td>com.opensymphony.xwork2.ActionProxyFactory</td>
076: * <td>struts.actionProxyFactory</td>
077: * <td>singleton</td>
078: * <td>Creates the ActionProxy</td>
079: * </tr>
080: * <tr>
081: * <td>com.opensymphony.xwork2.util.ObjectTypeDeterminer</td>
082: * <td>struts.objectTypeDeterminer</td>
083: * <td>singleton</td>
084: * <td>Determines what the key and and element class of a Map or Collection should be</td>
085: * </tr>
086: * <tr>
087: * <td>org.apache.struts2.dispatcher.mapper.ActionMapper</td>
088: * <td>struts.mapper.class</td>
089: * <td>singleton</td>
090: * <td>Determines the ActionMapping from a request and a URI from an ActionMapping</td>
091: * </tr>
092: * <tr>
093: * <td>org.apache.struts2.dispatcher.multipart.MultiPartRequest</td>
094: * <td>struts.multipart.parser</td>
095: * <td>per request</td>
096: * <td>Parses a multipart request (file upload)</td>
097: * </tr>
098: * <tr>
099: * <td>org.apache.struts2.views.freemarker.FreemarkerManager</td>
100: * <td>struts.freemarker.manager.classname</td>
101: * <td>singleton</td>
102: * <td>Loads and processes Freemarker templates</td>
103: * </tr>
104: * <tr>
105: * <td>org.apache.struts2.views.velocity.VelocityManager</td>
106: * <td>struts.velocity.manager.classname</td>
107: * <td>singleton</td>
108: * <td>Loads and processes Velocity templates</td>
109: * </tr>
110: * </table>
111: *
112: * <!-- END SNIPPET: extensionPoints -->
113: * </p>
114: * <p>
115: * Implementations are selected using the value of its associated property. That property is
116: * used to determine the implementation by:
117: * </p>
118: * <ol>
119: * <li>Trying to find an existing bean by that name in the container</li>
120: * <li>Trying to find a class by that name, then creating a new bean factory for it</li>
121: * <li>Creating a new delegation bean factory that delegates to the configured ObjectFactory at runtime</li>
122: * </ol>
123: * <p>
124: * Finally, this class overrides certain properties if dev mode is enabled:
125: * </p>
126: * <ul>
127: * <li><code>struts.i18n.reload = true</code></li>
128: * <li><code>struts.configuration.xml.reload = true</code></li>
129: * </ul>
130: */
131: public class BeanSelectionProvider implements ConfigurationProvider {
132: public static final String DEFAULT_BEAN_NAME = "struts";
133: private static final Log LOG = LogFactory
134: .getLog(BeanSelectionProvider.class);
135:
136: public void destroy() {
137: // NO-OP
138: }
139:
140: public void loadPackages() throws ConfigurationException {
141: // NO-OP
142: }
143:
144: public void init(Configuration configuration)
145: throws ConfigurationException {
146: // NO-OP
147:
148: }
149:
150: public boolean needsReload() {
151: return false;
152: }
153:
154: public void register(ContainerBuilder builder,
155: LocatableProperties props) {
156: alias(ObjectFactory.class,
157: StrutsConstants.STRUTS_OBJECTFACTORY, builder, props);
158: alias(XWorkConverter.class,
159: StrutsConstants.STRUTS_XWORKCONVERTER, builder, props);
160: alias(TextProvider.class,
161: StrutsConstants.STRUTS_XWORKTEXTPROVIDER, builder,
162: props);
163: alias(ActionProxyFactory.class,
164: StrutsConstants.STRUTS_ACTIONPROXYFACTORY, builder,
165: props);
166: alias(ObjectTypeDeterminer.class,
167: StrutsConstants.STRUTS_OBJECTTYPEDETERMINER, builder,
168: props);
169: alias(ActionMapper.class, StrutsConstants.STRUTS_MAPPER_CLASS,
170: builder, props);
171: alias(MultiPartRequest.class,
172: StrutsConstants.STRUTS_MULTIPART_PARSER, builder,
173: props, Scope.DEFAULT);
174: alias(FreemarkerManager.class,
175: StrutsConstants.STRUTS_FREEMARKER_MANAGER_CLASSNAME,
176: builder, props);
177: alias(VelocityManager.class,
178: StrutsConstants.STRUTS_VELOCITY_MANAGER_CLASSNAME,
179: builder, props);
180:
181: if ("true".equalsIgnoreCase(props
182: .getProperty(StrutsConstants.STRUTS_DEVMODE))) {
183: props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD,
184: "true");
185: props.setProperty(
186: StrutsConstants.STRUTS_CONFIGURATION_XML_RELOAD,
187: "true");
188: props.setProperty(
189: StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE,
190: "false");
191: // Convert struts properties into ones that xwork expects
192: props.setProperty("devMode", "true");
193: } else {
194: props.setProperty("devMode", "false");
195: }
196:
197: // TODO: This should be moved to XWork after 2.0.4
198: // struts.custom.i18n.resources
199:
200: LocalizedTextUtil
201: .addDefaultResourceBundle("org/apache/struts2/struts-messages");
202:
203: String bundles = props
204: .getProperty(StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES);
205: if (bundles != null && bundles.length() > 0) {
206: StringTokenizer customBundles = new StringTokenizer(
207: props
208: .getProperty(StrutsConstants.STRUTS_CUSTOM_I18N_RESOURCES),
209: ", ");
210:
211: while (customBundles.hasMoreTokens()) {
212: String name = customBundles.nextToken();
213: try {
214: LOG.info("Loading global messages from " + name);
215: LocalizedTextUtil.addDefaultResourceBundle(name);
216: } catch (Exception e) {
217: LOG.error("Could not find messages file " + name
218: + ".properties. Skipping");
219: }
220: }
221: }
222: }
223:
224: void alias(Class type, String key, ContainerBuilder builder,
225: Properties props) {
226: alias(type, key, builder, props, Scope.SINGLETON);
227: }
228:
229: void alias(Class type, String key, ContainerBuilder builder,
230: Properties props, Scope scope) {
231: if (!builder.contains(type)) {
232: String foundName = props
233: .getProperty(key, DEFAULT_BEAN_NAME);
234: if (builder.contains(type, foundName)) {
235: if (LOG.isDebugEnabled()) {
236: LOG.info("Choosing bean (" + foundName + ") for "
237: + type);
238: }
239: builder.alias(type, foundName, Container.DEFAULT_NAME);
240: } else {
241: try {
242: Class cls = ClassLoaderUtil.loadClass(foundName,
243: this .getClass());
244: if (LOG.isDebugEnabled()) {
245: LOG.debug("Choosing bean (" + cls + ") for "
246: + type);
247: }
248: builder.factory(type, cls, scope);
249: } catch (ClassNotFoundException ex) {
250: // Perhaps a spring bean id, so we'll delegate to the object factory at runtime
251: if (LOG.isDebugEnabled()) {
252: LOG
253: .debug("Choosing bean ("
254: + foundName
255: + ") for "
256: + type
257: + " to be loaded from the ObjectFactory");
258: }
259: if (DEFAULT_BEAN_NAME.equals(foundName)) {
260: // Probably an optional bean, will ignore
261: } else {
262: if (ObjectFactory.class != type) {
263: builder.factory(type,
264: new ObjectFactoryDelegateFactory(
265: foundName, type), scope);
266: } else {
267: throw new ConfigurationException(
268: "Cannot locate the chosen ObjectFactory implementation: "
269: + foundName);
270: }
271: }
272: }
273: }
274: } else {
275: LOG.warn("Unable to alias bean type " + type
276: + ", default mapping already assigned.");
277: }
278: }
279:
280: class ObjectFactoryDelegateFactory implements Factory {
281: String name;
282: Class type;
283:
284: ObjectFactoryDelegateFactory(String name, Class type) {
285: this .name = name;
286: this .type = type;
287: }
288:
289: public Object create(Context context) throws Exception {
290: ObjectFactory objFactory = context.getContainer()
291: .getInstance(ObjectFactory.class);
292: try {
293: return objFactory.buildBean(name, null, true);
294: } catch (ClassNotFoundException ex) {
295: throw new ConfigurationException("Unable to load bean "
296: + type.getName() + " (" + name + ")");
297: }
298: }
299: }
300: }
|