001 /* 002 * Copyright © 2008, 2012 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 Lesser 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 Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser 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.List; 032 033 import org.apache.log4j.Logger; 034 035 import com.softwarementors.extjs.djn.ClassUtils; 036 import com.softwarementors.extjs.djn.api.RegisteredAction; 037 import com.softwarementors.extjs.djn.api.RegisteredApi; 038 import com.softwarementors.extjs.djn.api.RegisteredPollMethod; 039 import com.softwarementors.extjs.djn.api.RegisteredStandardMethod; 040 import com.softwarementors.extjs.djn.api.Registry; 041 import com.softwarementors.extjs.djn.config.ApiConfiguration; 042 import com.softwarementors.extjs.djn.config.ApiConfigurationException; 043 import com.softwarementors.extjs.djn.config.annotations.DirectAction; 044 import com.softwarementors.extjs.djn.config.annotations.DirectFormPostMethod; 045 import com.softwarementors.extjs.djn.config.annotations.DirectMethod; 046 import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod; 047 048 import edu.umd.cs.findbugs.annotations.NonNull; 049 050 public class Scanner { 051 @NonNull private static final Logger logger = Logger.getLogger( Scanner.class); 052 @NonNull private Registry registry; 053 054 public Scanner(Registry registry) { 055 assert registry != null; 056 057 this.registry = registry; 058 } 059 060 public void scanAndRegisterApiConfigurations(List<ApiConfiguration> apiConfigurations) { 061 assert apiConfigurations != null; 062 063 for( ApiConfiguration api: apiConfigurations ) { 064 scanAndRegisterApiConfiguration(api); 065 } 066 } 067 068 /* package */ void scanAndRegisterApiConfiguration(ApiConfiguration api) { 069 assert api != null; 070 071 if( this.registry.hasApi( api.getName() )) { 072 ApiConfigurationException ex = ApiConfigurationException.forApiAlreadyRegistered( api.getName()); 073 logger.fatal( ex.getMessage(), ex ); 074 throw ex; 075 } 076 077 RegisteredApi registeredApi = this.registry.addApi( api.getName(), api.getApiFile(), api.getFullApiFileName(), api.getApiNamespace(), api.getActionsNamespace()); 078 079 List<Class<?>> actionClasses = api.getClasses(); 080 for( Class<?> cls : actionClasses ) { 081 assert cls != null; 082 scanAndRegisterActionClass(registeredApi, cls ); 083 } 084 } 085 086 public void scanAndRegisterActionClass( RegisteredApi api, Class<?> actionClass ) { 087 assert api != null; 088 assert actionClass != null; 089 090 /* 091 Map<Class<?>, RegisteredAction> actionsByClass = new HashMap<Class<?>, RegisteredAction>(); 092 093 if( actionsByClass.containsKey( actionClass ) ) { 094 ApiConfigurationException ex = ApiConfigurationException.forClassAlreadyRegisteredAsAction(actionClass); 095 logger.fatal( ex.getMessage(), ex ); 096 throw ex; 097 } 098 */ 099 100 if( logger.isDebugEnabled() ) { 101 logger.debug( "Scanning Java class: " + actionClass.getName() ); 102 } 103 104 List<RegisteredAction> actions = createActionsFromJavaClass(api, actionClass); 105 scanAndRegisterActionClass(actions); 106 //actionsByClass.put( actionClass, action); 107 } 108 109 private List<RegisteredAction> createActionsFromJavaClass(RegisteredApi api, Class<?> actionClass) { 110 assert api != null; 111 assert actionClass != null; 112 113 DirectAction actionAnnotation = actionClass.getAnnotation(DirectAction.class); 114 List<String> actionNames = new ArrayList<String>(); 115 if( actionAnnotation != null ) { 116 Collections.addAll( actionNames, actionAnnotation.action() ); 117 } 118 119 if( actionNames.isEmpty()) { 120 actionNames.add( ClassUtils.getSimpleName(actionClass) ); 121 } 122 123 List<RegisteredAction> actions = new ArrayList<RegisteredAction>(); 124 for( String actionName : actionNames ) { 125 if( this.registry.hasAction( actionName ) ) { 126 RegisteredAction existingAction = this.registry.getAction( actionName ); 127 ApiConfigurationException ex = ApiConfigurationException.forActionAlreadyRegistered(actionName, actionClass, existingAction.getActionClass()); 128 logger.fatal( ex.getMessage(), ex ); 129 throw ex; 130 } 131 RegisteredAction action = api.addAction( actionClass, actionName ); 132 actions.add( action ); 133 } 134 135 return actions; 136 } 137 138 private static final String POLL_METHOD_NAME_PREFIX = "djnpoll_"; 139 private static final String FORM_POST_METHOD_NAME_PREFIX = "djnform_"; 140 private static final String STANDARD_METHOD_NAME_PREFIX = "djn_"; 141 142 private void scanAndRegisterActionClass(List<RegisteredAction> actions) { 143 assert actions != null; 144 assert !actions.isEmpty(); 145 146 RegisteredAction actionTemplate = actions.get(0); 147 148 // *All* methods are candidates, including those in base classes, 149 // even if the base class does not have a DirectAction annotation! 150 List<Method> allMethods = new ArrayList<Method>(); 151 Class<?> cls = actionTemplate.getActionClass(); 152 while( cls != null ) { 153 Method[] methods = cls.getDeclaredMethods(); // Get private, protected and other methods! 154 Collections.addAll( allMethods, methods ); 155 cls = cls.getSuperclass(); 156 } 157 158 for( Method method : allMethods ) { 159 // Check if the kind of direct method -if any 160 DirectMethod methodAnnotation = method.getAnnotation(DirectMethod.class); 161 boolean isStandardMethod = methodAnnotation != null; 162 if( !isStandardMethod ) { 163 isStandardMethod = method.getName().startsWith(STANDARD_METHOD_NAME_PREFIX); 164 } 165 166 DirectFormPostMethod postMethodAnnotation = method.getAnnotation(DirectFormPostMethod.class); 167 boolean isFormPostMethod = postMethodAnnotation != null; 168 if( !isFormPostMethod ) { 169 isFormPostMethod = method.getName().startsWith(FORM_POST_METHOD_NAME_PREFIX); 170 } 171 172 DirectPollMethod pollMethodAnnotation = method.getAnnotation(DirectPollMethod.class); 173 boolean isPollMethod = pollMethodAnnotation != null; 174 if( !isPollMethod ) { 175 isPollMethod = method.getName().startsWith( POLL_METHOD_NAME_PREFIX ); 176 } 177 178 // Check that a method is just of only one kind of method 179 if( isStandardMethod && isFormPostMethod ) { 180 ApiConfigurationException ex = ApiConfigurationException.forMethodCantBeStandardAndFormPostMethodAtTheSameTime(actionTemplate, method); 181 logger.fatal( ex.getMessage(), ex ); 182 throw ex; 183 } 184 if( (methodAnnotation != null || postMethodAnnotation != null) && isPollMethod) { 185 ApiConfigurationException ex = ApiConfigurationException.forPollMethodCantBeStandardOrFormPostMethodAtTheSameTime(actionTemplate, method); 186 logger.fatal( ex.getMessage(), ex ); 187 throw ex; 188 } 189 190 // Process standard and form post methods together, as they are very similar 191 if( isStandardMethod || isFormPostMethod) { 192 193 String methodName = ""; 194 if( isStandardMethod ) { 195 methodName = getStandardMethodName(method, methodAnnotation); 196 } 197 else { 198 methodName = getFormPostMethodName( method, postMethodAnnotation); 199 } 200 if( actionTemplate.hasStandardMethod(methodName) ) { 201 ApiConfigurationException ex = ApiConfigurationException.forMethodAlreadyRegisteredInAction(methodName, actionTemplate.getName()); 202 logger.fatal( ex.getMessage(), ex ); 203 throw ex; 204 } 205 206 if( isFormPostMethod && !RegisteredStandardMethod.isValidFormHandlingMethod(method)) { 207 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAFormHandler( actionTemplate.getName(), methodName ); 208 logger.fatal( ex.getMessage(), ex ); 209 throw ex; 210 } 211 212 for( RegisteredAction actionToRegister : actions ) { 213 actionToRegister.addStandardMethod( methodName, method, isFormPostMethod ); 214 } 215 } 216 217 // Process "poll" method 218 if( isPollMethod ) { 219 for( RegisteredAction actionToRegister : actions ) { 220 createPollMethod(actionToRegister, method, pollMethodAnnotation); 221 } 222 } 223 } 224 } 225 226 private static String getFormPostMethodName(Method method, DirectFormPostMethod postMethodAnnotation) { 227 String methodName = ""; 228 if( postMethodAnnotation != null ) { 229 methodName = postMethodAnnotation.method(); 230 } 231 if( methodName.equals("" )) { 232 methodName = method.getName(); 233 } 234 if( methodName.startsWith(FORM_POST_METHOD_NAME_PREFIX)) { 235 methodName = method.getName().substring(FORM_POST_METHOD_NAME_PREFIX.length()); 236 } 237 return methodName; 238 } 239 240 private static String getStandardMethodName(Method method, DirectMethod methodAnnotation) { 241 String methodName = ""; 242 if( methodAnnotation != null ) { 243 methodName = methodAnnotation.method(); 244 } 245 if( methodName.equals("" )) { 246 methodName = method.getName(); 247 } 248 if( methodName.startsWith(STANDARD_METHOD_NAME_PREFIX)) { 249 methodName = method.getName().substring(STANDARD_METHOD_NAME_PREFIX.length()); 250 } 251 return methodName; 252 } 253 254 private RegisteredPollMethod createPollMethod(RegisteredAction action, Method method, DirectPollMethod pollMethodAnnotation) { 255 assert action != null; 256 assert method != null; 257 258 String eventName = getEventName(method, pollMethodAnnotation); 259 260 if( this.registry.hasPollMethod(eventName)) { 261 ApiConfigurationException ex = ApiConfigurationException.forPollEventAlreadyRegistered( eventName ); 262 logger.fatal( ex.getMessage(), ex ); 263 throw ex; 264 } 265 266 if( !RegisteredPollMethod.isValidPollMethod(method)) { 267 ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAPollHandler( method ); 268 logger.fatal( ex.getMessage(), ex ); 269 throw ex; 270 } 271 272 RegisteredPollMethod poll = action.addPollMethod( eventName, method); 273 return poll; 274 } 275 276 private static String getEventName(Method method, DirectPollMethod pollMethodAnnotation) { 277 assert method != null; 278 279 String eventName = ""; 280 if( pollMethodAnnotation != null ) { 281 eventName = pollMethodAnnotation.event(); 282 } 283 if( eventName.equals("")) { 284 eventName = method.getName(); 285 } 286 if( eventName.startsWith(POLL_METHOD_NAME_PREFIX)) { 287 eventName = method.getName().substring(POLL_METHOD_NAME_PREFIX.length()); 288 } 289 return eventName; 290 } 291 292 }