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

Code
Comments
Other
Rev Date Author Line
4208 07 Apr 08 nicklas 1 /**
4208 07 Apr 08 nicklas 2   $Id$
4208 07 Apr 08 nicklas 3
4208 07 Apr 08 nicklas 4   Copyright (C) Authors contributing to this file.
4208 07 Apr 08 nicklas 5
4208 07 Apr 08 nicklas 6   This file is part of BASE - BioArray Software Environment.
4208 07 Apr 08 nicklas 7   Available at http://base.thep.lu.se/
4208 07 Apr 08 nicklas 8
4208 07 Apr 08 nicklas 9   BASE is free software; you can redistribute it and/or
4208 07 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
4208 07 Apr 08 nicklas 12   of the License, or (at your option) any later version.
4208 07 Apr 08 nicklas 13
4208 07 Apr 08 nicklas 14   BASE is distributed in the hope that it will be useful,
4208 07 Apr 08 nicklas 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4208 07 Apr 08 nicklas 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4208 07 Apr 08 nicklas 17   GNU General Public License for more details.
4208 07 Apr 08 nicklas 18
4208 07 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/>.
4208 07 Apr 08 nicklas 21 */
4208 07 Apr 08 nicklas 22 package net.sf.basedb.util.extensions.debug;
4208 07 Apr 08 nicklas 23
4208 07 Apr 08 nicklas 24 import java.lang.reflect.InvocationTargetException;
4208 07 Apr 08 nicklas 25 import java.lang.reflect.Method;
4208 07 Apr 08 nicklas 26 import java.util.HashMap;
4208 07 Apr 08 nicklas 27 import java.util.Map;
4208 07 Apr 08 nicklas 28
4208 07 Apr 08 nicklas 29 import net.sf.basedb.util.ClassUtil;
4208 07 Apr 08 nicklas 30 import net.sf.basedb.util.Values;
4208 07 Apr 08 nicklas 31 import net.sf.basedb.util.extensions.Action;
4208 07 Apr 08 nicklas 32 import net.sf.basedb.util.extensions.ActionFactory;
4208 07 Apr 08 nicklas 33 import net.sf.basedb.util.extensions.InvokationContext;
4208 07 Apr 08 nicklas 34 import net.sf.basedb.util.extensions.xml.PathSetter;
4208 07 Apr 08 nicklas 35 import net.sf.basedb.util.extensions.xml.VariableSetter;
4208 07 Apr 08 nicklas 36
4208 07 Apr 08 nicklas 37 /**
4208 07 Apr 08 nicklas 38   Generic action factory class that can generate instances of
4208 07 Apr 08 nicklas 39   actions if there exists a bean-like implementation of the
4208 07 Apr 08 nicklas 40   action class.
4208 07 Apr 08 nicklas 41   <p>
4208 07 Apr 08 nicklas 42   
4208 07 Apr 08 nicklas 43   Parameters in the XML file will be set on the bean if there is 
4208 07 Apr 08 nicklas 44   a setter method with the same name as the parameter tag, 
4208 07 Apr 08 nicklas 45   following the usual syntax rules. Eg. the parameter value 
6881 21 Apr 15 nicklas 46   <code>&lt;icon&gt;/images/icon.png&lt;/icon&gt;</code> will
4208 07 Apr 08 nicklas 47   be passed to the <code>setIcon(String)</code> method on the
4208 07 Apr 08 nicklas 48   bean. If there is no such method the factory will check if 
4208 07 Apr 08 nicklas 49   there is a <code>getIcon()</code> or <code>isIcon()</code> 
4208 07 Apr 08 nicklas 50   method with a return type other than <code>String</code>. If so,
4208 07 Apr 08 nicklas 51   the factory will try a second time to find a <code>setIcon()</code>
4208 07 Apr 08 nicklas 52   method with the same parameter type. Methods corresponding to 
4208 07 Apr 08 nicklas 53   parameters that have not been set in the XML file are never
4208 07 Apr 08 nicklas 54   called.
4208 07 Apr 08 nicklas 55   <p>
4208 07 Apr 08 nicklas 56   
4208 07 Apr 08 nicklas 57   The factory can convert the string values to other data types for the 
4208 07 Apr 08 nicklas 58   following classes: int, long, float, double and boolean. It can handle 
4208 07 Apr 08 nicklas 59   both primitive and wrapper classes. Example:
6881 21 Apr 15 nicklas 60   <code>&lt;enabled&gt;1&lt;/enabled&gt;</code> will return <code>true</code> if
4208 07 Apr 08 nicklas 61   <code>isEnabled()</code> is called on the bean.
4208 07 Apr 08 nicklas 62   <p>
4208 07 Apr 08 nicklas 63   
4208 07 Apr 08 nicklas 64   <b>Parameters supported by this factory</b><br>
4208 07 Apr 08 nicklas 65   This factory supports all parameters. Some parameters may trigger special
4208 07 Apr 08 nicklas 66   actions:
4208 07 Apr 08 nicklas 67   
4208 07 Apr 08 nicklas 68   <ul>
4208 07 Apr 08 nicklas 69   <li>beanClass: The class name of a class that implements the
4208 07 Apr 08 nicklas 70     extension points action class.
4208 07 Apr 08 nicklas 71   <li>disabled: If <code>true</code> the {@link #prepareContext(InvokationContext)} 
4208 07 Apr 08 nicklas 72     method returns false
4208 07 Apr 08 nicklas 73   </ul>
4208 07 Apr 08 nicklas 74
4208 07 Apr 08 nicklas 75   @author nicklas
4208 07 Apr 08 nicklas 76   @version 2.7
4208 07 Apr 08 nicklas 77   @base.modified $Date$
4208 07 Apr 08 nicklas 78 */
4208 07 Apr 08 nicklas 79 public class BeanActionFactory
4208 07 Apr 08 nicklas 80   implements ActionFactory<Action>
4208 07 Apr 08 nicklas 81 {
4208 07 Apr 08 nicklas 82
4208 07 Apr 08 nicklas 83   private String beanClassName;
4208 07 Apr 08 nicklas 84   private boolean disabled;
4208 07 Apr 08 nicklas 85   private Action bean;
4208 07 Apr 08 nicklas 86   private Action[] actions;
4208 07 Apr 08 nicklas 87   private Map<String, String> parameters;
5384 13 Aug 10 nicklas 88   private volatile boolean initialised;
4208 07 Apr 08 nicklas 89   
4208 07 Apr 08 nicklas 90   /**
4208 07 Apr 08 nicklas 91     Creates a new proxy factory.
4208 07 Apr 08 nicklas 92   */
4208 07 Apr 08 nicklas 93   public BeanActionFactory()
4208 07 Apr 08 nicklas 94   {}
4208 07 Apr 08 nicklas 95
4208 07 Apr 08 nicklas 96   /*
4208 07 Apr 08 nicklas 97     From the ActionFactory interface
4208 07 Apr 08 nicklas 98     --------------------------------
4208 07 Apr 08 nicklas 99   */
4208 07 Apr 08 nicklas 100   @Override
4208 07 Apr 08 nicklas 101   public boolean prepareContext(InvokationContext<? super Action> context)
4208 07 Apr 08 nicklas 102   {
4208 07 Apr 08 nicklas 103     if (disabled) return false;
4208 07 Apr 08 nicklas 104     if (!initialised)
4208 07 Apr 08 nicklas 105     {
4208 07 Apr 08 nicklas 106       initBean(context.getExtensionPoint().getActionClass());
4208 07 Apr 08 nicklas 107     }
4208 07 Apr 08 nicklas 108     return bean != null;
4208 07 Apr 08 nicklas 109   }
4208 07 Apr 08 nicklas 110
4208 07 Apr 08 nicklas 111   @Override
6875 20 Apr 15 nicklas 112   public Action[] getActions(InvokationContext<? super Action> context)
4208 07 Apr 08 nicklas 113   {
4208 07 Apr 08 nicklas 114     return disabled ? null : actions;
4208 07 Apr 08 nicklas 115   }
4208 07 Apr 08 nicklas 116   // -----------------------------------
4208 07 Apr 08 nicklas 117   
4208 07 Apr 08 nicklas 118   /**
4208 07 Apr 08 nicklas 119     Set generic parameters.
4208 07 Apr 08 nicklas 120     @param name The name of the parameter
4208 07 Apr 08 nicklas 121     @param value The value of the parameter
4208 07 Apr 08 nicklas 122   */
4208 07 Apr 08 nicklas 123   @PathSetter
4208 07 Apr 08 nicklas 124   @VariableSetter
4208 07 Apr 08 nicklas 125   public void setParameter(String name, String value)
4208 07 Apr 08 nicklas 126   {
4208 07 Apr 08 nicklas 127     if (parameters == null) 
4208 07 Apr 08 nicklas 128     {
4208 07 Apr 08 nicklas 129       parameters = new HashMap<String, String>();
4208 07 Apr 08 nicklas 130     }
4208 07 Apr 08 nicklas 131     parameters.put(name, value);
4208 07 Apr 08 nicklas 132   }
4208 07 Apr 08 nicklas 133   
4208 07 Apr 08 nicklas 134   /**
4208 07 Apr 08 nicklas 135     Set the name of the class to use as a bean. The bean
4208 07 Apr 08 nicklas 136     must implement the action class of the extension point.
4208 07 Apr 08 nicklas 137   */
4208 07 Apr 08 nicklas 138   public void setBeanClass(String beanClass)
4208 07 Apr 08 nicklas 139   {
4208 07 Apr 08 nicklas 140     this.beanClassName = beanClass;
4208 07 Apr 08 nicklas 141   }
4208 07 Apr 08 nicklas 142   
4208 07 Apr 08 nicklas 143   /**
4208 07 Apr 08 nicklas 144     Sets the disabled/enabled status of this factory.
4208 07 Apr 08 nicklas 145     @param disabled A string that is parsed to a boolean
4208 07 Apr 08 nicklas 146       by {@link Values#getBoolean(String)}
4208 07 Apr 08 nicklas 147   */
4208 07 Apr 08 nicklas 148   public void setDisabled(String disabled)
4208 07 Apr 08 nicklas 149   {
4208 07 Apr 08 nicklas 150     this.disabled = Values.getBoolean(disabled);
4208 07 Apr 08 nicklas 151   }
4208 07 Apr 08 nicklas 152   
4208 07 Apr 08 nicklas 153   /**
4208 07 Apr 08 nicklas 154     Create the bean object. Call all setter methods that
4208 07 Apr 08 nicklas 155     corresponds to parameters found in the XML file.
4208 07 Apr 08 nicklas 156     @param actionClass The interface that the proxy must implement
4208 07 Apr 08 nicklas 157   */
4208 07 Apr 08 nicklas 158   private synchronized void initBean(Class<? extends Action> actionClass)
4208 07 Apr 08 nicklas 159   {
4208 07 Apr 08 nicklas 160     if (initialised) return;
4208 07 Apr 08 nicklas 161     
5384 13 Aug 10 nicklas 162     try
4208 07 Apr 08 nicklas 163     {
5384 13 Aug 10 nicklas 164       // Create the bean
5384 13 Aug 10 nicklas 165       bean = createBean(actionClass);
5384 13 Aug 10 nicklas 166   
5384 13 Aug 10 nicklas 167       // Set parameters on the bean
5384 13 Aug 10 nicklas 168       actions = new Action[] { bean };
6423 25 Feb 14 nicklas 169       if (parameters != null)
5014 25 Jun 09 martin 170       {
6423 25 Feb 14 nicklas 171         for (Map.Entry<String, String> entry : parameters.entrySet())
5384 13 Aug 10 nicklas 172         {
6423 25 Feb 14 nicklas 173           String name = entry.getKey();
6423 25 Feb 14 nicklas 174           String value = entry.getValue();
6423 25 Feb 14 nicklas 175           
6423 25 Feb 14 nicklas 176           // Find a setParam() method. Ignore parameter if not found
6423 25 Feb 14 nicklas 177           Method setter = findSetterMethod(name, bean.getClass());            
6423 25 Feb 14 nicklas 178           if (setter == null)
6423 25 Feb 14 nicklas 179           {
6423 25 Feb 14 nicklas 180             /* #### CONTINUE-STATEMENT #### */
6423 25 Feb 14 nicklas 181             continue;
6423 25 Feb 14 nicklas 182           }
6423 25 Feb 14 nicklas 183           
6423 25 Feb 14 nicklas 184           // Convert value to correct type for setParam(T) method
6423 25 Feb 14 nicklas 185           Object o = convertValue(value, setter.getParameterTypes()[0]);
6423 25 Feb 14 nicklas 186           try
6423 25 Feb 14 nicklas 187           {
6423 25 Feb 14 nicklas 188             setter.invoke(bean, o);
6423 25 Feb 14 nicklas 189           }
6423 25 Feb 14 nicklas 190           catch (IllegalAccessException ex)
6423 25 Feb 14 nicklas 191           {}
6423 25 Feb 14 nicklas 192           catch (InvocationTargetException ex)
6423 25 Feb 14 nicklas 193           {}
5384 13 Aug 10 nicklas 194         }
5014 25 Jun 09 martin 195       }
4208 07 Apr 08 nicklas 196     }
5384 13 Aug 10 nicklas 197     finally
5384 13 Aug 10 nicklas 198     {
5384 13 Aug 10 nicklas 199       initialised = true;
5384 13 Aug 10 nicklas 200     }
4208 07 Apr 08 nicklas 201   }
4208 07 Apr 08 nicklas 202
4208 07 Apr 08 nicklas 203   /**
4208 07 Apr 08 nicklas 204     Create the bean instance.
4208 07 Apr 08 nicklas 205     @return A bean instance
4208 07 Apr 08 nicklas 206   */
4208 07 Apr 08 nicklas 207   private Action createBean(Class<? extends Action> actionClass)
4208 07 Apr 08 nicklas 208   {
4208 07 Apr 08 nicklas 209     try
4208 07 Apr 08 nicklas 210     {
4208 07 Apr 08 nicklas 211       ClassLoader loader = actionClass.getClassLoader();    
6875 20 Apr 15 nicklas 212       Class<?> beanClass = ClassUtil.checkAndLoadClass(loader, beanClassName, true, actionClass);
7513 02 Nov 18 nicklas 213       Action bean = actionClass.cast(beanClass.getDeclaredConstructor().newInstance());
4208 07 Apr 08 nicklas 214       return bean;
4208 07 Apr 08 nicklas 215     }
4208 07 Apr 08 nicklas 216     catch (Exception ex)
4208 07 Apr 08 nicklas 217     {
4208 07 Apr 08 nicklas 218       throw new IllegalArgumentException("Could not create bean: " + beanClassName, ex);
4208 07 Apr 08 nicklas 219     }
4208 07 Apr 08 nicklas 220   }
4208 07 Apr 08 nicklas 221   
4208 07 Apr 08 nicklas 222   /**
4208 07 Apr 08 nicklas 223     Find a setter method for the specified parameter.
6898 12 May 15 nicklas 224     Try: setParam(String), T getParam() or T isParam() --&gt; setParam(T)
4208 07 Apr 08 nicklas 225   */
4208 07 Apr 08 nicklas 226   private Method findSetterMethod(String parameterName, Class<?> beanClass)
4208 07 Apr 08 nicklas 227   {
4208 07 Apr 08 nicklas 228     Method setter = null;
4208 07 Apr 08 nicklas 229     String setterMethodName = getMethodName("set", parameterName);
4208 07 Apr 08 nicklas 230     
4208 07 Apr 08 nicklas 231     // Look for setParam(String)
4208 07 Apr 08 nicklas 232     setter = getMethod(beanClass, setterMethodName, String.class);
4208 07 Apr 08 nicklas 233     if (setter != null) return setter;
4208 07 Apr 08 nicklas 234     
4208 07 Apr 08 nicklas 235     // Not found - Try: getParam()
4208 07 Apr 08 nicklas 236     String getterMethodName = getMethodName("get", parameterName);
4208 07 Apr 08 nicklas 237     Method getter = getMethod(beanClass, getterMethodName, null);
4208 07 Apr 08 nicklas 238     
4208 07 Apr 08 nicklas 239     if (getter == null)
4208 07 Apr 08 nicklas 240     {
4208 07 Apr 08 nicklas 241       // Last chance - Try: isParam()
4208 07 Apr 08 nicklas 242       getterMethodName = getMethodName("is", parameterName);
4208 07 Apr 08 nicklas 243       getter = getMethod(beanClass, getterMethodName, null);
4208 07 Apr 08 nicklas 244     }
4208 07 Apr 08 nicklas 245   
4208 07 Apr 08 nicklas 246     if (getter != null)
4208 07 Apr 08 nicklas 247     {
4208 07 Apr 08 nicklas 248       setter = getMethod(beanClass, setterMethodName, getter.getReturnType());
4208 07 Apr 08 nicklas 249     }
4208 07 Apr 08 nicklas 250     
4208 07 Apr 08 nicklas 251     return setter;
4208 07 Apr 08 nicklas 252   }
4208 07 Apr 08 nicklas 253   
4208 07 Apr 08 nicklas 254   /**
4208 07 Apr 08 nicklas 255     Convert the parameter name to a method name.
4208 07 Apr 08 nicklas 256     First character of parameter name is upper-cased. The prefix
6898 12 May 15 nicklas 257     is then prepended. Ex, 'icon' --&gt; 'setIcon'
4208 07 Apr 08 nicklas 258     @param parameterName The parameter name
4208 07 Apr 08 nicklas 259     @return The tag name prefixed with 'set' and the first letter captialized
4208 07 Apr 08 nicklas 260   */
4208 07 Apr 08 nicklas 261   private String getMethodName(String prefix, String parameterName)
4208 07 Apr 08 nicklas 262   {
4208 07 Apr 08 nicklas 263     String firstLetter = parameterName.substring(0, 1).toUpperCase();
4208 07 Apr 08 nicklas 264     String setterName = prefix + firstLetter + parameterName.substring(1);
4208 07 Apr 08 nicklas 265     return setterName;
4208 07 Apr 08 nicklas 266   }  
4208 07 Apr 08 nicklas 267   
4208 07 Apr 08 nicklas 268   /**
4208 07 Apr 08 nicklas 269     Get a method with a specific name with optionally, takes
4208 07 Apr 08 nicklas 270     a single parameter of given type.
4208 07 Apr 08 nicklas 271     @param beanClass The class to look for the method in
4208 07 Apr 08 nicklas 272     @param name The name of the method
4208 07 Apr 08 nicklas 273     @param param The type of the parameter, or null to look for
4208 07 Apr 08 nicklas 274       a method with not parameters
4208 07 Apr 08 nicklas 275     @return The Method or null if no such method can be found
4208 07 Apr 08 nicklas 276   */
4208 07 Apr 08 nicklas 277   private Method getMethod(Class<?> beanClass, String name, Class<?> param)
4208 07 Apr 08 nicklas 278   {
4208 07 Apr 08 nicklas 279     Method method = null;
4208 07 Apr 08 nicklas 280     try
4208 07 Apr 08 nicklas 281     {
6875 20 Apr 15 nicklas 282       Class<?>[] params = param == null ? null : new Class<?>[] { param };
4208 07 Apr 08 nicklas 283       method = beanClass.getDeclaredMethod(name, params);
4208 07 Apr 08 nicklas 284     }
4208 07 Apr 08 nicklas 285     catch (NoSuchMethodException ex)
4208 07 Apr 08 nicklas 286     {}
4208 07 Apr 08 nicklas 287     return method;
4208 07 Apr 08 nicklas 288   }
4208 07 Apr 08 nicklas 289   
4208 07 Apr 08 nicklas 290   /**
4208 07 Apr 08 nicklas 291     Convert a string value to another type.
4208 07 Apr 08 nicklas 292     @param sValue The value to convert
4208 07 Apr 08 nicklas 293     @param type The class to convert the string value to
4208 07 Apr 08 nicklas 294     @return The converted value, or null if it couldn't be converted
4208 07 Apr 08 nicklas 295   */
4208 07 Apr 08 nicklas 296   private Object convertValue(String sValue, Class<?> type)
4208 07 Apr 08 nicklas 297   {
4208 07 Apr 08 nicklas 298     // The value to return
4208 07 Apr 08 nicklas 299     Object value = null;
4208 07 Apr 08 nicklas 300
4208 07 Apr 08 nicklas 301     // Convert to proper object type
4208 07 Apr 08 nicklas 302     value = StringConverter.convertString(type, sValue);
4208 07 Apr 08 nicklas 303     
4208 07 Apr 08 nicklas 304     // Save the converted value in the cache
4208 07 Apr 08 nicklas 305     return value;
4208 07 Apr 08 nicklas 306   }
4208 07 Apr 08 nicklas 307 }