4207 |
04 Apr 08 |
nicklas |
1 |
/** |
4207 |
04 Apr 08 |
nicklas |
$Id$ |
4207 |
04 Apr 08 |
nicklas |
3 |
|
4207 |
04 Apr 08 |
nicklas |
Copyright (C) Authors contributing to this file. |
4207 |
04 Apr 08 |
nicklas |
5 |
|
4207 |
04 Apr 08 |
nicklas |
This file is part of BASE - BioArray Software Environment. |
4207 |
04 Apr 08 |
nicklas |
Available at http://base.thep.lu.se/ |
4207 |
04 Apr 08 |
nicklas |
8 |
|
4207 |
04 Apr 08 |
nicklas |
BASE is free software; you can redistribute it and/or |
4207 |
04 Apr 08 |
nicklas |
modify it under the terms of the GNU General Public License |
4479 |
05 Sep 08 |
jari |
as published by the Free Software Foundation; either version 3 |
4207 |
04 Apr 08 |
nicklas |
of the License, or (at your option) any later version. |
4207 |
04 Apr 08 |
nicklas |
13 |
|
4207 |
04 Apr 08 |
nicklas |
BASE is distributed in the hope that it will be useful, |
4207 |
04 Apr 08 |
nicklas |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
4207 |
04 Apr 08 |
nicklas |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4207 |
04 Apr 08 |
nicklas |
GNU General Public License for more details. |
4207 |
04 Apr 08 |
nicklas |
18 |
|
4207 |
04 Apr 08 |
nicklas |
You should have received a copy of the GNU General Public License |
4515 |
11 Sep 08 |
jari |
along with BASE. If not, see <http://www.gnu.org/licenses/>. |
4207 |
04 Apr 08 |
nicklas |
21 |
*/ |
4207 |
04 Apr 08 |
nicklas |
22 |
package net.sf.basedb.util.extensions.debug; |
4207 |
04 Apr 08 |
nicklas |
23 |
|
4207 |
04 Apr 08 |
nicklas |
24 |
import java.lang.reflect.InvocationHandler; |
4207 |
04 Apr 08 |
nicklas |
25 |
import java.lang.reflect.Method; |
4207 |
04 Apr 08 |
nicklas |
26 |
import java.lang.reflect.Proxy; |
4207 |
04 Apr 08 |
nicklas |
27 |
import java.util.HashMap; |
4207 |
04 Apr 08 |
nicklas |
28 |
import java.util.Map; |
4207 |
04 Apr 08 |
nicklas |
29 |
|
4207 |
04 Apr 08 |
nicklas |
30 |
import net.sf.basedb.util.Values; |
4207 |
04 Apr 08 |
nicklas |
31 |
import net.sf.basedb.util.extensions.Action; |
4207 |
04 Apr 08 |
nicklas |
32 |
import net.sf.basedb.util.extensions.ActionFactory; |
4207 |
04 Apr 08 |
nicklas |
33 |
import net.sf.basedb.util.extensions.ExtensionPoint; |
4207 |
04 Apr 08 |
nicklas |
34 |
import net.sf.basedb.util.extensions.InvokationContext; |
4208 |
07 Apr 08 |
nicklas |
35 |
import net.sf.basedb.util.extensions.xml.PathSetter; |
4208 |
07 Apr 08 |
nicklas |
36 |
import net.sf.basedb.util.extensions.xml.VariableSetter; |
4207 |
04 Apr 08 |
nicklas |
37 |
|
4207 |
04 Apr 08 |
nicklas |
38 |
/** |
4207 |
04 Apr 08 |
nicklas |
Generic action factory class that can generate instances of |
4207 |
04 Apr 08 |
nicklas |
actions if the action class is an interface. Use this factory |
4207 |
04 Apr 08 |
nicklas |
for development and debugging only. The performance and |
4207 |
04 Apr 08 |
nicklas |
memory requirements of this implementation is unsure for a |
4207 |
04 Apr 08 |
nicklas |
production environment. |
4207 |
04 Apr 08 |
nicklas |
<p> |
4207 |
04 Apr 08 |
nicklas |
45 |
|
4207 |
04 Apr 08 |
nicklas |
This class works ONLY if the {@link ExtensionPoint#getActionClass()} |
4207 |
04 Apr 08 |
nicklas |
is an interface, eg. {@link Class#isInterface()} return true. If so, |
4207 |
04 Apr 08 |
nicklas |
we use Java reflection to generate a {@link Proxy} that implements |
4207 |
04 Apr 08 |
nicklas |
the desired action class interface. |
4207 |
04 Apr 08 |
nicklas |
<p> |
4207 |
04 Apr 08 |
nicklas |
51 |
|
4207 |
04 Apr 08 |
nicklas |
Parameters in the XML file will be passed on as return values for |
4207 |
04 Apr 08 |
nicklas |
method calls on the proxy, following the usual syntax rules. Eg. |
6881 |
21 Apr 15 |
nicklas |
The parameter value <code><icon>/images/icon.png</icon></code> will |
4207 |
04 Apr 08 |
nicklas |
be returned as a result of calling <code>getIcon()</code> on the |
4207 |
04 Apr 08 |
nicklas |
proxy. Null is returned for parameters that have not been specified |
4207 |
04 Apr 08 |
nicklas |
in the XML file. |
4207 |
04 Apr 08 |
nicklas |
<p> |
4207 |
04 Apr 08 |
nicklas |
59 |
|
4207 |
04 Apr 08 |
nicklas |
The factory can convert the string values to other data types for the |
4207 |
04 Apr 08 |
nicklas |
following classes: int, long, float, double and boolean. It can handle |
4207 |
04 Apr 08 |
nicklas |
both primitive and wrapper classes. Example: |
6881 |
21 Apr 15 |
nicklas |
<code><enabled>1</enabled></code> will return <code>true</code> if |
4207 |
04 Apr 08 |
nicklas |
<code>isEnabled()</code> is called on the proxy. |
4207 |
04 Apr 08 |
nicklas |
<p> |
4207 |
04 Apr 08 |
nicklas |
66 |
|
4207 |
04 Apr 08 |
nicklas |
<b>Parameters supported by this factory</b><br> |
4207 |
04 Apr 08 |
nicklas |
This factory supports all parameters. Some parameters may trigger special |
4207 |
04 Apr 08 |
nicklas |
actions: |
4207 |
04 Apr 08 |
nicklas |
70 |
|
4207 |
04 Apr 08 |
nicklas |
<ul> |
4207 |
04 Apr 08 |
nicklas |
<li>disabled: If <code>true</code> the {@link #prepareContext(InvokationContext)} |
4207 |
04 Apr 08 |
nicklas |
method returns false |
4207 |
04 Apr 08 |
nicklas |
</ul> |
4207 |
04 Apr 08 |
nicklas |
75 |
|
4207 |
04 Apr 08 |
nicklas |
@author nicklas |
4207 |
04 Apr 08 |
nicklas |
@version 2.7 |
4207 |
04 Apr 08 |
nicklas |
@base.modified $Date$ |
4207 |
04 Apr 08 |
nicklas |
79 |
*/ |
4207 |
04 Apr 08 |
nicklas |
80 |
public class ProxyActionFactory |
4207 |
04 Apr 08 |
nicklas |
81 |
implements ActionFactory<Action>, InvocationHandler |
4207 |
04 Apr 08 |
nicklas |
82 |
{ |
4207 |
04 Apr 08 |
nicklas |
83 |
|
4207 |
04 Apr 08 |
nicklas |
84 |
private boolean disabled; |
4207 |
04 Apr 08 |
nicklas |
85 |
private Action proxy; |
4207 |
04 Apr 08 |
nicklas |
86 |
private Action[] actions; |
4207 |
04 Apr 08 |
nicklas |
87 |
private Map<String, String> parameters; |
4207 |
04 Apr 08 |
nicklas |
88 |
private Map<String, Object> valueCache; |
5384 |
13 Aug 10 |
nicklas |
89 |
private volatile boolean initialised; |
4207 |
04 Apr 08 |
nicklas |
90 |
|
4207 |
04 Apr 08 |
nicklas |
91 |
/** |
4207 |
04 Apr 08 |
nicklas |
Creates a new proxy factory. |
4207 |
04 Apr 08 |
nicklas |
93 |
*/ |
4207 |
04 Apr 08 |
nicklas |
94 |
public ProxyActionFactory() |
4207 |
04 Apr 08 |
nicklas |
95 |
{} |
4207 |
04 Apr 08 |
nicklas |
96 |
|
4207 |
04 Apr 08 |
nicklas |
97 |
/* |
4207 |
04 Apr 08 |
nicklas |
From the ActionFactory interface |
4207 |
04 Apr 08 |
nicklas |
99 |
-------------------------------- |
4207 |
04 Apr 08 |
nicklas |
100 |
*/ |
4207 |
04 Apr 08 |
nicklas |
101 |
@Override |
4207 |
04 Apr 08 |
nicklas |
102 |
public boolean prepareContext(InvokationContext<? super Action> context) |
4207 |
04 Apr 08 |
nicklas |
103 |
{ |
4207 |
04 Apr 08 |
nicklas |
104 |
if (disabled) return false; |
4208 |
07 Apr 08 |
nicklas |
105 |
if (!initialised) |
4207 |
04 Apr 08 |
nicklas |
106 |
{ |
4207 |
04 Apr 08 |
nicklas |
107 |
initProxy(context.getExtensionPoint().getActionClass()); |
4207 |
04 Apr 08 |
nicklas |
108 |
} |
4207 |
04 Apr 08 |
nicklas |
109 |
return true; |
4207 |
04 Apr 08 |
nicklas |
110 |
} |
4207 |
04 Apr 08 |
nicklas |
111 |
|
4207 |
04 Apr 08 |
nicklas |
112 |
|
4207 |
04 Apr 08 |
nicklas |
113 |
@Override |
6875 |
20 Apr 15 |
nicklas |
114 |
public Action[] getActions(InvokationContext<? super Action> context) |
4207 |
04 Apr 08 |
nicklas |
115 |
{ |
4207 |
04 Apr 08 |
nicklas |
116 |
return disabled ? null : actions; |
4207 |
04 Apr 08 |
nicklas |
117 |
} |
4207 |
04 Apr 08 |
nicklas |
118 |
// ----------------------------------- |
4207 |
04 Apr 08 |
nicklas |
119 |
|
4207 |
04 Apr 08 |
nicklas |
120 |
/* |
4207 |
04 Apr 08 |
nicklas |
From the InvokationHandler interface |
4207 |
04 Apr 08 |
nicklas |
122 |
------------------------------------ |
4207 |
04 Apr 08 |
nicklas |
123 |
*/ |
4207 |
04 Apr 08 |
nicklas |
124 |
@Override |
4207 |
04 Apr 08 |
nicklas |
125 |
public Object invoke(Object proxy, Method method, Object[] args) |
4207 |
04 Apr 08 |
nicklas |
126 |
throws Throwable |
4207 |
04 Apr 08 |
nicklas |
127 |
{ |
4207 |
04 Apr 08 |
nicklas |
128 |
String parameterName = getParameterName(method.getName()); |
4207 |
04 Apr 08 |
nicklas |
129 |
return getParameterValue(parameterName, method.getReturnType()); |
4207 |
04 Apr 08 |
nicklas |
130 |
} |
4207 |
04 Apr 08 |
nicklas |
131 |
// ------------------------------------- |
4207 |
04 Apr 08 |
nicklas |
132 |
|
4207 |
04 Apr 08 |
nicklas |
133 |
/** |
4207 |
04 Apr 08 |
nicklas |
Set generic parameters. |
4207 |
04 Apr 08 |
nicklas |
@param name The name of the parameter |
4207 |
04 Apr 08 |
nicklas |
@param value The value of the parameter |
4207 |
04 Apr 08 |
nicklas |
137 |
*/ |
4208 |
07 Apr 08 |
nicklas |
138 |
@PathSetter |
4208 |
07 Apr 08 |
nicklas |
139 |
@VariableSetter |
4207 |
04 Apr 08 |
nicklas |
140 |
public void setParameter(String name, String value) |
4207 |
04 Apr 08 |
nicklas |
141 |
{ |
4207 |
04 Apr 08 |
nicklas |
142 |
if (parameters == null) |
4207 |
04 Apr 08 |
nicklas |
143 |
{ |
4207 |
04 Apr 08 |
nicklas |
144 |
parameters = new HashMap<String, String>(); |
4207 |
04 Apr 08 |
nicklas |
145 |
} |
4207 |
04 Apr 08 |
nicklas |
146 |
parameters.put(name, value); |
4207 |
04 Apr 08 |
nicklas |
147 |
} |
4207 |
04 Apr 08 |
nicklas |
148 |
|
4207 |
04 Apr 08 |
nicklas |
149 |
/** |
4207 |
04 Apr 08 |
nicklas |
Sets the disabled/enabled status of this factory. |
4207 |
04 Apr 08 |
nicklas |
@param disabled A string that is parsed to a boolean |
4207 |
04 Apr 08 |
nicklas |
by {@link Values#getBoolean(String)} |
4207 |
04 Apr 08 |
nicklas |
153 |
*/ |
4207 |
04 Apr 08 |
nicklas |
154 |
public void setDisabled(String disabled) |
4207 |
04 Apr 08 |
nicklas |
155 |
{ |
4207 |
04 Apr 08 |
nicklas |
156 |
this.disabled = Values.getBoolean(disabled); |
4207 |
04 Apr 08 |
nicklas |
157 |
} |
4207 |
04 Apr 08 |
nicklas |
158 |
|
4207 |
04 Apr 08 |
nicklas |
159 |
/** |
4207 |
04 Apr 08 |
nicklas |
Initialise the proxy class. |
4207 |
04 Apr 08 |
nicklas |
@param actionClass The interface that the proxy must implement |
4207 |
04 Apr 08 |
nicklas |
162 |
*/ |
4208 |
07 Apr 08 |
nicklas |
163 |
private synchronized void initProxy(Class<? extends Action> actionClass) |
4207 |
04 Apr 08 |
nicklas |
164 |
{ |
4208 |
07 Apr 08 |
nicklas |
165 |
if (initialised) return; |
5384 |
13 Aug 10 |
nicklas |
166 |
try |
4207 |
04 Apr 08 |
nicklas |
167 |
{ |
5384 |
13 Aug 10 |
nicklas |
168 |
if (!actionClass.isInterface()) |
5384 |
13 Aug 10 |
nicklas |
169 |
{ |
5384 |
13 Aug 10 |
nicklas |
170 |
throw new IllegalArgumentException("Action class " + actionClass + " must be an interface"); |
5384 |
13 Aug 10 |
nicklas |
171 |
} |
5384 |
13 Aug 10 |
nicklas |
172 |
ClassLoader loader = actionClass.getClassLoader(); |
6875 |
20 Apr 15 |
nicklas |
173 |
proxy = actionClass.cast(Proxy.newProxyInstance(loader, new Class<?>[] { actionClass }, this)); |
5384 |
13 Aug 10 |
nicklas |
174 |
actions = new Action[] { proxy }; |
4207 |
04 Apr 08 |
nicklas |
175 |
} |
5384 |
13 Aug 10 |
nicklas |
176 |
finally |
5384 |
13 Aug 10 |
nicklas |
177 |
{ |
5384 |
13 Aug 10 |
nicklas |
178 |
initialised = true; |
5384 |
13 Aug 10 |
nicklas |
179 |
} |
4207 |
04 Apr 08 |
nicklas |
180 |
} |
4207 |
04 Apr 08 |
nicklas |
181 |
|
4207 |
04 Apr 08 |
nicklas |
182 |
/** |
4207 |
04 Apr 08 |
nicklas |
Convert a method name to a parameter name. Methods that |
4207 |
04 Apr 08 |
nicklas |
start with <code>get</code> or <code>is</code> will have that part |
4207 |
04 Apr 08 |
nicklas |
removed. The first character of the remaining name is converted to |
4207 |
04 Apr 08 |
nicklas |
lower case. All other method names are left as they are. |
4207 |
04 Apr 08 |
nicklas |
@param methodName The name of the method |
4207 |
04 Apr 08 |
nicklas |
@return The name of the parameter |
4207 |
04 Apr 08 |
nicklas |
189 |
*/ |
4207 |
04 Apr 08 |
nicklas |
190 |
private String getParameterName(String methodName) |
4207 |
04 Apr 08 |
nicklas |
191 |
{ |
4207 |
04 Apr 08 |
nicklas |
192 |
int index = 0; |
4207 |
04 Apr 08 |
nicklas |
193 |
if (methodName.startsWith("get")) |
4207 |
04 Apr 08 |
nicklas |
194 |
{ |
4207 |
04 Apr 08 |
nicklas |
195 |
index = 3; |
4207 |
04 Apr 08 |
nicklas |
196 |
} |
4207 |
04 Apr 08 |
nicklas |
197 |
else if (methodName.startsWith("is")) |
4207 |
04 Apr 08 |
nicklas |
198 |
{ |
4207 |
04 Apr 08 |
nicklas |
199 |
index = 2; |
4207 |
04 Apr 08 |
nicklas |
200 |
} |
4207 |
04 Apr 08 |
nicklas |
201 |
if (index > 0) |
4207 |
04 Apr 08 |
nicklas |
202 |
{ |
4207 |
04 Apr 08 |
nicklas |
203 |
String firstChar = methodName.substring(index, index+1).toLowerCase(); |
4207 |
04 Apr 08 |
nicklas |
204 |
methodName = firstChar + methodName.substring(index+1); |
4207 |
04 Apr 08 |
nicklas |
205 |
} |
4207 |
04 Apr 08 |
nicklas |
206 |
return methodName; |
4207 |
04 Apr 08 |
nicklas |
207 |
} |
4207 |
04 Apr 08 |
nicklas |
208 |
|
4207 |
04 Apr 08 |
nicklas |
209 |
/** |
4207 |
04 Apr 08 |
nicklas |
Get the value of a parameter. |
4207 |
04 Apr 08 |
nicklas |
@param parameterName The name of the parameter |
4207 |
04 Apr 08 |
nicklas |
@return The value of the parameter |
4207 |
04 Apr 08 |
nicklas |
213 |
*/ |
4207 |
04 Apr 08 |
nicklas |
214 |
private Object getParameterValue(String parameterName, Class<?> returnType) |
4207 |
04 Apr 08 |
nicklas |
215 |
{ |
4207 |
04 Apr 08 |
nicklas |
216 |
if (valueCache == null) |
4207 |
04 Apr 08 |
nicklas |
217 |
{ |
4207 |
04 Apr 08 |
nicklas |
218 |
valueCache = new HashMap<String, Object>(); |
4207 |
04 Apr 08 |
nicklas |
219 |
} |
4207 |
04 Apr 08 |
nicklas |
// The value to return |
4207 |
04 Apr 08 |
nicklas |
221 |
Object value = null; |
4207 |
04 Apr 08 |
nicklas |
222 |
|
4207 |
04 Apr 08 |
nicklas |
// Check the cache first |
4207 |
04 Apr 08 |
nicklas |
224 |
if (valueCache.containsKey(parameterName)) |
4207 |
04 Apr 08 |
nicklas |
225 |
{ |
4207 |
04 Apr 08 |
nicklas |
226 |
value = valueCache.get(parameterName); |
4207 |
04 Apr 08 |
nicklas |
227 |
} |
6423 |
25 Feb 14 |
nicklas |
228 |
else if (parameters != null) |
4207 |
04 Apr 08 |
nicklas |
229 |
{ |
4207 |
04 Apr 08 |
nicklas |
// Get the string value from the XML file |
4207 |
04 Apr 08 |
nicklas |
231 |
String sValue = parameters.get(parameterName); |
4207 |
04 Apr 08 |
nicklas |
// Convert to proper object type |
4207 |
04 Apr 08 |
nicklas |
233 |
value = StringConverter.convertString(returnType, sValue); |
4207 |
04 Apr 08 |
nicklas |
// Save the converted value in the cache |
4207 |
04 Apr 08 |
nicklas |
235 |
valueCache.put(parameterName, value); |
4207 |
04 Apr 08 |
nicklas |
236 |
} |
4207 |
04 Apr 08 |
nicklas |
237 |
return value; |
4207 |
04 Apr 08 |
nicklas |
238 |
} |
4207 |
04 Apr 08 |
nicklas |
239 |
} |