This is an automated email from the ASF dual-hosted git repository. rmannibucau pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/openwebbeans.git
The following commit(s) were added to refs/heads/master by this push: new 14ee3ef [OWB-1375] first step to use privateLookup for java 16 and drop --add-exports need (still need to optimize the defineClass strategy - ensure it is adapted to the runtime and does not rely on fallbacks) 14ee3ef is described below commit 14ee3ef408c8c1b9962d91f6cb4f882ec61eb1d3 Author: Romain Manni-Bucau <rmannibu...@gmail.com> AuthorDate: Mon Mar 15 17:06:38 2021 +0100 [OWB-1375] first step to use privateLookup for java 16 and drop --add-exports need (still need to optimize the defineClass strategy - ensure it is adapted to the runtime and does not rely on fallbacks) --- pom.xml | 19 ------- .../webbeans/custom/CustomProxyPackageMarker.java | 23 ++++++++ .../webbeans/proxy/AbstractProxyFactory.java | 4 +- .../java/org/apache/webbeans/proxy/Unsafe.java | 65 ++++++++++++++++++++-- 4 files changed, 84 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 2ff13a5..54a58ac 100644 --- a/pom.xml +++ b/pom.xml @@ -848,25 +848,6 @@ <profiles> <profile> - <id>java16-hacks</id> - <activation> - <jdk>16</jdk> - </activation> - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-plugin</artifactId> - <configuration> - <argLine>--add-exports java.base/jdk.internal.misc=ALL-UNNAMED</argLine> - </configuration> - - </plugin> - </plugins> - </build> - </profile> - - <profile> <id>reporting</id> <activation> <property> diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/custom/CustomProxyPackageMarker.java b/webbeans-impl/src/main/java/org/apache/webbeans/custom/CustomProxyPackageMarker.java new file mode 100644 index 0000000..8a14770 --- /dev/null +++ b/webbeans-impl/src/main/java/org/apache/webbeans/custom/CustomProxyPackageMarker.java @@ -0,0 +1,23 @@ +/* + * 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.webbeans.custom; + +public interface CustomProxyPackageMarker +{ +} diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java index f66d03b..47c67aa 100644 --- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java +++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/AbstractProxyFactory.java @@ -289,7 +289,7 @@ public abstract class AbstractProxyFactory if (className.startsWith(forbiddenPackagePrefix)) { - fixedClassName = "org.apache.webbeans.custom." + className.substring(forbiddenPackagePrefix.length()); + fixedClassName = "org.apache.webbeans.custom." + className.substring(className.lastIndexOf('.') + 1); } return fixedClassName; @@ -329,7 +329,7 @@ public abstract class AbstractProxyFactory { return definingService.defineAndLoad(proxyClassName, proxyBytes, classToProxy); } - return unsafe.defineAndLoadClass(classLoader, proxyClassName, proxyBytes); + return unsafe.defineAndLoadClass(classLoader, proxyClassName, proxyBytes, classToProxy); } protected <T> T newInstance(final Class<? extends T> proxyClass) diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/Unsafe.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/Unsafe.java index 9beb112..f0b49c3 100644 --- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/Unsafe.java +++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/Unsafe.java @@ -18,6 +18,7 @@ */ package org.apache.webbeans.proxy; +import java.lang.invoke.MethodHandles; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -29,6 +30,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.stream.IntStream; import java.util.stream.Stream; +import org.apache.webbeans.custom.CustomProxyPackageMarker; import org.apache.webbeans.exception.ProxyGenerationException; import org.apache.webbeans.logger.WebBeansLoggerFacade; @@ -44,6 +46,11 @@ public class Unsafe private Method unsafeAllocateInstance; private final AtomicReference<Method> unsafeDefineClass = new AtomicReference<>(); + // java 16 + private volatile Method privateLookup; + private Method defineClass; + private MethodHandles.Lookup lookup; + public Unsafe() { final Class<?> unsafeClass = getUnsafeClass(); @@ -130,7 +137,8 @@ public class Unsafe * The 'defineClass' method on the ClassLoader is protected, thus we need to invoke it via reflection. * @return the Class which got loaded in the classloader */ - public <T> Class<T> defineAndLoadClass(ClassLoader classLoader, String proxyName, byte[] proxyBytes) + public <T> Class<T> defineAndLoadClass(ClassLoader classLoader, String proxyName, byte[] proxyBytes, + Class<?> parent) throws ProxyGenerationException { Class<?> clazz = classLoader.getClass(); @@ -191,17 +199,62 @@ public class Unsafe // default error handling } } - throw new ProxyGenerationException( - le.getMessage() + (isJava16OrMore() ? "\n" + - "On Java 16 ensure to set --add-exports java.base/jdk.internal.misc=ALL-UNNAMED on the JVM" : ""), - le.getCause()); + throw onProxyGenerationError(le); } catch (Throwable e) { - throw new ProxyGenerationException(e); + // we can also defineHiddenClass but what would be the real point? let's keep it simple for now + try + { + if (privateLookup == null) + { + synchronized (this) + { + if (privateLookup == null) + { + lookup = MethodHandles.lookup(); + privateLookup = MethodHandles.class.getDeclaredMethod( + "privateLookupIn", Class.class, MethodHandles.Lookup.class); + defineClass = lookup.getClass().getMethod("defineClass", byte[].class); + } + } + } + final MethodHandles.Lookup lookupInstance = MethodHandles.Lookup.class.cast( + privateLookup.invoke( + null, + proxyName.startsWith("org.apache.webbeans.custom.") ? + CustomProxyPackageMarker.class : parent, + lookup)); + return (Class<T>) defineClass.invoke(lookupInstance, proxyBytes); + } + catch (final Exception exception) + { + if (LinkageError.class.isInstance(exception.getCause())) + { + try + { + return (Class<T>) Class.forName(proxyName.replace('/', '.'), true, classLoader); + } + catch (ClassNotFoundException ignored) + { + // default error handling + } + } + final ProxyGenerationException proxyGenerationException = onProxyGenerationError(e); + proxyGenerationException.addSuppressed(exception); + throw proxyGenerationException; + } } } + private ProxyGenerationException onProxyGenerationError(final Throwable throwable) + { + return new ProxyGenerationException( + throwable.getMessage() + (isJava16OrMore() ? "\n" + + "On Java 16 ensure to set --add-exports java.base/jdk.internal.misc=ALL-UNNAMED on the JVM" : ""), + throwable.getCause()); + } + private boolean isJava16OrMore() { final String version = System.getProperty("java.version", "-1");