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 static 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 }