You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2018/01/05 10:46:12 UTC

[isis] 13/16: ISIS-1756 on shutdown purge any state associated with the current web-app classloader

This is an automated email from the ASF dual-hosted git repository.

danhaywood pushed a commit to branch ISIS-1779-jax-rs-2
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 363639cfae2c6c355c4a5fc607e72e3226dddff8
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Dec 8 04:23:58 2017 +0100

    ISIS-1756 on shutdown purge any state associated with the current
    web-app classloader
---
 core/pom.xml                                       |   6 +-
 .../core/runtime/system/context/IsisContext.java   |   3 +-
 .../DataNucleusApplicationComponents.java          |  22 ++++
 .../persistence/PersistenceSessionFactory.java     |   6 +-
 .../datanucleus/DataNucleusLifeCycleHelper.java    | 125 +++++++++++++++++++++
 5 files changed, 157 insertions(+), 5 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 87d447f..737f95c 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -86,10 +86,10 @@
         <!-- Datanucleus Objectstore -->
         <jdo-api.version>3.2.0-m7</jdo-api.version>
 
-        <datanucleus-core.version>5.1.2</datanucleus-core.version>
-        <datanucleus-api-jdo.version>5.1.2</datanucleus-api-jdo.version>
+        <datanucleus-core.version>5.1.5-SNAPSHOT</datanucleus-core.version>
+        <datanucleus-api-jdo.version>5.1.5-SNAPSHOT</datanucleus-api-jdo.version>
         <datanucleus-jdo-query.version>5.0.2</datanucleus-jdo-query.version>
-        <datanucleus-rdbms.version>5.1.2</datanucleus-rdbms.version>
+        <datanucleus-rdbms.version>5.1.5-SNAPSHOT</datanucleus-rdbms.version>
         <datanucleus-jodatime.version>5.1.0-release</datanucleus-jodatime.version>
 	<!--
 	    ISIS-1288: seen integration tests to fail;
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
index 9aaf71b..059914b 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/context/IsisContext.java
@@ -127,7 +127,7 @@ public final class IsisContext {
 
 	/**
 	 * TODO [andi-huber] not sure if required, initial idea was to force log4j
-	 * re-configuration on a undeply/deploy cycle
+	 * re-configuration on a undeploy/deploy cycle
 	 */
 	private static void resetLogging() {
 		org.apache.log4j.Logger.getRootLogger().removeAllAppenders();
@@ -136,4 +136,5 @@ public final class IsisContext {
 
 
 
+
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/DataNucleusApplicationComponents.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/DataNucleusApplicationComponents.java
index 8fafca8..986e649 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/DataNucleusApplicationComponents.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/DataNucleusApplicationComponents.java
@@ -18,6 +18,9 @@
  */
 package org.apache.isis.core.runtime.system.persistence;
 
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
@@ -32,15 +35,19 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
 import org.apache.isis.core.runtime.system.context.IsisContext;
 import org.apache.isis.objectstore.jdo.datanucleus.CreateSchemaObjectFromClassMetadata;
+import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusLifeCycleHelper;
 import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPropertiesAware;
 import org.apache.isis.objectstore.jdo.metamodel.facets.object.query.JdoNamedQuery;
 import org.apache.isis.objectstore.jdo.metamodel.facets.object.query.JdoQueryFacet;
+import org.datanucleus.ClassLoaderResolver;
 import org.datanucleus.PersistenceNucleusContext;
 import org.datanucleus.PropertyNames;
 import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
 import org.datanucleus.metadata.MetaDataListener;
 import org.datanucleus.metadata.MetaDataManager;
+import org.datanucleus.store.AbstractStoreManager;
 import org.datanucleus.store.StoreManager;
+import org.datanucleus.store.autostart.AutoStartMechanism;
 import org.datanucleus.store.schema.SchemaAwareStoreManager;
 
 import com.google.common.base.Joiner;
@@ -109,6 +116,21 @@ public class DataNucleusApplicationComponents implements ApplicationScopedCompon
 
         namedQueryByName = catalogNamedQueries(persistableClassNameSet);
     }
+    
+    /** 
+     * Marks the end of DataNucleus' life-cycle. Purges any state associated with DN. 
+     * Subsequent calls have no effect.  
+     * 
+     * @author ahuber@apache.org
+     * @since 2.0.0
+     */
+    public void shutdown() {
+    	instance = null;
+    	if(persistenceManagerFactory != null) {
+    		DataNucleusLifeCycleHelper.cleanUp(persistenceManagerFactory);
+    		persistenceManagerFactory = null;
+    	}
+    }
 
     private static boolean isSchemaAwareStoreManager(Map<String,String> datanucleusProps) {
 
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory.java
index 4be046c..989c989 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/system/persistence/PersistenceSessionFactory.java
@@ -164,7 +164,11 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
     //region > shutdown
     @Programmatic
     public final void shutdown() {
-        // no-op
+    	//XXX ISIS-1756 purge any DataNucleus State
+    	if(applicationComponents != null) {
+    		applicationComponents.shutdown();
+    		applicationComponents = null;
+    	}
     }
 
     //endregion
diff --git a/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusLifeCycleHelper.java b/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusLifeCycleHelper.java
new file mode 100644
index 0000000..93ba482
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusLifeCycleHelper.java
@@ -0,0 +1,125 @@
+/*
+ *  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.isis.objectstore.jdo.datanucleus;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.function.Consumer;
+
+import javax.jdo.PersistenceManagerFactory;
+
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.datanucleus.ClassLoaderResolver;
+import org.datanucleus.PersistenceNucleusContext;
+import org.datanucleus.api.jdo.JDOPersistenceManagerFactory;
+import org.datanucleus.enhancer.EnhancementHelper;
+import org.datanucleus.store.AbstractStoreManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 
+ * Purges any state associated with DataNucleus.
+ * 
+ * @author ahuber@apache.org
+ * @since 2.0.0
+ *
+ */
+public class DataNucleusLifeCycleHelper {
+	
+    private static final Logger LOG = LoggerFactory.getLogger(DataNucleusLifeCycleHelper.class);
+
+	public static void cleanUp(PersistenceManagerFactory persistenceManagerFactory) {
+		
+		try {
+			
+			final ClassLoader cl = IsisContext.getClassLoader();
+			
+			if(persistenceManagerFactory instanceof JDOPersistenceManagerFactory) {
+				
+				final JDOPersistenceManagerFactory jdoPMF = 
+						(JDOPersistenceManagerFactory) persistenceManagerFactory;
+				final PersistenceNucleusContext nucleusContext = jdoPMF.getNucleusContext();
+				final AbstractStoreManager storeManager = 
+						(AbstractStoreManager)nucleusContext.getStoreManager();
+				
+			
+				persistenceManagerFactory.getManagedClasses()
+				.forEach(clazz->{
+			        final ClassLoaderResolver clr = nucleusContext.getClassLoaderResolver(cl);
+	    		     	    		        
+			        // Un-manage from the store
+			        storeManager.unmanageClass(clr,	clazz.getName(), false);
+			        
+					 // Unload the meta-data for this class
+			        nucleusContext.getMetaDataManager().unloadMetaDataForClass(clazz.getName());
+				});
+			}
+			
+			persistenceManagerFactory.close();
+			dnUnregisterClassesManagedBy(cl);
+			
+			
+		} catch (Exception e) {
+			// ignore, since it only affects re-deploy-ability, which is nice to have but not critical
+		}
+
+	}
+    
+    // -- HELPER
+    
+    private static void dnUnregisterClassesManagedBy(ClassLoader cl) {
+    	if(cl==null)
+    		return;
+		visitDNRegisteredClasses(map->
+			map.entrySet()
+			.removeIf(entry->cl.equals(entry.getKey().getClassLoader()))
+		);
+	}
+    
+    // -- LOW LEVEL REFLECTION
+    
+	private final static MethodHandle getRegisteredClassesMH;
+	static {
+		MethodHandle mh;		
+		try {
+			Field registeredClasses = EnhancementHelper.class.getDeclaredField("registeredClasses");
+			registeredClasses.setAccessible(true);
+			mh = MethodHandles.lookup().unreflectGetter(registeredClasses);
+			registeredClasses.setAccessible(false);
+		} catch (Exception e) {
+			mh = null;
+			e.printStackTrace();
+		}
+		getRegisteredClassesMH = mh;
+	}
+	
+	private static void visitDNRegisteredClasses(Consumer<Map<Class<?>, ?>> visitor){
+		try {
+			visitor.accept( (Map<Class<?>, ?>) getRegisteredClassesMH.invoke() );
+		} catch (Throwable e) {
+			LOG.warn("Failed to access DataNucleus' EnhancementHelper via reflection.", e);
+		}
+	}
+
+
+
+}

-- 
To stop receiving notification emails like this one, please contact
"commits@isis.apache.org" <co...@isis.apache.org>.