/* * Copyright © 2008, 2012 Pedro Agulló Soliveres. * * This file is part of DirectJNgine. * * DirectJNgine is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License. * * Commercial use is permitted to the extent that the code/component(s) * do NOT become part of another Open Source or Commercially developed * licensed development library or toolkit without explicit permission. * * DirectJNgine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with DirectJNgine. If not, see <http://www.gnu.org/licenses/>. * * This software uses the ExtJs library (http://extjs.com), which is * distributed under the GPL v3 license (see http://extjs.com/license). */ package com.softwarementors.extjs.djn.scanner; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.log4j.Logger; import com.softwarementors.extjs.djn.ClassUtils; import com.softwarementors.extjs.djn.api.RegisteredAction; import com.softwarementors.extjs.djn.api.RegisteredApi; import com.softwarementors.extjs.djn.api.RegisteredPollMethod; import com.softwarementors.extjs.djn.api.RegisteredStandardMethod; import com.softwarementors.extjs.djn.api.Registry; import com.softwarementors.extjs.djn.config.ApiConfiguration; import com.softwarementors.extjs.djn.config.ApiConfigurationException; import com.softwarementors.extjs.djn.config.annotations.DirectAction; import com.softwarementors.extjs.djn.config.annotations.DirectFormPostMethod; import com.softwarementors.extjs.djn.config.annotations.DirectMethod; import com.softwarementors.extjs.djn.config.annotations.DirectPollMethod; import edu.umd.cs.findbugs.annotations.NonNull; public class Scanner { @NonNull private static final Logger logger = Logger.getLogger( Scanner.class); @NonNull private Registry registry; public Scanner(Registry registry) { assert registry != null; this.registry = registry; } public void scanAndRegisterApiConfigurations(List<ApiConfiguration> apiConfigurations) { assert apiConfigurations != null; for( ApiConfiguration api: apiConfigurations ) { scanAndRegisterApiConfiguration(api); } } /* package */ void scanAndRegisterApiConfiguration(ApiConfiguration api) { assert api != null; if( this.registry.hasApi( api.getName() )) { ApiConfigurationException ex = ApiConfigurationException.forApiAlreadyRegistered( api.getName()); logger.fatal( ex.getMessage(), ex ); throw ex; } RegisteredApi registeredApi = this.registry.addApi( api.getName(), api.getApiFile(), api.getFullApiFileName(), api.getApiNamespace(), api.getActionsNamespace()); List<Class<?>> actionClasses = api.getClasses(); for( Class<?> cls : actionClasses ) { assert cls != null; scanAndRegisterActionClass(registeredApi, cls ); } } public void scanAndRegisterActionClass( RegisteredApi api, Class<?> actionClass ) { assert api != null; assert actionClass != null; /* Map<Class<?>, RegisteredAction> actionsByClass = new HashMap<Class<?>, RegisteredAction>(); if( actionsByClass.containsKey( actionClass ) ) { ApiConfigurationException ex = ApiConfigurationException.forClassAlreadyRegisteredAsAction(actionClass); logger.fatal( ex.getMessage(), ex ); throw ex; } */ if( logger.isDebugEnabled() ) { logger.debug( "Scanning Java class: " + actionClass.getName() ); } List<RegisteredAction> actions = createActionsFromJavaClass(api, actionClass); scanAndRegisterActionClass(actions); //actionsByClass.put( actionClass, action); } private List<RegisteredAction> createActionsFromJavaClass(RegisteredApi api, Class<?> actionClass) { assert api != null; assert actionClass != null; DirectAction actionAnnotation = actionClass.getAnnotation(DirectAction.class); List<String> actionNames = new ArrayList<String>(); if( actionAnnotation != null ) { Collections.addAll( actionNames, actionAnnotation.action() ); } if( actionNames.isEmpty()) { actionNames.add( ClassUtils.getSimpleName(actionClass) ); } List<RegisteredAction> actions = new ArrayList<RegisteredAction>(); for( String actionName : actionNames ) { if( this.registry.hasAction( actionName ) ) { RegisteredAction existingAction = this.registry.getAction( actionName ); ApiConfigurationException ex = ApiConfigurationException.forActionAlreadyRegistered(actionName, actionClass, existingAction.getActionClass()); logger.fatal( ex.getMessage(), ex ); throw ex; } RegisteredAction action = api.addAction( actionClass, actionName ); actions.add( action ); } return actions; } private static final String POLL_METHOD_NAME_PREFIX = "djnpoll_"; private static final String FORM_POST_METHOD_NAME_PREFIX = "djnform_"; private static final String STANDARD_METHOD_NAME_PREFIX = "djn_"; private void scanAndRegisterActionClass(List<RegisteredAction> actions) { assert actions != null; assert !actions.isEmpty(); RegisteredAction actionTemplate = actions.get(0); // *All* methods are candidates, including those in base classes, // even if the base class does not have a DirectAction annotation! List<Method> allMethods = new ArrayList<Method>(); Class<?> cls = actionTemplate.getActionClass(); while( cls != null ) { Method[] methods = cls.getDeclaredMethods(); // Get private, protected and other methods! Collections.addAll( allMethods, methods ); cls = cls.getSuperclass(); } for( Method method : allMethods ) { // Check if the kind of direct method -if any DirectMethod methodAnnotation = method.getAnnotation(DirectMethod.class); boolean isStandardMethod = methodAnnotation != null; if( !isStandardMethod ) { isStandardMethod = method.getName().startsWith(STANDARD_METHOD_NAME_PREFIX); } DirectFormPostMethod postMethodAnnotation = method.getAnnotation(DirectFormPostMethod.class); boolean isFormPostMethod = postMethodAnnotation != null; if( !isFormPostMethod ) { isFormPostMethod = method.getName().startsWith(FORM_POST_METHOD_NAME_PREFIX); } DirectPollMethod pollMethodAnnotation = method.getAnnotation(DirectPollMethod.class); boolean isPollMethod = pollMethodAnnotation != null; if( !isPollMethod ) { isPollMethod = method.getName().startsWith( POLL_METHOD_NAME_PREFIX ); } // Check that a method is just of only one kind of method if( isStandardMethod && isFormPostMethod ) { ApiConfigurationException ex = ApiConfigurationException.forMethodCantBeStandardAndFormPostMethodAtTheSameTime(actionTemplate, method); logger.fatal( ex.getMessage(), ex ); throw ex; } if( (methodAnnotation != null || postMethodAnnotation != null) && isPollMethod) { ApiConfigurationException ex = ApiConfigurationException.forPollMethodCantBeStandardOrFormPostMethodAtTheSameTime(actionTemplate, method); logger.fatal( ex.getMessage(), ex ); throw ex; } // Process standard and form post methods together, as they are very similar if( isStandardMethod || isFormPostMethod) { String methodName = ""; if( isStandardMethod ) { methodName = getStandardMethodName(method, methodAnnotation); } else { methodName = getFormPostMethodName( method, postMethodAnnotation); } if( actionTemplate.hasStandardMethod(methodName) ) { ApiConfigurationException ex = ApiConfigurationException.forMethodAlreadyRegisteredInAction(methodName, actionTemplate.getName()); logger.fatal( ex.getMessage(), ex ); throw ex; } if( isFormPostMethod && !RegisteredStandardMethod.isValidFormHandlingMethod(method)) { ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAFormHandler( actionTemplate.getName(), methodName ); logger.fatal( ex.getMessage(), ex ); throw ex; } for( RegisteredAction actionToRegister : actions ) { actionToRegister.addStandardMethod( methodName, method, isFormPostMethod ); } } // Process "poll" method if( isPollMethod ) { for( RegisteredAction actionToRegister : actions ) { createPollMethod(actionToRegister, method, pollMethodAnnotation); } } } } private static String getFormPostMethodName(Method method, DirectFormPostMethod postMethodAnnotation) { String methodName = ""; if( postMethodAnnotation != null ) { methodName = postMethodAnnotation.method(); } if( methodName.equals("" )) { methodName = method.getName(); } if( methodName.startsWith(FORM_POST_METHOD_NAME_PREFIX)) { methodName = method.getName().substring(FORM_POST_METHOD_NAME_PREFIX.length()); } return methodName; } private static String getStandardMethodName(Method method, DirectMethod methodAnnotation) { String methodName = ""; if( methodAnnotation != null ) { methodName = methodAnnotation.method(); } if( methodName.equals("" )) { methodName = method.getName(); } if( methodName.startsWith(STANDARD_METHOD_NAME_PREFIX)) { methodName = method.getName().substring(STANDARD_METHOD_NAME_PREFIX.length()); } return methodName; } private RegisteredPollMethod createPollMethod(RegisteredAction action, Method method, DirectPollMethod pollMethodAnnotation) { assert action != null; assert method != null; String eventName = getEventName(method, pollMethodAnnotation); if( this.registry.hasPollMethod(eventName)) { ApiConfigurationException ex = ApiConfigurationException.forPollEventAlreadyRegistered( eventName ); logger.fatal( ex.getMessage(), ex ); throw ex; } if( !RegisteredPollMethod.isValidPollMethod(method)) { ApiConfigurationException ex = ApiConfigurationException.forMethodHasWrongParametersForAPollHandler( method ); logger.fatal( ex.getMessage(), ex ); throw ex; } RegisteredPollMethod poll = action.addPollMethod( eventName, method); return poll; } private static String getEventName(Method method, DirectPollMethod pollMethodAnnotation) { assert method != null; String eventName = ""; if( pollMethodAnnotation != null ) { eventName = pollMethodAnnotation.event(); } if( eventName.equals("")) { eventName = method.getName(); } if( eventName.startsWith(POLL_METHOD_NAME_PREFIX)) { eventName = method.getName().substring(POLL_METHOD_NAME_PREFIX.length()); } return eventName; } }