1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.fatwire.gst.foundation.controller.action.support;
17
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.InvocationTargetException;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.commons.logging.Log;
28
29 import COM.FutureTense.Interfaces.ICS;
30
31 import com.fatwire.gst.foundation.controller.action.Factory;
32 import com.fatwire.gst.foundation.controller.annotation.ServiceProducer;
33 import com.fatwire.gst.foundation.facade.logging.LogUtil;
34
35
36
37
38
39
40
41
42
43
44
45 public abstract class BaseFactory implements Factory {
46
47 protected static final Log LOG = LogUtil.getLog(IcsBackedObjectFactoryTemplate.class);
48
49 protected final ICS ics;
50
51 private final Map<String, Object> objectCache = new HashMap<String, Object>();
52 private Factory[] roots = new Factory[0];;
53
54 public BaseFactory(ICS ics) {
55 super();
56 this.ics = ics;
57
58 }
59
60 public BaseFactory(ICS ics, Factory... roots) {
61 super();
62 this.ics = ics;
63 if (roots != null)
64 this.roots = roots;
65 }
66
67 @Override
68 public final <T> T getObject(final String name, final Class<T> fieldType) {
69
70 T o;
71 try {
72 o = locate(name, fieldType);
73 if (o == null) {
74 for (Factory root : roots) {
75 o = root.getObject(name, fieldType);
76 if (o != null)
77 return o;
78
79 }
80
81
82 if (roots.length == 0)
83 o = ctorStrategy(name, fieldType);
84 }
85 } catch (InvocationTargetException e) {
86 throw new RuntimeException(e.getTargetException());
87 }
88 return o;
89 }
90
91
92
93
94
95
96
97
98
99 @SuppressWarnings("unchecked")
100 protected <T> T locate(final String askedName, final Class<T> c) throws InvocationTargetException {
101 if (ICS.class.isAssignableFrom(c)) {
102 return (T) ics;
103 }
104 if (c.isArray()) {
105 throw new IllegalArgumentException("Arrays are not supported");
106 }
107 final String name = StringUtils.isNotBlank(askedName) ? askedName : c.getSimpleName();
108
109 if (StringUtils.isBlank(name)) {
110 return null;
111 }
112
113 Object o = objectCache.get(name);
114 if (o != null && !c.isAssignableFrom(o.getClass()))
115 throw new IllegalStateException("Name conflict: '" + name + "' is in cache and is of type '"
116 + o.getClass() + "' but a '" + c.getName()
117 + "' was asked for. Please check your factories for naming conflicts.");
118 if (o == null) {
119 o = namedAnnotationStrategy(name, c);
120 }
121 if (o == null) {
122 o = unnamedAnnotationStrategy(name, c);
123 }
124
125 if (o == null) {
126 o = reflectionStrategy(name, c);
127 }
128
129 return (T) o;
130 }
131
132
133
134
135
136
137
138
139
140 protected Class<?>[] factoryClasses(ICS ics) {
141 return new Class[] { getClass() };
142 }
143
144
145
146
147
148
149
150
151
152
153 protected <T> T namedAnnotationStrategy(String name, Class<T> c) throws InvocationTargetException {
154
155 for (Class<?> reflectionClass : factoryClasses(ics)) {
156 for (Method m : reflectionClass.getMethods()) {
157 if (m.isAnnotationPresent(ServiceProducer.class)) {
158 if (m.getReturnType().isAssignableFrom(c)) {
159 String n = m.getAnnotation(ServiceProducer.class).name();
160 if (name.equals(n)) {
161 return createFromMethod(name, c, m);
162 }
163 }
164 }
165
166 }
167 }
168 return null;
169 }
170
171
172
173
174
175
176
177
178
179
180 protected <T> T unnamedAnnotationStrategy(String name, Class<T> c) throws InvocationTargetException {
181
182 for (Class<?> reflectionClass : factoryClasses(ics)) {
183 for (Method m : reflectionClass.getMethods()) {
184 if (m.isAnnotationPresent(ServiceProducer.class)) {
185 if (m.getReturnType().isAssignableFrom(c)) {
186 String n = m.getAnnotation(ServiceProducer.class).name();
187 if (StringUtils.isBlank(n)) {
188 return createFromMethod(name, c, m);
189 }
190 }
191 }
192
193 }
194 }
195 return null;
196 }
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 protected <T> T reflectionStrategy(String name, Class<T> c) throws InvocationTargetException {
220
221 for (Class<?> reflectionClass : factoryClasses(ics)) {
222
223 for (Method m : reflectionClass.getMethods()) {
224 if (m.getName().equals("create" + c.getSimpleName())) {
225 if (m.getReturnType().isAssignableFrom(c)) {
226 return createFromMethod(name, c, m);
227 }
228 }
229 }
230 }
231 return null;
232 }
233
234
235
236
237
238
239
240
241 @SuppressWarnings("unchecked")
242 protected <T> T createFromMethod(String name, Class<T> c, Method m) throws InvocationTargetException {
243 Object o = null;
244 if (LOG.isTraceEnabled()) {
245 LOG.trace("trying to create a " + c.getName() + " object with name " + name + " from method "
246 + m.toGenericString());
247 }
248
249 if (m.getReturnType().isAssignableFrom(c)) {
250 Object from = null;
251
252 if (!Modifier.isStatic(m.getModifiers())) {
253 Class<?> reflectionClass = m.getDeclaringClass();
254
255 if (reflectionClass.isAssignableFrom(getClass())) {
256 from = this;
257
258 } else {
259 Constructor<?> ctor;
260
261 try {
262 ctor = reflectionClass.getConstructor(ICS.class, Factory.class);
263 if (Modifier.isPublic(ctor.getModifiers())) {
264 from = ctor.newInstance(ics, this);
265 } else {
266 throw new NoSuchMethodExceptionRuntimeException(reflectionClass.getName()
267 + " does not have a public (ICS,Factory) constructor.");
268 }
269 } catch (NoSuchMethodException e) {
270 throw new NoSuchMethodExceptionRuntimeException(reflectionClass.getName()
271 + " should have a public constructor accepting a ICS and Factory.");
272 } catch (InstantiationException e) {
273 LOG.error(e.getMessage());
274 } catch (IllegalArgumentException e) {
275 LOG.error("Huh, Can't happen, the arguments are checked: " + m.toString() + ", "
276 + e.getMessage());
277 } catch (IllegalAccessException e) {
278 LOG.error("Huh, Can't happen, the modifier is checked for public: " + m.toString() + ", "
279 + e.getMessage());
280 }
281 }
282
283 }
284 if (m.getParameterTypes().length == 2 && m.getParameterTypes()[0].isAssignableFrom(ICS.class)
285 && m.getParameterTypes()[1].isAssignableFrom(Factory.class)) {
286 o = invokeCreateMethod(m, from, name, ics, this);
287 } else if (m.getParameterTypes().length == 1 && m.getParameterTypes()[0].isAssignableFrom(ICS.class)) {
288 o = invokeCreateMethod(m, from, name, ics);
289 } else if (m.getParameterTypes().length == 0) {
290 o = invokeCreateMethod(m, from, name);
291 }
292 if (shouldCache(m))
293 objectCache.put(name, o);
294
295 }
296 return (T) o;
297 }
298
299
300
301
302
303
304
305
306
307 protected Object invokeCreateMethod(Method m, Object from, String name, Object... arguments)
308 throws InvocationTargetException {
309 try {
310 return m.invoke(from, arguments);
311 } catch (IllegalArgumentException e) {
312 LOG.error("Huh, Can't happen, the arguments are checked: " + m.toString() + ", " + e.getMessage());
313 } catch (IllegalAccessException e) {
314 LOG.error("Huh, Can't happen, the modifier is checked for public: " + m.toString() + ", " + e.getMessage());
315 }
316 return null;
317 }
318
319 protected boolean shouldCache(Method m) {
320 boolean r = false;
321 if (m.isAnnotationPresent(ServiceProducer.class)) {
322 ServiceProducer annon = m.getAnnotation(ServiceProducer.class);
323 r = annon.cache();
324 }
325 return r;
326 }
327
328
329
330
331 protected void throwRuntimeException(InvocationTargetException e) {
332 Throwable t = e.getTargetException();
333 if (t == null) {
334 throw new RuntimeException(e);
335 } else if (t instanceof RuntimeException) {
336 throw (RuntimeException) t;
337 } else {
338 throw new RuntimeException(t);
339 }
340 }
341
342
343
344
345
346
347
348 protected <T> T ctorStrategy(final String name, final Class<T> c) throws InvocationTargetException {
349 T o = null;
350 try {
351 if (c.isInterface() || Modifier.isAbstract(c.getModifiers())) {
352 if (LOG.isDebugEnabled())
353 LOG.debug("Could not create a " + c.getName() + " via a Template method. The class '"
354 + c.getName()
355 + "' is an interface or abstract class, giving up as a class cannot be constructed.");
356 return null;
357 }
358
359 if (LOG.isDebugEnabled())
360 LOG.debug("Could not create a " + c.getName() + " via a Template method, trying via constructor.");
361 final Constructor<T> constr = c.getConstructor(ICS.class);
362 o = constr.newInstance(ics);
363 } catch (final NoSuchMethodException e1) {
364 LOG.debug("Could not create a " + c.getName() + " via a constructor method.");
365 } catch (IllegalArgumentException e) {
366 LOG.debug("Could not create a " + c.getName() + " via a constructor method.");
367 } catch (InstantiationException e) {
368 LOG.debug("Could not create a " + c.getName() + " via a constructor method.");
369 } catch (IllegalAccessException e) {
370 LOG.debug("Could not create a " + c.getName() + " via a constructor method.");
371 }
372 return o;
373 }
374
375 @ServiceProducer(cache = false)
376 public ICS createICS(final ICS ics) {
377 return ics;
378 }
379
380 @Override
381 public String toString() {
382 return "BaseFactory [roots=" + Arrays.toString(roots) + "]";
383 }
384
385 }