| /* |
| * 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 javax.el; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| /** |
| * @since EL 3.0 |
| */ |
| public class ImportHandler { |
| |
| private static final Map<String,Set<String>> standardPackages = new HashMap<>(); |
| |
| static { |
| // Servlet 4.0 |
| Set<String> servletClassNames = new HashSet<>(); |
| // Interfaces |
| servletClassNames.add("AsyncContext"); |
| servletClassNames.add("AsyncListener"); |
| servletClassNames.add("Filter"); |
| servletClassNames.add("FilterChain"); |
| servletClassNames.add("FilterConfig"); |
| servletClassNames.add("FilterRegistration"); |
| servletClassNames.add("FilterRegistration.Dynamic"); |
| servletClassNames.add("ReadListener"); |
| servletClassNames.add("Registration"); |
| servletClassNames.add("Registration.Dynamic"); |
| servletClassNames.add("RequestDispatcher"); |
| servletClassNames.add("Servlet"); |
| servletClassNames.add("ServletConfig"); |
| servletClassNames.add("ServletContainerInitializer"); |
| servletClassNames.add("ServletContext"); |
| servletClassNames.add("ServletContextAttributeListener"); |
| servletClassNames.add("ServletContextListener"); |
| servletClassNames.add("ServletRegistration"); |
| servletClassNames.add("ServletRegistration.Dynamic"); |
| servletClassNames.add("ServletRequest"); |
| servletClassNames.add("ServletRequestAttributeListener"); |
| servletClassNames.add("ServletRequestListener"); |
| servletClassNames.add("ServletResponse"); |
| servletClassNames.add("SessionCookieConfig"); |
| servletClassNames.add("SingleThreadModel"); |
| servletClassNames.add("WriteListener"); |
| // Classes |
| servletClassNames.add("AsyncEvent"); |
| servletClassNames.add("GenericFilter"); |
| servletClassNames.add("GenericServlet"); |
| servletClassNames.add("HttpConstraintElement"); |
| servletClassNames.add("HttpMethodConstraintElement"); |
| servletClassNames.add("MultipartConfigElement"); |
| servletClassNames.add("ServletContextAttributeEvent"); |
| servletClassNames.add("ServletContextEvent"); |
| servletClassNames.add("ServletInputStream"); |
| servletClassNames.add("ServletOutputStream"); |
| servletClassNames.add("ServletRequestAttributeEvent"); |
| servletClassNames.add("ServletRequestEvent"); |
| servletClassNames.add("ServletRequestWrapper"); |
| servletClassNames.add("ServletResponseWrapper"); |
| servletClassNames.add("ServletSecurityElement"); |
| // Enums |
| servletClassNames.add("DispatcherType"); |
| servletClassNames.add("SessionTrackingMode"); |
| // Exceptions |
| servletClassNames.add("ServletException"); |
| servletClassNames.add("UnavailableException"); |
| standardPackages.put("javax.servlet", servletClassNames); |
| |
| // Servlet 4.0 |
| Set<String> servletHttpClassNames = new HashSet<>(); |
| // Interfaces |
| servletHttpClassNames.add("HttpServletMapping"); |
| servletHttpClassNames.add("HttpServletRequest"); |
| servletHttpClassNames.add("HttpServletResponse"); |
| servletHttpClassNames.add("HttpSession"); |
| servletHttpClassNames.add("HttpSessionActivationListener"); |
| servletHttpClassNames.add("HttpSessionAttributeListener"); |
| servletHttpClassNames.add("HttpSessionBindingListener"); |
| servletHttpClassNames.add("HttpSessionContext"); |
| servletHttpClassNames.add("HttpSessionIdListener"); |
| servletHttpClassNames.add("HttpSessionListener"); |
| servletHttpClassNames.add("HttpUpgradeHandler"); |
| servletHttpClassNames.add("Part"); |
| servletHttpClassNames.add("PushBuilder"); |
| servletHttpClassNames.add("WebConnection"); |
| // Classes |
| servletHttpClassNames.add("Cookie"); |
| servletHttpClassNames.add("HttpFilter"); |
| servletHttpClassNames.add("HttpServlet"); |
| servletHttpClassNames.add("HttpServletRequestWrapper"); |
| servletHttpClassNames.add("HttpServletResponseWrapper"); |
| servletHttpClassNames.add("HttpSessionBindingEvent"); |
| servletHttpClassNames.add("HttpSessionEvent"); |
| servletHttpClassNames.add("HttpUtils"); |
| // Enums |
| servletHttpClassNames.add("MappingMatch"); |
| standardPackages.put("javax.servlet.http", servletHttpClassNames); |
| |
| // JSP 2.3 |
| Set<String> servletJspClassNames = new HashSet<>(); |
| //Interfaces |
| servletJspClassNames.add("HttpJspPage"); |
| servletJspClassNames.add("JspApplicationContext"); |
| servletJspClassNames.add("JspPage"); |
| // Classes |
| servletJspClassNames.add("ErrorData"); |
| servletJspClassNames.add("JspContext"); |
| servletJspClassNames.add("JspEngineInfo"); |
| servletJspClassNames.add("JspFactory"); |
| servletJspClassNames.add("JspWriter"); |
| servletJspClassNames.add("PageContext"); |
| servletJspClassNames.add("Exceptions"); |
| servletJspClassNames.add("JspException"); |
| servletJspClassNames.add("JspTagException"); |
| servletJspClassNames.add("SkipPageException"); |
| standardPackages.put("javax.servlet.jsp", servletJspClassNames); |
| |
| Set<String> javaLangClassNames = new HashSet<>(); |
| // Taken from Java 11 EA18 Javadoc |
| // Interfaces |
| javaLangClassNames.add("Appendable"); |
| javaLangClassNames.add("AutoCloseable"); |
| javaLangClassNames.add("CharSequence"); |
| javaLangClassNames.add("Cloneable"); |
| javaLangClassNames.add("Comparable"); |
| javaLangClassNames.add("Iterable"); |
| javaLangClassNames.add("ProcessHandle"); |
| javaLangClassNames.add("ProcessHandle.Info"); |
| javaLangClassNames.add("Readable"); |
| javaLangClassNames.add("Runnable"); |
| javaLangClassNames.add("StackWalker.StackFrame"); |
| javaLangClassNames.add("System.Logger"); |
| javaLangClassNames.add("Thread.UncaughtExceptionHandler"); |
| //Classes |
| javaLangClassNames.add("Boolean"); |
| javaLangClassNames.add("Byte"); |
| javaLangClassNames.add("Character"); |
| javaLangClassNames.add("Character.Subset"); |
| javaLangClassNames.add("Character.UnicodeBlock"); |
| javaLangClassNames.add("Class"); |
| javaLangClassNames.add("ClassLoader"); |
| javaLangClassNames.add("ClassValue"); |
| javaLangClassNames.add("Compiler"); |
| javaLangClassNames.add("Double"); |
| javaLangClassNames.add("Enum"); |
| javaLangClassNames.add("Enum.EnumDesc"); |
| javaLangClassNames.add("Float"); |
| javaLangClassNames.add("InheritableThreadLocal"); |
| javaLangClassNames.add("Integer"); |
| javaLangClassNames.add("Long"); |
| javaLangClassNames.add("Math"); |
| javaLangClassNames.add("Module"); |
| javaLangClassNames.add("ModuleLayer"); |
| javaLangClassNames.add("ModuleLayer.Controller"); |
| javaLangClassNames.add("Number"); |
| javaLangClassNames.add("Object"); |
| javaLangClassNames.add("Package"); |
| javaLangClassNames.add("Process"); |
| javaLangClassNames.add("ProcessBuilder"); |
| javaLangClassNames.add("ProcessBuilder.Redirect"); |
| javaLangClassNames.add("Runtime"); |
| javaLangClassNames.add("Runtime.Version"); |
| javaLangClassNames.add("RuntimePermission"); |
| javaLangClassNames.add("SecurityManager"); |
| javaLangClassNames.add("Short"); |
| javaLangClassNames.add("StackTraceElement"); |
| javaLangClassNames.add("StackWalker"); |
| javaLangClassNames.add("StrictMath"); |
| javaLangClassNames.add("String"); |
| javaLangClassNames.add("StringBuffer"); |
| javaLangClassNames.add("StringBuilder"); |
| javaLangClassNames.add("System"); |
| javaLangClassNames.add("System.LoggerFinder"); |
| javaLangClassNames.add("Thread"); |
| javaLangClassNames.add("ThreadGroup"); |
| javaLangClassNames.add("ThreadLocal"); |
| javaLangClassNames.add("Throwable"); |
| javaLangClassNames.add("Void"); |
| //Enums |
| javaLangClassNames.add("Character.UnicodeScript"); |
| javaLangClassNames.add("ProcessBuilder.Redirect.Type"); |
| javaLangClassNames.add("StackWalker.Option"); |
| javaLangClassNames.add("System.Logger.Level"); |
| javaLangClassNames.add("Thread.State"); |
| //Exceptions |
| javaLangClassNames.add("ArithmeticException"); |
| javaLangClassNames.add("ArrayIndexOutOfBoundsException"); |
| javaLangClassNames.add("ArrayStoreException"); |
| javaLangClassNames.add("ClassCastException"); |
| javaLangClassNames.add("ClassNotFoundException"); |
| javaLangClassNames.add("CloneNotSupportedException"); |
| javaLangClassNames.add("EnumConstantNotPresentException"); |
| javaLangClassNames.add("Exception"); |
| javaLangClassNames.add("IllegalAccessException"); |
| javaLangClassNames.add("IllegalArgumentException"); |
| javaLangClassNames.add("IllegalCallerException"); |
| javaLangClassNames.add("IllegalMonitorStateException"); |
| javaLangClassNames.add("IllegalStateException"); |
| javaLangClassNames.add("IllegalThreadStateException"); |
| javaLangClassNames.add("IndexOutOfBoundsException"); |
| javaLangClassNames.add("InstantiationException"); |
| javaLangClassNames.add("InterruptedException"); |
| javaLangClassNames.add("LayerInstantiationException"); |
| javaLangClassNames.add("NegativeArraySizeException"); |
| javaLangClassNames.add("NoSuchFieldException"); |
| javaLangClassNames.add("NoSuchMethodException"); |
| javaLangClassNames.add("NullPointerException"); |
| javaLangClassNames.add("NumberFormatException"); |
| javaLangClassNames.add("ReflectiveOperationException"); |
| javaLangClassNames.add("RuntimeException"); |
| javaLangClassNames.add("SecurityException"); |
| javaLangClassNames.add("StringIndexOutOfBoundsException"); |
| javaLangClassNames.add("TypeNotPresentException"); |
| javaLangClassNames.add("UnsupportedOperationException"); |
| //Errors |
| javaLangClassNames.add("AbstractMethodError"); |
| javaLangClassNames.add("AssertionError"); |
| javaLangClassNames.add("BootstrapMethodError"); |
| javaLangClassNames.add("ClassCircularityError"); |
| javaLangClassNames.add("ClassFormatError"); |
| javaLangClassNames.add("Error"); |
| javaLangClassNames.add("ExceptionInInitializerError"); |
| javaLangClassNames.add("IllegalAccessError"); |
| javaLangClassNames.add("IncompatibleClassChangeError"); |
| javaLangClassNames.add("InstantiationError"); |
| javaLangClassNames.add("InternalError"); |
| javaLangClassNames.add("LinkageError"); |
| javaLangClassNames.add("NoClassDefFoundError"); |
| javaLangClassNames.add("NoSuchFieldError"); |
| javaLangClassNames.add("NoSuchMethodError"); |
| javaLangClassNames.add("OutOfMemoryError"); |
| javaLangClassNames.add("StackOverflowError"); |
| javaLangClassNames.add("ThreadDeath"); |
| javaLangClassNames.add("UnknownError"); |
| javaLangClassNames.add("UnsatisfiedLinkError"); |
| javaLangClassNames.add("UnsupportedClassVersionError"); |
| javaLangClassNames.add("VerifyError"); |
| javaLangClassNames.add("VirtualMachineError"); |
| //Annotation Types |
| javaLangClassNames.add("Deprecated"); |
| javaLangClassNames.add("FunctionalInterface"); |
| javaLangClassNames.add("Override"); |
| javaLangClassNames.add("SafeVarargs"); |
| javaLangClassNames.add("SuppressWarnings"); |
| standardPackages.put("java.lang", javaLangClassNames); |
| |
| } |
| |
| private Map<String,Set<String>> packageNames = new ConcurrentHashMap<>(); |
| private Map<String,String> classNames = new ConcurrentHashMap<>(); |
| private Map<String,Class<?>> clazzes = new ConcurrentHashMap<>(); |
| private Map<String,Class<?>> statics = new ConcurrentHashMap<>(); |
| |
| |
| public ImportHandler() { |
| importPackage("java.lang"); |
| } |
| |
| |
| public void importStatic(String name) throws javax.el.ELException { |
| int lastPeriod = name.lastIndexOf('.'); |
| |
| if (lastPeriod < 0) { |
| throw new ELException(Util.message( |
| null, "importHandler.invalidStaticName", name)); |
| } |
| |
| String className = name.substring(0, lastPeriod); |
| String fieldOrMethodName = name.substring(lastPeriod + 1); |
| |
| Class<?> clazz = findClass(className, true); |
| |
| if (clazz == null) { |
| throw new ELException(Util.message( |
| null, "importHandler.invalidClassNameForStatic", |
| className, name)); |
| } |
| |
| boolean found = false; |
| |
| for (Field field : clazz.getFields()) { |
| if (field.getName().equals(fieldOrMethodName)) { |
| int modifiers = field.getModifiers(); |
| if (Modifier.isStatic(modifiers) && |
| Modifier.isPublic(modifiers)) { |
| found = true; |
| break; |
| } |
| } |
| } |
| |
| if (!found) { |
| for (Method method : clazz.getMethods()) { |
| if (method.getName().equals(fieldOrMethodName)) { |
| int modifiers = method.getModifiers(); |
| if (Modifier.isStatic(modifiers) && |
| Modifier.isPublic(modifiers)) { |
| found = true; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!found) { |
| throw new ELException(Util.message(null, |
| "importHandler.staticNotFound", fieldOrMethodName, |
| className, name)); |
| } |
| |
| Class<?> conflict = statics.get(fieldOrMethodName); |
| if (conflict != null) { |
| throw new ELException(Util.message(null, |
| "importHandler.ambiguousStaticImport", name, |
| conflict.getName() + '.' + fieldOrMethodName)); |
| } |
| |
| statics.put(fieldOrMethodName, clazz); |
| } |
| |
| |
| public void importClass(String name) throws javax.el.ELException { |
| int lastPeriodIndex = name.lastIndexOf('.'); |
| |
| if (lastPeriodIndex < 0) { |
| throw new ELException(Util.message( |
| null, "importHandler.invalidClassName", name)); |
| } |
| |
| String unqualifiedName = name.substring(lastPeriodIndex + 1); |
| String currentName = classNames.putIfAbsent(unqualifiedName, name); |
| |
| if (currentName != null && !currentName.equals(name)) { |
| // Conflict. Same unqualifiedName, different fully qualified names |
| throw new ELException(Util.message(null, |
| "importHandler.ambiguousImport", name, currentName)); |
| } |
| } |
| |
| |
| public void importPackage(String name) { |
| // Import ambiguity is handled at resolution, not at import |
| // Whether the package exists is not checked, |
| // a) for sake of performance when used in JSPs (BZ 57142), |
| // b) java.lang.Package.getPackage(name) is not reliable (BZ 57574), |
| // c) such check is not required by specification. |
| Set<String> preloaded = standardPackages.get(name); |
| if (preloaded == null) { |
| packageNames.put(name, Collections.emptySet()); |
| } else { |
| packageNames.put(name, preloaded); |
| } |
| } |
| |
| |
| public java.lang.Class<?> resolveClass(String name) { |
| if (name == null || name.contains(".")) { |
| return null; |
| } |
| |
| // Has it been previously resolved? |
| Class<?> result = clazzes.get(name); |
| |
| if (result != null) { |
| if (NotFound.class.equals(result)) { |
| return null; |
| } else { |
| return result; |
| } |
| } |
| |
| // Search the class imports |
| String className = classNames.get(name); |
| if (className != null) { |
| Class<?> clazz = findClass(className, true); |
| if (clazz != null) { |
| clazzes.put(name, clazz); |
| return clazz; |
| } |
| } |
| |
| // Search the package imports - note there may be multiple matches |
| // (which correctly triggers an error) |
| for (Map.Entry<String,Set<String>> entry : packageNames.entrySet()) { |
| if (!entry.getValue().isEmpty()) { |
| // Standard package where we know all the class names |
| if (!entry.getValue().contains(name)) { |
| // Requested name isn't in the list so it isn't in this |
| // package so move on to next package. This allows the |
| // class loader look-up to be skipped. |
| continue; |
| } |
| } |
| className = entry.getKey() + '.' + name; |
| Class<?> clazz = findClass(className, false); |
| if (clazz != null) { |
| if (result != null) { |
| throw new ELException(Util.message(null, |
| "importHandler.ambiguousImport", className, |
| result.getName())); |
| } |
| result = clazz; |
| } |
| } |
| if (result == null) { |
| // Cache NotFound results to save repeated calls to findClass() |
| // which is relatively slow |
| clazzes.put(name, NotFound.class); |
| } else { |
| clazzes.put(name, result); |
| } |
| |
| return result; |
| } |
| |
| |
| public java.lang.Class<?> resolveStatic(String name) { |
| return statics.get(name); |
| } |
| |
| |
| private Class<?> findClass(String name, boolean throwException) { |
| Class<?> clazz; |
| ClassLoader cl = Util.getContextClassLoader(); |
| String path = name.replace('.', '/') + ".class"; |
| try { |
| /* Given that findClass() has to be called for every imported |
| * package and that getResource() is a lot faster then loadClass() |
| * for resources that don't exist, the overhead of the getResource() |
| * for the case where the class does exist is a lot less than the |
| * overhead we save by not calling loadClass(). |
| */ |
| if (cl.getResource(path) == null) { |
| return null; |
| } |
| } catch (ClassCircularityError cce) { |
| // May happen under a security manager. Ignore it and try loading |
| // the class normally. |
| } |
| try { |
| clazz = cl.loadClass(name); |
| } catch (ClassNotFoundException e) { |
| return null; |
| } |
| |
| // Class must be public, non-abstract and not an interface |
| int modifiers = clazz.getModifiers(); |
| if (!Modifier.isPublic(modifiers) || Modifier.isAbstract(modifiers) || |
| Modifier.isInterface(modifiers)) { |
| if (throwException) { |
| throw new ELException(Util.message( |
| null, "importHandler.invalidClass", name)); |
| } else { |
| return null; |
| } |
| } |
| |
| return clazz; |
| } |
| |
| |
| /* |
| * Marker class used because null values are not permitted in a |
| * ConcurrentHashMap. |
| */ |
| private static class NotFound { |
| } |
| } |