You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by rm...@apache.org on 2020/06/07 14:31:14 UTC
[openwebbeans] branch master updated: [OWB-1325] better impl of
stable names for proxies
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 234c0fc [OWB-1325] better impl of stable names for proxies
234c0fc is described below
commit 234c0fcf2015f815fd6aa2ab0c8acdf5f8574408
Author: Romain Manni-Bucau <rm...@gmail.com>
AuthorDate: Sun Jun 7 16:31:06 2020 +0200
[OWB-1325] better impl of stable names for proxies
---
.../java/org/apache/webbeans/hash/XxHash64.java | 174 +++++++++++++++++++++
.../webbeans/proxy/AbstractProxyFactory.java | 36 +++++
.../proxy/InterceptorDecoratorProxyFactory.java | 8 +-
.../webbeans/proxy/NormalScopeProxyFactory.java | 9 +-
.../org/apache/webbeans/hash/XxHash64Test.java | 32 ++++
.../webbeans/test/managed/ProxyFactoryTest.java | 92 ++++++++++-
6 files changed, 340 insertions(+), 11 deletions(-)
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/hash/XxHash64.java b/webbeans-impl/src/main/java/org/apache/webbeans/hash/XxHash64.java
new file mode 100644
index 0000000..2f19ad2
--- /dev/null
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/hash/XxHash64.java
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ */
+
+// original header
+/*
+ * Copyright 2015 Higher Frequency Trading http://www.higherfrequencytrading.com
+ *
+ * Licensed 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.hash;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+// forked from @OpenHFT/Zero-Allocation-Hashing/blob/master/src/main/java/net/openhft/hashing/XxHash.java
+// (ASFv2 license)
+public final class XxHash64
+{
+ private static final long PRIME64_1 = 0x9E3779B185EBCA87L;
+ private static final long PRIME64_2 = 0xC2B2AE3D27D4EB4FL;
+ private static final long PRIME64_3 = 0x165667B19E3779F9L;
+ private static final long PRIME64_4 = 0x85EBCA77C2b2AE63L;
+ private static final long PRIME64_5 = 0x27D4EB2F165667C5L;
+
+ private XxHash64()
+ {
+ // no-op
+ }
+
+ public static long apply(final String input)
+ {
+ return apply(ByteBuffer.wrap(input.getBytes(StandardCharsets.UTF_8)));
+ }
+
+ public static long apply(final ByteBuffer input)
+ {
+ int length = input.remaining();
+ long remaining = length;
+
+ long hash;
+ int off = 0;
+ if (remaining >= 32)
+ {
+ long v1 = PRIME64_1 + PRIME64_2;
+ long v2 = PRIME64_2;
+ long v3 = 0;
+ long v4 = -PRIME64_1;
+
+ do
+ {
+ v1 += input.getLong(off) * PRIME64_2;
+ v1 = Long.rotateLeft(v1, 31);
+ v1 *= PRIME64_1;
+
+ v2 += input.getLong(off + 8) * PRIME64_2;
+ v2 = Long.rotateLeft(v2, 31);
+ v2 *= PRIME64_1;
+
+ v3 += input.getLong(off + 16) * PRIME64_2;
+ v3 = Long.rotateLeft(v3, 31);
+ v3 *= PRIME64_1;
+
+ v4 += input.getLong(off + 24) * PRIME64_2;
+ v4 = Long.rotateLeft(v4, 31);
+ v4 *= PRIME64_1;
+
+ off += 32;
+ remaining -= 32;
+ }
+ while (remaining >= 32);
+
+ hash = Long.rotateLeft(v1, 1)
+ + Long.rotateLeft(v2, 7)
+ + Long.rotateLeft(v3, 12)
+ + Long.rotateLeft(v4, 18);
+
+ v1 *= PRIME64_2;
+ v1 = Long.rotateLeft(v1, 31);
+ v1 *= PRIME64_1;
+ hash ^= v1;
+ hash = hash * PRIME64_1 + PRIME64_4;
+
+ v2 *= PRIME64_2;
+ v2 = Long.rotateLeft(v2, 31);
+ v2 *= PRIME64_1;
+ hash ^= v2;
+ hash = hash * PRIME64_1 + PRIME64_4;
+
+ v3 *= PRIME64_2;
+ v3 = Long.rotateLeft(v3, 31);
+ v3 *= PRIME64_1;
+ hash ^= v3;
+ hash = hash * PRIME64_1 + PRIME64_4;
+
+ v4 *= PRIME64_2;
+ v4 = Long.rotateLeft(v4, 31);
+ v4 *= PRIME64_1;
+ hash ^= v4;
+ hash = hash * PRIME64_1 + PRIME64_4;
+ }
+ else
+ {
+ hash = PRIME64_5;
+ }
+
+ hash += length;
+
+ while (remaining >= 8)
+ {
+ long k1 = input.getLong(off);
+ k1 *= PRIME64_2;
+ k1 = Long.rotateLeft(k1, 31);
+ k1 *= PRIME64_1;
+ hash ^= k1;
+ hash = Long.rotateLeft(hash, 27) * PRIME64_1 + PRIME64_4;
+ off += 8;
+ remaining -= 8;
+ }
+
+ if (remaining >= 4)
+ {
+ hash ^= (input.getInt(off) & 0xFFFFFFFFL) * PRIME64_1;
+ hash = Long.rotateLeft(hash, 23) * PRIME64_2 + PRIME64_3;
+ off += 4;
+ remaining -= 4;
+ }
+
+ while (remaining != 0)
+ {
+ hash ^= (input.get(off) & 0xFF) * PRIME64_5;
+ hash = Long.rotateLeft(hash, 11) * PRIME64_1;
+ --remaining;
+ ++off;
+ }
+
+ return finalize(hash);
+ }
+
+ private static long finalize(long hash)
+ {
+ hash ^= hash >>> 33;
+ hash *= PRIME64_2;
+ hash ^= hash >>> 29;
+ hash *= PRIME64_3;
+ hash ^= hash >>> 32;
+ return hash;
+ }
+}
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 2e90df4..34868df 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
@@ -18,6 +18,7 @@
*/
package org.apache.webbeans.proxy;
+import static java.util.stream.Collectors.joining;
import static org.apache.xbean.asm8.ClassReader.SKIP_CODE;
import static org.apache.xbean.asm8.ClassReader.SKIP_DEBUG;
import static org.apache.xbean.asm8.ClassReader.SKIP_FRAMES;
@@ -28,10 +29,12 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.stream.Stream;
import org.apache.webbeans.config.WebBeansContext;
import org.apache.webbeans.exception.ProxyGenerationException;
import org.apache.webbeans.exception.WebBeansException;
+import org.apache.webbeans.hash.XxHash64;
import org.apache.webbeans.spi.DefiningClassService;
import org.apache.xbean.asm8.ClassReader;
import org.apache.xbean.asm8.ClassWriter;
@@ -59,6 +62,7 @@ public abstract class AbstractProxyFactory
private final DefiningClassService definingService;
private final boolean useStaticNames;
+ private final boolean useXXhash64;
protected WebBeansContext webBeansContext;
@@ -80,6 +84,8 @@ public abstract class AbstractProxyFactory
definingService = webBeansContext.getService(DefiningClassService.class);
useStaticNames = Boolean.parseBoolean(webBeansContext.getOpenWebBeansConfiguration()
.getProperty("org.apache.webbeans.proxy.useStaticNames"));
+ useXXhash64 = Boolean.parseBoolean(webBeansContext.getOpenWebBeansConfiguration()
+ .getProperty("org.apache.webbeans.proxy.staticNames.useXxHash64"));
unsafe = definingService == null ? new Unsafe() : null;
}
@@ -190,7 +196,9 @@ public abstract class AbstractProxyFactory
* Detect a free classname based on the given one
* @param proxyClassName
* @return
+ * @deprecated use {@link #getUnusedProxyClassName(ClassLoader, String, Method[], Method[])}.
*/
+ @Deprecated
protected String getUnusedProxyClassName(ClassLoader classLoader, String proxyClassName)
{
proxyClassName = fixPreservedPackages(proxyClassName);
@@ -219,6 +227,34 @@ public abstract class AbstractProxyFactory
throw new WebBeansException("Unable to detect a free proxy class name based on: " + proxyClassName);
}
+ protected String getUnusedProxyClassName(ClassLoader classLoader, String proxyClassName,
+ Method[] proxiedMethods, Method[] notProxiedMethods)
+ {
+ proxyClassName = fixPreservedPackages(proxyClassName);
+
+
+ if (useStaticNames)
+ {
+ return proxyClassName + uniqueHash(proxiedMethods, notProxiedMethods);
+ }
+ return getUnusedProxyClassName(classLoader, proxyClassName);
+ }
+
+ protected String uniqueHash(Method[] proxiedMethods, Method[] notProxiedMethods)
+ {
+ if (useXXhash64)
+ {
+ // xxhash64 has very low collision so for this kind of has it is safe enough
+ // and enables to avoid a big concatenation for names
+ return Long.toString(Math.abs(XxHash64.apply(Stream.concat(
+ Stream.of(proxiedMethods).map(Method::toGenericString).sorted(),
+ Stream.of(notProxiedMethods).map(Method::toGenericString).map(it -> "<NOT>" + it).sorted()
+ ).collect(joining("_")))));
+ }
+ // else unsafe - 1 proxy per class max!
+ return "0";
+ }
+
protected <T> String getSignedClassProxyName(final Class<T> classToProxy)
{
// avoid java.lang.SecurityException: class's signer information
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
index 37ad044..55b0687 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/InterceptorDecoratorProxyFactory.java
@@ -23,7 +23,6 @@ import org.apache.webbeans.config.WebBeansContext;
import org.apache.webbeans.exception.ProxyGenerationException;
import org.apache.webbeans.exception.WebBeansConfigurationException;
import org.apache.webbeans.intercept.InterceptorResolutionService;
-import org.apache.webbeans.logger.WebBeansLoggerFacade;
import org.apache.webbeans.util.Asserts;
import org.apache.webbeans.util.ExceptionUtil;
import org.apache.xbean.asm8.ClassWriter;
@@ -42,7 +41,6 @@ import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import java.util.logging.Logger;
/**
* Generate a dynamic subclass which has exactly 1 delegation point instance
@@ -55,9 +53,6 @@ import java.util.logging.Logger;
*/
public class InterceptorDecoratorProxyFactory extends AbstractProxyFactory
{
- private static final Logger logger = WebBeansLoggerFacade.getLogger(InterceptorDecoratorProxyFactory.class);
-
-
/** the name of the field which stores the proxied instance */
public static final String FIELD_PROXIED_INSTANCE = "owbIntDecProxiedInstance";
@@ -206,7 +201,8 @@ public class InterceptorDecoratorProxyFactory extends AbstractProxyFactory
{
String proxyClassName = getUnusedProxyClassName(
classLoader,
- (classToProxy.getSigners() != null ? getSignedClassProxyName(classToProxy) : classToProxy.getName()) + "$$OwbInterceptProxy");
+ (classToProxy.getSigners() != null ? getSignedClassProxyName(classToProxy) : classToProxy.getName()) + "$$OwbInterceptProxy",
+ interceptedMethods, nonInterceptedMethods);
Class<T> clazz = createProxyClass(classLoader, proxyClassName, classToProxy, interceptedMethods, nonInterceptedMethods);
diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
index 1070370..8d5a57e 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/proxy/NormalScopeProxyFactory.java
@@ -232,10 +232,6 @@ public class NormalScopeProxyFactory extends AbstractProxyFactory
public <T> Class<T> createProxyClass(ClassLoader classLoader, Class<T> classToProxy)
throws ProxyGenerationException
{
- String proxyClassName = getUnusedProxyClassName(
- classLoader,
- (classToProxy.getSigners() != null ? getSignedClassProxyName(classToProxy) : classToProxy.getName()) + "$$OwbNormalScopeProxy");
-
Method[] nonInterceptedMethods;
Method[] interceptedMethods = null;
if (classToProxy.isInterface())
@@ -268,6 +264,11 @@ public class NormalScopeProxyFactory extends AbstractProxyFactory
interceptedMethods = protectedMethods.toArray(new Method[protectedMethods.size()]);
}
+ String proxyClassName = getUnusedProxyClassName(
+ classLoader,
+ (classToProxy.getSigners() != null ? getSignedClassProxyName(classToProxy) : classToProxy.getName()) + "$$OwbNormalScopeProxy",
+ interceptedMethods, nonInterceptedMethods);
+
Class<T> clazz = createProxyClass(classLoader, proxyClassName, classToProxy, interceptedMethods, nonInterceptedMethods);
if (interceptedMethods != null && interceptedMethods.length > 0)
diff --git a/webbeans-impl/src/test/java/org/apache/webbeans/hash/XxHash64Test.java b/webbeans-impl/src/test/java/org/apache/webbeans/hash/XxHash64Test.java
new file mode 100644
index 0000000..53e490a
--- /dev/null
+++ b/webbeans-impl/src/test/java/org/apache/webbeans/hash/XxHash64Test.java
@@ -0,0 +1,32 @@
+/*
+ * 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.hash;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class XxHash64Test
+{
+ @Test
+ public void sanityCheck()
+ {
+ assertEquals(3728699739546630719L, XxHash64.apply("foo"));
+ }
+}
diff --git a/webbeans-impl/src/test/java/org/apache/webbeans/test/managed/ProxyFactoryTest.java b/webbeans-impl/src/test/java/org/apache/webbeans/test/managed/ProxyFactoryTest.java
index 1c70807..38f3945 100644
--- a/webbeans-impl/src/test/java/org/apache/webbeans/test/managed/ProxyFactoryTest.java
+++ b/webbeans-impl/src/test/java/org/apache/webbeans/test/managed/ProxyFactoryTest.java
@@ -19,6 +19,11 @@
package org.apache.webbeans.test.managed;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
@@ -26,9 +31,22 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.Produces;
+import javax.enterprise.inject.Typed;
+import javax.enterprise.inject.literal.NamedLiteral;
import javax.enterprise.inject.spi.Bean;
-
+import javax.enterprise.inject.spi.Extension;
+import javax.enterprise.inject.spi.InterceptionFactory;
+import javax.enterprise.util.AnnotationLiteral;
+import javax.inject.Named;
+import javax.inject.Qualifier;
+import javax.interceptor.Interceptor;
+import javax.interceptor.InterceptorBinding;
+import javax.interceptor.InvocationContext;
+
+import org.apache.webbeans.test.discovery.InterceptorAnnotatedDiscoveryTest;
import org.junit.Assert;
import org.apache.webbeans.config.WebBeansContext;
@@ -37,8 +55,80 @@ import org.apache.webbeans.test.AbstractUnitTest;
import org.apache.webbeans.test.managed.multipleinterfaces.MyEntityServiceImpl;
import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
public class ProxyFactoryTest extends AbstractUnitTest
{
+ @InterceptorBinding
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target({ElementType.METHOD, ElementType.TYPE})
+ public @interface Foo
+ {
+ }
+
+ @Interceptor
+ @Foo
+ public static class FooInterceptor
+ {
+ public Object foo(InvocationContext ctx) throws Exception
+ {
+ return ctx.proceed();
+ }
+ }
+
+ public static class ABean
+ {
+ public String foo()
+ {
+ return "foo";
+ }
+
+ public String bar()
+ {
+ return "bar";
+ }
+ }
+
+ @ApplicationScoped
+ public static class ABeanFactories
+ {
+ @Produces
+ @Named("bean1")
+ @ApplicationScoped
+ public ABean bean1()
+ {
+ return new ABean();
+ }
+
+ @Foo
+ @Named("bean2")
+ @Produces
+ public ABean bean2(final InterceptionFactory<ABean> factory)
+ {
+ factory.configure()
+ .methods()
+ .iterator().next().add(new AnnotationLiteral<Foo>()
+ {
+ });
+ return factory.createInterceptedInstance(new ABean());
+ }
+ }
+
+ @Test
+ public void stableNameMultipleTypes()
+ {
+ addConfiguration("org.apache.webbeans.proxy.useStaticNames", "true");
+ addConfiguration("org.apache.webbeans.proxy.staticNames.useXxHash64", "true");
+ startContainer(ABeanFactories.class, FooInterceptor.class);
+ final ABean bean1 = getInstance("bean1");
+ final ABean bean2 = getInstance("bean2");
+ assertEquals(
+ "org.apache.webbeans.test.managed.ProxyFactoryTest$ABean$$OwbNormalScopeProxy8050522010792129812",
+ bean1.getClass().getName());
+ assertEquals(
+ "org.apache.webbeans.test.managed.ProxyFactoryTest$ABean$$OwbInterceptProxy5751833139562769786",
+ bean2.getClass().getName());
+ }
@SuppressWarnings("unchecked")
@Test