1 /*
2 * Copyright (c) 2002-2006 by OpenSymphony
3 * All rights reserved.
4 */
5 package com.opensymphony.xwork2;
6
7 import com.opensymphony.xwork2.config.ConfigurationException;
8 import com.opensymphony.xwork2.config.entities.ActionConfig;
9 import com.opensymphony.xwork2.config.entities.InterceptorConfig;
10 import com.opensymphony.xwork2.config.entities.ResultConfig;
11 import com.opensymphony.xwork2.inject.Container;
12 import com.opensymphony.xwork2.inject.Inject;
13 import com.opensymphony.xwork2.interceptor.Interceptor;
14 import com.opensymphony.xwork2.util.ClassLoaderUtil;
15 import com.opensymphony.xwork2.util.logging.Logger;
16 import com.opensymphony.xwork2.util.logging.LoggerFactory;
17 import com.opensymphony.xwork2.util.reflection.ReflectionException;
18 import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
19 import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
20 import com.opensymphony.xwork2.validator.Validator;
21
22 import java.io.Serializable;
23 import java.util.HashMap;
24 import java.util.Map;
25
26
27 /**
28 * ObjectFactory is responsible for building the core framework objects. Users may register their
29 * own implementation of the ObjectFactory to control instantiation of these Objects.
30 * <p/>
31 * This default implementation uses the {@link #buildBean(Class,java.util.Map) buildBean}
32 * method to create all classes (interceptors, actions, results, etc).
33 * <p/>
34 *
35 * @author Jason Carreira
36 */
37 public class ObjectFactory implements Serializable {
38 private static final Logger LOG = LoggerFactory.getLogger(ObjectFactory.class);
39
40 private transient ClassLoader ccl;
41 private Container container;
42 protected ReflectionProvider reflectionProvider;
43
44 @Inject(value="objectFactory.classloader", required=false)
45 public void setClassLoader(ClassLoader cl) {
46 this.ccl = cl;
47 }
48
49 @Inject
50 public void setReflectionProvider(ReflectionProvider prov) {
51 this.reflectionProvider = prov;
52 }
53
54 public ObjectFactory() {
55 }
56
57 public ObjectFactory(ReflectionProvider prov) {
58 this.reflectionProvider = prov;
59 }
60
61 @Inject
62 public void setContainer(Container container) {
63 this.container = container;
64 }
65
66 /**
67 * @deprecated Since 2.1
68 */
69 @Deprecated public static ObjectFactory getObjectFactory() {
70 return ActionContext.getContext().getContainer().getInstance(ObjectFactory.class);
71 }
72
73 /**
74 * Allows for ObjectFactory implementations that support
75 * Actions without no-arg constructors.
76 *
77 * @return true if no-arg constructor is required, false otherwise
78 */
79 public boolean isNoArgConstructorRequired() {
80 return true;
81 }
82
83 /**
84 * Utility method to obtain the class matched to className. Caches look ups so that subsequent
85 * lookups will be faster.
86 *
87 * @param className The fully qualified name of the class to return
88 * @return The class itself
89 * @throws ClassNotFoundException
90 */
91 public Class getClassInstance(String className) throws ClassNotFoundException {
92 if (ccl != null) {
93 return ccl.loadClass(className);
94 }
95
96 return ClassLoaderUtil.loadClass(className, this.getClass());
97 }
98
99 /**
100 * Build an instance of the action class to handle a particular request (eg. web request)
101 * @param actionName the name the action configuration is set up with in the configuration
102 * @param namespace the namespace the action is configured in
103 * @param config the action configuration found in the config for the actionName / namespace
104 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
105 * @return instance of the action class to handle a web request
106 * @throws Exception
107 */
108 public Object buildAction(String actionName, String namespace, ActionConfig config, Map<String, Object> extraContext) throws Exception {
109 return buildBean(config.getClassName(), extraContext);
110 }
111
112 /**
113 * Build a generic Java object of the given type.
114 *
115 * @param clazz the type of Object to build
116 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
117 */
118 public Object buildBean(Class clazz, Map<String, Object> extraContext) throws Exception {
119 return clazz.newInstance();
120 }
121
122 /**
123 * @param obj
124 */
125 protected Object injectInternalBeans(Object obj) {
126 if (obj != null && container != null) {
127 container.inject(obj);
128 }
129 return obj;
130 }
131
132 /**
133 * Build a generic Java object of the given type.
134 *
135 * @param className the type of Object to build
136 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
137 */
138 public Object buildBean(String className, Map<String, Object> extraContext) throws Exception {
139 return buildBean(className, extraContext, true);
140 }
141
142 /**
143 * Build a generic Java object of the given type.
144 *
145 * @param className the type of Object to build
146 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
147 */
148 public Object buildBean(String className, Map<String, Object> extraContext, boolean injectInternal) throws Exception {
149 Class clazz = getClassInstance(className);
150 Object obj = buildBean(clazz, extraContext);
151 if (injectInternal) {
152 injectInternalBeans(obj);
153 }
154 return obj;
155 }
156
157 /**
158 * Builds an Interceptor from the InterceptorConfig and the Map of
159 * parameters from the interceptor reference. Implementations of this method
160 * should ensure that the Interceptor is parameterized with both the
161 * parameters from the Interceptor config and the interceptor ref Map (the
162 * interceptor ref params take precedence), and that the Interceptor.init()
163 * method is called on the Interceptor instance before it is returned.
164 *
165 * @param interceptorConfig the InterceptorConfig from the configuration
166 * @param interceptorRefParams a Map of params provided in the Interceptor reference in the
167 * Action mapping or InterceptorStack definition
168 */
169 public Interceptor buildInterceptor(InterceptorConfig interceptorConfig, Map<String, String> interceptorRefParams) throws ConfigurationException {
170 String interceptorClassName = interceptorConfig.getClassName();
171 Map<String, String> thisInterceptorClassParams = interceptorConfig.getParams();
172 Map<String, String> params = (thisInterceptorClassParams == null) ? new HashMap<String, String>() : new HashMap<String, String>(thisInterceptorClassParams);
173 params.putAll(interceptorRefParams);
174
175 String message;
176 Throwable cause;
177
178 try {
179 // interceptor instances are long-lived and used across user sessions, so don't try to pass in any extra context
180 Interceptor interceptor = (Interceptor) buildBean(interceptorClassName, null);
181 reflectionProvider.setProperties(params, interceptor);
182 interceptor.init();
183
184 return interceptor;
185 } catch (InstantiationException e) {
186 cause = e;
187 message = "Unable to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
188 } catch (IllegalAccessException e) {
189 cause = e;
190 message = "IllegalAccessException while attempting to instantiate an instance of Interceptor class [" + interceptorClassName + "].";
191 } catch (ClassCastException e) {
192 cause = e;
193 message = "Class [" + interceptorClassName + "] does not implement com.opensymphony.xwork2.interceptor.Interceptor";
194 } catch (Exception e) {
195 cause = e;
196 message = "Caught Exception while registering Interceptor class " + interceptorClassName;
197 } catch (NoClassDefFoundError e) {
198 cause = e;
199 message = "Could not load class " + interceptorClassName + ". Perhaps it exists but certain dependencies are not available?";
200 }
201
202 throw new ConfigurationException(message, cause, interceptorConfig);
203 }
204
205 /**
206 * Build a Result using the type in the ResultConfig and set the parameters in the ResultConfig.
207 *
208 * @param resultConfig the ResultConfig found for the action with the result code returned
209 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
210 */
211 public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {
212 String resultClassName = resultConfig.getClassName();
213 Result result = null;
214
215 if (resultClassName != null) {
216 result = (Result) buildBean(resultClassName, extraContext);
217 Map<String, String> params = resultConfig.getParams();
218 if (params != null) {
219 for (Map.Entry<String, String> paramEntry : params.entrySet()) {
220 try {
221 reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);
222 } catch (ReflectionException ex) {
223 if (result instanceof ReflectionExceptionHandler) {
224 ((ReflectionExceptionHandler) result).handle(ex);
225 }
226 }
227 }
228 }
229 }
230
231 return result;
232 }
233
234 /**
235 * Build a Validator of the given type and set the parameters on it
236 *
237 * @param className the type of Validator to build
238 * @param params property name -> value Map to set onto the Validator instance
239 * @param extraContext a Map of extra context which uses the same keys as the {@link com.opensymphony.xwork2.ActionContext}
240 */
241 public Validator buildValidator(String className, Map<String, String> params, Map<String, Object> extraContext) throws Exception {
242 Validator validator = (Validator) buildBean(className, null);
243 reflectionProvider.setProperties(params, validator);
244
245 return validator;
246 }
247
248 static class ContinuationsClassLoader extends ClassLoader {
249
250 }
251 }