001 /* 002 * Copyright © 2008, 2009 Pedro Agulló Soliveres. 003 * 004 * This file is part of DirectJNgine. 005 * 006 * DirectJNgine is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License. 009 * 010 * Commercial use is permitted to the extent that the code/component(s) 011 * do NOT become part of another Open Source or Commercially developed 012 * licensed development library or toolkit without explicit permission. 013 * 014 * DirectJNgine is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 017 * GNU General Public License for more details. 018 * 019 * You should have received a copy of the GNU General Public License 020 * along with DirectJNgine. If not, see <http://www.gnu.org/licenses/>. 021 * 022 * This software uses the ExtJs library (http://extjs.com), which is 023 * distributed under the GPL v3 license (see http://extjs.com/license). 024 */ 025 026 package com.softwarementors.extjs.djn.scanner; 027 028 import java.lang.reflect.Method; 029 import java.util.ArrayList; 030 import java.util.Collections; 031 import java.util.HashMap; 032 import java.util.List; 033 import java.util.Map; 034 035 import org.apache.log4j.Logger; 036 037 import com.softwarementors.extjs.djn.ClassUtils; 038 import com.softwarementors.extjs.djn.api.RegisteredAction; 039 import com.softwarementors.extjs.djn.api.RegisteredApi; 040 import com.softwarementors.extjs.djn.api.RegisteredMethod; 041 import com.softwarementors.extjs.djn.api.RegisteredPollMethod; 042 import com.softwarementors.extjs.djn.api.Registry; 043 import com.softwarementors.extjs.djn.config.ApiConfiguration; 044 import com.softwarementors.extjs.djn.config.ApiConfigurationException; 045 import com.softwarementors.extjs.djn.config.annotations.DirectAction; 046 import com.softwarementors.extjs.djn.config.annotations.DirectFormPostMethod; 047 import com.softwarementors.extjs.djn.config.annotations.DirectMethod; 048 import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod; 049 050 public class Scanner { 051 052 private static final Logger logger = Logger.getLogger( Scanner.class); 053 054 private Registry registry; 055 056 public Scanner(Registry registry) { 057 assert registry != null; 058 059 this.registry = registry; 060 } 061 062 public void scanAndRegisterApiConfigurations(List<ApiConfiguration> apiConfigurations) { 063 assert apiConfigurations != null; 064 065 for( ApiConfiguration api: apiConfigurations ) { 066 scanAndRegisterApiConfiguration(api); 067 } 068 } 069 070 /* package */ void scanAndRegisterApiConfiguration(ApiConfiguration api) { 071 assert api != null; 072 073 if( this.registry.hasApi( api.getName() )) { 074 ApiConfigurationException ex = ApiConfigurationException.forApiAlreadyRegistered( api.getName()); 075 logger.fatal( ex.getMessage(), ex ); 076 throw ex; 077 } 078 079 RegisteredApi registeredApi = this.registry.addApi( api.getName(), api.getFullApiFileName(), api.getApiNamespace(), api.getActionsNamespace()); 080 081 List<Class<?>> actionClasses = api.getClasses(); 082 for( Class<?> cls : actionClasses ) { 083 assert cls != null; 084 scanAndRegisterActionClass(registeredApi, cls ); 085 } 086 } 087 088 public void scanAndRegisterActionClass( RegisteredApi api, Class<?> actionClass ) { 089 assert api != null; 090 assert actionClass != null; 091 092 Map<Class<?>, RegisteredAction> actionsByClass = new HashMap<Class<?>, RegisteredAction>(); 093 094 if( actionsByClass.containsKey( actionClass ) ) { 095 ApiConfigurationException ex = ApiConfigurationException.forClassAlreadyRegisteredAsAction(actionClass); 096 logger.fatal( ex.getMessage(), ex ); 097 throw ex; 098 } 099 100 if( logger.isDebugEnabled() ) { 101 logger.debug( "Scanning Java class: " + actionClass.getName() ); 102 } 103 104 RegisteredAction action = createActionFromJavaClass(api, actionClass); 105 106 scanAndRegisterActionClass(action); 107 actionsByClass.put( actionClass, action); 108 } 109 110 private RegisteredAction createActionFromJavaClass(RegisteredApi api, Class<?> actionClass) { 111 assert api != null; 112 assert actionClass != null; 113 114 DirectAction actionAnnotation = actionClass.getAnnotation(DirectAction.class); 115 String actionName = ""; 116 if( actionAnnotation != null ) { 117 actionName = actionAnnotation.action(); 118 } 119 120 if( actionName.equals("")) { 121 actionName = ClassUtils.getSimpleName(actionClass); 122 } 123 124 if( this.registry.hasAction( actionName ) ) { 125 RegisteredAction existingAction = this.registry.getAction( actionName ); 126 ApiConfigurationException ex = ApiConfigurationException.forActionAlreadyRegistered(actionName, actionClass, existingAction.getActionClass()); 127 logger.fatal( ex.getMessage(), ex ); 128 throw ex; 129 } 130 RegisteredAction action = api.addAction( actionClass, actionName ); 131 132 return action; 133 } 134 135 private static final String POLL_METHOD_NAME_PREFIX = "djnpoll_"; 136 private static final String FORM_POST_METHOD_NAME_PREFIX = "djnform_"; 137 private static final String STANDARD_METHOD_NAME_PREFIX = "djn_"; 138 139 private void scanAndRegisterActionClass(RegisteredAction action) { 140 assert action != null; 141 142 RegisteredApi api = action.getApi(); 143 144 // *All* methods are candidates, including those in base classes, 145 // even if the base class does not have a DirectAction annotation! 146 List<Method> allMethods = new ArrayList<Method>(); 147 Class<?> cls = action.getActionClass(); 148 while( cls != null ) { 149 Method[] methods = cls.getDeclaredMethods(); // Get private, protected and other methods! 150 Collections.addAll( allMethods, methods ); 151 cls = cls.getSuperclass(); 152 } 153 154 for( Method method : allMethods ) { 155 // Check if the kind of direct method -if any 156 DirectMethod methodAnnotation = method.getAnnotation(DirectMethod.class); 157 boolean isStandardMethod = methodAnnotation != null; 158 if( !isStandardMethod ) { 159 isStandardMethod = method.getName().startsWith(STANDARD_METHOD_NAME_PREFIX); 160 } 161 162 DirectFormPostMethod postMethodAnnotation = method.getAnnotation(DirectFormPostMethod.class); 163 boolean isFormPostMethod = postMethodAnnotation != null; 164 if( !isFormPostMethod ) { 165 isFormPostMethod = method.getName().startsWith(FORM_POST_METHOD_NAME_PREFIX); 166 } 167 168 DirectPollMethod pollMethodAnnotation = method.getAnnotation(DirectPollMethod.class); 169 boolean isPollMethod = pollMethodAnnotation != null; 170 if( !isPollMethod ) { 171 isPollMethod = method.getName().startsWith( POLL_METHOD_NAME_PREFIX ); 172 } 173 174 // Check that a method is just of only one kind of method 175 if( isStandardMethod && isFormPostMethod ) { 176 ApiConfigurationException ex = ApiConfigurationException.forMethodCantBeStandardAndFormPostMethodAtTheSameTime(action, method); 177 logger.fatal( ex.getMessage(), ex ); 178 throw ex; 179 } 180 if( (methodAnnotation != null || postMethodAnnotation != null) && isPollMethod) { 181 ApiConfigurationException ex = ApiConfigurationException.forPollMethodCantBeStandardOrFormPostMethodAtTheSameTime(action, method); 182 logger.fatal( ex.getMessage(), ex ); 183 throw ex; 184 } 185 186 // Process standard and form post methods together, as they are very similar 187 if( isStandardMethod || isFormPostMethod) { 188 189 String methodName = ""; 190 if( isStandardMethod ) { 191 methodName = getStandardMethodName(method, methodAnnotation); 192 } 193 else { 194 methodName = getFormPostMethodName( method, postMethodAnnotation); 195 } 196 if( action.hasMethod(methodName) ) { 197 ApiConfigurationException ex = ApiConfigurationException.forMethodAlreadyRegisteredInAction(methodName, action.getName()); 198 logger.fatal( ex.getMessage(), ex ); 199 throw ex; 200 } 201 202 if( isFormPostMethod && !RegisteredMethod.isValidFormHandlingMethod(method)) { 203 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAFormHandler( action.getName(), methodName ); 204 logger.fatal( ex.getMessage(), ex ); 205 throw ex; 206 } 207 208 209 action.addMethod( methodName, method, isFormPostMethod ); 210 } 211 212 // Process "poll" method 213 if( isPollMethod ) { 214 createPollMethod(api, action.getActionClass(), method, pollMethodAnnotation); 215 } 216 } 217 } 218 219 private String getFormPostMethodName(Method method, DirectFormPostMethod postMethodAnnotation) { 220 String methodName = ""; 221 if( postMethodAnnotation != null ) { 222 methodName = postMethodAnnotation.method(); 223 } 224 if( methodName.equals("" )) { 225 methodName = method.getName(); 226 } 227 if( methodName.startsWith(FORM_POST_METHOD_NAME_PREFIX)) { 228 methodName = method.getName().substring(FORM_POST_METHOD_NAME_PREFIX.length()); 229 } 230 return methodName; 231 } 232 233 private String getStandardMethodName(Method method, DirectMethod methodAnnotation) { 234 String methodName = ""; 235 if( methodAnnotation != null ) { 236 methodName = methodAnnotation.method(); 237 } 238 if( methodName.equals("" )) { 239 methodName = method.getName(); 240 } 241 if( methodName.startsWith(STANDARD_METHOD_NAME_PREFIX)) { 242 methodName = method.getName().substring(STANDARD_METHOD_NAME_PREFIX.length()); 243 } 244 return methodName; 245 } 246 247 private RegisteredPollMethod createPollMethod(RegisteredApi api, Class<?> owningClass, Method method, DirectPollMethod pollMethodAnnotation) { 248 assert api != null; 249 assert owningClass != null; 250 assert method != null; 251 252 String eventName = getEventName(method, pollMethodAnnotation); 253 254 if( this.registry.hasPollMethod(eventName)) { 255 ApiConfigurationException ex = ApiConfigurationException.forPollEventAlreadyRegistered( eventName ); 256 logger.fatal( ex.getMessage(), ex ); 257 throw ex; 258 } 259 260 if( !RegisteredPollMethod.isValidPollMethod(method)) { 261 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAPollHandler( method ); 262 logger.fatal( ex.getMessage(), ex ); 263 throw ex; 264 } 265 266 RegisteredPollMethod poll = api.addPollMethod( eventName, owningClass, method); 267 return poll; 268 } 269 270 private String getEventName(Method method, DirectPollMethod pollMethodAnnotation) { 271 assert method != null; 272 273 String eventName = ""; 274 if( pollMethodAnnotation != null ) { 275 eventName = pollMethodAnnotation.event(); 276 } 277 if( eventName.equals("")) { 278 eventName = method.getName(); 279 } 280 if( eventName.startsWith(POLL_METHOD_NAME_PREFIX)) { 281 eventName = method.getName().substring(POLL_METHOD_NAME_PREFIX.length()); 282 } 283 return eventName; 284 } 285 286 }