> interfaces = new ArrayList<>();
if (collectionFieldClass.isInterface()) {
// if collectionFieldClass is an interface, simply use it
- interfaces = new Class>[] { collectionFieldClass };
+ interfaces.add(collectionFieldClass);
} else {
// else, use all interfaces
- interfaces = collectionFieldClass.getInterfaces();
+ interfaces.addAll(List.of(collectionFieldClass.getInterfaces()));
}
- return (Class>[]) ArrayUtils.add(interfaces, OcmProxy.class);
+ interfaces.add(OcmProxy.class);
+ return interfaces;
}
}
Index: src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyBuddy.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyBuddy.java b/src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyBuddy.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/main/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyBuddy.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,321 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+import static java.lang.invoke.MethodHandles.lookup;
+import static java.lang.invoke.MethodHandles.privateLookupIn;
+import static net.bytebuddy.description.modifier.Visibility.PRIVATE;
+import static net.bytebuddy.description.modifier.Visibility.PUBLIC;
+import static net.bytebuddy.implementation.MethodCall.invoke;
+import static net.bytebuddy.implementation.MethodDelegation.withDefaultConfiguration;
+import static net.bytebuddy.implementation.bind.annotation.Pipe.Binder.install;
+import static net.bytebuddy.matcher.ElementMatchers.isPublic;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+import net.bytebuddy.implementation.FieldAccessor;
+import net.bytebuddy.implementation.bind.annotation.AllArguments;
+import net.bytebuddy.implementation.bind.annotation.Origin;
+import net.bytebuddy.implementation.bind.annotation.Pipe;
+import net.bytebuddy.implementation.bind.annotation.RuntimeType;
+import net.bytebuddy.implementation.bind.annotation.This;
+
+/**
+ * {@code ProxyBuddy} is a simple factory for creating dynamic proxies for arbitrary
+ * Java classes and interfaces with ByteBuddy.
+ *
+ * var proxy = new ProxyBuddy<>(MyClass.class, (pipe, method, arguments) ->
+ * switch (method.getName()) {
+ * case "method1" -> 1;
+ * case "method2" -> 2;
+ * case "method3" -> 3;
+ * default -> 0;
+ * })
+ * .withInterface(Interface1.class)
+ * .withInterface(Interface2.class)
+ * .withInterface(Interface3.class)
+ * .createProxy();
+ *
+ *
+ * Adapted from ProxyBuddy
+ */
+public class ProxyBuddy {
+ private final Class superClass;
+ private final Cons> interfaces;
+ private final Constructor constructor;
+ private final Object[] arguments;
+ private final InvocationHandler invocationHandler;
+
+ private ProxyBuddy(
+ Class superClass,
+ Cons> interfaces,
+ Constructor constructor,
+ Object[] arguments,
+ InvocationHandler invocationHandler) {
+ this.superClass = superClass;
+ this.interfaces = interfaces;
+ this.constructor = constructor;
+ this.arguments = arguments;
+ this.invocationHandler = invocationHandler;
+ }
+
+ /**
+ * Implementations of {@code InvocationHandler} are responsible for dispatching calls
+ * to the proxy instance.
+ *
+ * @param
+ */
+ public interface InvocationHandler {
+
+ /**
+ * The {@code invoke} method is called for each call to a method of a proxy.
+ * @param proxy the proxy instance the method is invoked on
+ * @param pipe a function for forwarding a proxy call to a real instance of the same type {@code T}.
+ * @param method the method that was called on the proxy
+ * @param arguments the arguments that were passed to the proxy method
+ * @return result of the method call
+ * @throws Exception
+ */
+ Object invoke(T proxy, Function pipe, Method method, Object... arguments) throws Throwable;
+ }
+
+ /**
+ * Create a proxy of type {@code T} for the given {@code superClass}. The returned instance
+ * is a subclass of {@code superClass}.
+ * @param superClass class to proxy
+ * @param invocationHandler handler receiving all calls to the returned proxy
+ */
+ public ProxyBuddy(Class superClass, InvocationHandler invocationHandler) {
+ this(superClass, cons(ProxyBuddyProxy.class, null), null, new Object[]{}, invocationHandler);
+ }
+
+ /**
+ * Create a proxy implementing the given interface.
+ * @param interfaze interface to implement
+ * @return a new {@code ProxyBuddy} instance
+ */
+ public ProxyBuddy withInterface(Class> interfaze) {
+ return new ProxyBuddy<>(superClass, cons(interfaze, interfaces), constructor, arguments, invocationHandler);
+ }
+
+ public ProxyBuddy withInterfaces(List> interfaces) {
+ return new ProxyBuddy<>(superClass, Cons.cons(interfaces), constructor, arguments, invocationHandler);
+ }
+
+ interface Witness {
+ Object get();
+ }
+
+ /**
+ * Create a proxy with correct implementations for {@code equals} and {@code hashCode}.
+ * With this implementation a proxy will never equal an instance of the proxied class.
+ * @param witness a object for witnessing the equality between a proxy and proxied instance.
+ * The only requirement for the witness is to implement {@code equals} and
+ * {@code hashCode} consistently with the proxied class.
+ * @return a new {@code ProxyBuddy} instance
+ */
+ public ProxyBuddy withProxyNeverEqualsTarget(Object witness) {
+ return new ProxyBuddy<>(superClass, interfaces, constructor, arguments, (proxy, pipe, method, arguments) -> {
+ if ("equals".equals(method.getName())) {
+ if (isProxy(arguments[0])) {
+ return arguments[0].equals((Witness) () -> witness);
+ } else {
+ if (arguments[0] instanceof Witness) {
+ return witness.equals(((Witness) arguments[0]).get());
+ } else {
+ return false;
+ }
+ }
+ } else if ("hashCode".equals(method.getName())) {
+ return 31 * witness.hashCode();
+ } else {
+ return invocationHandler.invoke(proxy, pipe, method, arguments);
+ }
+ });
+ }
+
+ /**
+ * Create a proxy with correct implementations for {@code equals} and {@code hashCode}.
+ * With this implementation a may equal an instance of the proxied class.
+ * @param witness a object for witnessing the equality between a proxy and proxied instance.
+ * The only requirement for the witness is to implement {@code equals} and
+ * {@code hashCode} consistently with the proxied class.
+ * @return a new {@code ProxyBuddy} instance
+ */
+ public ProxyBuddy withProxyCanEqualTarget(Object witness) {
+ return new ProxyBuddy<>(superClass, interfaces, constructor, arguments, (proxy, pipe, method, arguments) -> {
+ if ("equals".equals(method.getName())) {
+ return witness.equals(arguments[0]);
+ } else if ("hashCode".equals(method.getName())) {
+ return witness.hashCode();
+ } else {
+ return invocationHandler.invoke(proxy,pipe, method, arguments);
+ }
+ });
+ }
+
+ /**
+ * Create a proxy that uses the supplied constructor and arguments for creating a super class
+ * instance.
+ * @param constructor constructor for creating the super class instance
+ * @param arguments arguments passed to the constructor for creating the super class instance
+ * @return
+ */
+ public ProxyBuddy withConstructor(Constructor constructor, Object... arguments) {
+ return new ProxyBuddy<>(superClass, interfaces, constructor, arguments, invocationHandler);
+ }
+
+ /**
+ * Create a proxy for a class of type {@code T}
+ * @return a new proxy instance of type {@code T}
+ * @throws IllegalAccessException
+ * @throws NoSuchMethodException
+ * @throws InvocationTargetException
+ * @throws InstantiationException
+ */
+ public T createProxy()
+ throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
+ return new ByteBuddy()
+ .subclass(superClass)
+ .implement(toList(interfaces))
+ .method(isPublic())
+ .intercept(withDefaultConfiguration()
+ .withBinders(install(Function.class))
+ .toField("INVOKER"))
+ .defineField("INVOKER", Invoker.class, PRIVATE)
+ .defineConstructor(PUBLIC)
+ .withParameters(Invoker.class)
+ .intercept((constructor == null
+ ? invoke(superClass.getConstructor())
+ : invoke(constructor).with(arguments))
+ .andThen(FieldAccessor.ofField("INVOKER").setsArgumentAt(0)))
+ .make()
+ .load(superClass.getClassLoader(), ClassLoadingStrategy.UsingLookup.of(privateLookupIn(superClass, lookup())))
+ .getLoaded()
+ .getConstructor(Invoker.class)
+ .newInstance(new Invoker<>(invocationHandler));
+ }
+
+ /**
+ * Helper method to determine whether an instance is a proxy created through {@code ProxyBuddy}.
+ * @param object an instance to check
+ * @return {@code true} if and only if {@code object} is a proxy created by {@code ProxyBuddy}.
+ */
+ public static boolean isProxy(Object object) {
+ return object instanceof ProxyBuddyProxy;
+ }
+
+ /**
+ * Marker interface for {@code ProxyBuddy} proxies.
+ */
+ public interface ProxyBuddyProxy {}
+
+ /**
+ * Internal helper class for delegating method calls from a proxy instance to
+ * the {@link InvocationHandler}.
+ * @param
+ */
+ public static final class Invoker {
+
+ private final InvocationHandler invocationHandler;
+
+ private Invoker(InvocationHandler invocationHandler) {
+ this.invocationHandler = invocationHandler;
+ }
+
+ @RuntimeType
+ public Object delegate(@This T proxy, @Pipe Function pipe, @Origin Method method, @AllArguments Object[] args)
+ throws Throwable {
+ return invocationHandler.invoke(proxy, pipe, method, args);
+ }
+ }
+
+ private static final class Cons {
+ private final T value;
+ private final Cons values;
+
+ private Cons(T value, Cons values) {
+ this.value = value;
+ this.values = values;
+ }
+
+ private static Cons cons(List ts) {
+ if (ts.isEmpty()) {
+ return null;
+ } else {
+ return new Cons<>(ts.get(0), cons(ts.subList(1, ts.size())));
+ }
+ }
+
+ public T value() {
+ return value;
+ }
+
+ public Cons values() {
+ return values;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (obj == null || obj.getClass() != this.getClass()) {
+ return false;
+ }
+ var that = (Cons) obj;
+ return Objects.equals(this.value, that.value) &&
+ Objects.equals(this.values, that.values);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(value, values);
+ }
+
+ @Override
+ public String toString() {
+ return "Cons[" +
+ "value=" + value + ", " +
+ "values=" + values + ']';
+ }
+
+ }
+
+ private static Cons cons(T value, Cons values) {
+ return new Cons<>(value, values);
+ }
+
+ private static List toList(Cons values) {
+ var result = new ArrayList();
+
+ while (values != null) {
+ result.add(values.value);
+ values = values.values;
+ }
+ return result;
+ }
+
+}
Index: src/main/java/org/apache/jackrabbit/ocm/mapper/model/MappingDescriptor.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/org/apache/jackrabbit/ocm/mapper/model/MappingDescriptor.java b/src/main/java/org/apache/jackrabbit/ocm/mapper/model/MappingDescriptor.java
--- a/src/main/java/org/apache/jackrabbit/ocm/mapper/model/MappingDescriptor.java (revision c1069b74c36ee39fda56163df450d557971636fa)
+++ b/src/main/java/org/apache/jackrabbit/ocm/mapper/model/MappingDescriptor.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -91,7 +91,14 @@
* @return the class descriptor found or null
*/
public ClassDescriptor getClassDescriptorByName(String className) {
- return (ClassDescriptor) classDescriptorsByClassName.get(className);
+ return (ClassDescriptor) classDescriptorsByClassName.get(unmangleClassname(className));
+ }
+
+ private static String unmangleClassname(String className) {
+ int i = className.indexOf("$ByteBuddy$");
+ return i >= 0
+ ? className.substring(0, i)
+ : className;
}
public ClassDescriptor getClassDescriptorByNodeType(String nodeType)
Index: src/main/java/org/apache/jackrabbit/ocm/reflection/ReflectionUtils.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/main/java/org/apache/jackrabbit/ocm/reflection/ReflectionUtils.java b/src/main/java/org/apache/jackrabbit/ocm/reflection/ReflectionUtils.java
--- a/src/main/java/org/apache/jackrabbit/ocm/reflection/ReflectionUtils.java (revision c1069b74c36ee39fda56163df450d557971636fa)
+++ b/src/main/java/org/apache/jackrabbit/ocm/reflection/ReflectionUtils.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -31,8 +31,7 @@
import org.apache.commons.beanutils2.ConstructorUtils;
import org.apache.commons.beanutils2.PropertyUtils;
import org.apache.jackrabbit.ocm.exception.JcrMappingException;
-
-import net.sf.cglib.proxy.Enhancer;
+import org.apache.jackrabbit.ocm.manager.objectconverter.proxy.ProxyBuddy;
/**
@@ -189,9 +188,8 @@
}
}
- public static boolean isProxy(Class beanClass)
- {
- return Enhancer.isEnhanced(beanClass);
+ public static boolean isProxy(Class beanClass) {
+ return ProxyBuddy.isProxy(beanClass);
}
public static Class getBeanClass(Object bean)
@@ -199,7 +197,6 @@
Class beanClass = bean.getClass();
if (isProxy(beanClass))
{
- //CGLIB specific
return beanClass.getSuperclass();
}
return beanClass;
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ImplementInterfacesTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ImplementInterfacesTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ImplementInterfacesTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ImplementInterfacesTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,65 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+public class ImplementInterfacesTest {
+
+ public static class C { }
+
+ interface I1 {
+ int m1();
+ }
+
+ interface I2 {
+ int m2();
+ }
+
+ interface I3 {
+ int m3();
+ }
+
+ @Test
+ public void implementInterfaces() throws Exception {
+ var proxy = new ProxyBuddy<>(C.class, (thisProxy, pipe, method, arguments) -> {
+ switch (method.getName()) {
+ case "m1": return 1;
+ case "m2": return 2;
+ case "m3": return 3;
+ default: return 0;
+ }})
+ .withInterface(I1.class)
+ .withInterface(I2.class)
+ .withInterface(I3.class)
+ .createProxy();
+
+ assertTrue(proxy instanceof I1);
+ assertEquals(1, ((I1)proxy).m1());
+
+ assertTrue(proxy instanceof I2);
+ assertEquals(2, ((I2)proxy).m2());
+
+ assertTrue(proxy instanceof I3);
+ assertEquals(3, ((I3)proxy).m3());
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/InvalidProxyTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/InvalidProxyTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/InvalidProxyTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/InvalidProxyTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,56 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class InvalidProxyTest {
+
+ @Test(expected = IllegalAccessException.class)
+ public void proxyNonOpenPackage() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ var proxyBuddy = new ProxyBuddy<>(Object.class,
+ (proxy, pipe, method, arguments) -> null);
+
+ proxyBuddy.createProxy();
+ }
+
+ public static final class T { }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void proxyFinalClass() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ var proxyBuddy = new ProxyBuddy<>(T.class, (proxy, pipe, method, arguments) -> null);
+
+ proxyBuddy.createProxy();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void proxyArray() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ var proxyBuddy = new ProxyBuddy<>(T[].class, (proxy, pipe, method, arguments) -> null);
+
+ proxyBuddy.createProxy();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void proxyPrimitive() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ var proxyBuddy = new ProxyBuddy<>(int.class, (proxy, pipe, method, arguments) -> null);
+
+ proxyBuddy.createProxy();
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/NonArgProxyTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/NonArgProxyTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/NonArgProxyTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/NonArgProxyTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,121 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+import static org.apache.jackrabbit.ocm.manager.objectconverter.proxy.ProxyBuddy.isProxy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class NonArgProxyTest {
+ private static class TestException extends Exception{}
+
+ public static class Target {
+ boolean voidCalled;
+
+ public int add(int a, int b) {
+ return a + b;
+ }
+
+ public String noArgMethod() {
+ return "noArg";
+ }
+
+ public void voidMethod() {
+ voidCalled = true;
+ }
+
+ public void exception() throws TestException {
+ throw new TestException();
+ }
+
+ @Override
+ public int hashCode() {
+ return 42;
+ }
+
+ @Override
+ public String toString() {
+ return "four two";
+ }
+ }
+
+ @Test
+ public void testReflectingProxyCalls() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ Target target = new Target();
+ Target proxy = new ProxyBuddy<>(Target.class, (thisProxy, pipe, method, arguments) -> method.invoke(target, arguments))
+ .createProxy();
+
+ assertTrue(isProxy(proxy));
+ assertFalse(isProxy(target));
+
+ assertEquals(3, proxy.add(1, 2));
+ assertEquals("noArg", proxy.noArgMethod());
+
+ proxy.voidMethod();
+ assertTrue(target.voidCalled);
+
+ Exception ex = null;
+ try {
+ proxy.exception();
+ } catch (Exception e) {
+ ex = e;
+ }
+ assertNotNull(ex);
+
+ assertEquals(InvocationTargetException.class, ex.getClass());
+ assertEquals(TestException.class, ex.getCause().getClass());
+
+ assertEquals(42, proxy.hashCode());
+ assertEquals("four two", proxy.toString());
+ assertEquals("four two", proxy + "");
+ }
+
+ @Test
+ public void testDelegatingProxyCalls() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ Target target = new Target();
+ Target proxy = new ProxyBuddy<>(Target.class, (thisProxy, pipe, method, arguments) -> pipe.apply(target))
+ .createProxy();
+
+ assertTrue(isProxy(proxy));
+ assertFalse(isProxy(target));
+
+ assertEquals(3, proxy.add(1, 2));
+ assertEquals("noArg", proxy.noArgMethod());
+
+ proxy.voidMethod();
+ assertTrue(target.voidCalled);
+
+ Exception ex = null;
+ try {
+ proxy.exception();
+ } catch (Exception e) {
+ ex = e;
+ }
+ assertNotNull(ex);
+
+ assertEquals(42, proxy.hashCode());
+ assertEquals("four two", proxy.toString());
+ assertEquals("four two", proxy + "");
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyCanEqualTargetTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyCanEqualTargetTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyCanEqualTargetTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyCanEqualTargetTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,163 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class ProxyCanEqualTargetTest {
+
+ @SuppressWarnings("ClassCanBeRecord")
+ public static class Target {
+ private final int value;
+
+ public Target(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof Target) {
+ return value == ((Target) obj).getValue();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return getValue();
+ }
+ }
+
+ private static Target createProxy(Target target)
+ throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
+ return new ProxyBuddy<>(Target.class, (proxy, pipe, method, arguments) -> pipe.apply(target))
+ .withConstructor(Target.class.getConstructor(int.class), 0)
+ .withProxyCanEqualTarget(target)
+ .createProxy();
+ }
+
+ @SuppressWarnings({"EqualsWithItself", "SimplifiableAssertion"})
+ @Test
+ public void testEquality()
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
+ Target target1 = new Target(1);
+ Target target2 = new Target(1);
+
+ Target proxy1 = createProxy(target1);
+ Target proxy2 = createProxy(target2);
+
+ assertTrue(target1.equals(proxy1));
+ assertTrue(target1.equals(proxy2));
+
+ assertTrue(target2.equals(proxy1));
+ assertTrue(target2.equals(proxy2));
+
+ assertTrue(proxy1.equals(target1));
+ assertTrue(proxy1.equals(target2));
+
+ assertTrue(proxy1.equals(proxy1));
+ assertTrue(proxy1.equals(proxy2));
+
+ assertTrue(proxy2.equals(target1));
+ assertTrue(proxy2.equals(target2));
+
+ assertTrue(proxy2.equals(proxy1));
+ assertTrue(proxy2.equals(proxy2));
+
+ assertEquals(target1.hashCode(), proxy1.hashCode());
+ assertEquals(target1.hashCode(), proxy2.hashCode());
+
+ assertEquals(target2.hashCode(), proxy1.hashCode());
+ assertEquals(target2.hashCode(), proxy2.hashCode());
+
+ assertEquals(proxy1.hashCode(), target1.hashCode());
+ assertEquals(proxy1.hashCode(), target2.hashCode());
+
+ assertEquals(proxy1.hashCode(), proxy1.hashCode());
+ assertEquals(proxy1.hashCode(), proxy2.hashCode());
+
+ assertEquals(proxy2.hashCode(), target1.hashCode());
+ assertEquals(proxy2.hashCode(), target2.hashCode());
+
+ assertEquals(proxy2.hashCode(), proxy1.hashCode());
+ assertEquals(proxy2.hashCode(), proxy2.hashCode());
+ }
+
+ @SuppressWarnings({"SimplifiableAssertion", "EqualsWithItself"})
+ @Test
+ public void testInequality()
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
+ Target target1 = new Target(1);
+ Target target2 = new Target(2);
+
+ Target proxy1 = createProxy(target1);
+ Target proxy2 = createProxy(target2);
+
+ assertTrue(target1.equals(proxy1));
+ assertFalse(target1.equals(proxy2));
+
+ assertFalse(target2.equals(proxy1));
+ assertTrue(target2.equals(proxy2));
+
+ assertTrue(proxy1.equals(target1));
+ assertFalse(proxy1.equals(target2));
+
+ assertTrue(proxy1.equals(proxy1));
+ assertFalse(proxy1.equals(proxy2));
+
+ assertFalse(proxy2.equals(target1));
+ assertTrue(proxy2.equals(target2));
+
+ assertFalse(proxy2.equals(proxy1));
+ assertTrue(proxy2.equals(proxy2));
+
+ assertEquals(target1.hashCode(), proxy1.hashCode());
+ assertNotEquals(target1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(target2.hashCode(), proxy1.hashCode());
+ assertEquals(target2.hashCode(), proxy2.hashCode());
+
+ assertEquals(proxy1.hashCode(), target1.hashCode());
+ assertNotEquals(proxy1.hashCode(), target2.hashCode());
+
+ assertEquals(proxy1.hashCode(), proxy1.hashCode());
+ assertNotEquals(proxy1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(proxy2.hashCode(), target1.hashCode());
+ assertEquals(proxy2.hashCode(), target2.hashCode());
+
+ assertNotEquals(proxy2.hashCode(), proxy1.hashCode());
+ assertEquals(proxy2.hashCode(), proxy2.hashCode());
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyLocalTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyLocalTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyLocalTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyLocalTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,42 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
+public class ProxyLocalTest {
+
+ @SuppressWarnings("JavaReflectionMemberAccess")
+ @Test
+ public void proxyLocalClass() throws Exception {
+ class Local {
+ public Local() {}
+ public int get() { return 42; }
+ }
+
+ var localProxy = new ProxyBuddy<>(Local.class,
+ (proxy, pipe, method, arguments) -> pipe.apply(new Local()))
+ .withConstructor(Local.class.getConstructor(ProxyLocalTest.class), new Object[]{this})
+ .createProxy();
+
+ assertEquals(42, localProxy.get());
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyNeverEqualsTargetTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyNeverEqualsTargetTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyNeverEqualsTargetTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/ProxyNeverEqualsTargetTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,162 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class ProxyNeverEqualsTargetTest {
+
+ @SuppressWarnings("ClassCanBeRecord")
+ public static class Target {
+ private final int value;
+
+ public Target(int value) {
+ this.value = value;
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ }
+ if (object.getClass() == Target.class) {
+ return value == ((Target) object).getValue();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+ }
+
+ private static Target createProxy(Target target)
+ throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
+ return new ProxyBuddy<>(Target.class, (proxy, pipe, method, arguments) -> pipe.apply(target))
+ .withConstructor(Target.class.getConstructor(int.class), 0)
+ .withProxyNeverEqualsTarget(target)
+ .createProxy();
+ }
+
+ @SuppressWarnings({"SimplifiableAssertion", "EqualsWithItself"})
+ @Test
+ public void testEquality()
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
+ Target target1 = new Target(1);
+ Target target2 = new Target(1);
+
+ Target proxy1 = createProxy(target1);
+ Target proxy2 = createProxy(target2);
+
+ assertFalse(target1.equals(proxy1));
+ assertFalse(target1.equals(proxy2));
+
+ assertFalse(target2.equals(proxy1));
+ assertFalse(target2.equals(proxy2));
+
+ assertFalse(proxy1.equals(target1));
+ assertFalse(proxy1.equals(target2));
+
+ assertTrue(proxy1.equals(proxy1));
+ assertTrue(proxy1.equals(proxy2));
+
+ assertFalse(proxy2.equals(target1));
+ assertFalse(proxy2.equals(target2));
+
+ assertTrue(proxy2.equals(proxy1));
+ assertTrue(proxy2.equals(proxy2));
+
+ assertNotEquals(target1.hashCode(), proxy1.hashCode());
+ assertNotEquals(target1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(target2.hashCode(), proxy1.hashCode());
+ assertNotEquals(target2.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(proxy1.hashCode(), target1.hashCode());
+ assertNotEquals(proxy1.hashCode(), target2.hashCode());
+
+ assertEquals(proxy1.hashCode(), proxy1.hashCode());
+ assertEquals(proxy1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(proxy2.hashCode(), target1.hashCode());
+ assertNotEquals(proxy2.hashCode(), target2.hashCode());
+
+ assertEquals(proxy2.hashCode(), proxy1.hashCode());
+ assertEquals(proxy2.hashCode(), proxy2.hashCode());
+ }
+
+ @SuppressWarnings({"SimplifiableAssertion", "EqualsWithItself"})
+ @Test
+ public void testInequality()
+ throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
+ Target target1 = new Target(1);
+ Target target2 = new Target(2);
+
+ Target proxy1 = createProxy(target1);
+ Target proxy2 = createProxy(target2);
+
+ assertFalse(target1.equals(proxy1));
+ assertFalse(target1.equals(proxy2));
+
+ assertFalse(target2.equals(proxy1));
+ assertFalse(target2.equals(proxy2));
+
+ assertFalse(proxy1.equals(target1));
+ assertFalse(proxy1.equals(target2));
+
+ assertTrue(proxy1.equals(proxy1));
+ assertFalse(proxy1.equals(proxy2));
+
+ assertFalse(proxy2.equals(target1));
+ assertFalse(proxy2.equals(target2));
+
+ assertFalse(proxy2.equals(proxy1));
+ assertTrue(proxy2.equals(proxy2));
+
+ assertNotEquals(target1.hashCode(), proxy1.hashCode());
+ assertNotEquals(target1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(target2.hashCode(), proxy1.hashCode());
+ assertNotEquals(target2.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(proxy1.hashCode(), target1.hashCode());
+ assertNotEquals(proxy1.hashCode(), target2.hashCode());
+
+ assertEquals(proxy1.hashCode(), proxy1.hashCode());
+ assertNotEquals(proxy1.hashCode(), proxy2.hashCode());
+
+ assertNotEquals(proxy2.hashCode(), target1.hashCode());
+ assertNotEquals(proxy2.hashCode(), target2.hashCode());
+
+ assertNotEquals(proxy2.hashCode(), proxy1.hashCode());
+ assertEquals(proxy2.hashCode(), proxy2.hashCode());
+ }
+
+}
Index: src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/WithArgProxyTest.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/WithArgProxyTest.java b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/WithArgProxyTest.java
new file mode 100644
--- /dev/null (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
+++ b/src/test/java/org/apache/jackrabbit/ocm/manager/objectconverter/proxy/WithArgProxyTest.java (revision ac417d1c058fe23449b16b8784fa6a7a449e0b4e)
@@ -0,0 +1,74 @@
+/*
+ * 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.jackrabbit.ocm.manager.objectconverter.proxy;
+
+import static org.apache.jackrabbit.ocm.manager.objectconverter.proxy.ProxyBuddy.isProxy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.junit.Test;
+
+public class WithArgProxyTest {
+
+ public static class Target {
+
+ private final int a;
+
+ public Target(int a) {
+ this.a = a;
+ }
+ public int add(int b) {
+ return a + b;
+ }
+
+ public String noArgMethod() {
+ return "noArg";
+ }
+ }
+
+ @Test
+ public void testReflectingProxyCalls() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ Target target = new Target(1);
+ Target proxy = new ProxyBuddy<>(Target.class, (thisProxy, pipe, method, arguments) -> method.invoke(target, arguments))
+ .withConstructor(Target.class.getConstructor(int.class), 3)
+ .createProxy();
+
+ assertTrue(isProxy(proxy));
+ assertFalse(isProxy(target));
+
+ assertEquals(3, proxy.add(2));
+ assertEquals("noArg", proxy.noArgMethod());
+ }
+
+ @Test
+ public void testDelegatingProxyCalls() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
+ Target target = new Target(1);
+ Target proxy = new ProxyBuddy<>(Target.class, (thisProxy, pipe, method, arguments) -> pipe.apply(target))
+ .withConstructor(Target.class.getConstructor(int.class), 3)
+ .createProxy();
+
+ assertTrue(isProxy(proxy));
+ assertFalse(isProxy(target));
+
+ assertEquals(3, proxy.add(2));
+ assertEquals("noArg", proxy.noArgMethod());
+ }
+
+}