001 package com.softwarementors.extjs.djn.scanner; 002 003 import java.lang.reflect.Method; 004 import java.util.ArrayList; 005 import java.util.Collections; 006 import java.util.HashMap; 007 import java.util.List; 008 import java.util.Map; 009 010 import org.apache.log4j.Logger; 011 012 import com.softwarementors.extjs.djn.ClassUtils; 013 import com.softwarementors.extjs.djn.api.RegisteredAction; 014 import com.softwarementors.extjs.djn.api.RegisteredApi; 015 import com.softwarementors.extjs.djn.api.RegisteredMethod; 016 import com.softwarementors.extjs.djn.api.RegisteredPollMethod; 017 import com.softwarementors.extjs.djn.api.Registry; 018 import com.softwarementors.extjs.djn.config.ApiConfiguration; 019 import com.softwarementors.extjs.djn.config.ApiConfigurationException; 020 import com.softwarementors.extjs.djn.config.annotations.DirectAction; 021 import com.softwarementors.extjs.djn.config.annotations.DirectFormPostMethod; 022 import com.softwarementors.extjs.djn.config.annotations.DirectMethod; 023 import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod; 024 025 public class Scanner { 026 027 private static final Logger logger = Logger.getLogger( Scanner.class); 028 029 private Registry registry; 030 031 public Scanner(Registry registry) { 032 assert registry != null; 033 034 this.registry = registry; 035 } 036 037 public void scanAndRegisterApiConfigurations(List<ApiConfiguration> apiConfigurations) { 038 assert apiConfigurations != null; 039 040 for( ApiConfiguration api: apiConfigurations ) { 041 scanAndRegisterApiConfiguration(api); 042 } 043 } 044 045 /* package */ void scanAndRegisterApiConfiguration(ApiConfiguration api) { 046 assert api != null; 047 048 if( this.registry.hasApi( api.getName() )) { 049 ApiConfigurationException ex = ApiConfigurationException.forApiAlreadyRegistered( api.getName()); 050 logger.fatal( ex.getMessage(), ex ); 051 throw ex; 052 } 053 054 RegisteredApi registeredApi = this.registry.addApi( api.getName(), api.getFullApiFileName(), api.getApiNamespace(), api.getActionsNamespace()); 055 056 List<Class<?>> actionClasses = api.getClasses(); 057 for( Class<?> cls : actionClasses ) { 058 assert cls != null; 059 scanAndRegisterActionClass(registeredApi, cls ); 060 } 061 } 062 063 public void scanAndRegisterActionClass( RegisteredApi api, Class<?> actionClass ) { 064 assert api != null; 065 assert actionClass != null; 066 067 Map<Class<?>, RegisteredAction> actionsByClass = new HashMap<Class<?>, RegisteredAction>(); 068 069 if( actionsByClass.containsKey( actionClass ) ) { 070 ApiConfigurationException ex = ApiConfigurationException.forClassAlreadyRegisteredAsAction(actionClass); 071 logger.fatal( ex.getMessage(), ex ); 072 throw ex; 073 } 074 075 if( logger.isDebugEnabled() ) { 076 logger.debug( "Scanning Java class: " + actionClass.getName() ); 077 } 078 079 RegisteredAction action = createActionFromJavaClass(api, actionClass); 080 081 scanAndRegisterActionClass(action); 082 actionsByClass.put( actionClass, action); 083 } 084 085 private RegisteredAction createActionFromJavaClass(RegisteredApi api, Class<?> actionClass) { 086 assert api != null; 087 assert actionClass != null; 088 089 DirectAction actionAnnotation = actionClass.getAnnotation(DirectAction.class); 090 String actionName = ""; 091 if( actionAnnotation != null ) { 092 actionName = actionAnnotation.action(); 093 } 094 095 if( actionName.equals("")) { 096 actionName = ClassUtils.getSimpleName(actionClass); 097 } 098 099 if( this.registry.hasAction( actionName ) ) { 100 RegisteredAction existingAction = this.registry.getAction( actionName ); 101 ApiConfigurationException ex = ApiConfigurationException.forActionAlreadyRegistered(actionName, actionClass, existingAction.getActionClass()); 102 logger.fatal( ex.getMessage(), ex ); 103 throw ex; 104 } 105 RegisteredAction action = api.addAction( actionClass, actionName ); 106 107 return action; 108 } 109 110 private static final String POLL_METHOD_NAME_PREFIX = "djnpoll_"; 111 private static final String FORM_POST_METHOD_NAME_PREFIX = "djnform_"; 112 private static final String STANDARD_METHOD_NAME_PREFIX = "djn_"; 113 114 private void scanAndRegisterActionClass(RegisteredAction action) { 115 assert action != null; 116 117 RegisteredApi api = action.getApi(); 118 119 // *All* methods are candidates, including those in base classes, 120 // even if the base class does not have a DirectAction annotation! 121 List<Method> allMethods = new ArrayList<Method>(); 122 Class<?> cls = action.getActionClass(); 123 while( cls != null ) { 124 Method[] methods = cls.getDeclaredMethods(); // Get private, protected and other methods! 125 Collections.addAll( allMethods, methods ); 126 cls = cls.getSuperclass(); 127 } 128 129 for( Method method : allMethods ) { 130 // Check if the kind of direct method -if any 131 DirectMethod methodAnnotation = method.getAnnotation(DirectMethod.class); 132 boolean isStandardMethod = methodAnnotation != null; 133 if( !isStandardMethod ) { 134 isStandardMethod = method.getName().startsWith(STANDARD_METHOD_NAME_PREFIX); 135 } 136 137 DirectFormPostMethod postMethodAnnotation = method.getAnnotation(DirectFormPostMethod.class); 138 boolean isFormPostMethod = postMethodAnnotation != null; 139 if( !isFormPostMethod ) { 140 isFormPostMethod = method.getName().startsWith(FORM_POST_METHOD_NAME_PREFIX); 141 } 142 143 DirectPollMethod pollMethodAnnotation = method.getAnnotation(DirectPollMethod.class); 144 boolean isPollMethod = pollMethodAnnotation != null; 145 if( !isPollMethod ) { 146 isPollMethod = method.getName().startsWith( POLL_METHOD_NAME_PREFIX ); 147 } 148 149 // Check that a method is just of only one kind of method 150 if( isStandardMethod && isFormPostMethod ) { 151 ApiConfigurationException ex = ApiConfigurationException.forMethodCantBeStandardAndFormPostMethodAtTheSameTime(action, method); 152 logger.fatal( ex.getMessage(), ex ); 153 throw ex; 154 } 155 if( (methodAnnotation != null || postMethodAnnotation != null) && isPollMethod) { 156 ApiConfigurationException ex = ApiConfigurationException.forPollMethodCantBeStandardOrFormPostMethodAtTheSameTime(action, method); 157 logger.fatal( ex.getMessage(), ex ); 158 throw ex; 159 } 160 161 // Process standard and form post methods together, as they are very similar 162 if( isStandardMethod || isFormPostMethod) { 163 164 String methodName = ""; 165 if( isStandardMethod ) { 166 methodName = getStandardMethodName(method, methodAnnotation); 167 } 168 else { 169 methodName = getFormPostMethodName( method, postMethodAnnotation); 170 } 171 if( action.hasMethod(methodName) ) { 172 ApiConfigurationException ex = ApiConfigurationException.forMethodAlreadyRegisteredInAction(methodName, action.getName()); 173 logger.fatal( ex.getMessage(), ex ); 174 throw ex; 175 } 176 177 if( isFormPostMethod && !RegisteredMethod.isValidFormHandlingMethod(method)) { 178 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAFormHandler( action.getName(), methodName ); 179 logger.fatal( ex.getMessage(), ex ); 180 throw ex; 181 } 182 183 184 action.addMethod( methodName, method, isFormPostMethod ); 185 } 186 187 // Process "poll" method 188 if( isPollMethod ) { 189 createPollMethod(api, method, pollMethodAnnotation); 190 } 191 } 192 } 193 194 private String getFormPostMethodName(Method method, DirectFormPostMethod postMethodAnnotation) { 195 String methodName = ""; 196 if( postMethodAnnotation != null ) { 197 methodName = postMethodAnnotation.method(); 198 } 199 if( methodName.equals("" )) { 200 methodName = method.getName(); 201 } 202 if( methodName.startsWith(FORM_POST_METHOD_NAME_PREFIX)) { 203 methodName = method.getName().substring(FORM_POST_METHOD_NAME_PREFIX.length()); 204 } 205 return methodName; 206 } 207 208 private String getStandardMethodName(Method method, DirectMethod methodAnnotation) { 209 String methodName = ""; 210 if( methodAnnotation != null ) { 211 methodName = methodAnnotation.method(); 212 } 213 if( methodName.equals("" )) { 214 methodName = method.getName(); 215 } 216 if( methodName.startsWith(STANDARD_METHOD_NAME_PREFIX)) { 217 methodName = method.getName().substring(STANDARD_METHOD_NAME_PREFIX.length()); 218 } 219 return methodName; 220 } 221 222 private RegisteredPollMethod createPollMethod(RegisteredApi api, Method method, DirectPollMethod pollMethodAnnotation) { 223 assert api != null; 224 assert method != null; 225 226 String eventName = getEventName(method, pollMethodAnnotation); 227 228 if( this.registry.hasPollMethod(eventName)) { 229 ApiConfigurationException ex = ApiConfigurationException.forPollEventAlreadyRegistered( eventName ); 230 logger.fatal( ex.getMessage(), ex ); 231 throw ex; 232 } 233 234 if( !RegisteredPollMethod.isValidPollMethod(method)) { 235 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAPollHandler( method ); 236 logger.fatal( ex.getMessage(), ex ); 237 throw ex; 238 } 239 240 RegisteredPollMethod poll = api.addPollMethod( eventName, method); 241 return poll; 242 } 243 244 private String getEventName(Method method, DirectPollMethod pollMethodAnnotation) { 245 assert method != null; 246 247 String eventName = ""; 248 if( pollMethodAnnotation != null ) { 249 eventName = pollMethodAnnotation.event(); 250 } 251 if( eventName.equals("")) { 252 eventName = method.getName(); 253 } 254 if( eventName.startsWith(POLL_METHOD_NAME_PREFIX)) { 255 eventName = method.getName().substring(POLL_METHOD_NAME_PREFIX.length()); 256 } 257 return eventName; 258 } 259 260 }