KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > jodd > madvoc > config > AutomagicMadvocConfig


1 // Copyright (c) 2003-2007, Jodd Team (jodd.sf.net). All Rights Reserved.
2

3 package jodd.madvoc.config;
4
5 import jodd.introspector.ClassDescriptor;
6 import jodd.introspector.DefaultIntrospector;
7 import jodd.io.findfile.FindClass;
8 import jodd.madvoc.ActionConfig;
9 import jodd.madvoc.MadvocException;
10 import jodd.madvoc.WebApplication;
11 import jodd.madvoc.result.ActionResult;
12 import jodd.madvoc.interceptor.ActionInterceptor;
13 import jodd.madvoc.interceptor.ActionInterceptorStack;
14 import jodd.madvoc.interceptor.DefaultMadvocInterceptors;
15 import jodd.madvoc.meta.Action;
16 import jodd.madvoc.meta.InterceptedBy;
17 import jodd.madvoc.meta.MadvocAction;
18 import jodd.util.*;
19
20 import java.lang.reflect.Method JavaDoc;
21 import java.util.*;
22 import java.net.URL JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.io.InputStream JavaDoc;
25
26 /**
27  * Default Madvoc configurator uses automagic to configure {@link WebApplication}.
28  * It searches the class path for all classes which names ends with Action, Intreceptor,
29  * and Configuration prefixes. Each such class will be loaded and introspected for
30  * corresponding annotation.
31  * <p>
32  * Action class is scanned for the {@link MadvocAction}. All public methods are candidates for the
33  * actions. If no {@link Action} is specified, than all public methods becomes registered;
34  * otherwise, registration occurs only for annotated methods.
35  * <p>
36  * Interceptors may be defined both for all actions on class level or for the action itself.
37  * Interceptors are singletons in the web application context.
38  * <p>
39  * Naming conventions for action path.<br>
40  * Action path is commonly build from class annotation {@link MadvocAction}
41  * and {@link Action} method annotation.
42  * Class annotation is always the absolute one, i.e. will always start from '/'.
43  * If class annotation doesn't end with '/', a dot ('.') will be appended at the end.
44  * It is not required to explicitly specify class annotation value: if not set, action path
45  * will be equals to the class name with uncapitalized first letter and removed suffix.
46  * Additionaly, name jocker may be used to templatize the value.
47  * <p>
48  * When method annotation value is not specified, it is implicitly builded from method name.
49  * If annotation value starts with '/' then class annotation is simply ignored.
50  * There are few jockers that can be used for templatizing the method annotation:
51  * named jocker - will be replaces with method name; prefix jocker - will be replaced with
52  * class annotation value.
53  * <p>
54  * Configuration classes are loaded during the search, but they are invoked after the classpath
55  * scanning ends.
56  * <p>
57  * The latest step is applying the default configurations, such as default interceptors to all
58  * registered actions.
59  */

60 public class AutomagicMadvocConfig extends FindClass implements MadvocConfig {
61
62     protected WebApplication webapp;
63     protected String JavaDoc extension;
64     protected String JavaDoc actionClassSuffix;
65     protected String JavaDoc configClassSuffix;
66     protected String JavaDoc resultClassSuffix;
67     protected String JavaDoc jockerName;
68     protected String JavaDoc jockerPrefix;
69     protected long elapsed;
70
71     private Map<Class JavaDoc<? extends ActionInterceptor>, ActionInterceptor> interceptorsPool = new HashMap<Class JavaDoc<? extends ActionInterceptor>, ActionInterceptor>();
72     private List<MadvocConfig> externalConfigs = new ArrayList<MadvocConfig>();
73
74     public AutomagicMadvocConfig() {
75         super();
76         extension = "html";
77         actionClassSuffix = "Action";
78         configClassSuffix = "Config";
79         resultClassSuffix = "Result";
80         elapsed = 0;
81         jockerName = "{N}";
82         jockerPrefix = "{P}";
83     }
84
85     /**
86      * Configures web application from system classpath
87      * @see #configure(jodd.madvoc.WebApplication, java.net.URL[])
88      */

89     public void configure(WebApplication webapp) {
90         configure(webapp, ClassLoaderUtil.getFullClassPath(AutomagicMadvocConfig.class));
91     }
92
93     /**
94      * Configures web application from specified classpath. The whole process is done in the following steps:
95      * <ol>
96      * <li>scanning web application classpath</li>
97      * <li>invoking external configurations, if exist</li>
98      * <li>applying defaults</li>
99      * </ol>
100      * @see #configure(jodd.madvoc.WebApplication)
101      */

102     public void configure(WebApplication webapp, URL JavaDoc[] classpath) {
103         this.webapp = webapp;
104         elapsed = System.currentTimeMillis();
105
106         for (URL JavaDoc path : classpath) {
107             try {
108                 scanUrl(path);
109             } catch (IOException JavaDoc ioex) {
110                 throw new MadvocException("Unable to scan classpath: '" + path + "'.", ioex);
111             }
112         }
113         for (MadvocConfig madvocConfig : externalConfigs) {
114             madvocConfig.configure(webapp);
115         }
116
117         // apply default interceptors
118
ActionInterceptor[] defaultIntercetors = buildInterceptors(webapp.getDefaultInterceptors());
119         if (defaultIntercetors != null) {
120             Map<String JavaDoc, ActionConfig> map = webapp.getAllActionConfigs();
121             for (ActionConfig cfg : map.values()) {
122                 if (cfg.interceptors == null) {
123                     cfg.interceptors = defaultIntercetors;
124                 } else {
125                     // apply default interceptors
126
if (defaultIntercetors.length > 0) {
127                         for (int i = 0; i < cfg.interceptors.length; i++) {
128                             ActionInterceptor interceptor = cfg.interceptors[i];
129                             if (interceptor instanceof DefaultMadvocInterceptors) {
130                                 cfg.interceptors = (ActionInterceptor[]) ArraysUtil.insertAt(cfg.interceptors, defaultIntercetors, i, ActionInterceptor.class);
131                             }
132                         }
133                     }
134                 }
135             }
136         }
137
138
139         elapsed = System.currentTimeMillis() - elapsed;
140         System.out.println("Madvoc configured in " + elapsed + "ms");
141     }
142
143
144     /**
145      * Parses class name that matches madvoc-related names.
146      */

147     @Override JavaDoc
148     protected void onClassName(String JavaDoc className, InputStream JavaDoc inputStream) {
149         if (StringUtil.endsWithIgnoreCase(className, actionClassSuffix) == true) {
150             onActionClass(className);
151         } else if (StringUtil.endsWithIgnoreCase(className, configClassSuffix) == true) {
152             onConfigClass(className);
153         } else if (StringUtil.endsWithIgnoreCase(className, resultClassSuffix) == true) {
154             onResultClass(className);
155         }
156     }
157
158     // ---------------------------------------------------------------- handlers
159

160     /**
161      * Builds action config on founded action class.
162      * Action classes are annotated with {@link jodd.madvoc.meta.Action} annotation.
163      */

164     @SuppressWarnings JavaDoc({"unchecked", "NonConstantStringShouldBeStringBuffer"})
165     protected void onActionClass(String JavaDoc className) {
166         Class JavaDoc actionClass;
167         try {
168             actionClass = ClassLoaderUtil.loadClass(className, this.getClass());
169         } catch (ClassNotFoundException JavaDoc cnfex) {
170             throw new MadvocException("Unable to load Madvoc action class '" + className + "'.", cnfex);
171         }
172
173         // class annotation: madvoc action
174
MadvocAction madvocActionAnnotation = (MadvocAction) actionClass.getAnnotation(MadvocAction.class);
175         if (madvocActionAnnotation == null) {
176             return;
177         }
178         String JavaDoc classActionPath = madvocActionAnnotation.value().trim();
179         if (classActionPath.length() == 0) {
180             classActionPath = parseActionPath(actionClass.getName());
181         } else {
182             classActionPath = StringUtil.replace(classActionPath, jockerName, parseActionPath(actionClass.getName()));
183         }
184         if (classActionPath.charAt(0) != '/') {
185             classActionPath = '/' + classActionPath;
186         }
187
188
189         // class annotation: interceptedby
190
ActionInterceptor[] classInterceptors = null;
191         InterceptedBy interceptedBy = (InterceptedBy) actionClass.getAnnotation(InterceptedBy.class);
192         if (interceptedBy != null) {
193             classInterceptors = buildInterceptors(interceptedBy.value());
194         }
195
196         ClassDescriptor cd = DefaultIntrospector.lookup(actionClass);
197         Method JavaDoc[] allPublicMethods = cd.getAllMethods();
198         for (Method JavaDoc method : allPublicMethods) {
199             if (ReflectUtil.isBeanProperty(method) == true) {
200                 continue;
201             }
202             // method annotation: action
203
String JavaDoc actionPath = classActionPath;
204             if (actionPath.endsWith("/") == false) {
205                 actionPath += '.';
206             }
207             Action methodAnnotation = method.getAnnotation(Action.class);
208             if ((methodAnnotation != null) && (methodAnnotation.value().length() > 0)) {
209                 String JavaDoc value = methodAnnotation.value().trim();
210                 if (value.equals(Action.IGNORE) == true) {
211                     continue;
212                 }
213                 value = StringUtil.replace(value, jockerName, method.getName());
214                 value = StringUtil.replace(value, jockerPrefix, classActionPath);
215                 if (value.charAt(0) == '/') {
216                     actionPath = value;
217                 } else {
218                     actionPath += value;
219                 }
220             } else {
221                 actionPath += method.getName() + '.' + extension;
222             }
223
224             // method annotation: interceptedby
225
ActionInterceptor[] actionInterceptors = classInterceptors;
226             interceptedBy = (InterceptedBy) actionClass.getAnnotation(InterceptedBy.class);
227             if (interceptedBy != null) {
228                 actionInterceptors = buildInterceptors(interceptedBy.value());
229             }
230
231             // action config
232
ActionConfig actionConfig = new ActionConfig(actionPath, actionClass, method, actionInterceptors);
233             webapp.register(actionConfig);
234         }
235     }
236
237
238     /**
239      * Creates madvoc configuration from founded {@link MadvocConfig} instance.
240      * All configurations will be invoked at the end of automagic process.
241      */

242     protected void onConfigClass(String JavaDoc className) {
243         Class JavaDoc configClass;
244         try {
245             configClass = ClassLoaderUtil.loadClass(className, this.getClass());
246         } catch (ClassNotFoundException JavaDoc cnfex) {
247             throw new MadvocException("Unable to load Madvoc config class '" + className + "'.", cnfex);
248         }
249
250         if ((configClass == this.getClass()) || (configClass == MadvocConfig.class)) {
251             return;
252         }
253         if (ReflectUtil.isSubclass(configClass, MadvocConfig.class) == true) {
254             try {
255                 externalConfigs.add((MadvocConfig) configClass.newInstance());
256             } catch (Exception JavaDoc ex) {
257                 throw new MadvocException("Unable to use Madvoc configuration class '" + className + "'.", ex);
258             }
259         }
260     }
261
262
263     /**
264      * Loads madvoc result from founded {@link jodd.madvoc.result.ActionResult} instance.
265      */

266     protected void onResultClass(String JavaDoc className) {
267         Class JavaDoc resultClass;
268         try {
269             resultClass = ClassLoaderUtil.loadClass(className, this.getClass());
270         } catch (ClassNotFoundException JavaDoc cnfex) {
271             throw new MadvocException("Unable to load Madvoc result class '" + className + "'.", cnfex);
272         }
273
274         if (resultClass == ActionResult.class) {
275             return;
276         }
277         if (ReflectUtil.isSubclass(resultClass, ActionResult.class) == true) {
278             try {
279                 ActionResult result = (ActionResult) resultClass.newInstance();
280                 webapp.register(result);
281             } catch (Exception JavaDoc ex) {
282                 throw new MadvocException("Unable to use Madvoc result class '" + className + "'.");
283             }
284         }
285     }
286
287     // ---------------------------------------------------------------- interceptors
288

289
290     /**
291      * Builds interceptors from their classes. Interceptors are singletons for
292      * web application, therefore internal map is used for storing already
293      * builded interceptor instances.
294      */

295     @SuppressWarnings JavaDoc({"unchecked"})
296     protected ActionInterceptor[] buildInterceptors(Class JavaDoc<? extends ActionInterceptor>[] classes) {
297         ArrayList<ActionInterceptor> actionInterceptors = new ArrayList<ActionInterceptor>(classes.length);
298         for (int i = 0; i < classes.length; i++) {
299             Class JavaDoc<? extends ActionInterceptor> actionInterceptorClass = classes[i];
300             if (ReflectUtil.isSubclass(actionInterceptorClass, ActionInterceptorStack.class) == true) {
301                 ActionInterceptorStack stack = (ActionInterceptorStack) webapp.buildInterceptor(actionInterceptorClass);
302                 Class JavaDoc<? extends ActionInterceptor>[] stackInterceptors = stack.getInterceptors();
303                 classes = (Class JavaDoc<? extends ActionInterceptor>[]) ArraysUtil.insert(classes, stackInterceptors, i + 1, Class JavaDoc.class);
304                 continue;
305             }
306             ActionInterceptor interceptor = interceptorsPool.get(actionInterceptorClass);
307             if (interceptor == null) {
308                 interceptor = webapp.buildInterceptor(actionInterceptorClass);
309                 interceptorsPool.put(actionInterceptorClass, interceptor);
310             }
311             actionInterceptors.add(interceptor);
312         }
313         return actionInterceptors.toArray(new ActionInterceptor[actionInterceptors.size()]);
314     }
315
316     // ---------------------------------------------------------------- utils
317

318     /**
319      * Converts class name to action path.
320      */

321     protected String JavaDoc parseActionPath(String JavaDoc className) {
322         int i = className.lastIndexOf('.');
323         if (i != -1) {
324             className = className.substring(i + 1);
325         }
326         className = className.substring(0, className.length() - actionClassSuffix.length());
327         return StringUtil.uncapitalize(className);
328     }
329
330 }
331
Popular Tags