Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=43819
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 30 Dec 2009 22:20:30 +0000 (22:20 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 30 Dec 2009 22:20:30 +0000 (22:20 +0000)
Implement ExpressionFactory.newInstance() and ensure that Jasper does not refer to org.apache.el directly
JSP 2.1 TCK passes with these changes applied
Based on a patch by Christoph Beck

git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@894720 13f79535-47bb-0310-9956-ffa450edef68

java/javax/el/ExpressionFactory.java
java/org/apache/jasper/compiler/JspUtil.java
java/org/apache/jasper/compiler/PageInfo.java
java/org/apache/jasper/compiler/Validator.java
java/org/apache/jasper/runtime/JspApplicationContextImpl.java

index cc8b810..144d6ca 100644 (file)
 
 package javax.el;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.util.Properties;
 
 /**
@@ -25,6 +35,15 @@ import java.util.Properties;
  */
 public abstract class ExpressionFactory {
 
+    private static final String SERVICE_RESOURCE_NAME =
+        "META-INF/services/javax.el.ExpressionFactory";
+
+    private static final String SEP = System.getProperty("file.separator");
+    private static final String PROPERTY_FILE =
+        System.getProperty("java.home") + "jre" + SEP + "lib" + SEP +
+        "el.properties";
+    private static final String PROPERTY_NAME = "javax.el.ExpressionFactory";
+
     public abstract Object coerceToType(Object obj, Class<?> expectedType)
             throws ELException;
 
@@ -39,13 +58,172 @@ public abstract class ExpressionFactory {
             String expression, Class<?> expectedReturnType,
             Class<?>[] expectedParamTypes) throws ELException,
             NullPointerException;
-    
+
+    /**
+     * Create a new {@link ExpressionFactory}. The class to use is determined by
+     * the following search order:
+     * <ol>
+     * <li>services API (META-INF/services/javax.el.ExpressionFactory)</li>
+     * <li>$JRE_HOME/lib/el.properties - key javax.el.ExpressionFactory</li>
+     * <li>javax.el.ExpressionFactory</li>
+     * <li>Platform default implementation -
+     *     org.apache.el.ExpressionFactoryImpl</li>
+     * </ol>
+     * @return
+     */
     public static ExpressionFactory newInstance() {
         return newInstance(null);
     }
 
+    /**
+     * Create a new {@link ExpressionFactory} passing in the provided
+     * {@link Properties}. Search order is the same as {@link #newInstance()}.
+     * 
+     * @param properties
+     * @return
+     */
     public static ExpressionFactory newInstance(Properties properties) {
-        // TODO
+        String className = null;
+        ExpressionFactory result = null;
+        
+        ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+
+        // First services API
+        className = getClassNameServices(tccl);
+        if (className == null) {
+            // Second el.properties file
+            className = getClassNameJreDir();
+        }
+        if (className == null) {
+            // Third system property 
+            className = getClassNameSysProp();
+        }
+        if (className == null) {
+            // Fourth - default
+            className = "org.apache.el.ExpressionFactoryImpl";
+        }
+        
+        try {
+            Class<?> clazz = null;
+            if (tccl == null) {
+                clazz = Class.forName(className);
+            } else {
+                clazz = tccl.loadClass(className);
+            }
+            Constructor<?> constructor = null;
+            // Do we need to look for a constructor that will take properties?
+            if (properties != null) {
+                try {
+                    constructor = clazz.getConstructor(Properties.class);
+                } catch (SecurityException se) {
+                    throw new ELException(se);
+                } catch (NoSuchMethodException nsme) {
+                    // This can be ignored
+                    // This is OK for this constructor not to exist
+                }
+            }
+            if (constructor == null) {
+                result = (ExpressionFactory) clazz.newInstance();
+            } else {
+                result =
+                    (ExpressionFactory) constructor.newInstance(properties);
+            }
+            
+        } catch (ClassNotFoundException e) {
+            throw new ELException(
+                    "Unable to find ExpressionFactory of type: " + className,
+                    e);
+        } catch (InstantiationException e) {
+            throw new ELException(
+                    "Unable to create ExpressionFactory of type: " + className,
+                    e);
+        } catch (IllegalAccessException e) {
+            throw new ELException(
+                    "Unable to create ExpressionFactory of type: " + className,
+                    e);
+        } catch (IllegalArgumentException e) {
+            throw new ELException(
+                    "Unable to create ExpressionFactory of type: " + className,
+                    e);
+        } catch (InvocationTargetException e) {
+            throw new ELException(
+                    "Unable to create ExpressionFactory of type: " + className,
+                    e);
+        }
+        
+        return result;
+    }
+    
+    private static String getClassNameServices(ClassLoader tccl) {
+        InputStream is = null;
+        
+        if (tccl == null) {
+            is = ClassLoader.getSystemResourceAsStream(SERVICE_RESOURCE_NAME);
+        } else {
+            is = tccl.getResourceAsStream(SERVICE_RESOURCE_NAME);
+        }
+
+        if (is != null) {
+            String line = null;
+            try {
+                BufferedReader br =
+                    new BufferedReader(new InputStreamReader(is, "UTF-8"));
+                line = br.readLine();
+                if (line != null && line.trim().length() > 0) {
+                    return line.trim();
+                }
+            } catch (UnsupportedEncodingException e) {
+                // Should never happen with UTF-8
+                // If it does - ignore & return null
+            } catch (IOException e) {
+                throw new ELException("Failed to read " + SERVICE_RESOURCE_NAME,
+                        e);
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException ioe) {
+                    // Ignore
+                }
+            }
+        }
+        
+        return null;
+    }
+    
+    private static String getClassNameJreDir() {
+        File file = new File(PROPERTY_FILE);
+        if (file.canRead()) {
+            InputStream is = null;
+            try {
+                is = new FileInputStream(file);
+                Properties props = new Properties();
+                props.load(is);
+                String value = props.getProperty(PROPERTY_NAME);
+                if (value != null && value.trim().length() > 0) {
+                    return value.trim();
+                }
+            } catch (FileNotFoundException e) {
+                // Should not happen - ignore it if it does
+            } catch (IOException e) {
+                throw new ELException("Failed to read " + PROPERTY_FILE, e);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+        return null;
+    }
+    
+    private static final String getClassNameSysProp() {
+        String value = System.getProperty(PROPERTY_NAME);
+        if (value != null && value.trim().length() > 0) {
+            return value.trim();
+        }
         return null;
     }
 }
index ae21fec..075efb0 100644 (file)
@@ -49,9 +49,6 @@ public class JspUtil {
     private static final String OPEN_EXPR = "<%=";
     private static final String CLOSE_EXPR = "%>";
 
-    // private static ExpressionEvaluatorImpl expressionEvaluator
-    // = new ExpressionEvaluatorImpl();
-
     private static final String javaKeywords[] = { "abstract", "assert",
             "boolean", "break", "byte", "case", "catch", "char", "class",
             "const", "continue", "default", "do", "double", "else", "enum",
index 0510122..c2b4a30 100644 (file)
@@ -24,7 +24,6 @@ import java.util.List;
 import java.util.Set;
 import java.util.Vector;
 
-import org.apache.el.ExpressionFactoryImpl;
 import org.apache.jasper.Constants;
 import org.apache.jasper.JasperException;
 
@@ -76,7 +75,8 @@ class PageInfo {
     // JSP 2.1
     private String deferredSyntaxAllowedAsLiteralValue;
     private boolean deferredSyntaxAllowedAsLiteral = false;
-    private ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
+    private ExpressionFactory expressionFactory =
+        ExpressionFactory.newInstance();
     private String trimDirectiveWhitespacesValue;
     private boolean trimDirectiveWhitespaces = false;
     
index 2474456..f07e67e 100644 (file)
@@ -35,7 +35,6 @@ import javax.servlet.jsp.tagext.TagInfo;
 import javax.servlet.jsp.tagext.TagLibraryInfo;
 import javax.servlet.jsp.tagext.ValidationMessage;
 
-import org.apache.el.lang.ELSupport;
 import org.apache.jasper.JasperException;
 import org.apache.jasper.el.ELContextImpl;
 import org.xml.sax.Attributes;
@@ -502,6 +501,9 @@ class Validator {
                 new JspUtil.ValidAttribute("doctype-public"),
                 new JspUtil.ValidAttribute("doctype-system") };
 
+        private static final ExpressionFactory EXPRESSION_FACTORY =
+            ExpressionFactory.newInstance();
+
         /*
          * Constructor
          */
@@ -1160,7 +1162,7 @@ class Validator {
                                     }
                                     // Check casting
                                     try {
-                                        ELSupport.checkType(attrs.getValue(i), expectedClass);
+                                        EXPRESSION_FACTORY.coerceToType(attrs.getValue(i), expectedClass);
                                     } catch (Exception e) {
                                         err.jspError
                                             (n, "jsp.error.coerce_to_type",
index 0087a90..9f85da2 100644 (file)
@@ -38,7 +38,6 @@ import javax.servlet.jsp.JspContext;
 import javax.servlet.jsp.el.ImplicitObjectELResolver;
 import javax.servlet.jsp.el.ScopedAttributeELResolver;
 
-import org.apache.el.ExpressionFactoryImpl;
 import org.apache.jasper.Constants;
 import org.apache.jasper.el.ELContextImpl;
 
@@ -51,7 +50,8 @@ public class JspApplicationContextImpl implements JspApplicationContext {
 
        private final static String KEY = JspApplicationContextImpl.class.getName();
 
-       private final static ExpressionFactory expressionFactory = new ExpressionFactoryImpl();
+       private final static ExpressionFactory expressionFactory =
+           ExpressionFactory.newInstance();
 
        private final List<ELContextListener> contextListeners = new ArrayList<ELContextListener>();