/* * 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.tomcat.util.compat; import java.io.File; import java.io.IOException; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.Deque; import java.util.Set; import java.util.jar.JarFile; import java.util.zip.ZipFile; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.res.StringManager; class Jre9Compat extends Jre8Compat { private static final Log log = LogFactory.getLog(Jre9Compat.class); private static final StringManager sm = StringManager.getManager(Jre9Compat.class); private static final Class inaccessibleObjectExceptionClazz; private static final Method setApplicationProtocolsMethod; private static final Method getApplicationProtocolMethod; private static final Method setDefaultUseCachesMethod; private static final Method bootMethod; private static final Method configurationMethod; private static final Method modulesMethod; private static final Method referenceMethod; private static final Method locationMethod; private static final Method isPresentMethod; private static final Method getMethod; private static final Constructor jarFileConstructor; private static final Method isMultiReleaseMethod; private static final Object RUNTIME_VERSION; private static final int RUNTIME_MAJOR_VERSION; private static final Method canAccessMethod; private static final Method getModuleMethod; private static final Method isExportedMethod; static { Class c1 = null; Method m2 = null; Method m3 = null; Method m4 = null; Method m5 = null; Method m6 = null; Method m7 = null; Method m8 = null; Method m9 = null; Method m10 = null; Method m11 = null; Constructor c12 = null; Method m13 = null; Object o14 = null; Object o15 = null; Method m16 = null; Method m17 = null; Method m18 = null; try { // Order is important for the error handling below. // Must look up c1 first. c1 = Class.forName("java.lang.reflect.InaccessibleObjectException"); Class moduleLayerClazz = Class.forName("java.lang.ModuleLayer"); Class configurationClazz = Class.forName("java.lang.module.Configuration"); Class resolvedModuleClazz = Class.forName("java.lang.module.ResolvedModule"); Class moduleReferenceClazz = Class.forName("java.lang.module.ModuleReference"); Class optionalClazz = Class.forName("java.util.Optional"); Class versionClazz = Class.forName("java.lang.Runtime$Version"); Method runtimeVersionMethod = JarFile.class.getMethod("runtimeVersion"); Method majorMethod = versionClazz.getMethod("major"); m2 = SSLParameters.class.getMethod("setApplicationProtocols", String[].class); m3 = SSLEngine.class.getMethod("getApplicationProtocol"); m4 = URLConnection.class.getMethod("setDefaultUseCaches", String.class, boolean.class); m5 = moduleLayerClazz.getMethod("boot"); m6 = moduleLayerClazz.getMethod("configuration"); m7 = configurationClazz.getMethod("modules"); m8 = resolvedModuleClazz.getMethod("reference"); m9 = moduleReferenceClazz.getMethod("location"); m10 = optionalClazz.getMethod("isPresent"); m11 = optionalClazz.getMethod("get"); c12 = JarFile.class.getConstructor(File.class, boolean.class, int.class, versionClazz); m13 = JarFile.class.getMethod("isMultiRelease"); o14 = runtimeVersionMethod.invoke(null); o15 = majorMethod.invoke(o14); m16 = AccessibleObject.class.getMethod("canAccess", new Class[] { Object.class }); m17 = Class.class.getMethod("getModule"); Class moduleClass = Class.forName("java.lang.Module"); m18 = moduleClass.getMethod("isExported", String.class); } catch (ClassNotFoundException e) { if (c1 == null) { // Must be pre-Java 9 log.debug(sm.getString("jre9Compat.javaPre9"), e); } else { // Should never happen - signature error in lookup? log.error(sm.getString("jre9Compat.unexpected"), e); } } catch (ReflectiveOperationException | IllegalArgumentException e) { // Should never happen log.error(sm.getString("jre9Compat.unexpected"), e); } inaccessibleObjectExceptionClazz = c1; setApplicationProtocolsMethod = m2; getApplicationProtocolMethod = m3; setDefaultUseCachesMethod = m4; bootMethod = m5; configurationMethod = m6; modulesMethod = m7; referenceMethod = m8; locationMethod = m9; isPresentMethod = m10; getMethod = m11; jarFileConstructor = c12; isMultiReleaseMethod = m13; RUNTIME_VERSION = o14; if (o15 != null) { RUNTIME_MAJOR_VERSION = ((Integer) o15).intValue(); } else { // Must be Java 8 RUNTIME_MAJOR_VERSION = 8; } canAccessMethod = m16; getModuleMethod = m17; isExportedMethod = m18; } static boolean isSupported() { return inaccessibleObjectExceptionClazz != null; } @Override public boolean isInstanceOfInaccessibleObjectException(Throwable t) { if (t == null) { return false; } return inaccessibleObjectExceptionClazz.isAssignableFrom(t.getClass()); } @Override public void setApplicationProtocols(SSLParameters sslParameters, String[] protocols) { try { setApplicationProtocolsMethod.invoke(sslParameters, (Object) protocols); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } } @Override public String getApplicationProtocol(SSLEngine sslEngine) { try { return (String) getApplicationProtocolMethod.invoke(sslEngine); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } } @Override public void disableCachingForJarUrlConnections() throws IOException { try { setDefaultUseCachesMethod.invoke(null, "JAR", Boolean.FALSE); } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { throw new UnsupportedOperationException(e); } } @Override public void addBootModulePath(Deque classPathUrlsToProcess) { try { Object bootLayer = bootMethod.invoke(null); Object bootConfiguration = configurationMethod.invoke(bootLayer); Set resolvedModules = (Set) modulesMethod.invoke(bootConfiguration); for (Object resolvedModule : resolvedModules) { Object moduleReference = referenceMethod.invoke(resolvedModule); Object optionalURI = locationMethod.invoke(moduleReference); Boolean isPresent = (Boolean) isPresentMethod.invoke(optionalURI); if (isPresent.booleanValue()) { URI uri = (URI) getMethod.invoke(optionalURI); try { URL url = uri.toURL(); classPathUrlsToProcess.add(url); } catch (MalformedURLException e) { log.warn(sm.getString("jre9Compat.invalidModuleUri", uri), e); } } } } catch (ReflectiveOperationException e) { throw new UnsupportedOperationException(e); } } @Override public JarFile jarFileNewInstance(File f) throws IOException { try { return jarFileConstructor.newInstance( f, Boolean.TRUE, Integer.valueOf(ZipFile.OPEN_READ), RUNTIME_VERSION); } catch (ReflectiveOperationException | IllegalArgumentException e) { throw new IOException(e); } } @Override public boolean jarFileIsMultiRelease(JarFile jarFile) { try { return ((Boolean) isMultiReleaseMethod.invoke(jarFile)).booleanValue(); } catch (ReflectiveOperationException | IllegalArgumentException e) { return false; } } @Override public int jarFileRuntimeMajorVersion() { return RUNTIME_MAJOR_VERSION; } @Override public boolean canAcccess(Object base, AccessibleObject accessibleObject) { try { return ((Boolean) canAccessMethod.invoke(accessibleObject, base)).booleanValue(); } catch (ReflectiveOperationException | IllegalArgumentException e) { return false; } } @Override public boolean isExported(Class type) { try { String packageName = type.getPackage().getName(); Object module = getModuleMethod.invoke(type); return ((Boolean) isExportedMethod.invoke(module, packageName)).booleanValue(); } catch (ReflectiveOperationException e) { return false; } } }