From c90f2bb1bb9fd19f803021d34b2a35fc173b1625 Mon Sep 17 00:00:00 2001 From: Moritz Becker Date: Thu, 28 Sep 2023 19:27:18 +0200 Subject: [PATCH] [#1805] Add logic to EntityViewManager and ProxyFactory to maintain evm references in proxies --- .../manual/en_US/getting_started.adoc | 10 ++++ .../persistence/view/EntityViewManager.java | 6 +++ .../view/SerializableEntityViewManager.java | 6 +++ .../view/impl/EntityViewManagerImpl.java | 5 ++ .../view/impl/proxy/ProxyFactory.java | 46 +++++++++++++++---- .../jackson/EntityViewMessageBodyReader.java | 5 ++ .../jsonb/EntityViewMessageBodyReader.java | 5 ++ 7 files changed, 74 insertions(+), 9 deletions(-) diff --git a/documentation/src/main/asciidoc/entity-view/manual/en_US/getting_started.adoc b/documentation/src/main/asciidoc/entity-view/manual/en_US/getting_started.adoc index d602d6143d..7c56116048 100644 --- a/documentation/src/main/asciidoc/entity-view/manual/en_US/getting_started.adoc +++ b/documentation/src/main/asciidoc/entity-view/manual/en_US/getting_started.adoc @@ -323,6 +323,11 @@ public class EntityViewManagerProducer { evm = config.createEntityViewManager(criteriaBuilderFactory); } + @PreDestroy + public void closeEvm() { + evm.close(); + } + @Produces @ApplicationScoped public EntityViewManager createEntityViewManager() { @@ -377,6 +382,11 @@ public class EntityViewManagerProducer { evm = config.createEntityViewManager(criteriaBuilderFactory); } + @PreDestroy + public void closeEvm() { + evm.close(); + } + @Produces @ApplicationScoped public EntityViewManager createEntityViewManager() { diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewManager.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewManager.java index 658cbd8109..e07a9a79e5 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewManager.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewManager.java @@ -502,4 +502,10 @@ public interface EntityViewManager extends ServiceProvider { * @since 1.2.0 */ public > Q applySetting(EntityViewSetting setting, CriteriaBuilder criteriaBuilder, String entityViewRoot); + + /** + * Closes this {@link EntityViewManager} and frees resources. The entity view manager will not be usable + * anymore after this method has been invoked. + */ + public void close(); } diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/SerializableEntityViewManager.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/SerializableEntityViewManager.java index 8c3b62af8f..39b50d6ed7 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/SerializableEntityViewManager.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/SerializableEntityViewManager.java @@ -36,6 +36,7 @@ public class SerializableEntityViewManager implements EntityViewManager, Seriali public static final String EVM_FIELD_NAME = "ENTITY_VIEW_MANAGER"; public static final String SERIALIZABLE_EVM_FIELD_NAME = "SERIALIZABLE_ENTITY_VIEW_MANAGER"; + public static final String SERIALIZABLE_EVM_DELEGATE_FIELD_NAME = "evm"; private final Class entityViewClass; private transient volatile EntityViewManager evm; @@ -275,4 +276,9 @@ public > Q applySetting(EntityViewSetting T getService(Class serviceClass) { return getEvm().getService(serviceClass); } + + @Override + public void close() { + getEvm().close(); + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java index e0e9f0dfa4..55352bf8c1 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java @@ -1450,6 +1450,11 @@ public EntityViewManager getSerializableDelegate(Class entityViewClass) { return serializableDelegates.get(proxyFactory.getProxy(this, metamodel.managedViewOrError(entityViewClass))); } + @Override + public void close() { + proxyFactory.clear(); + } + /** * @author Christian Beikov * @since 1.2.0 diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java index add8819dd1..c40cb0b82c 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java @@ -1011,6 +1011,8 @@ private Class defineOrGetClass(Class clazz, Class neighbo } private Class defineOrGetClass(EntityViewManager entityViewManager, boolean unsafe, Class clazz, Class neighbourClazz, CtClass cc) throws IOException, IllegalAccessException, NoSuchFieldException, CannotCompileException { + Class c; + boolean newlyDefined = false; try { // Ask the package opener to allow deep access, otherwise defining the class will fail if (clazz.getPackage() != null) { @@ -1021,13 +1023,8 @@ private Class defineOrGetClass(EntityViewManager entityViewMana cc.writeFile(DEBUG_DUMP_DIRECTORY.toString()); } - Class c = (Class) UnsafeHelper.define(cc.getName(), cc.toBytecode(), neighbourClazz); - - if (entityViewManager != null) { - c.getField(SerializableEntityViewManager.EVM_FIELD_NAME).set(null, entityViewManager); - } - - return c; + c = (Class) UnsafeHelper.define(cc.getName(), cc.toBytecode(), neighbourClazz); + newlyDefined = true; } catch (CannotCompileException | LinkageError ex) { // If there are multiple proxy factories for the same class loader // we could end up in defining a class multiple times, so we check if the classloader @@ -1037,7 +1034,7 @@ private Class defineOrGetClass(EntityViewManager entityViewMana || ex.getCause() instanceof InvocationTargetException && ex.getCause().getCause() instanceof LinkageError && (error = (LinkageError) ex.getCause().getCause()) != null || ex.getCause() instanceof LinkageError && (error = (LinkageError) ex.getCause()) != null) { try { - return (Class) pool.getClassLoader().loadClass(cc.getName()); + c = (Class) pool.getClassLoader().loadClass(cc.getName()); } catch (ClassNotFoundException cnfe) { // Something we can't handle happened throw error; @@ -1049,12 +1046,33 @@ private Class defineOrGetClass(EntityViewManager entityViewMana // With Java 9 it's actually the case that Javassist doesn't throw the LinkageError but instead tries to define the class differently // Too bad that this different path lead to a NullPointerException try { - return (Class) pool.getClassLoader().loadClass(cc.getName()); + c = (Class) pool.getClassLoader().loadClass(cc.getName()); } catch (ClassNotFoundException cnfe) { // Something we can't handle happened throw ex; } } + if (entityViewManager != null) { + updateEvmReferences(c, entityViewManager, !newlyDefined); + } + return c; + } + + private void updateEvmReferences(Class entityViewClass, EntityViewManager evm, boolean updateSerializableEvmDelegate) { + try { + entityViewClass.getField(SerializableEntityViewManager.EVM_FIELD_NAME).set(null, evm); + if (updateSerializableEvmDelegate) { + SerializableEntityViewManager serializableEvm = + (SerializableEntityViewManager) entityViewClass.getField( + SerializableEntityViewManager.SERIALIZABLE_EVM_FIELD_NAME).get(null); + Field serializableEvmDelegateField = SerializableEntityViewManager.class.getDeclaredField( + SerializableEntityViewManager.SERIALIZABLE_EVM_DELEGATE_FIELD_NAME); + serializableEvmDelegateField.setAccessible(true); + serializableEvmDelegateField.set(serializableEvm, evm); + } + } catch (IllegalAccessException | NoSuchFieldException e) { + throw new RuntimeException(e); + } } private boolean shouldAddDefaultConstructor(boolean hasEmptyConstructor, boolean addedReferenceConstructor, CtField[] attributeFields) { @@ -3489,4 +3507,14 @@ private String getGenericSignature(MethodAttribute attribute, CtField attr return sb.toString(); } + + public void clear() { + baseClasses.clear(); + for (Class proxyClass : proxyClasses.values()) { + updateEvmReferences(proxyClass, null, true); + } + proxyClasses.clear(); + unsafeProxyClasses.clear(); + proxyClassesToViewClasses.clear(); + } } diff --git a/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java index 457c6bc567..e402bcdd32 100644 --- a/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java +++ b/integration/jaxrs-jackson/src/main/java/com/blazebit/persistence/integration/jaxrs/jackson/EntityViewMessageBodyReader.java @@ -542,5 +542,10 @@ public > Q applySetting(EntityViewSetting T getService(Class serviceClass) { return entityViewManager.get().getService(serviceClass); } + + @Override + public void close() { + entityViewManager.get().close(); + } } } diff --git a/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java index 5b128b21e8..9d8cc6af7c 100644 --- a/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java +++ b/integration/jaxrs-jsonb/src/main/java/com/blazebit/persistence/integration/jaxrs/jsonb/EntityViewMessageBodyReader.java @@ -539,5 +539,10 @@ public > Q applySetting(EntityViewSetting T getService(Class serviceClass) { return entityViewManager.get().getService(serviceClass); } + + @Override + public void close() { + entityViewManager.get().close(); + } } }