diff --git a/src/main/java/org/apache/commons/jxpath/ClassFunctions.java b/src/main/java/org/apache/commons/jxpath/ClassFunctions.java index dbe29237f..e209400be 100644 --- a/src/main/java/org/apache/commons/jxpath/ClassFunctions.java +++ b/src/main/java/org/apache/commons/jxpath/ClassFunctions.java @@ -23,6 +23,7 @@ import org.apache.commons.jxpath.functions.ConstructorFunction; import org.apache.commons.jxpath.functions.MethodFunction; +import org.apache.commons.jxpath.ri.JXPathFilter; import org.apache.commons.jxpath.util.MethodLookupUtils; /** @@ -92,6 +93,28 @@ public Function getFunction( String namespace, String name, Object[] parameters) { + return getFunction(namespace, name, parameters, null); + } + + public Function getFunction( + String namespace, + String name, + Object[] parameters, + JXPathFilter jxPathFilter) { + + // give chance to ClassFilter to filter out, if present + try { + if (jxPathFilter != null && !jxPathFilter.exposeToXPath(functionClass.getName())) { + throw new ClassNotFoundException(functionClass.getName()); + } + } + catch (ClassNotFoundException ex) { + throw new JXPathException( + "Cannot invoke extension function " + + (namespace != null ? namespace + ":" + name : name), + ex); + } + if (namespace == null) { if (this.namespace != null) { return null; diff --git a/src/main/java/org/apache/commons/jxpath/FunctionLibrary.java b/src/main/java/org/apache/commons/jxpath/FunctionLibrary.java index 171adbd75..13443a226 100644 --- a/src/main/java/org/apache/commons/jxpath/FunctionLibrary.java +++ b/src/main/java/org/apache/commons/jxpath/FunctionLibrary.java @@ -16,6 +16,8 @@ */ package org.apache.commons.jxpath; +import org.apache.commons.jxpath.ri.JXPathFilter; + import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -77,12 +79,30 @@ public Set getUsedNamespaces() { */ public Function getFunction(String namespace, String name, Object[] parameters) { + return getFunction(namespace, name, parameters, null); + } + + /** + * Returns a Function, if any, for the specified namespace, + * name and parameter types. + * @param namespace function namespace + * @param name function name + * @param parameters parameters + * @param jxPathFilter the XPath filter + * @return Function found + */ + public Function getFunction( + String namespace, + String name, + Object[] parameters, + JXPathFilter jxPathFilter) { Object candidates = functionCache().get(namespace); if (candidates instanceof Functions) { return ((Functions) candidates).getFunction( namespace, name, - parameters); + parameters, + jxPathFilter); } if (candidates instanceof List) { List list = (List) candidates; @@ -92,7 +112,8 @@ public Function getFunction(String namespace, String name, ((Functions) list.get(i)).getFunction( namespace, name, - parameters); + parameters, + jxPathFilter); if (function != null) { return function; } diff --git a/src/main/java/org/apache/commons/jxpath/Functions.java b/src/main/java/org/apache/commons/jxpath/Functions.java index 216f7229f..4bce6ffab 100644 --- a/src/main/java/org/apache/commons/jxpath/Functions.java +++ b/src/main/java/org/apache/commons/jxpath/Functions.java @@ -16,6 +16,8 @@ */ package org.apache.commons.jxpath; +import org.apache.commons.jxpath.ri.JXPathFilter; + import java.util.Set; /** @@ -46,4 +48,15 @@ public interface Functions { * @return Function */ Function getFunction(String namespace, String name, Object[] parameters); + + /** + * Returns a Function, if any, for the specified namespace, + * name and parameter types. + * @param namespace ns + * @param name function name + * @param parameters Object[] + * @param jxPathFilter the XPath filter + * @return Function + */ + Function getFunction(String namespace, String name, Object[] parameters, JXPathFilter jxPathFilter); } diff --git a/src/main/java/org/apache/commons/jxpath/PackageFunctions.java b/src/main/java/org/apache/commons/jxpath/PackageFunctions.java index 86b09e426..612cfc678 100644 --- a/src/main/java/org/apache/commons/jxpath/PackageFunctions.java +++ b/src/main/java/org/apache/commons/jxpath/PackageFunctions.java @@ -25,6 +25,7 @@ import org.apache.commons.jxpath.functions.ConstructorFunction; import org.apache.commons.jxpath.functions.MethodFunction; +import org.apache.commons.jxpath.ri.JXPathFilter; import org.apache.commons.jxpath.util.ClassLoaderUtil; import org.apache.commons.jxpath.util.MethodLookupUtils; import org.apache.commons.jxpath.util.TypeUtils; @@ -112,10 +113,41 @@ public Set getUsedNamespaces() { * @return a MethodFunction, a ConstructorFunction or null if no function * is found */ + public Function getFunction( + String namespace, + String name, + Object[] parameters) { + return getFunction(namespace, name, parameters, null); + } + + /** + * Returns a {@link Function}, if found, for the specified namespace, + * name and parameter types. + *
+ * @param namespace - if it is not the same as specified in the + * construction, this method returns null + * @param name - name of the method, which can one these forms: + *
className using the classLoader
* @throws ClassNotFoundException if the class is not found
*/
- public static Class getClass(ClassLoader classLoader, String className, boolean initialize)
+ public static Class getClass(ClassLoader classLoader, String className, boolean initialize, JXPathFilter jxPathFilter)
throws ClassNotFoundException {
Class clazz;
+
+ // give chance to ClassFilter to filter out, if present
+ if (jxPathFilter != null && !jxPathFilter.exposeToXPath(className)) {
+ throw new ClassNotFoundException(className);
+ }
+
if (abbreviationMap.containsKey(className)) {
String clsName = "[" + abbreviationMap.get(className);
clazz = Class.forName(clsName, initialize, classLoader).getComponentType();
@@ -91,6 +100,38 @@ public static Class getClass(ClassLoader classLoader, String className, boolean
return clazz;
}
+ /**
+ * Returns the class represented by className using the
+ * classLoader. This implementation supports names like
+ * "java.lang.String[]" as well as "[Ljava.lang.String;".
+ *
+ * @param classLoader the class loader to use to load the class
+ * @param className the class name
+ * @param initialize whether the class must be initialized
+ * @return the class represented by className using the classLoader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(ClassLoader classLoader, String className, boolean initialize)
+ throws ClassNotFoundException {
+ return getClass(classLoader, className, initialize, null);
+ }
+
+ /**
+ * Returns the (initialized) class represented by className
+ * using the classLoader. This implementation supports names
+ * like "java.lang.String[]" as well as
+ * "[Ljava.lang.String;".
+ *
+ * @param classLoader the class loader to use to load the class
+ * @param className the class name
+ * @param jxPathFilter the XPath filter
+ * @return the class represented by className using the classLoader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(ClassLoader classLoader, String className, JXPathFilter jxPathFilter) throws ClassNotFoundException {
+ return getClass(classLoader, className, true, jxPathFilter);
+ }
+
/**
* Returns the (initialized) class represented by className
* using the classLoader. This implementation supports names
@@ -103,7 +144,7 @@ public static Class getClass(ClassLoader classLoader, String className, boolean
* @throws ClassNotFoundException if the class is not found
*/
public static Class getClass(ClassLoader classLoader, String className) throws ClassNotFoundException {
- return getClass(classLoader, className, true);
+ return getClass(classLoader, className, true, null);
}
/**
@@ -117,7 +158,22 @@ public static Class getClass(ClassLoader classLoader, String className) throws C
* @throws ClassNotFoundException if the class is not found
*/
public static Class getClass(String className) throws ClassNotFoundException {
- return getClass(className, true);
+ return getClass(className, true, null);
+ }
+
+ /**
+ * Returns the (initialized) class represented by className
+ * using the current thread's context class loader. This implementation
+ * supports names like "java.lang.String[]" as well as
+ * "[Ljava.lang.String;".
+ *
+ * @param className the class name
+ * @param jxPathFilter the XPath filter
+ * @return the class represented by className using the current thread's context class loader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(String className, JXPathFilter jxPathFilter) throws ClassNotFoundException {
+ return getClass(className, true, jxPathFilter);
}
/**
@@ -132,17 +188,33 @@ public static Class getClass(String className) throws ClassNotFoundException {
* @throws ClassNotFoundException if the class is not found
*/
public static Class getClass(String className, boolean initialize) throws ClassNotFoundException {
+ return getClass(className, initialize, null);
+ }
+
+ /**
+ * Returns the class represented by className using the
+ * current thread's context class loader. This implementation supports
+ * names like "java.lang.String[]" as well as
+ * "[Ljava.lang.String;".
+ *
+ * @param className the class name
+ * @param initialize whether the class must be initialized
+ * @param jxPathFilter the XPath filter
+ * @return the class represented by className using the current thread's context class loader
+ * @throws ClassNotFoundException if the class is not found
+ */
+ public static Class getClass(String className, boolean initialize, JXPathFilter jxPathFilter) throws ClassNotFoundException {
ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
ClassLoader currentCL = ClassLoaderUtil.class.getClassLoader();
if (contextCL != null) {
try {
- return getClass(contextCL, className, initialize);
+ return getClass(contextCL, className, initialize, jxPathFilter);
}
catch (ClassNotFoundException e) {//NOPMD
// ignore this exception and try the current class loader.
}
}
- return getClass(currentCL, className, initialize);
+ return getClass(currentCL, className, initialize, jxPathFilter);
}
/**
diff --git a/src/test/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunctionTest.java b/src/test/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunctionTest.java
index 7a222e4d4..7ec7f9419 100644
--- a/src/test/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunctionTest.java
+++ b/src/test/java/org/apache/commons/jxpath/ri/compiler/ExtensionFunctionTest.java
@@ -16,23 +16,10 @@
*/
package org.apache.commons.jxpath.ri.compiler;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Locale;
-
-import org.apache.commons.jxpath.ClassFunctions;
-import org.apache.commons.jxpath.ExpressionContext;
-import org.apache.commons.jxpath.Function;
-import org.apache.commons.jxpath.FunctionLibrary;
-import org.apache.commons.jxpath.Functions;
-import org.apache.commons.jxpath.JXPathContext;
-import org.apache.commons.jxpath.JXPathTestCase;
-import org.apache.commons.jxpath.NodeSet;
-import org.apache.commons.jxpath.PackageFunctions;
-import org.apache.commons.jxpath.Pointer;
-import org.apache.commons.jxpath.TestBean;
-import org.apache.commons.jxpath.Variables;
+import java.util.*;
+
+import org.apache.commons.jxpath.*;
+import org.apache.commons.jxpath.ri.JXPathFilter;
import org.apache.commons.jxpath.ri.model.NodePointer;
import org.apache.commons.jxpath.util.JXPath11CompatibleTypeConverter;
import org.apache.commons.jxpath.util.TypeConverter;
@@ -49,6 +36,7 @@ public class ExtensionFunctionTest extends JXPathTestCase {
private JXPathContext context;
private TestBean testBean;
private TypeConverter typeConverter;
+ JXPathFilter jxPathFilter = new JXPathFilter();
public void setUp() {
if (context == null) {
@@ -385,6 +373,139 @@ public void testBCNodeSetHack() {
Boolean.TRUE);
}
+ public void testClassFunctionsWithClassFilter() {
+ try {
+ Functions iFunctions = new ClassFunctions(TestFunctions3.class, "test3");
+
+ System.setProperty("jxpath.class.deny", "org.apache.commons.jxpath.ri.compiler.TestFunctions3");
+ jxPathFilter.init();
+
+ Function classFunction = iFunctions.getFunction("test3", "testFunction3Method1", null, jxPathFilter);
+ classFunction = null;
+ throw new Exception("testClassFunctionsWithClassFilter() failed.");
+ } catch (Throwable t) {
+ assertTrue((t.getMessage().indexOf("Cannot invoke extension function test3:testFunction3Method1; org.apache.commons.jxpath.ri.compiler.TestFunctions3") > -1)
+ || (t.getMessage().indexOf("java.lang.ClassNotFoundException: org.apache.commons.jxpath.ri.compiler.TestFunctions3") > -1));
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ }
+ }
+
+ public void testClassFunctionsWithoutClassFilter() {
+ Function classFunction = null;
+ try {
+ Functions iFunctions = new ClassFunctions(TestFunctions3.class, "test3");
+
+ System.clearProperty("jxpath.class.deny");
+ jxPathFilter.init();
+
+ classFunction = iFunctions.getFunction("test3", "testFunction3Method1", null, jxPathFilter);
+ } catch (Throwable t) {
+ fail(t.getMessage());
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ assertTrue(classFunction != null);
+ classFunction = null;
+ }
+ }
+
+ public void testPackageFunctionsWithClassFilter() {
+ try {
+ Functions iFunctions = new PackageFunctions("org.apache.commons.jxpath.ri.compiler.","jxpathtests");
+
+ System.setProperty("jxpath.class.deny", "org.apache.commons.jxpath.ri.compiler.TestFunctions3");
+ jxPathFilter.init();
+
+ Function packageFunction = iFunctions.getFunction("jxpathtests", "TestFunctions3.testFunction3Method1", null, jxPathFilter);
+ packageFunction = null;
+ throw new Exception("testPackageFunctionsWithClassFilter() failed.");
+ } catch (Throwable t) {
+ assertTrue((t.getMessage().indexOf("Cannot invoke extension function jxpathtests:TestFunctions3.testFunction3Method1; org.apache.commons.jxpath.ri.compiler.TestFunctions3") > -1)
+ || (t.getMessage().indexOf("java.lang.ClassNotFoundException: org.apache.commons.jxpath.ri.compiler.TestFunctions3") > -1));
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ }
+ }
+
+ public void testPackageFunctionsWithoutClassFilter() {
+ Function packageFunction = null;
+ try {
+ Functions iFunctions = new PackageFunctions("org.apache.commons.jxpath.ri.compiler.","jxpathtests");
+
+ System.clearProperty("jxpath.class.deny");
+ jxPathFilter.init();
+
+ packageFunction = iFunctions.getFunction("jxpathtests", "TestFunctions3.testFunction3Method1", null, jxPathFilter);
+ } catch (Throwable t) {
+ fail(t.getMessage());
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ assertTrue(packageFunction != null);
+ packageFunction = null;
+ }
+ }
+
+ public void testJXPathContextFunctionsWithClassFilter() {
+ String failedMethods = null;
+ try {
+ System.setProperty("jxpath.class.deny", "java.lang.Thread");
+ jxPathFilter.init();
+
+ try {
+ context.iterate("java.lang.Thread.sleep(5000)");
+ throw new Exception("testJXPathContextFunctionsWithClassFilter() failed for iterate()");
+ } catch (Throwable t) {
+ if ((t.getMessage().indexOf("Cannot invoke extension function java.lang.Thread.sleep; java.lang.Thread") > -1)
+ || (t.getMessage().indexOf("java.lang.ClassNotFoundException: java.lang.Thread") > -1)) {
+ //success
+ } else {
+ failedMethods = "org.apache.commons.jxpath.JXPathContext.iterate()";
+ }
+ }
+
+ try {
+ context.selectSingleNode("java.lang.Thread.sleep(5000)");
+ throw new Exception("testJXPathContextFunctionsWithClassFilter() failed for iterate()");
+ } catch (Throwable t) {
+ if ((t.getMessage().indexOf("Cannot invoke extension function java.lang.Thread.sleep; java.lang.Thread") > -1)
+ || (t.getMessage().indexOf("java.lang.ClassNotFoundException: java.lang.Thread") > -1)) {
+ //success
+ } else {
+ failedMethods += ("".equals(failedMethods) ? "org.apache.commons.jxpath.JXPathContext.selectSingleNode()" : ", org.apache.commons.jxpath.JXPathContext.selectSingleNode()");
+ }
+ }
+
+ } catch (Throwable t) {
+ fail(t.getMessage());
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ if (failedMethods != null) {
+ fail("Problem exists in: " + failedMethods);
+ }
+ }
+ }
+
+ public void testJXPathContextFunctionsWithoutClassFilter() {
+ try {
+ System.clearProperty("jxpath.class.deny");
+ jxPathFilter.init();
+
+ long t = System.currentTimeMillis();
+ context.iterate("java.lang.Thread.sleep(5000)");
+ t = System.currentTimeMillis() - t;
+ assertTrue(t >= 5);
+
+ t = System.currentTimeMillis();
+ context.selectSingleNode("java.lang.Thread.sleep(5000)");
+ t = System.currentTimeMillis() - t;
+ assertTrue(t >= 5);
+ } catch (Throwable t) {
+ fail(t.getMessage());
+ } finally {
+ System.clearProperty("jxpath.class.deny");
+ }
+ }
+
private static class Context implements ExpressionContext {
private Object object;
diff --git a/src/test/java/org/apache/commons/jxpath/ri/compiler/TestFunctions3.java b/src/test/java/org/apache/commons/jxpath/ri/compiler/TestFunctions3.java
new file mode 100644
index 000000000..d8d0c92a6
--- /dev/null
+++ b/src/test/java/org/apache/commons/jxpath/ri/compiler/TestFunctions3.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.jxpath.ri.compiler;
+
+import org.apache.commons.jxpath.Functions;
+import org.apache.commons.jxpath.JXPathContext;
+
+import java.util.*;
+
+/**
+ * A test class with few methods, with different argument list
+ *
+ * @author bhmohanr-techie
+ * @version $Revision$ $Date$
+ */
+public final class TestFunctions3 {
+
+ static {
+ System.out.println("TestFunctions3: static block...");
+ }
+
+ public TestFunctions3() {
+ System.out.println("TestFunctions3: constructor...");
+ }
+
+ public static String testFunction3Method1() {
+ System.out.println("TestFunctions3: testFunction3Method1 method...");
+ return "testFunction3Method1";
+ }
+
+ public String testFunction3Method2(String str) {
+ System.out.println("TestFunctions3: testFunction3Method2 method..." + str);
+ return "testFunction3Method2:" + str;
+ }
+
+ public String testFunction3Method3(String str1, String str2) {
+ System.out.println("TestFunctions3: testFunction3Method3 method..." + str1 + ", " + str2);
+ return "testFunction3Method3:" + str1 + ":" + str2;
+ }
+
+}
\ No newline at end of file