/* * 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.router.dispatcher; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.security.AccessController; import java.security.PrivilegedAction; import com.softwarementors.extjs.djn.ClassUtils; import com.softwarementors.extjs.djn.Timer; import com.softwarementors.extjs.djn.api.RegisteredMethod; import com.softwarementors.extjs.djn.router.processor.RequestException; import edu.umd.cs.findbugs.annotations.CheckForNull; import edu.umd.cs.findbugs.annotations.NonNull; public abstract class DispatcherBase implements Dispatcher { public Object dispatch( RegisteredMethod method, Object[] parameters ) { assert method != null; assert parameters != null; Method javaMethod = method.getMethod(); int expectedArgumentCount = method.getParameterCount(); if( parameters.length != expectedArgumentCount ) { throw RequestException.forWrongMethodArgumentCount( method, parameters.length ); } Timer timer = new Timer(); try { Object actionInstance = null; try { actionInstance = getInvokeInstance(method); } catch( Exception e ) { throw MethodExecutionException.forUnableToGetActionInstance( method, e ); } try { Object result = invokeMethod( method, actionInstance, parameters); return result; } catch( Exception e ) { throw MethodExecutionException.forJavaMethodInvocationError( method, e ); } } finally { timer.stop(); if( Timer.logger.isDebugEnabled()) { timer.logDebugTimeInMilliseconds(" - Java method dispatch time (" + ClassUtils.getSimpleName(javaMethod.getDeclaringClass()) + "." + method.getName() + ")" ); } } } private @CheckForNull Object getInvokeInstance(RegisteredMethod method) throws Exception { Object actionInstance = null; if( !Modifier.isStatic(method.getMethod().getModifiers())) { actionInstance = getInvokeInstanceForNonStaticMethod(method); assert actionInstance != null; } return actionInstance; } protected abstract Object getInvokeInstanceForNonStaticMethod(RegisteredMethod method) throws Exception; protected Object createInvokeInstanceForMethodWithDefaultConstructor(RegisteredMethod method) throws Exception { assert method != null; Class<?> instanceClass = method.getActionClass(); Object methodInstance; Constructor<?> c = instanceClass.getConstructor(); // Invoke private constructors too boolean accessible = c.isAccessible(); try { c.setAccessible(true); methodInstance = c.newInstance(); } finally { c.setAccessible(accessible); } return methodInstance; } // This class is a gadget needed to invoke the Method.setAccessible method // 'the correct way' according to FindBugs: it flags direct usage as dangerous. private static class MethodVisibilityModifier implements PrivilegedAction<Object> { private boolean accessible; private @NonNull Method method; public MethodVisibilityModifier(@NonNull Method method) { assert method != null; this.method = method; } public Object run() { this.method.setAccessible(this.accessible); return null; } public void setAccessible( boolean accessible ) { this.accessible = accessible; } } protected Object invokeMethod(RegisteredMethod method, Object actionInstance, Object[] parameters) throws Exception { assert method != null; assert parameters != null; Method javaMethod = method.getMethod(); return invokeJavaMethod( actionInstance, javaMethod, parameters ); } protected static final Object invokeJavaMethod(Object instance, @NonNull Method method, @NonNull Object[] parameters) throws Exception { boolean accessible = method.isAccessible(); MethodVisibilityModifier visibilityModifier = new MethodVisibilityModifier(method); try { visibilityModifier.setAccessible(true); AccessController.doPrivileged(visibilityModifier); return method.invoke( instance, parameters ); } finally { visibilityModifier.setAccessible(accessible); AccessController.doPrivileged(visibilityModifier); } } }