This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/main by this push:
new e98123755f2 CAUSEWAY-3883: re-implements ProxyFactoryService
e98123755f2 is described below
commit e98123755f20463b3d8c5a2f4e2a3f7dbfef74ff
Author: Andi Huber <[email protected]>
AuthorDate: Wed Jun 25 17:16:36 2025 +0200
CAUSEWAY-3883: re-implements ProxyFactoryService
---
.../commons/internal/debug/_MemoryUsage.java | 40 ++-----
.../internal/proxy/CachableInvocationHandler.java | 30 +++++
.../internal/proxy/CachingProxyFactoryService.java | 79 +++++++++++++
.../{_ProxyFactory.java => ProxyFactory.java} | 12 +-
...actoryService.java => ProxyFactoryService.java} | 48 +++++---
.../proxy/_ProxyFactoryServiceAbstract.java | 68 -----------
.../services/ProxyFactoryServiceByteBuddy.java | 127 ++++++++++-----------
.../classsubstitutor/ClassSubstitutorAbstract.java | 4 +-
.../runtime/wrap/WrapperInvocationHandler.java | 40 +++----
.../causeway/core/runtime/wrap/WrappingObject.java | 2 +-
.../wrapper/WrapperFactoryDefault.java | 37 +++---
.../handlers/DomainObjectInvocationHandler.java | 31 ++---
.../wrapper/handlers/ProxyGenerator.java | 64 +++++------
.../RuntimeServicesTestAbstract.java | 3 +-
.../wrapper/WrapperFactoryDefaultTest.java | 4 +-
.../ProxyCreatorTestUsingCodegenPlugin.java | 70 +++---------
.../WrapperFactoryMetaspaceMemoryLeakTest.java | 62 ++++++----
17 files changed, 373 insertions(+), 348 deletions(-)
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/debug/_MemoryUsage.java
b/commons/src/main/java/org/apache/causeway/commons/internal/debug/_MemoryUsage.java
index ab6958541a1..255a8794e9c 100644
---
a/commons/src/main/java/org/apache/causeway/commons/internal/debug/_MemoryUsage.java
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/debug/_MemoryUsage.java
@@ -18,11 +18,10 @@
*/
package org.apache.causeway.commons.internal.debug;
-import lombok.SneakyThrows;
-
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
-import java.util.concurrent.Callable;
+
+import org.apache.causeway.commons.functional.ThrowingRunnable;
/**
* <h1>- internal use only -</h1>
@@ -35,36 +34,25 @@
* @since 3.4.0
*/
public record _MemoryUsage(long usedInKibiBytes) {
-
+
// -- UTILITIES
-
+
static int indent = 0;
- @SneakyThrows
- public static <T> T measureMetaspace(final String desc, final Callable<T>
runnable) {
- var before = metaspace();
- try {
- indent++;
- return runnable.call();
- } finally {
- var after = metaspace();
- System.out.printf("%s%s : %s%n", spaces(indent),
after.minus(before), desc);
- indent--;
- }
- }
-
- public static void measureMetaspace(String desc, final Runnable runnable) {
+ public static _MemoryUsage measureMetaspace(final ThrowingRunnable
runnable) {
var before = metaspace();
+ var after = (_MemoryUsage) null;
try {
runnable.run();
+ } catch (Throwable e) {
} finally {
- var after = metaspace();
- System.out.printf("%s : %s%n", after.minus(before), desc);
+ after = metaspace();
}
+ return after.minus(before);
}
-
+
// -- FACTORY
-
+
private static _MemoryUsage metaspace() {
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans())
{
if (pool.getName().contains("Metaspace")) {
@@ -73,7 +61,7 @@ private static _MemoryUsage metaspace() {
}
throw new RuntimeException("Metaspace Usage not found");
}
-
+
// -- NON CANONICAL CONSTRUCTOR
private _MemoryUsage(java.lang.management.MemoryUsage usage) {
@@ -87,10 +75,6 @@ public String toString() {
// -- HELPER
- private static String spaces(int indent) {
- return " ".repeat(indent * 2);
- }
-
private _MemoryUsage minus(_MemoryUsage before) {
return new _MemoryUsage(this.usedInKibiBytes - before.usedInKibiBytes);
}
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachableInvocationHandler.java
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachableInvocationHandler.java
new file mode 100644
index 00000000000..ad70b6e6154
--- /dev/null
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachableInvocationHandler.java
@@ -0,0 +1,30 @@
+/*
+ * 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.causeway.commons.internal.proxy;
+
+import java.lang.reflect.InvocationHandler;
+
+public interface CachableInvocationHandler extends InvocationHandler {
+
+ /**
+ * key under which this {@link InvocationHandler} can be put into a cache
+ */
+ String key();
+
+}
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachingProxyFactoryService.java
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachingProxyFactoryService.java
new file mode 100644
index 00000000000..2d2141ae1fe
--- /dev/null
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/CachingProxyFactoryService.java
@@ -0,0 +1,79 @@
+/*
+ * 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.causeway.commons.internal.proxy;
+
+import java.lang.reflect.InvocationHandler;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Partly implements {@link ProxyFactoryService} and adds caching.
+ *
+ * @since 3.4
+ */
+public abstract class CachingProxyFactoryService implements
ProxyFactoryService {
+
+ private final Map<Class<?>, ProxyFactory<?>> proxyFactoryCache =
+ Collections.synchronizedMap(new WeakHashMap<>());
+ private final Map<String, Class<?>> proxyClassCache =
+ Collections.synchronizedMap(new WeakHashMap<>());
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final <T> Class<? extends T> proxyClass(
+ InvocationHandler handler,
+ Class<T> base,
+ Class<?>[] interfaces,
+ @Nullable List<AdditionalField> additionalFields) {
+ var proxyClass = handler instanceof CachableInvocationHandler
cachableInvocationHandler
+ ?
proxyClassCache.computeIfAbsent(cachableInvocationHandler.key(), __->
+ createProxyClass(handler, base, interfaces,
additionalFields))
+ : createProxyClass(handler, base, interfaces,
additionalFields);
+ return (Class<? extends T>) proxyClass;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public final <T> ProxyFactory<T> factory(
+ Class<T> proxyClass,
+ @Nullable Class<?>[] constructorArgTypes) {
+
+ var factory = proxyFactoryCache.computeIfAbsent(proxyClass, __->
+ createFactory(proxyClass, constructorArgTypes));
+
+ return (ProxyFactory<T>) factory;
+ }
+
+ // -- ABSTRACT METHODS
+
+ protected abstract <T> Class<? extends T> createProxyClass(
+ InvocationHandler handler,
+ Class<T> base,
+ Class<?>[] interfaces,
+ @Nullable List<AdditionalField> additionalFields);
+
+ protected abstract <T> ProxyFactory<T> createFactory(
+ Class<T> proxyClass,
+ @Nullable Class<?>[] constructorArgTypes);
+
+}
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactory.java
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactory.java
similarity index 85%
rename from
commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactory.java
rename to
commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactory.java
index 7a1462540c4..3fa5d42694f 100644
---
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactory.java
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactory.java
@@ -18,34 +18,28 @@
*/
package org.apache.causeway.commons.internal.proxy;
-import java.lang.reflect.InvocationHandler;
-
/**
* Generates dynamic classes and corresponding instances by rebasing a given
'base' class.
*
* @since 2.0
* @param <T> type of proxy objects this factory creates
*/
-public interface _ProxyFactory<T> {
-
- // -- INTERFACE
+public interface ProxyFactory<T> {
/**
- * @param handler
* @param initialize whether to call a constructor on object instantiation
* @return new proxy instance of type T
* @throws IllegalArgumentException when using initialize=true and the
number of
* constructorArgTypes specified while building this factory is greater
than 0.
*/
- public T createInstance(InvocationHandler handler, boolean initialize);
+ public T createInstance(boolean initialize);
/**
- * @param handler
* @param constructorArgs passed to the constructor with matching
signature on object instantiation
* @return new proxy instance of type T (always uses a constructor)
* @throws IllegalArgumentException if constructorArgs is null or empty or
does not
* conform to the number of constructorArgTypes specified while building
this factory.
*/
- public T createInstance(InvocationHandler handler, Object[]
constructorArgs);
+ public T createInstance(Object[] constructorArgs);
}
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryService.java
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactoryService.java
similarity index 57%
rename from
commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryService.java
rename to
commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactoryService.java
index 4cc5fa8fb2e..1478f7b3e9d 100644
---
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryService.java
+++
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/ProxyFactoryService.java
@@ -18,28 +18,46 @@
*/
package org.apache.causeway.commons.internal.proxy;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Type;
import java.util.List;
import org.jspecify.annotations.Nullable;
+import org.apache.causeway.commons.internal.collections._Arrays;
+
/**
- * Internal service, that generates {@link _ProxyFactory}(s).
- *
- * @since 2.0
+ * Internal service, that generates {@link ProxyFactory}(s).
+ *
+ * @since 3.4
*/
-public interface _ProxyFactoryService {
+public interface ProxyFactoryService {
- <T> _ProxyFactory<T> factory(
+ <T> Class<? extends T> proxyClass(
+ InvocationHandler handler,
Class<T> base,
- @Nullable Class<?>[] interfaces,
- @Nullable List<AdditionalField> additionalFields,
- @Nullable Class<?>[] constructorArgTypes);
+ Class<?>[] interfaces,
+ @Nullable List<AdditionalField> additionalFields);
- <T> _ProxyFactory<T> factory(
- Class<T> toProxyClass,
+ default <T> Class<? extends T> proxyClass(
+ InvocationHandler handler,
+ Class<T> classToBeProxied,
@Nullable Class<?> additionalClass,
- @Nullable List<AdditionalField> additionalFields);
+ @Nullable List<AdditionalField> additionalFields) {
+ final Class<?>[] interfaces = additionalClass!=null
+ ? _Arrays.combine(classToBeProxied.getInterfaces(),
ProxyEnhanced.class, additionalClass)
+ : _Arrays.combine(classToBeProxied.getInterfaces(),
ProxyEnhanced.class);
+ return proxyClass(handler, classToBeProxied, interfaces,
additionalFields);
+ }
+
+ <T> ProxyFactory<T> factory(
+ Class<T> proxyClass,
+ @Nullable Class<?>[] constructorArgTypes);
+
+ default <T> ProxyFactory<T> factory(
+ Class<T> proxyClass) {
+ return factory(proxyClass, null);
+ }
/**
* Marker interface for entities/services that have been enhanced with
@@ -48,13 +66,15 @@ <T> _ProxyFactory<T> factory(
interface ProxyEnhanced {
}
-
+
/**
* Defines a field, that can be added to a proxy class.
*/
record AdditionalField(
- String name,
- Type type,
+ String name,
+ Type type,
int modifiers) {
}
+
+
}
diff --git
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryServiceAbstract.java
b/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryServiceAbstract.java
deleted file mode 100644
index 13b6a2937d2..00000000000
---
a/commons/src/main/java/org/apache/causeway/commons/internal/proxy/_ProxyFactoryServiceAbstract.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.causeway.commons.internal.proxy;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-import org.jspecify.annotations.NonNull;
-import org.jspecify.annotations.Nullable;
-
-import org.apache.causeway.commons.internal.base._Casts;
-import org.apache.causeway.commons.internal.collections._Arrays;
-
-/**
- * Partly implements {@link _ProxyFactoryService} and adds caching.
- *
- * @since 2.0
- */
-public abstract class _ProxyFactoryServiceAbstract implements
_ProxyFactoryService {
-
- @NonNull
- private final Map<Class<?>, _ProxyFactory<?>> proxyFactoryByClass =
- Collections.synchronizedMap(new WeakHashMap<>());
-
- @Override
- public final <T> _ProxyFactory<T> factory(
- final Class<T> classToBeProxied,
- final @Nullable Class<?> additionalClass,
- final @Nullable List<AdditionalField> additionalFields) {
- _ProxyFactory<T> proxyFactory =
_Casts.uncheckedCast(proxyFactoryByClass.get(classToBeProxied));
- if(proxyFactory == null) {
- proxyFactory = createFactory(classToBeProxied, additionalClass,
additionalFields);
- proxyFactoryByClass.put(classToBeProxied, proxyFactory);
- }
- return proxyFactory;
- }
-
- private <T> _ProxyFactory<T> createFactory(
- final Class<T> classToBeProxied,
- final @Nullable Class<?> additionalClass,
- final @Nullable List<AdditionalField> additionalFields) {
-
- final Class<?>[] interfaces = additionalClass!=null
- ? _Arrays.combine(classToBeProxied.getInterfaces(),
ProxyEnhanced.class, additionalClass)
- : _Arrays.combine(classToBeProxied.getInterfaces(),
ProxyEnhanced.class);
-
- return factory(classToBeProxied, interfaces, additionalFields, null);
- }
-
-}
diff --git
a/core/codegen-bytebuddy/src/main/java/org/apache/causeway/core/codegen/bytebuddy/services/ProxyFactoryServiceByteBuddy.java
b/core/codegen-bytebuddy/src/main/java/org/apache/causeway/core/codegen/bytebuddy/services/ProxyFactoryServiceByteBuddy.java
index 5c07d247384..bfd88d49115 100644
---
a/core/codegen-bytebuddy/src/main/java/org/apache/causeway/core/codegen/bytebuddy/services/ProxyFactoryServiceByteBuddy.java
+++
b/core/codegen-bytebuddy/src/main/java/org/apache/causeway/core/codegen/bytebuddy/services/ProxyFactoryServiceByteBuddy.java
@@ -21,7 +21,6 @@
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
-import java.util.function.Function;
import org.jspecify.annotations.Nullable;
@@ -32,8 +31,8 @@
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.base._NullSafe;
import org.apache.causeway.commons.internal.context._Context;
-import org.apache.causeway.commons.internal.proxy._ProxyFactory;
-import org.apache.causeway.commons.internal.proxy._ProxyFactoryServiceAbstract;
+import org.apache.causeway.commons.internal.proxy.CachingProxyFactoryService;
+import org.apache.causeway.commons.internal.proxy.ProxyFactory;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
@@ -42,84 +41,84 @@
import net.bytebuddy.matcher.ElementMatchers;
@Service
-public class ProxyFactoryServiceByteBuddy extends _ProxyFactoryServiceAbstract
{
+public class ProxyFactoryServiceByteBuddy extends CachingProxyFactoryService {
private final ClassLoadingStrategyAdvisor strategyAdvisor = new
ClassLoadingStrategyAdvisor();
- @Override
- public <T> _ProxyFactory<T> factory(
- final Class<T> base,
- final Class<?>[] interfaces,
- @Nullable List<AdditionalField> additionalFields,
- final Class<?>[] constructorArgTypes) {
+ private record ProxyFactoryByteBuddy<T>(
+ Class<T> proxyClass,
+ Class<?>[] constructorArgTypes,
+ ObjenesisStd objenesis) implements ProxyFactory<T> {
- var objenesis = new ObjenesisStd();
+ @Override public T createInstance(final boolean initialize) {
- final Function<InvocationHandler, Class<? extends T>>
proxyClassFactory = handler->{
- var def = proxyDef(base, interfaces, additionalFields);
- return def
- .intercept(InvocationHandlerAdapter.of(handler))
- .make()
- .load(_Context.getDefaultClassLoader(),
- strategyAdvisor.getSuitableStrategy(base))
- .getLoaded();
- };
+ try {
- return new _ProxyFactory<T>() {
+ if(initialize) {
+ ensureSameSize(constructorArgTypes, null);
+ return _Casts.uncheckedCast( createUsingConstructor(null)
);
+ } else {
+ return _Casts.uncheckedCast( createNotUsingConstructor() );
+ }
- @Override
- public T createInstance(final InvocationHandler handler, final
boolean initialize) {
+ } catch (NoSuchMethodException | IllegalArgumentException |
InstantiationException |
+ IllegalAccessException | InvocationTargetException e) {
+ throw new RuntimeException(e);
+ }
- try {
+ }
- if(initialize) {
- ensureSameSize(constructorArgTypes, null);
- return _Casts.uncheckedCast(
createUsingConstructor(handler, null) );
- } else {
- return _Casts.uncheckedCast(
createNotUsingConstructor(handler) );
- }
+ @Override public T createInstance(final Object[] constructorArgs) {
- } catch (NoSuchMethodException | IllegalArgumentException |
InstantiationException |
- IllegalAccessException | InvocationTargetException e) {
- throw new RuntimeException(e);
- }
+ ensureNonEmtpy(constructorArgs);
+ ensureSameSize(constructorArgTypes, constructorArgs);
+ try {
+ return _Casts.uncheckedCast(
createUsingConstructor(constructorArgs) );
+ } catch (NoSuchMethodException | InstantiationException |
IllegalAccessException |
+ IllegalArgumentException | InvocationTargetException |
SecurityException e) {
+ throw new RuntimeException(e);
}
+ }
- @Override
- public T createInstance(final InvocationHandler handler, final
Object[] constructorArgs) {
-
- ensureNonEmtpy(constructorArgs);
- ensureSameSize(constructorArgTypes, constructorArgs);
+ // -- HELPER (create w/o initialize)
- try {
- return _Casts.uncheckedCast(
createUsingConstructor(handler, constructorArgs) );
- } catch (NoSuchMethodException | InstantiationException |
IllegalAccessException |
- IllegalArgumentException | InvocationTargetException |
SecurityException e) {
- throw new RuntimeException(e);
- }
- }
+ private Object createNotUsingConstructor() {
+ final Object object = objenesis.newInstance(proxyClass);
+ return object;
+ }
- // -- HELPER (create w/o initialize)
+ // -- HELPER (create with initialize)
- private Object createNotUsingConstructor(final InvocationHandler
invocationHandler) {
- final Class<? extends T> proxyClass =
proxyClassFactory.apply(invocationHandler);
- final Object object = objenesis.newInstance(proxyClass);
- return object;
- }
+ private Object createUsingConstructor(final @Nullable Object[]
constructorArgs)
+ throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
SecurityException {
+ return proxyClass
+ .getConstructor(constructorArgTypes==null ?
_Constants.emptyClasses : constructorArgTypes)
+ .newInstance(constructorArgs==null ?
_Constants.emptyObjects : constructorArgs);
+ }
- // -- HELPER (create with initialize)
+ };
- private Object createUsingConstructor(final InvocationHandler
invocationHandler, final @Nullable Object[] constructorArgs)
- throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException, NoSuchMethodException,
SecurityException {
- final Class<? extends T> proxyClass =
proxyClassFactory.apply(invocationHandler);
- return proxyClass
- .getConstructor(constructorArgTypes==null ?
_Constants.emptyClasses : constructorArgTypes)
- .newInstance(constructorArgs==null ?
_Constants.emptyObjects : constructorArgs);
- }
+ @Override
+ public <T> Class<? extends T> createProxyClass(
+ final InvocationHandler handler,
+ final Class<T> base,
+ final Class<?>[] interfaces,
+ @Nullable List<AdditionalField> additionalFields) {
- };
+ return proxyDef(base, interfaces, additionalFields)
+ .intercept(InvocationHandlerAdapter.of(handler))
+ .make()
+ .load(_Context.getDefaultClassLoader(),
+ strategyAdvisor.getSuitableStrategy(base))
+ .getLoaded();
+ }
+ @Override
+ public <T> ProxyFactory<T> createFactory(
+ final Class<T> proxyClass,
+ final Class<?>[] constructorArgTypes) {
+ return new ProxyFactoryByteBuddy<T>(proxyClass, constructorArgTypes,
new ObjenesisStd());
}
// -- HELPER
@@ -127,19 +126,19 @@ private Object createUsingConstructor(final
InvocationHandler invocationHandler,
/**
* @implNote could not find a simple way to use the ByteBuddy builder
* to add zero, one or multiple additional fields via {@code
defineField},
- * so we do those 3 cases conditionally all picking up on a shared
+ * so we do those 3 cases conditionally all picking up on a shared
* {@code prolog}
*/
private static <T> ImplementationDefinition<T> proxyDef(
final Class<T> base,
final Class<?>[] interfaces,
@Nullable List<AdditionalField> additionalFields) {
-
+
var prolog = new ByteBuddy()
.with(new NamingStrategy.SuffixingRandom("bb"))
.subclass(base)
.implement(interfaces);
-
+
int additionalFieldCount = _NullSafe.size(additionalFields);
if(additionalFieldCount==0) {
return prolog
diff --git
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
index db92c914163..0bb4c1e2b28 100644
---
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
+++
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/services/classsubstitutor/ClassSubstitutorAbstract.java
@@ -23,7 +23,7 @@
import org.apache.causeway.commons.internal.collections._Sets;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.proxy._ProxyFactoryService;
+import org.apache.causeway.commons.internal.proxy.ProxyFactoryService;
import org.apache.causeway.commons.internal.reflection._ClassCache;
import org.apache.causeway.core.config.progmodel.ProgrammingModelConstants;
import org.apache.causeway.core.metamodel.commons.ClassUtil;
@@ -74,7 +74,7 @@ protected Class<?> getReplacement(final Class<?> cls) {
if(superclass != null && superclass.isEnum()) {
return superclass;
}
- if (ClassUtil.directlyImplements(cls,
_ProxyFactoryService.ProxyEnhanced.class)) {
+ if (ClassUtil.directlyImplements(cls,
ProxyFactoryService.ProxyEnhanced.class)) {
// REVIEW: arguably this should now go back to the
ClassSubstitorRegistry
return getReplacement(cls.getSuperclass());
}
diff --git
a/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrapperInvocationHandler.java
b/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrapperInvocationHandler.java
index 1a087d6420e..fbe759c1843 100644
---
a/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrapperInvocationHandler.java
+++
b/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrapperInvocationHandler.java
@@ -18,7 +18,6 @@
*/
package org.apache.causeway.core.runtime.wrap;
-import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Objects;
@@ -28,18 +27,19 @@
import org.apache.causeway.applib.services.wrapper.control.ExecutionMode;
import org.apache.causeway.commons.internal._Constants;
import org.apache.causeway.commons.internal.base._Lazy;
+import org.apache.causeway.commons.internal.proxy.CachableInvocationHandler;
+
+public interface WrapperInvocationHandler extends CachableInvocationHandler {
-public interface WrapperInvocationHandler extends InvocationHandler {
-
ClassMetaData classMetaData();
-
+
Object invoke(WrapperInvocation wrapperInvocation) throws Throwable;
-
+
@Override
default Object invoke(Object target, Method method, Object[] args) throws
Throwable {
return invoke(WrapperInvocation.of(target, method, args));
}
-
+
public record ClassMetaData(
/** underlying class that is to be proxied */
Class<?> pojoClass,
@@ -47,31 +47,31 @@ public record ClassMetaData(
Method equalsMethod,
Method hashCodeMethod,
Method toStringMethod,
-
+
/**
* The <tt>title()</tt> method; may be <tt>null</tt>.
*/
@Nullable Method titleMethod) {
-
+
/**
* The <tt>__causeway_origin()</tt> method from {@link
WrappingObject#__causeway_origin()}.
*/
- static final _Lazy<Method> __causeway_originMethod =
_Lazy.threadSafe(()->
+ static final _Lazy<Method> __causeway_originMethod =
_Lazy.threadSafe(()->
WrappingObject.class.getMethod(WrappingObject.ORIGIN_GETTER_NAME,
_Constants.emptyClasses));
-
+
/**
* The <tt>__causeway_save()</tt> method from {@link
WrappingObject#__causeway_save()}.
*/
static final _Lazy<Method> __causeway_saveMethod =
_Lazy.threadSafe(()->
WrappingObject.class.getMethod(WrappingObject.SAVE_METHOD_NAME,
_Constants.emptyClasses));
-
+
public static ClassMetaData of(
final @NonNull Class<?> pojoClass) {
try {
var equalsMethod = pojoClass.getMethod("equals",
_Constants.classesOfObject);
var hashCodeMethod = pojoClass.getMethod("hashCode",
_Constants.emptyClasses);
var toStringMethod = pojoClass.getMethod("toString",
_Constants.emptyClasses);
-
+
var titleMethod = (Method)null;
try {
titleMethod = pojoClass.getMethod("title",
_Constants.emptyClasses);
@@ -80,17 +80,17 @@ public static ClassMetaData of(
}
return new WrapperInvocationHandler
.ClassMetaData(pojoClass, equalsMethod,
hashCodeMethod, toStringMethod, titleMethod);
-
+
} catch (final NoSuchMethodException e) {
// ///CLOVER:OFF
throw new RuntimeException("An Object method could not be
found: " + e.getMessage());
// ///CLOVER:ON
}
}
-
+
public boolean isObjectMethod(final Method method) {
- return toStringMethod().equals(method)
- || hashCodeMethod().equals(method)
+ return toStringMethod().equals(method)
+ || hashCodeMethod().equals(method)
|| equalsMethod().equals(method);
}
@@ -104,7 +104,7 @@ public boolean isSaveMethod(Method method) {
return method.equals(__causeway_saveMethod.get());
}
}
-
+
public record WrapperInvocation(
WrappingObject.@NonNull Origin origin,
@NonNull Method method,
@@ -112,12 +112,12 @@ public record WrapperInvocation(
static WrapperInvocation of(Object target, Method method, Object[]
args) {
Objects.requireNonNull(target);
- var origin = target instanceof WrappingObject wrappingObject
+ var origin = target instanceof WrappingObject wrappingObject
? WrappingObject.getOrigin(wrappingObject)
: WrappingObject.Origin.fallback(target);
return new WrapperInvocation(origin, method, args!=null ? args :
_Constants.emptyObjects);
}
-
+
public boolean shouldEnforceRules() {
return
!origin().syncControl().getExecutionModes().contains(ExecutionMode.SKIP_RULE_VALIDATION);
}
@@ -126,5 +126,5 @@ public boolean shouldExecute() {
return
!origin().syncControl().getExecutionModes().contains(ExecutionMode.SKIP_EXECUTION);
}
}
-
+
}
diff --git
a/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrappingObject.java
b/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrappingObject.java
index e4db23562c4..dec7ce86e0b 100644
---
a/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrappingObject.java
+++
b/core/runtime/src/main/java/org/apache/causeway/core/runtime/wrap/WrappingObject.java
@@ -25,7 +25,7 @@
import org.apache.causeway.applib.services.wrapper.WrapperFactory;
import org.apache.causeway.applib.services.wrapper.control.SyncControl;
-import
org.apache.causeway.commons.internal.proxy._ProxyFactoryService.AdditionalField;
+import
org.apache.causeway.commons.internal.proxy.ProxyFactoryService.AdditionalField;
import org.apache.causeway.commons.internal.reflection._Reflect;
import org.apache.causeway.core.metamodel.object.ManagedObject;
diff --git
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
index df197befd95..84015204d17 100644
---
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
+++
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefault.java
@@ -18,6 +18,7 @@
*/
package org.apache.causeway.core.runtimeservices.wrapper;
+import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
@@ -83,7 +84,7 @@
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.collections._Lists;
import org.apache.causeway.commons.internal.exceptions._Exceptions;
-import org.apache.causeway.commons.internal.proxy._ProxyFactoryService;
+import org.apache.causeway.commons.internal.proxy.ProxyFactoryService;
import org.apache.causeway.commons.internal.reflection._GenericResolver;
import
org.apache.causeway.commons.internal.reflection._GenericResolver.ResolvedMethod;
import
org.apache.causeway.core.config.progmodel.ProgrammingModelConstants.MixinConstructor;
@@ -128,7 +129,7 @@ public class WrapperFactoryDefault
@Inject private FactoryService factoryService;
@Inject @Getter(onMethod_= {@Override}) MetaModelContext metaModelContext;
// HasMetaModelContext
- @Inject protected _ProxyFactoryService proxyFactoryService; // protected:
in support of JUnit tests
+ @Inject protected ProxyFactoryService proxyFactoryService; // protected:
in support of JUnit tests
@Inject @Lazy private CommandDtoFactory commandDtoFactory;
@Inject private Provider<InteractionService> interactionServiceProvider;
@@ -288,11 +289,7 @@ public <T,R> T asyncWrap(
+ "use WrapperFactory.asyncWrapMixin(...) instead");
}
- var proxyFactory = proxyFactoryService
- .<T>factory(_Casts.uncheckedCast(domainObject.getClass()),
WrappingObject.class, WrappingObject.ADDITIONAL_FIELDS);
-
- return proxyFactory.createInstance((proxy, method, args) -> {
-
+ final InvocationHandler handler = (proxy, method, args) -> {
var resolvedMethod = _GenericResolver.resolveMethod(method,
domainObject.getClass())
.orElseThrow(); // fail early on attempt to invoke method
that is not part of the meta-model
@@ -311,7 +308,14 @@ public <T,R> T asyncWrap(
}
return submitAsync(memberAndTarget, args, asyncControl);
- }, false);
+ };
+
+ @SuppressWarnings("unchecked")
+ var proxyClass = proxyFactoryService
+ .proxyClass(handler,
+ (Class<T>)domainObject.getClass(),
WrappingObject.class, WrappingObject.ADDITIONAL_FIELDS);
+ var proxyFactory = proxyFactoryService.factory(proxyClass);
+ return proxyFactory.createInstance(false);
}
@Override
@@ -328,12 +332,7 @@ public <T, R> T asyncWrapMixin(
var mixinConstructor =
MixinConstructor.PUBLIC_SINGLE_ARG_RECEIVING_MIXEE
.getConstructorElseFail(mixinClass, mixee.getClass());
- var proxyFactory = proxyFactoryService
- .factory(mixinClass, new Class[]{WrappingObject.class},
WrappingObject.ADDITIONAL_FIELDS,
- mixinConstructor.getParameterTypes());
-
- return proxyFactory.createInstance((proxy, method, args) -> {
-
+ final InvocationHandler handler = (proxy, method, args) -> {
var resolvedMethod = _GenericResolver.resolveMethod(method,
mixinClass)
.orElseThrow(); // fail early on attempt to invoke method
that is not part of the meta-model
@@ -353,7 +352,15 @@ public <T, R> T asyncWrapMixin(
}
return submitAsync(actionAndTarget, args, asyncControl);
- }, new Object[]{ mixee });
+ };
+
+ var proxyClass = proxyFactoryService
+ .proxyClass(handler, mixinClass, new
Class[]{WrappingObject.class}, WrappingObject.ADDITIONAL_FIELDS);
+
+ var proxyFactory = proxyFactoryService
+ .factory(proxyClass, mixinConstructor.getParameterTypes());
+
+ return proxyFactory.createInstance(new Object[]{ mixee });
}
private boolean isInheritedFromJavaLangObject(final Method method) {
diff --git
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
index 3badeb16cf0..d9dc132084f 100644
---
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
+++
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/DomainObjectInvocationHandler.java
@@ -71,9 +71,11 @@
final class DomainObjectInvocationHandler
implements WrapperInvocationHandler {
- @Getter(onMethod_ = {@Override}) @Accessors(fluent=true)
+ @Getter(onMethod_ = {@Override}) @Accessors(fluent=true)
private final WrapperInvocationHandler.ClassMetaData classMetaData;
-
+ @Getter(onMethod_ = {@Override}) @Accessors(fluent=true)
+ private final String key;
+
private final ProxyGenerator proxyGenerator;
private final ObjectSpecification targetSpec;
@@ -83,15 +85,16 @@ final class DomainObjectInvocationHandler
this.targetSpec = targetSpec;
this.classMetaData =
WrapperInvocationHandler.ClassMetaData.of(targetSpec.getCorrespondingClass());
this.proxyGenerator = proxyGenerator;
+ this.key = targetSpec.getCorrespondingClass().getName();
}
@Override
public Object invoke(WrapperInvocation wrapperInvocation) throws Throwable
{
-
+
final Object target = wrapperInvocation.origin().pojo();
final Method method = wrapperInvocation.method();
- final ManagedObject managedMixee = (ManagedObject)
wrapperInvocation.origin().managedMixee();
-
+ final ManagedObject managedMixee =
wrapperInvocation.origin().managedMixee();
+
if (classMetaData().isObjectMethod(method)
|| isEnhancedEntityMethod(method)) {
return method.invoke(target, wrapperInvocation.args());
@@ -165,7 +168,7 @@ public Object invoke(WrapperInvocation wrapperInvocation)
throws Throwable {
}
var objectAction = (ObjectAction) objectMember;
-
+
if(targetAdapter.objSpec().isMixin()) {
if (managedMixee == null) {
throw _Exceptions.illegalState(
@@ -236,7 +239,7 @@ private boolean isEnhancedEntityMethod(final Method method)
{
}
private Object handleTitleMethod(
- final WrapperInvocation wrapperInvocation,
+ final WrapperInvocation wrapperInvocation,
final ManagedObject targetAdapter) {
var targetNoSpec = targetAdapter.objSpec();
@@ -248,8 +251,8 @@ private Object handleTitleMethod(
}
private Object handleSaveMethod(
- final WrapperInvocation wrapperInvocation,
- final ManagedObject targetAdapter,
+ final WrapperInvocation wrapperInvocation,
+ final ManagedObject targetAdapter,
final ObjectSpecification targetNoSpec) {
runValidationTask(wrapperInvocation, ()->{
@@ -288,8 +291,8 @@ private Object handleGetterMethodOnProperty(
var currentReferencedObj =
MmUnwrapUtils.single(currentReferencedAdapter);
targetSpec.getWrapperFactory().notifyListeners(new
PropertyAccessEvent(
- targetAdapter.getPojo(),
- property.getFeatureIdentifier(),
+ targetAdapter.getPojo(),
+ property.getFeatureIdentifier(),
currentReferencedObj));
return currentReferencedObj;
@@ -348,12 +351,12 @@ private Object handleGetterMethodOnCollection(
if (currentReferencedObj instanceof Collection) {
var collectionViewObject = wrapCollection(
- (Collection<?>) currentReferencedObj,
+ (Collection<?>) currentReferencedObj,
collection);
targetSpec.getWrapperFactory().notifyListeners(collectionAccessEvent);
return collectionViewObject;
} else if (currentReferencedObj instanceof Map) {
- var mapViewObject = wrapMap(
+ var mapViewObject = wrapMap(
(Map<?, ?>) currentReferencedObj,
collection);
targetSpec.getWrapperFactory().notifyListeners(collectionAccessEvent);
@@ -474,7 +477,7 @@ private void checkUsability(
private void notifyListenersAndVetoIfRequired(final InteractionResult
interactionResult) {
var interactionEvent = interactionResult.getInteractionEvent();
-
+
targetSpec.getWrapperFactory().notifyListeners(interactionEvent);
if (interactionEvent.isVeto()) {
throw toException(interactionEvent);
diff --git
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyGenerator.java
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyGenerator.java
index 55e8421968b..85a1e9a4466 100644
---
a/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyGenerator.java
+++
b/core/runtimeservices/src/main/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyGenerator.java
@@ -27,7 +27,7 @@
import org.apache.causeway.applib.services.wrapper.control.SyncControl;
import org.apache.causeway.commons.internal.base._Casts;
import org.apache.causeway.commons.internal.context._Context;
-import org.apache.causeway.commons.internal.proxy._ProxyFactoryService;
+import org.apache.causeway.commons.internal.proxy.ProxyFactoryService;
import org.apache.causeway.commons.semantics.CollectionSemantics;
import org.apache.causeway.core.metamodel.object.ManagedObject;
import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
@@ -35,25 +35,25 @@
import org.apache.causeway.core.runtime.wrap.WrapperInvocationHandler;
import org.apache.causeway.core.runtime.wrap.WrappingObject;
-public record ProxyGenerator(@NonNull _ProxyFactoryService
proxyFactoryService) {
+public record ProxyGenerator(@NonNull ProxyFactoryService proxyFactoryService)
{
+ @SuppressWarnings("unchecked")
public <T> T objectProxy(
- final T domainObject,
- final ObjectSpecification domainObjectSpec,
- final SyncControl syncControl) {
-
- return instantiateProxy(handler(domainObjectSpec), new
WrappingObject.Origin(domainObject, syncControl));
+ final T domainObject,
+ final ObjectSpecification domainObjectSpec,
+ final SyncControl syncControl) {
+ return (T) instantiateProxy(domainObjectSpec, new
WrappingObject.Origin(domainObject, syncControl));
}
+ @SuppressWarnings("unchecked")
public <T> T mixinProxy(
final T mixin,
final ManagedObject managedMixee,
final ObjectSpecification mixinSpec,
final SyncControl syncControl) {
-
- return instantiateProxy(handler(mixinSpec), new
WrappingObject.Origin(mixin, managedMixee, syncControl));
+ return (T) instantiateProxy(mixinSpec, new
WrappingObject.Origin(mixin, managedMixee, syncControl));
}
-
+
/**
* Whether to execute or not will be picked up from the supplied parent
* handler.
@@ -61,18 +61,18 @@ public <T> T mixinProxy(
public <T, E> Collection<E> collectionProxy(
final Collection<E> collectionToBeProxied,
final OneToManyAssociation otma) {
-
+
var collectionInvocationHandler = PluralInvocationHandler
.forCollection(collectionToBeProxied, otma);
-
+
var proxyBase = CollectionSemantics
.valueOfElseFail(collectionToBeProxied.getClass())
.getContainerType();
-
- return instantiatePluralProxy(_Casts.uncheckedCast(proxyBase),
+
+ return instantiatePluralProxy(_Casts.uncheckedCast(proxyBase),
collectionInvocationHandler);
}
-
+
/**
* Whether to execute or not will be picked up from the supplied parent
* handler.
@@ -80,37 +80,33 @@ public <T, E> Collection<E> collectionProxy(
public <T, P, Q> Map<P, Q> mapProxy(
final Map<P, Q> mapToBeProxied,
final OneToManyAssociation otma) {
-
+
var proxyBase = Map.class;
-
- return instantiatePluralProxy(_Casts.uncheckedCast(proxyBase),
+
+ return instantiatePluralProxy(_Casts.uncheckedCast(proxyBase),
PluralInvocationHandler.forMap(mapToBeProxied, otma));
}
-
- // -- HELPER
- <T> T instantiateProxy(final WrapperInvocationHandler handler,
WrappingObject.Origin origin) {
- return
_Casts.uncheckedCast(instantiateProxy(handler.classMetaData().pojoClass(),
handler, origin));
- }
+ // -- HELPER
/**
- * Creates a proxy, using given {@code base} type as the proxy's base.
- * @implNote introduced to circumvent access issues on cases,
- * where {@code handler.getDelegate().getClass()} is not visible
- * (eg. nested private type)
+ * Creates a proxy, using given {@code targetSpec} type as the proxy's
base.
*/
- private <T> T instantiateProxy(final Class<T> base, final
WrapperInvocationHandler handler, WrappingObject.Origin origin) {
- T proxy = proxyFactoryService
- .factory(base, WrappingObject.class,
WrappingObject.ADDITIONAL_FIELDS)
- .createInstance(handler, false);
+ private Object instantiateProxy(final ObjectSpecification targetSpec,
WrappingObject.Origin origin) {
+ var proxyClass = proxyFactoryService
+ .proxyClass(handler(targetSpec),
+ targetSpec.getCorrespondingClass(), WrappingObject.class,
WrappingObject.ADDITIONAL_FIELDS);
+ var proxy = proxyFactoryService
+ .factory(proxyClass)
+ .createInstance(false);
return WrappingObject.withOrigin(proxy, origin);
}
-
+
private <T, P> P instantiatePluralProxy(final Class<T> base, final
PluralInvocationHandler<T, P> pluralInvocationHandler) {
var proxyWithoutFields = Proxy.newProxyInstance(
_Context.getDefaultClassLoader(),
new Class<?>[] {base},
- pluralInvocationHandler);
+ pluralInvocationHandler);
return _Casts.uncheckedCast(proxyWithoutFields);
}
@@ -119,5 +115,5 @@ public WrapperInvocationHandler handler(ObjectSpecification
targetSpec) {
targetSpec,
this);
}
-
+
}
diff --git
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
index d47f60378aa..c526db0d484 100644
---
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
+++
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/RuntimeServicesTestAbstract.java
@@ -63,7 +63,8 @@ public abstract class RuntimeServicesTestAbstract
@BeforeEach
final void setUp() throws Exception {
var mmcBuilder = MetaModelContext_forTesting.builder()
- .memberExecutor(Mockito.mock(MemberExecutorService.class));
+ .memberExecutor(Mockito.mock(MemberExecutorService.class))
+ ;
// install runtime services into MMC (extend as needed)
diff --git
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefaultTest.java
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefaultTest.java
index 9331895caca..3c78be6e899 100644
---
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefaultTest.java
+++
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/WrapperFactoryDefaultTest.java
@@ -30,7 +30,7 @@
import org.apache.causeway.applib.services.wrapper.control.ExecutionMode;
import org.apache.causeway.applib.services.wrapper.control.SyncControl;
-import org.apache.causeway.commons.internal.proxy._ProxyFactoryService;
+import org.apache.causeway.commons.internal.proxy.ProxyFactoryService;
import org.apache.causeway.core.metamodel._testing.MetaModelContext_forTesting;
import org.apache.causeway.core.metamodel.execution.MemberExecutorService;
import org.apache.causeway.core.runtime.wrap.WrappingObject;
@@ -73,7 +73,7 @@ public void setUp() throws Exception {
@Override
public void init() {
this.metaModelContext = mmc;
- this.proxyFactoryService =
Mockito.mock(_ProxyFactoryService.class);
+ this.proxyFactoryService =
Mockito.mock(ProxyFactoryService.class);
super.init();
}
diff --git
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyCreatorTestUsingCodegenPlugin.java
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyCreatorTestUsingCodegenPlugin.java
index 4df6e38801f..ea62989b938 100644
---
a/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyCreatorTestUsingCodegenPlugin.java
+++
b/core/runtimeservices/src/test/java/org/apache/causeway/core/runtimeservices/wrapper/handlers/ProxyCreatorTestUsingCodegenPlugin.java
@@ -18,35 +18,25 @@
*/
package org.apache.causeway.core.runtimeservices.wrapper.handlers;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import org.apache.causeway.applib.annotation.DomainObject;
+import org.apache.causeway.applib.annotation.Nature;
import org.apache.causeway.applib.services.wrapper.control.SyncControl;
import
org.apache.causeway.core.codegen.bytebuddy.services.ProxyFactoryServiceByteBuddy;
-import org.apache.causeway.core.runtime.wrap.WrapperInvocationHandler;
import org.apache.causeway.core.runtime.wrap.WrappingObject;
+import org.apache.causeway.core.runtimeservices.RuntimeServicesTestAbstract;
-import lombok.Getter;
-import lombok.Setter;
-
-class ProxyCreatorTestUsingCodegenPlugin {
+class ProxyCreatorTestUsingCodegenPlugin extends RuntimeServicesTestAbstract {
- private ProxyGenerator proxyGenerator;
-
- @BeforeEach
- void setUp() throws Exception {
- proxyGenerator = new ProxyGenerator(new
ProxyFactoryServiceByteBuddy());
- }
+ private ProxyGenerator proxyGenerator = new ProxyGenerator(new
ProxyFactoryServiceByteBuddy());
+ @DomainObject(nature = Nature.VIEW_MODEL)
public static class Employee {
private String name;
public String getName() {
@@ -57,48 +47,22 @@ public void setName(final String name) {
}
}
- private static class WrapperInvocationHandlerForTest implements
WrapperInvocationHandler {
- private final Employee delegate = new Employee();
- private final Set<String> invoked = new HashSet<>();
- private final WrapperInvocationHandler.ClassMetaData classMetaData =
new WrapperInvocationHandler.ClassMetaData(
- Employee.class, null, null, null, null);
-
- @Getter @Setter
- private boolean resolveObjectChangedEnabled = false;
-
- public boolean wasInvoked(final String methodName) {
- return invoked.contains(methodName);
- }
-
- @Override
- public WrapperInvocationHandler.ClassMetaData classMetaData() {
- return classMetaData;
- }
-
- @Override
- public Object invoke(WrapperInvocation wrapperInvocation) throws
Throwable {
- invoked.add(wrapperInvocation.method().getName());
- return "hi";
- }
-
- }
-
@Test
void proxyShouldDelegateCalls() {
- final WrapperInvocationHandlerForTest handler = new
WrapperInvocationHandlerForTest();
- final Employee proxyOfEmployee =
proxyGenerator.instantiateProxy(handler, new
WrappingObject.Origin(handler.delegate, SyncControl.control()));
-
- assertNotNull(proxyOfEmployee);
-
- assertNotEquals(Employee.class.getName(),
proxyOfEmployee.getClass().getName());
-
- assertFalse(handler.wasInvoked("getName"));
+ final Employee employee = new Employee();
+ var employeeSpec =
getMetaModelContext().getSpecificationLoader().loadSpecification(Employee.class);
- assertEquals("hi", proxyOfEmployee.getName());
+ var proxy = proxyGenerator.objectProxy(employee, employeeSpec,
SyncControl.control());
- assertTrue(handler.wasInvoked("getName"));
+ assertNotNull(proxy);
+ assertTrue(proxy instanceof WrappingObject);
+ assertNotEquals(Employee.class.getName(), proxy.getClass().getName());
+ assertNull(proxy.getName());
+ // requires interaction infrastructure ... (however, tested with
regression tests separately)
+ //proxy.setName("hi");
+ //assertEquals("hi", proxy.getName());
}
}
diff --git
a/regressiontests/persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/wrapper/WrapperFactoryMetaspaceMemoryLeakTest.java
b/regressiontests/persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/wrapper/WrapperFactoryMetaspaceMemoryLeakTest.java
index 18dd21de935..7e8ae290f90 100644
---
a/regressiontests/persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/wrapper/WrapperFactoryMetaspaceMemoryLeakTest.java
+++
b/regressiontests/persistence-jpa/src/test/java/org/apache/causeway/testdomain/persistence/jpa/wrapper/WrapperFactoryMetaspaceMemoryLeakTest.java
@@ -21,8 +21,10 @@
import jakarta.inject.Inject;
import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
@@ -38,6 +40,8 @@
import org.apache.causeway.testdomain.jpa.entities.JpaProduct;
import
org.apache.causeway.testing.integtestsupport.applib.CausewayIntegrationTestAbstract;
+import lombok.RequiredArgsConstructor;
+
@SpringBootTest(
classes = {
Configuration_usingJpa.class,
@@ -65,30 +69,42 @@ void uninstallFixture() {
this.lock.release();
}
- @Test
- void testWrapper_waitingOnDomainEvent() throws InterruptedException {
- _MemoryUsage.measureMetaspace("exercise", ()->{
-// with caching
-// exercise(1, 0); // 2,221 KB
-// exercise(1, 2000); // 3,839 KB. // some leakage from
collections
-// exercise(20, 0); // 2,112 KB
-// exercise(20, 2000); // 3,875 KB
-// exercise(2000, 0); // 3,263 KB. // ? increased some, is it
significant; a lot less than without caching
-// exercise(2000, 200); // 4,294 KB.
-// exercise(20000, 0); // 3,243 KB // no noticeable leakage
compared to 2000; MUCH less than without caching
-
-// without caching
-// exercise(1, 0); // 2,244 KB
-// exercise(1, 2000); //. 3,669 KB // some leakage from
collections
-// exercise(20, 0); // 2,440 KB
-// exercise(20, 2000); //. 4,286 KB
- exercise(2000, 0); // 14,580 KB // significant leakage from 20
-// exercise(2000, 200); // 20,423 KB
-// exercise(20000, 0); //.115,729 KB
- });
+ //with caching
+ // exercise(1, 0); // 2,221 KB
+ // exercise(1, 2000); // 3,839 KB. // some leakage from collections
+ // exercise(20, 0); // 2,112 KB
+ // exercise(20, 2000); // 3,875 KB
+ // exercise(2000, 0); // 3,263 KB // ? increased some, is it
significant; a lot less than without caching
+ // exercise(2000, 200); // 4,294 KB
+ // exercise(20000, 0); // 3,243 KB // no noticeable leakage compared
to 2000; MUCH less than without caching
+ //without caching
+ // exercise(1, 0); // 2,244 KB
+ // exercise(1, 2000); // 3,669 KB // some leakage from collections
+ // exercise(20, 0); // 2,440 KB
+ // exercise(20, 2000); // 4,286 KB
+ // exercise(2000, 0); // 14,580 KB // significant leakage from 20
+ // exercise(2000, 200); // 20,423 KB
+ // exercise(20000, 0); //.115,729 KB
+ @RequiredArgsConstructor
+ enum Scenario {
+ TWO_K_TIMES_ONE(2000, 0, 4000),
+ TWO_K_TIMES_TEN(2000, 10, 4000),;
+ final int instances;
+ final int loops;
+ final int thresholdKibi;
+ }
+
+ @ParameterizedTest
+ @EnumSource(Scenario.class)
+ void testWrapper_waitingOnDomainEventScenario(final Scenario scenario)
throws InterruptedException {
+ var usage = _MemoryUsage.measureMetaspace(()->
+ exercise(scenario.instances, scenario.loops));
+ Assertions.assertTrue(usage.usedInKibiBytes() < scenario.thresholdKibi,
+ ()->"%s exceeds expected %dKB threshold".formatted(usage,
scenario.thresholdKibi));
+ System.out.printf("scenario %s usage %s%n", scenario.name(), usage);
}
- private void exercise(int instances, int loops) {
+ private void exercise(final int instances, final int loops) {
for (int i = 0; i < instances; i++) {
var jpaInventoryManager =
wrapper.wrap(factoryService.viewModel(JpaInventoryManager.class));
jpaInventoryManager.foo();