velocity-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cbris...@apache.org
Subject svn commit: r1855070 - in /velocity/tools/trunk: src/changes/ velocity-tools-generic/src/main/java/org/apache/velocity/tools/ velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ velocity-tools-generic/src/test/java/org/apache/velocit...
Date Fri, 08 Mar 2019 23:41:10 GMT
Author: cbrisson
Date: Fri Mar  8 23:41:10 2019
New Revision: 1855070

URL: http://svn.apache.org/viewvc?rev=1855070&view=rev
Log:
[tools/generic] Added a factory="FactoryClassname" attribute to <tool>.

The factory class will have to have an accessible static method with no argument, and called
createXXX(), newXXX() or getXXX(),
where XXX is the simple class name of the tool, and return a non-null instance assignable
to the tool class.


Modified:
    velocity/tools/trunk/src/changes/changes.xml
    velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ClassUtils.java
    velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ToolInfo.java
    velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ToolConfiguration.java
    velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/ConfigTests.java

Modified: velocity/tools/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/src/changes/changes.xml?rev=1855070&r1=1855069&r2=1855070&view=diff
==============================================================================
--- velocity/tools/trunk/src/changes/changes.xml (original)
+++ velocity/tools/trunk/src/changes/changes.xml Fri Mar  8 23:41:10 2019
@@ -24,6 +24,11 @@
     <title>Changelog</title>
   </properties>
   <body>
+    <release version="3.1" date="Not yet released">
+      <action type="add" dev="cbrisson">
+        Added an optional 'factory' attribute to tools with the classname of a factory for
creating new tools instances
+      </action>
+    </release>
     <release version="3.0" date="2018-10-01">
       <action type="add" dev="michaelo">
         upgraded to servlet API 3.1.0

Modified: velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ClassUtils.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ClassUtils.java?rev=1855070&r1=1855069&r2=1855070&view=diff
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ClassUtils.java
(original)
+++ velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ClassUtils.java
Fri Mar  8 23:41:10 2019
@@ -106,6 +106,14 @@ public class ClassUtils
         }
     }
 
+    /**
+     * Get an instance of a named class.
+     * @param classname class name
+     * @return new class instance
+     * @throws ClassNotFoundException if class is not found
+     * @throws IllegalAccessException if not granted
+     * @throws InstantiationException if instance creation throwed
+     */
     public static Object getInstance(String classname)
          throws ClassNotFoundException, IllegalAccessException,
                 InstantiationException
@@ -320,6 +328,14 @@ public class ClassUtils
         return inputStream;
     }
 
+    /**
+     * Find a callable method in a class
+     * @param clazz target class
+     * @param name method name
+     * @param params method arguments classes
+     * @return method object
+     * @throws SecurityException if not granted
+     */
     public static Method findMethod(Class clazz, String name, Class[] params)
         throws SecurityException
     {
@@ -335,6 +351,14 @@ public class ClassUtils
         return findDeclaredMethod(clazz, name, params);
     }
 
+    /**
+     * Find a declared method in a class. It will be made accessible if needed and allowed.
+     * @param clazz target class
+     * @param name method name
+     * @param params method arguments classes
+     * @return
+     * @throws SecurityException if not allowed
+     */
     public static Method findDeclaredMethod(Class clazz, String name, Class[] params)
         throws SecurityException
     {
@@ -365,6 +389,15 @@ public class ClassUtils
         return null;
     }
 
+    /**
+     * Given a static field path, aka <i>classname</i>.<i>field</i>,
get the field value.
+     * @param fieldPath field path
+     * @return field value
+     * @throws ClassNotFoundException if class hasn't been found
+     * @throws NoSuchFieldException if field hasn't been found
+     * @throws SecurityException if not granted
+     * @throws IllegalAccessException if field is not accessible
+     */
     public static Object getFieldValue(String fieldPath)
         throws ClassNotFoundException, NoSuchFieldException,
                SecurityException, IllegalAccessException
@@ -377,6 +410,15 @@ public class ClassUtils
         return getFieldValue(clazz, fieldname);
     }
 
+    /**
+     * Given a class and a static field name, get the field value.
+     * @param clazz target class
+     * @param fieldname field name
+     * @return field value
+     * @throws NoSuchFieldException if field hasn't been found
+     * @throws SecurityException if not granted
+     * @throws IllegalAccessException if field is not accessible
+     */
     public static Object getFieldValue(Class clazz, String fieldname)
         throws NoSuchFieldException, SecurityException, IllegalAccessException
     {
@@ -442,4 +484,30 @@ public class ClassUtils
         }
     }
 
+    private static String factoryMethodPrefixes[] = { "create", "new", "get" };
+
+    /**
+     * <p>Given a factory class and a target class, search for the following methods:</p>
+     * <ul>
+     *     <li><code>create<i>TargetClassname</i>()</code>,</li>
+     *     <li><code>new<i>TargetClassname</i>()</code>, or</li>
+     *     <li><code>get<i>TargetClassname</i>()</code>.</li>
+     * </ul>
+     * @param factory factory class
+     * @param target target class
+     * @return first factory method found, or null otherwise
+     */
+    public static Method findFactoryMethod(Class factory, Class target)
+    {
+        Method ret = null;
+        String undecoratedName = target.getSimpleName();
+        for (String prefix : factoryMethodPrefixes)
+        {
+            String methodName = prefix + undecoratedName;
+            ret = findMethod(factory, methodName, new Class[] {});
+            if (ret != null) break;
+        }
+        return ret;
+    }
+
 }

Modified: velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ToolInfo.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ToolInfo.java?rev=1855070&r1=1855069&r2=1855070&view=diff
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ToolInfo.java
(original)
+++ velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/ToolInfo.java
Fri Mar  8 23:41:10 2019
@@ -41,6 +41,8 @@ public class ToolInfo implements java.io
 
     private String key;
     private Class clazz;
+    private Class factory;
+    private transient Method create;
     private boolean restrictToIsExact;
     private String restrictTo;
     private Map<String,Object> properties;
@@ -55,8 +57,20 @@ public class ToolInfo implements java.io
      */
     public ToolInfo(String key, Class clazz)
     {
+        this(key, clazz, null);
+    }
+
+    /**
+     * Creates a new instance using the minimum required info
+     * necessary for a tool.
+     * @param key tool key
+     * @param clazz tool class
+     */
+    public ToolInfo(String key, Class clazz, Class factory)
+    {
         setKey(key);
         setClass(clazz);
+        setFactory(factory);
     }
 
 
@@ -76,9 +90,7 @@ public class ToolInfo implements java.io
     }
 
     /**
-     * Tries to create an instance of the specified Class, then looks for a
-     * configure(Map&lt;String,Object&gt;) method.
-     *
+     * Set the tool class
      * @param clazz the java.lang.Class of the tool
      */
     public void setClass(Class clazz)
@@ -97,6 +109,24 @@ public class ToolInfo implements java.io
     }
 
     /**
+     * <p>Set the factory class used to create tool instances.</p>
+     * <p>The factory is supposed to have one of those three methods:</p>
+     * <ul>
+     *     <li>create<i>ToolClassName</i>()</li>
+     *     <li>new<i>ToolClassName</i>()</li>
+     *     <li>get<i>ToolClassName</i>()</li>
+     * </ul>
+     * <p>where <i>ToolClassName</i> is the tool's class name.</p>
+     * <p>If this method takes one <code>java.util.Map</code> argument,
it will be given the tool's configuration map.</p>
+     *
+     * @param factory factory class
+     */
+    public void setFactory(Class factory)
+    {
+        this.factory = factory;
+    }
+
+    /**
      * @param path the full or partial request path restriction of the tool
      */
     public void restrictTo(String path)
@@ -210,6 +240,15 @@ public class ToolInfo implements java.io
     }
 
     /**
+     * Get factory class
+     * @return factory class or null if not provided
+     */
+    public Class getFactory()
+    {
+        return factory;
+    }
+
+    /**
      * Get tool properties
      * @return tools properties
      */
@@ -389,7 +428,16 @@ public class ToolInfo implements java.io
     {
         try
         {
-            return clazz.newInstance();
+            Class factory = getFactory();
+            if (factory == null)
+            {
+                return clazz.newInstance();
+            }
+            else
+            {
+                Method factoryMethod = ClassUtils.findFactoryMethod(factory, clazz);
+                return factoryMethod.invoke(null, new Object[] {});
+            }
         }
         /* we shouldn't get either of these exceptions here because
          * we already got an instance of this class during setClass().
@@ -400,12 +448,13 @@ public class ToolInfo implements java.io
                   getClassname() + "\"";
             throw new IllegalStateException(message, iae);
         }
-        catch (InstantiationException ie)
+        catch (InstantiationException | InvocationTargetException e)
         {
             String message = "Exception while instantiating instance of \"" +
                   getClassname() + "\"";
-            throw new IllegalStateException(message, ie);
+            throw new IllegalStateException(message, e);
         }
+
     }
 
     /**

Modified: velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ToolConfiguration.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ToolConfiguration.java?rev=1855070&r1=1855069&r2=1855070&view=diff
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ToolConfiguration.java
(original)
+++ velocity/tools/trunk/velocity-tools-generic/src/main/java/org/apache/velocity/tools/config/ToolConfiguration.java
Fri Mar  8 23:41:10 2019
@@ -19,6 +19,8 @@ package org.apache.velocity.tools.config
  * under the License.
  */
 
+import java.lang.invoke.WrongMethodTypeException;
+import java.lang.reflect.Method;
 import java.util.Map;
 
 import org.apache.velocity.tools.ToolContext;
@@ -46,6 +48,7 @@ public class ToolConfiguration extends C
 
     private String key;
     private String classname;
+    private String factoryClassname;
     private String restrictTo;
     private Boolean skipSetters;
     private Status status;
@@ -73,6 +76,17 @@ public class ToolConfiguration extends C
         this.status = null;
     }
 
+    public void setFactory(Class factory)
+    {
+        setFactoryClassname(factory.getName());
+    }
+
+    public void setFactoryClassname(String factoryClassname)
+    {
+        this.factoryClassname = factoryClassname;
+        this.status = null;
+    }
+
     public void setRestrictTo(String path)
     {
         this.restrictTo = path;
@@ -142,6 +156,8 @@ public class ToolConfiguration extends C
         return this.classname;
     }
 
+    public String getFactoryClassname() { return factoryClassname; }
+
     public Class getToolClass()
     {
         try
@@ -154,6 +170,19 @@ public class ToolConfiguration extends C
         }
     }
 
+    public Class getFactory()
+    {
+        try
+        {
+            String factoryClassname = getFactoryClassname();
+            return factoryClassname == null ? null : ClassUtils.getClass(factoryClassname);
+        }
+        catch (ClassNotFoundException cnfe)
+        {
+            throw new ConfigurationException(this, cnfe);
+        }
+    }
+
     public String[] getInvalidScopes()
     {
         InvalidScope invalid =
@@ -190,36 +219,61 @@ public class ToolConfiguration extends C
             {
                 this.status = Status.NONE;
             }
-
-            try
+            else
             {
-                // make sure the classname resolves to a class we have
-                Class clazz = ClassUtils.getClass(getClassname());
-
-                // try hard to ensure we have all necessary supporting classes
-                digForDependencies(clazz);
+                try
+                {
+                    // make sure the classname resolves to a class we have
+                    Class clazz = ClassUtils.getClass(getClassname());
+
+                    // try hard to ensure we have all necessary supporting classes
+                    digForDependencies(clazz);
+
+                    // create an instance to make sure we can do that
+                    if (factoryClassname == null)
+                    {
+                        clazz.newInstance();
+                    }
+                    else
+                    {
+                        Class factory = ClassUtils.getClass(getFactoryClassname());
+                        Method factoryMethod = ClassUtils.findFactoryMethod(factory, clazz);
+                        if (factoryMethod == null)
+                        {
+                            String target = clazz.getSimpleName();
+                            throw new IllegalArgumentException("Factory class hasn't any
method named create" + target + "(), new" + target + "() or get" + target + "()");
+                        }
+                        Object instance = factoryMethod.invoke(null, new Object[]{});
+                        if (instance == null)
+                        {
+                            throw new WrongMethodTypeException("Factory method '" + factoryMethod.toString()
+ "' returned null");
+                        }
+                        Class instanceClass = instance.getClass();
+                        if (!clazz.isAssignableFrom(instanceClass))
+                        {
+                            throw new WrongMethodTypeException("Factory method '" + factoryMethod
+ "' is expected to return an instance of class '" + getClassname() + "'");
+                        }
+                    }
 
-                // create an instance to make sure we can do that
-                clazz.newInstance();
-
-                this.status = Status.VALID;
-                this.problem = null;
-            }
-            catch (ClassNotFoundException cnfe)
-            {
-                this.status = Status.MISSING;
-                this.problem = cnfe;
-            }
-            catch (NoClassDefFoundError ncdfe)
-            {
-                this.status = Status.UNSUPPORTED;
-                this.problem = ncdfe;
-            }
-            catch (Throwable t)
-            {
-                // for all other problems...
-                this.status = Status.UNINSTANTIABLE;
-                this.problem = t;
+                    this.status = Status.VALID;
+                    this.problem = null;
+                }
+                catch (ClassNotFoundException cnfe)
+                {
+                    this.status = Status.MISSING;
+                    this.problem = cnfe;
+                }
+                catch (NoClassDefFoundError ncdfe)
+                {
+                    this.status = Status.UNSUPPORTED;
+                    this.problem = ncdfe;
+                }
+                catch (Throwable t)
+                {
+                    // for all other problems...
+                    this.status = Status.UNINSTANTIABLE;
+                    this.problem = t;
+                }
             }
         }
         return this.status;
@@ -254,7 +308,7 @@ public class ToolConfiguration extends C
         switch (status)
         {
             case VALID:
-                info = new ToolInfo(getKey(), getToolClass());
+                info = new ToolInfo(getKey(), getToolClass(), getFactory());
                 break;
             default:
                 throw problem == null ?
@@ -396,22 +450,30 @@ public class ToolConfiguration extends C
         }
         else
         {
-            switch (getStatus())
+            if (status == null)
             {
-                case VALID:
-                    break;
-                case NONE:
-                case MISSING:
-                    out.append("Invalid ");
-                    break;
-                case UNSUPPORTED:
-                    out.append("Unsupported ");
-                    break;
-                case UNINSTANTIABLE:
-                    out.append("Unusable ");
-                    break;
-                default:
-                    break;
+                // toString() sbould not change the status
+                out.append("Unchecked ");
+            }
+            else
+            {
+                switch (getStatus())
+                {
+                    case VALID:
+                        break;
+                    case NONE:
+                    case MISSING:
+                        out.append("Invalid ");
+                        break;
+                    case UNSUPPORTED:
+                        out.append("Unsupported ");
+                        break;
+                    case UNINSTANTIABLE:
+                        out.append("Unusable ");
+                        break;
+                    default:
+                        break;
+                }
             }
             out.append("Tool '");
             out.append(getKey());

Modified: velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/ConfigTests.java
URL: http://svn.apache.org/viewvc/velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/ConfigTests.java?rev=1855070&r1=1855069&r2=1855070&view=diff
==============================================================================
--- velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/ConfigTests.java
(original)
+++ velocity/tools/trunk/velocity-tools-generic/src/test/java/org/apache/velocity/tools/test/whitebox/ConfigTests.java
Fri Mar  8 23:41:10 2019
@@ -354,6 +354,23 @@ public class ConfigTests {
     //TODO: add tests for FactoryConfiguration
 
 
+    public @Test void testFactories()
+    {
+        ToolConfiguration tool = new ToolConfiguration();
+        tool.setClassname(FactoredTool.class.getName());
+        tool.setFactoryClassname(BadFactory.class.getName());
+        assertInvalid(tool);
+
+        tool.setFactoryClassname(NullFactory.class.getName());
+        assertInvalid(tool);
+
+        tool.setFactoryClassname(WrongPurposeFactory.class.getName());
+        assertInvalid(tool);
+
+        tool.setFactoryClassname(GoodFactory.class.getName());
+        assertValid(tool);
+    }
+
 
     /************* Support classes and methods ******************/
 
@@ -364,6 +381,34 @@ public class ConfigTests {
         public @Test @Ignore void foo() {}
     }
 
+    @DefaultKey("factored")
+    public static class FactoredTool
+    {
+        public FactoredTool(String dummyArg) {}
+    }
+
+    public static class BadFactory
+    {
+        public static void doNothing() {}
+    }
+
+    public static class NullFactory
+    {
+        public static FactoredTool createFactoredTool() { return null; }
+    }
+
+    public static class WrongPurposeFactory
+    {
+        public static FakeTool createFactoredTool() { return new FakeTool(); }
+    }
+
+    public static class GoodFactory
+    {
+        public static FactoredTool createFactoredTool()
+        {
+            return new FactoredTool("dummy");
+        }
+    }
 
     protected void assertConfigEquals(Configuration one, Configuration two)
     {
@@ -379,6 +424,9 @@ public class ConfigTests {
         assertNotNull(one);
         assertNotNull(two);
 
+        one.validate();
+        two.validate();
+
         // for now, just compare the toString() output without source info
         assertEquals(one.toString(false), two.toString(false));
     }



Mime
View raw message