src/core/net/sf/basedb/util/extensions/debug/ProxyActionFactory.java

Code
Comments
Other
Rev Date Author Line
4207 04 Apr 08 nicklas 1 /**
4207 04 Apr 08 nicklas 2   $Id$
4207 04 Apr 08 nicklas 3
4207 04 Apr 08 nicklas 4   Copyright (C) Authors contributing to this file.
4207 04 Apr 08 nicklas 5
4207 04 Apr 08 nicklas 6   This file is part of BASE - BioArray Software Environment.
4207 04 Apr 08 nicklas 7   Available at http://base.thep.lu.se/
4207 04 Apr 08 nicklas 8
4207 04 Apr 08 nicklas 9   BASE is free software; you can redistribute it and/or
4207 04 Apr 08 nicklas 10   modify it under the terms of the GNU General Public License
4479 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
4207 04 Apr 08 nicklas 12   of the License, or (at your option) any later version.
4207 04 Apr 08 nicklas 13
4207 04 Apr 08 nicklas 14   BASE is distributed in the hope that it will be useful,
4207 04 Apr 08 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4207 04 Apr 08 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4207 04 Apr 08 nicklas 17   GNU General Public License for more details.
4207 04 Apr 08 nicklas 18
4207 04 Apr 08 nicklas 19   You should have received a copy of the GNU General Public License
4515 11 Sep 08 jari 20   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 39   Generic action factory class that can generate instances of
4207 04 Apr 08 nicklas 40   actions if the action class is an interface. Use this factory
4207 04 Apr 08 nicklas 41   for development and debugging only. The performance and
4207 04 Apr 08 nicklas 42   memory requirements of this implementation is unsure for a 
4207 04 Apr 08 nicklas 43   production environment.
4207 04 Apr 08 nicklas 44   <p>
4207 04 Apr 08 nicklas 45   
4207 04 Apr 08 nicklas 46   This class works ONLY if the {@link ExtensionPoint#getActionClass()}
4207 04 Apr 08 nicklas 47   is an interface, eg. {@link Class#isInterface()} return true. If so,
4207 04 Apr 08 nicklas 48   we use Java reflection to generate a {@link Proxy} that implements
4207 04 Apr 08 nicklas 49   the desired action class interface.
4207 04 Apr 08 nicklas 50   <p>
4207 04 Apr 08 nicklas 51   
4207 04 Apr 08 nicklas 52   Parameters in the XML file will be passed on as return values for
4207 04 Apr 08 nicklas 53   method calls on the proxy, following the usual syntax rules. Eg.
6881 21 Apr 15 nicklas 54   The parameter value <code>&lt;icon&gt;/images/icon.png&lt;/icon&gt;</code> will
4207 04 Apr 08 nicklas 55   be returned as a result of calling <code>getIcon()</code> on the
4207 04 Apr 08 nicklas 56   proxy. Null is returned for parameters that have not been specified 
4207 04 Apr 08 nicklas 57   in the XML file.
4207 04 Apr 08 nicklas 58   <p>
4207 04 Apr 08 nicklas 59   
4207 04 Apr 08 nicklas 60   The factory can convert the string values to other data types for the 
4207 04 Apr 08 nicklas 61   following classes: int, long, float, double and boolean. It can handle 
4207 04 Apr 08 nicklas 62   both primitive and wrapper classes. Example:
6881 21 Apr 15 nicklas 63   <code>&lt;enabled&gt;1&lt;/enabled&gt;</code> will return <code>true</code> if
4207 04 Apr 08 nicklas 64   <code>isEnabled()</code> is called on the proxy.
4207 04 Apr 08 nicklas 65   <p>
4207 04 Apr 08 nicklas 66   
4207 04 Apr 08 nicklas 67   <b>Parameters supported by this factory</b><br>
4207 04 Apr 08 nicklas 68   This factory supports all parameters. Some parameters may trigger special
4207 04 Apr 08 nicklas 69   actions:
4207 04 Apr 08 nicklas 70   
4207 04 Apr 08 nicklas 71   <ul>
4207 04 Apr 08 nicklas 72   <li>disabled: If <code>true</code> the {@link #prepareContext(InvokationContext)} 
4207 04 Apr 08 nicklas 73     method returns false
4207 04 Apr 08 nicklas 74   </ul>
4207 04 Apr 08 nicklas 75
4207 04 Apr 08 nicklas 76   @author nicklas
4207 04 Apr 08 nicklas 77   @version 2.7
4207 04 Apr 08 nicklas 78   @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 92     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 98     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 121     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 134     Set generic parameters.
4207 04 Apr 08 nicklas 135     @param name The name of the parameter
4207 04 Apr 08 nicklas 136     @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 150     Sets the disabled/enabled status of this factory.
4207 04 Apr 08 nicklas 151     @param disabled A string that is parsed to a boolean
4207 04 Apr 08 nicklas 152       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 160     Initialise the proxy class.
4207 04 Apr 08 nicklas 161     @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 183     Convert a method name to a parameter name. Methods that
4207 04 Apr 08 nicklas 184     start with <code>get</code> or <code>is</code> will have that part
4207 04 Apr 08 nicklas 185     removed. The first character of the remaining name is converted to 
4207 04 Apr 08 nicklas 186     lower case. All other method names are left as they are.
4207 04 Apr 08 nicklas 187     @param methodName The name of the method
4207 04 Apr 08 nicklas 188     @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 210     Get the value of a parameter.
4207 04 Apr 08 nicklas 211     @param parameterName The name of the parameter
4207 04 Apr 08 nicklas 212     @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 220     // The value to return
4207 04 Apr 08 nicklas 221     Object value = null;
4207 04 Apr 08 nicklas 222
4207 04 Apr 08 nicklas 223     // 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 230       // Get the string value from the XML file
4207 04 Apr 08 nicklas 231       String sValue = parameters.get(parameterName);
4207 04 Apr 08 nicklas 232       // Convert to proper object type
4207 04 Apr 08 nicklas 233       value = StringConverter.convertString(returnType, sValue);
4207 04 Apr 08 nicklas 234       // 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 }