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.router.dispatcher;
027    
028    import java.lang.reflect.Constructor;
029    import java.lang.reflect.Method;
030    import java.lang.reflect.Modifier;
031    import java.security.AccessController;
032    import java.security.PrivilegedAction;
033    
034    import com.softwarementors.extjs.djn.ClassUtils;
035    import com.softwarementors.extjs.djn.Timer;
036    import com.softwarementors.extjs.djn.api.RegisteredMethod;
037    import com.softwarementors.extjs.djn.router.processor.RequestException;
038    
039    import edu.umd.cs.findbugs.annotations.CheckForNull;
040    import edu.umd.cs.findbugs.annotations.NonNull;
041    
042    public abstract class DispatcherBase implements Dispatcher {
043      public Object dispatch( RegisteredMethod method, Object[] parameters ) {
044        assert method != null;
045        assert parameters != null;
046    
047        Method javaMethod = method.getMethod();
048        int expectedArgumentCount = method.getParameterCount();
049        if( parameters.length != expectedArgumentCount ) {
050          throw RequestException.forWrongMethodArgumentCount( method, parameters.length );
051        }
052    
053        Timer timer = new Timer();
054        try {
055          Object actionInstance = null;
056          try {
057            actionInstance = getInvokeInstance(method);
058          }
059          catch( Exception e ) {
060            throw MethodExecutionException.forUnableToGetActionInstance( method, e );
061          }
062          try {
063            Object result = invokeMethod( method, actionInstance, parameters);
064            return result;
065          }
066          catch( Exception e ) {
067            throw MethodExecutionException.forJavaMethodInvocationError( method, e );
068          }
069        }
070        finally {
071          timer.stop();
072          if( Timer.logger.isDebugEnabled()) {
073            timer.logDebugTimeInMilliseconds("  - Java method dispatch time (" + ClassUtils.getSimpleName(javaMethod.getDeclaringClass()) + "." + method.getName() + ")" );
074          }
075        }
076      }
077    
078      private @CheckForNull Object getInvokeInstance(RegisteredMethod method) throws Exception {
079        Object actionInstance = null;
080        if( !Modifier.isStatic(method.getMethod().getModifiers())) {
081          actionInstance = getInvokeInstanceForNonStaticMethod(method);
082          assert actionInstance != null;
083        }
084        return actionInstance;
085      }
086    
087      protected abstract Object getInvokeInstanceForNonStaticMethod(RegisteredMethod method) throws Exception;
088    
089      protected Object createInvokeInstanceForMethodWithDefaultConstructor(RegisteredMethod method) throws Exception {
090        assert method != null;
091    
092        Class<?> instanceClass = method.getActionClass();
093        Object methodInstance;
094        Constructor<?> c = instanceClass.getConstructor();
095        // Invoke private constructors too
096        boolean accessible = c.isAccessible();
097        try {
098          c.setAccessible(true);
099          methodInstance = c.newInstance();
100        }
101        finally {
102          c.setAccessible(accessible);
103        }
104        return methodInstance;
105      }
106    
107      // This class is a gadget needed to invoke the Method.setAccessible method
108      // 'the correct way' according to FindBugs: it flags direct usage as dangerous.
109      private static class MethodVisibilityModifier implements PrivilegedAction<Object> {
110        private boolean accessible;
111        private @NonNull Method method;
112    
113        public MethodVisibilityModifier(@NonNull Method method) {
114          assert method != null;
115          this.method = method;
116        }
117    
118        public Object run() {
119          this.method.setAccessible(this.accessible);
120          return null;
121        }
122    
123        public void setAccessible( boolean accessible ) {
124          this.accessible = accessible;
125        }
126      }
127    
128      protected Object invokeMethod(RegisteredMethod method, Object actionInstance, Object[] parameters) throws Exception {
129        assert method != null;
130        assert parameters != null;
131    
132        Method javaMethod = method.getMethod();
133        return invokeJavaMethod( actionInstance, javaMethod, parameters );
134      }
135    
136      protected final Object invokeJavaMethod(Object instance, @NonNull Method method, @NonNull Object[] parameters) throws Exception {
137        boolean accessible = method.isAccessible();
138        MethodVisibilityModifier visibilityModifier = new MethodVisibilityModifier(method);
139        try {
140          visibilityModifier.setAccessible(true);
141          AccessController.doPrivileged(visibilityModifier);
142          return method.invoke( instance, parameters );
143        }
144        finally {
145          visibilityModifier.setAccessible(accessible);
146          AccessController.doPrivileged(visibilityModifier);
147        }
148      }
149    
150    }