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