KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > kohsuke > stapler > MetaClass


1 package org.kohsuke.stapler;
2
3 import org.apache.commons.jelly.Script;
4 import org.kohsuke.stapler.jelly.JellyClassTearOff;
5 import org.kohsuke.stapler.jelly.groovy.GroovyClassTearOff;
6
7 import javax.servlet.RequestDispatcher JavaDoc;
8 import javax.servlet.ServletException JavaDoc;
9 import java.io.IOException JavaDoc;
10 import java.lang.reflect.Field JavaDoc;
11 import java.lang.reflect.InvocationTargetException JavaDoc;
12 import java.util.ArrayList JavaDoc;
13 import java.util.List JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.WeakHashMap JavaDoc;
16
17 /**
18  * The stapler version of the {@link Class} object,
19  * that retains some useful cache about a class and its view.
20  *
21  * @author Kohsuke Kawaguchi
22  */

23 public class MetaClass extends TearOffSupport {
24     /**
25      * This meta class wraps this class
26      */

27     public final Class JavaDoc clazz;
28
29     /**
30      * {@link MetaClassLoader} that wraps {@code clazz.getClassLoader()}.
31      * Null if the class is loaded by the bootstrap classloader.
32      */

33     public final MetaClassLoader classLoader;
34
35     public final List JavaDoc<Dispatcher> dispatchers = new ArrayList JavaDoc<Dispatcher>();
36
37     /**
38      * Base metaclass.
39      * Note that <tt>baseClass.clazz==clazz.getSuperClass()</tt>
40      */

41     public final MetaClass baseClass;
42
43     private MetaClass(Class JavaDoc clazz) {
44         this.clazz = clazz;
45         this.baseClass = get(clazz.getSuperclass());
46         this.classLoader = MetaClassLoader.get(clazz.getClassLoader());
47
48         buildDispatchers(
49             new ClassDescriptor(clazz,null/*support wrappers*/));
50     }
51
52
53     private void buildDispatchers( ClassDescriptor node ) {
54         // check action <obj>.do<token>(StaplerRequest,StaplerResponse)
55
for( final Function f : node.methods.prefix("do").signature(StaplerRequest.class,StaplerResponse.class) ) {
56             String JavaDoc name = camelize(f.getName().substring(2)); // 'doFoo' -> 'foo'
57
dispatchers.add(new NameBasedDispatcher(name,0) {
58                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
59                     f.invoke(req, node,req,rsp);
60                 }
61             });
62         }
63         
64         dispatchers.add(new Dispatcher() {
65             public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
66                 // check JSP views
67
// I thought about generalizing this to invoke other resources (such as static images, etc)
68
// but I realized that those would require a very different handling.
69
// so for now we just assume it's a JSP
70
String JavaDoc next = req.tokens.peek();
71                 if(next==null) return false;
72
73                 Stapler stapler = req.getStapler();
74
75                 RequestDispatcher JavaDoc disp = stapler.getResourceDispatcher(node,next+".jsp");
76                 if(disp==null) return false;
77
78                 req.tokens.next();
79                 stapler.forward(disp,req,rsp);
80                 return true;
81             }
82         });
83
84         try {
85             dispatchers.add(new Dispatcher() {
86                 final JellyClassTearOff tearOff = loadTearOff(JellyClassTearOff.class);
87
88                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
89                     // check Jelly view
90
String JavaDoc next = req.tokens.peek();
91                     if(next==null) return false;
92
93                     try {
94                         Script script = tearOff.findScript(next+".jelly");
95
96                         if(script==null) return false; // no Jelly script found
97

98                         req.tokens.next();
99
100                         JellyClassTearOff.invokeScript(req, rsp, script, node);
101
102                         return true;
103                     } catch (RuntimeException JavaDoc e) {
104                         throw e;
105                     } catch (IOException JavaDoc e) {
106                         throw e;
107                     } catch (Exception JavaDoc e) {
108                         throw new ServletException JavaDoc(e);
109                     }
110                 }
111             });
112         } catch (LinkageError JavaDoc e) {
113             // jelly not present. ignore
114
}
115
116         try {
117             dispatchers.add(new Dispatcher() {
118                 final GroovyClassTearOff tearOff = loadTearOff(GroovyClassTearOff.class);
119
120                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
121                     // check Groovy view
122
String JavaDoc next = req.tokens.peek();
123                     if(next==null) return false;
124
125                     try {
126                         Script script = tearOff.findScript(next+".groovy");
127                         if(script==null) return false; // no Groovy script found
128

129                         req.tokens.next();
130
131                         JellyClassTearOff.invokeScript(req, rsp, script, node);
132
133                         return true;
134                     } catch (RuntimeException JavaDoc e) {
135                         throw e;
136                     } catch (IOException JavaDoc e) {
137                         throw e;
138                     } catch (Exception JavaDoc e) {
139                         throw new ServletException JavaDoc(e);
140                     }
141                 }
142             });
143         } catch (LinkageError JavaDoc e) {
144             // groovy not present. ignore
145
}
146
147         // check action <obj>.doIndex(req,rsp)
148
for( final Function f : node.methods
149             .signature(StaplerRequest.class,StaplerResponse.class)
150             .name("doIndex") ) {
151
152             dispatchers.add(new Dispatcher() {
153                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
154                     if(req.tokens.hasMore())
155                         return false; // applicable only when there's no more token
156
f.invoke(req,node,req,rsp);
157                     return true;
158                 }
159             });
160         }
161
162         // check public properties of the form NODE.TOKEN
163
for (final Field JavaDoc f : node.fields) {
164             dispatchers.add(new NameBasedDispatcher(f.getName()) {
165                 final String JavaDoc role = getProtectedRole(f);
166                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc, IllegalAccessException JavaDoc {
167                     if(role!=null && !req.isUserInRole(role))
168                         throw new IllegalAccessException JavaDoc("Needs to be in role "+role);
169                     req.getStapler().invoke(req, rsp, f.get(node));
170                 }
171             });
172         }
173
174         FunctionList getMethods = node.methods.prefix("get");
175
176         // check public selector methods of the form NODE.getTOKEN()
177
for( final Function f : getMethods.signature() ) {
178             if(f.getName().length()<=3)
179                 continue;
180             String JavaDoc name = camelize(f.getName().substring(3)); // 'getFoo' -> 'foo'
181
dispatchers.add(new NameBasedDispatcher(name) {
182                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc, IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
183                     req.getStapler().invoke(req,rsp,f.invoke(req, node));
184                 }
185             });
186         }
187
188         // check public selector methods of the form static NODE.getTOKEN(StaplerRequest)
189
for( final Function f : getMethods.signature(StaplerRequest.class) ) {
190             if(f.getName().length()<=3)
191                 continue;
192             String JavaDoc name = camelize(f.getName().substring(3)); // 'getFoo' -> 'foo'
193
dispatchers.add(new NameBasedDispatcher(name) {
194                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc, IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
195                     req.getStapler().invoke(req,rsp,f.invoke(req, node,req));
196                 }
197             });
198         }
199
200         // check public selector methods <obj>.get<Token>(String)
201
for( final Function f : getMethods.signature(String JavaDoc.class) ) {
202             if(f.getName().length()<=3)
203                 continue;
204             String JavaDoc name = camelize(f.getName().substring(3)); // 'getFoo' -> 'foo'
205
dispatchers.add(new NameBasedDispatcher(name,1) {
206                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc, IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
207                     req.getStapler().invoke(req,rsp,f.invoke(req, node,req.tokens.next()));
208                 }
209             });
210         }
211
212         // check public selector methods <obj>.get<Token>(int)
213
for( final Function f : getMethods.signature(int.class) ) {
214             if(f.getName().length()<=3)
215                 continue;
216             String JavaDoc name = camelize(f.getName().substring(3)); // 'getFoo' -> 'foo'
217
dispatchers.add(new NameBasedDispatcher(name,1) {
218                 public void doDispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc, IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
219                     int idx = Integer.valueOf(req.tokens.next());
220                     req.getStapler().invoke(req,rsp,f.invoke(req, node,idx));
221                 }
222             });
223         }
224
225         if(node.clazz.isArray()) {
226             dispatchers.add(new Dispatcher() {
227                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
228                     if(!req.tokens.hasMore())
229                         return false;
230                     try {
231                         req.getStapler().invoke(req,rsp,((Object JavaDoc[])node)[req.tokens.nextAsInt()]);
232                         return true;
233                     } catch (NumberFormatException JavaDoc e) {
234                         return false; // try next
235
}
236                 }
237             });
238         }
239
240         if(List JavaDoc.class.isAssignableFrom(node.clazz)) {
241             dispatchers.add(new Dispatcher() {
242                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
243                     if(!req.tokens.hasMore())
244                         return false;
245                     try {
246                         req.getStapler().invoke(req,rsp,((List JavaDoc)node).get(req.tokens.nextAsInt()));
247                         return true;
248                     } catch (NumberFormatException JavaDoc e) {
249                         return false; // try next
250
}
251                 }
252             });
253         }
254
255         if(Map JavaDoc.class.isAssignableFrom(node.clazz)) {
256             dispatchers.add(new Dispatcher() {
257                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IOException JavaDoc, ServletException JavaDoc {
258                     if(!req.tokens.hasMore())
259                         return false;
260                     try {
261                         Object JavaDoc item = ((Map JavaDoc)node).get(req.tokens.peek());
262                         if(item!=null) {
263                             req.tokens.next();
264                             req.getStapler().invoke(req,rsp,item);
265                             return true;
266                         } else {
267                             // otherwise just fall through
268
return false;
269                         }
270                     } catch (NumberFormatException JavaDoc e) {
271                         return false; // try next
272
}
273                 }
274             });
275         }
276
277         // TODO: check if we can route to static resources
278
// which directory shall we look up a resource from?
279

280         // check action <obj>.doDynamic(req,rsp)
281
for( final Function f : node.methods
282             .signature(StaplerRequest.class,StaplerResponse.class)
283             .name("doDynamic") ) {
284
285             dispatchers.add(new Dispatcher() {
286                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc {
287                     f.invoke(req,node,req,rsp);
288                     return true;
289                 }
290             });
291         }
292
293         // check public selector methods <obj>.getDynamic(<token>,req,rsp)
294
for( final Function f : getMethods.signature(String JavaDoc.class,StaplerRequest.class,StaplerResponse.class).name("getDynamic")) {
295             dispatchers.add(new Dispatcher() {
296                 public boolean dispatch(RequestImpl req, ResponseImpl rsp, Object JavaDoc node) throws IllegalAccessException JavaDoc, InvocationTargetException JavaDoc, IOException JavaDoc, ServletException JavaDoc {
297                     if(!req.tokens.hasMore())
298                         return false;
299                     String JavaDoc token = req.tokens.next();
300                     req.getStapler().invoke(req,rsp,f.invoke(req,node,token,req,rsp));
301                     return true;
302                 }
303             });
304         }
305     }
306
307     private String JavaDoc getProtectedRole(Field JavaDoc f) {
308         try {
309             LimitedTo a = f.getAnnotation(LimitedTo.class);
310             return (a!=null)?a.value():null;
311         } catch (LinkageError JavaDoc e) {
312             return null; // running in JDK 1.4
313
}
314     }
315
316     private static String JavaDoc camelize(String JavaDoc name) {
317         return Character.toLowerCase(name.charAt(0))+name.substring(1);
318     }
319
320     /**
321      * Don't cache anything in memory, so that any change
322      * will take effect instantly.
323      */

324     public static boolean NO_CACHE = false;
325
326     static {
327         try {
328             NO_CACHE = Boolean.getBoolean("stapler.jelly.noCache");
329         } catch (SecurityException JavaDoc e) {
330             // ignore.
331
}
332     }
333
334
335
336     public static MetaClass get(Class JavaDoc c) {
337         if(c==null) return null;
338         synchronized(classMap) {
339             MetaClass mc = classMap.get(c);
340             if(mc==null) {
341                 mc = new MetaClass(c);
342                 classMap.put(c,mc);
343             }
344             return mc;
345         }
346     }
347
348     /**
349      * All {@link MetaClass}es.
350      *
351      * Avoids class leaks by {@link WeakHashMap}.
352      */

353     private static final Map JavaDoc<Class JavaDoc,MetaClass> classMap = new WeakHashMap JavaDoc<Class JavaDoc,MetaClass>();
354 }
355
Popular Tags