PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/core/impl/main/java/org/directwebremoting/annotations/AnnotationsConfigurator.java

http://github.com/burris/dwr
Java | 358 lines | 251 code | 30 blank | 77 comment | 48 complexity | 8492d5084e3ca809a7195134ce681029 MD5 | raw file
  1. /*
  2. * Copyright 2006 Maik Schreiber <blizzy AT blizzy DOT de>
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.directwebremoting.annotations;
  17. import java.beans.Introspector;
  18. import java.lang.reflect.Field;
  19. import java.lang.reflect.Method;
  20. import java.util.Arrays;
  21. import java.util.HashMap;
  22. import java.util.HashSet;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import org.directwebremoting.AjaxFilter;
  26. import org.directwebremoting.Container;
  27. import org.directwebremoting.convert.BeanConverter;
  28. import org.directwebremoting.create.NewCreator;
  29. import org.directwebremoting.extend.AccessControl;
  30. import org.directwebremoting.extend.AjaxFilterManager;
  31. import org.directwebremoting.extend.Configurator;
  32. import org.directwebremoting.extend.Converter;
  33. import org.directwebremoting.extend.ConverterManager;
  34. import org.directwebremoting.extend.Creator;
  35. import org.directwebremoting.extend.CreatorManager;
  36. import org.directwebremoting.util.LocalUtil;
  37. import org.directwebremoting.util.Loggers;
  38. /**
  39. * A Configurator that works off Annotations.
  40. * @author Maik Schreiber [blizzy AT blizzy DOT de]
  41. * @author Joe Walker [joe at getahead dot ltd dot uk]
  42. */
  43. public class AnnotationsConfigurator implements Configurator
  44. {
  45. /* (non-Javadoc)
  46. * @see org.directwebremoting.Configurator#configure(org.directwebremoting.Container)
  47. */
  48. public void configure(Container container)
  49. {
  50. for (Class<?> clazz : getClasses(container))
  51. {
  52. try
  53. {
  54. processClass(clazz, container);
  55. }
  56. catch (Exception ex)
  57. {
  58. Loggers.STARTUP.error("Failed to process class: " + clazz.getName(), ex);
  59. }
  60. }
  61. }
  62. /**
  63. * Allow subclasses to override the default way we find out which classes
  64. * have DWR annotations for us to work with
  65. * @param container Commonly we get configuration information from here
  66. * @return A set of classes with DWR annotations
  67. */
  68. protected Set<Class<?>> getClasses(Container container)
  69. {
  70. Set<Class<?>> classes = new HashSet<Class<?>>();
  71. Object data = container.getBean("classes");
  72. if (data != null)
  73. {
  74. if (data instanceof String)
  75. {
  76. String classesStr = (String) data;
  77. for (String element : classesStr.split(","))
  78. {
  79. element = element.trim();
  80. if (element.length() == 0)
  81. {
  82. continue;
  83. }
  84. try
  85. {
  86. Class<?> clazz = LocalUtil.classForName(element);
  87. classes.add(clazz);
  88. }
  89. catch (Exception ex)
  90. {
  91. Loggers.STARTUP.error("Failed to process class: " + element, ex);
  92. }
  93. }
  94. }
  95. else
  96. {
  97. try
  98. {
  99. classes.add(data.getClass());
  100. }
  101. catch (Exception ex)
  102. {
  103. Loggers.STARTUP.error("Failed to process class: " + data.getClass().getName(), ex);
  104. }
  105. }
  106. }
  107. return classes;
  108. }
  109. /**
  110. * Process the annotations on a given class
  111. * @param clazz The class to search for annotations
  112. * @param container The IoC container to configure
  113. * @throws IllegalAccessException If annotation processing fails
  114. * @throws InstantiationException If annotation processing fails
  115. */
  116. protected void processClass(Class<?> clazz, Container container) throws InstantiationException, IllegalAccessException
  117. {
  118. RemoteProxy createAnn = clazz.getAnnotation(RemoteProxy.class);
  119. if (createAnn != null)
  120. {
  121. processCreate(clazz, createAnn, container);
  122. }
  123. DataTransferObject convertAnn = clazz.getAnnotation(DataTransferObject.class);
  124. if (convertAnn != null)
  125. {
  126. processConvert(clazz, convertAnn, container);
  127. }
  128. GlobalFilter globalFilterAnn = clazz.getAnnotation(GlobalFilter.class);
  129. if (globalFilterAnn != null)
  130. {
  131. processGlobalFilter(clazz, globalFilterAnn, container);
  132. }
  133. }
  134. /**
  135. * Process the @RemoteProxy annotation on a given class
  136. * @param clazz The class annotated with @RemoteProxy
  137. * @param createAnn The annotation
  138. * @param container The IoC container to configure
  139. */
  140. protected void processCreate(Class<?> clazz, RemoteProxy createAnn, Container container)
  141. {
  142. Class<? extends Creator> creator = createAnn.creator();
  143. String creatorClass = creator.getName();
  144. Map<String, String> creatorParams = getParamsMap(createAnn.creatorParams());
  145. ScriptScope scope = createAnn.scope();
  146. CreatorManager creatorManager = container.getBean(CreatorManager.class);
  147. String creatorName = creatorClass.replace(".", "_");
  148. creatorManager.addCreatorType(creatorName, creatorClass);
  149. Map<String, String> params = new HashMap<String, String>();
  150. if (NewCreator.class.isAssignableFrom(creator))
  151. {
  152. params.put("class", clazz.getName());
  153. }
  154. params.putAll(creatorParams);
  155. params.put("scope", scope.getValue());
  156. String name = createAnn.name();
  157. if (name == null || name.length() == 0)
  158. {
  159. name = clazz.getSimpleName();
  160. }
  161. try
  162. {
  163. Loggers.STARTUP.debug("Adding class " + clazz.getName() + " as " + name);
  164. creatorManager.addCreator(name, creatorName, params);
  165. }
  166. catch (Exception ex)
  167. {
  168. Loggers.STARTUP.error("Failed to add class as Creator: " + clazz.getName(), ex);
  169. }
  170. AccessControl accessControl = container.getBean(AccessControl.class);
  171. for (Method method : clazz.getMethods())
  172. {
  173. if (method.getAnnotation(RemoteMethod.class) != null)
  174. {
  175. accessControl.addIncludeRule(name, method.getName());
  176. Auth authAnn = method.getAnnotation(Auth.class);
  177. if (authAnn != null)
  178. {
  179. for (String role : authAnn.role())
  180. {
  181. accessControl.addRoleRestriction(name, method.getName(), role);
  182. }
  183. }
  184. }
  185. }
  186. Filters filtersAnn = clazz.getAnnotation(Filters.class);
  187. if (filtersAnn != null)
  188. {
  189. Filter[] fs = filtersAnn.value();
  190. for (Filter filter : fs)
  191. {
  192. processFilter(filter, name, container);
  193. }
  194. }
  195. // process single filter for convenience
  196. else
  197. {
  198. Filter filterAnn = clazz.getAnnotation(Filter.class);
  199. if (filterAnn != null)
  200. {
  201. processFilter(filterAnn, name, container);
  202. }
  203. }
  204. }
  205. /**
  206. * Process the @Filter annotation
  207. * @param filterAnn The filter annotation
  208. * @param name The Javascript name of the class to filter
  209. * @param container The IoC container to configure
  210. */
  211. protected void processFilter(Filter filterAnn, String name, Container container)
  212. {
  213. Map<String, String> filterParams = getParamsMap(filterAnn.params());
  214. AjaxFilter filter = LocalUtil.classNewInstance(name, filterAnn.type().getName(), AjaxFilter.class);
  215. if (filter != null)
  216. {
  217. LocalUtil.setParams(filter, filterParams, null);
  218. AjaxFilterManager filterManager = container.getBean(AjaxFilterManager.class);
  219. filterManager.addAjaxFilter(filter, name);
  220. }
  221. }
  222. /**
  223. * Process the @DataTransferObject annotation on a given class
  224. * @param clazz The class annotated with @DataTransferObject
  225. * @param convertAnn The annotation
  226. * @param container The IoC container to configure
  227. * @throws InstantiationException If there are problems instantiating the Converter
  228. * @throws IllegalAccessException If there are problems instantiating the Converter
  229. */
  230. protected void processConvert(Class<?> clazz, DataTransferObject convertAnn, Container container) throws InstantiationException, IllegalAccessException
  231. {
  232. Class<? extends Converter> converter = convertAnn.converter();
  233. String converterClass = converter.getName();
  234. Map<String, String> params = getParamsMap(convertAnn.params());
  235. ConverterManager converterManager = container.getBean(ConverterManager.class);
  236. String converterName = converterClass.replace(".", "_");
  237. converterManager.addConverterType(converterName, converterClass);
  238. if (BeanConverter.class.isAssignableFrom(converter))
  239. {
  240. StringBuilder properties = new StringBuilder();
  241. Set<Field> fields = new HashSet<Field>();
  242. fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
  243. fields.addAll(Arrays.asList(clazz.getFields()));
  244. for (Field field : fields)
  245. {
  246. if (field.getAnnotation(RemoteProperty.class) != null)
  247. {
  248. properties.append(',').append(field.getName());
  249. }
  250. }
  251. for (Method method : clazz.getMethods())
  252. {
  253. if (method.getAnnotation(RemoteProperty.class) != null)
  254. {
  255. String name = method.getName();
  256. if (name.startsWith(METHOD_PREFIX_GET) || name.startsWith(METHOD_PREFIX_IS))
  257. {
  258. if (name.startsWith(METHOD_PREFIX_GET))
  259. {
  260. name = name.substring(3);
  261. }
  262. else
  263. {
  264. name = name.substring(2);
  265. }
  266. name = Introspector.decapitalize(name);
  267. properties.append(',').append(name);
  268. }
  269. }
  270. }
  271. if (properties.length() > 0)
  272. {
  273. properties.deleteCharAt(0);
  274. params.put("include", properties.toString());
  275. }
  276. }
  277. converterManager.addConverter(clazz.getName(), converterName, params);
  278. }
  279. /**
  280. * Global Filters apply to all classes
  281. * @param clazz The class to use as a filter
  282. * @param globalFilterAnn The filter annotation
  283. * @param container The IoC container to configure
  284. * @throws InstantiationException In case we can't create the given clazz
  285. * @throws IllegalAccessException In case we can't create the given clazz
  286. */
  287. protected void processGlobalFilter(Class<?> clazz, GlobalFilter globalFilterAnn, Container container) throws InstantiationException, IllegalAccessException
  288. {
  289. if (!AjaxFilter.class.isAssignableFrom(clazz))
  290. {
  291. throw new IllegalArgumentException(clazz.getName() + " is not an AjaxFilter implementation");
  292. }
  293. Map<String, String> filterParams = getParamsMap(globalFilterAnn.params());
  294. AjaxFilter filter = (AjaxFilter) clazz.newInstance();
  295. if (filter != null)
  296. {
  297. LocalUtil.setParams(filter, filterParams, null);
  298. AjaxFilterManager filterManager = container.getBean(AjaxFilterManager.class);
  299. filterManager.addAjaxFilter(filter);
  300. }
  301. }
  302. /**
  303. * Utility to turn a Param array into a Map<String, String>.
  304. * @param params The params array from annotations
  305. * @return A Map<String, String>
  306. */
  307. protected Map<String, String> getParamsMap(Param[] params)
  308. {
  309. // TODO: Should we move this code into Param? Is that even possible?
  310. Map<String, String> result = new HashMap<String, String>();
  311. if (params != null)
  312. {
  313. for (Param param : params)
  314. {
  315. result.put(param.name(), param.value());
  316. }
  317. }
  318. return result;
  319. }
  320. /**
  321. * The getter prefix for boolean variables
  322. */
  323. private static final String METHOD_PREFIX_IS = "is";
  324. /**
  325. * The getter prefix for non-boolean variables
  326. */
  327. private static final String METHOD_PREFIX_GET = "get";
  328. }