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