You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/02/23 09:53:39 UTC

[isis] 02/04: ISIS-1871 ISIS-1756 ISIS-1775 proper life-cycling + context-path support

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

ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 6e06e73c81b76fdb6db2a6850c74996d2def6e6b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Fri Feb 23 10:40:31 2018 +0100

    ISIS-1871 ISIS-1756 ISIS-1775 proper life-cycling + context-path support
    
    this works for DN 4 or 5 >= 5.1.5
---
 .../services/swagger/SwaggerServiceDefault.java    | 15 +++-
 .../isis/core/webapp/WebAppContextSupport.java     | 50 +++++++++++
 .../core/runtime/system/context/IsisContext.java   | 18 +++-
 .../DataNucleusApplicationComponents.java          | 21 ++++-
 .../persistence/PersistenceSessionFactory.java     | 31 ++-----
 .../datanucleus/DataNucleusLifeCycleHelper.java    | 57 +++++++++++++
 .../wicket/viewer/IsisWicketApplication.java       | 99 ++++++++++++----------
 7 files changed, 215 insertions(+), 76 deletions(-)

diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
index 968a93c..192b5f6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/swagger/SwaggerServiceDefault.java
@@ -22,15 +22,15 @@ import java.util.Map;
 
 import javax.annotation.PostConstruct;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.NatureOfService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.swagger.SwaggerService;
 import org.apache.isis.core.metamodel.services.swagger.internal.SwaggerSpecGenerator;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
+import org.apache.isis.core.webapp.WebAppContextSupport;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 @DomainService(
         nature = NatureOfService.DOMAIN,
@@ -48,7 +48,14 @@ public class SwaggerServiceDefault implements SwaggerService {
 
     @PostConstruct
     public void init(final Map<String,String> properties) {
-        this.basePath = getPropertyElse(properties, KEY_RESTFUL_BASE_PATH, KEY_RESTFUL_BASE_PATH_DEFAULT);
+    	
+    	final String webappContextPath = 
+    			getPropertyElse(properties, WebAppContextSupport.WEB_APP_CONTEXT_PATH, "/");
+    	
+    	final String basePath = 
+    			getPropertyElse(properties, KEY_RESTFUL_BASE_PATH, KEY_RESTFUL_BASE_PATH_DEFAULT);
+    	
+    	this.basePath = WebAppContextSupport.prependContextPathIfPresent(webappContextPath, basePath);
     }
 
     static String getPropertyElse(final Map<String, String> properties, final String key, final String dflt) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/webapp/WebAppContextSupport.java b/core/metamodel/src/main/java/org/apache/isis/core/webapp/WebAppContextSupport.java
new file mode 100644
index 0000000..10aa314
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/webapp/WebAppContextSupport.java
@@ -0,0 +1,50 @@
+/*
+ *  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.core.webapp;
+
+import javax.servlet.ServletContext;
+
+import com.google.common.base.Strings;
+
+public class WebAppContextSupport {
+
+    /**
+     * Property name given to the context path of the web application as returned by 
+     * {@link ServletContext#getContextPath()}.
+     */
+	public static final String WEB_APP_CONTEXT_PATH = "application.webapp.context-path";
+	
+	
+	public static String prependContextPathIfPresent(String contextPath, String path) {
+		if(Strings.isNullOrEmpty(contextPath) || contextPath.equals("/"))
+			return path;
+		
+		if(!contextPath.startsWith("/"))
+			throw new IllegalArgumentException(
+					"contextPath must start with a slash '/' character, got '"+contextPath+"'");
+
+		if(!path.startsWith("/"))
+			throw new IllegalArgumentException(
+					"path must start with a slash '/' character, got '"+path+"'");
+		
+		return contextPath + path;
+	}
+	
+}
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 ee1cc1c..9763ff5 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
@@ -48,7 +48,7 @@ public interface IsisContext {
 				()->new IllegalStateException(
 						"internal error: should have been populated by IsisSessionFactoryBuilder") );
 	}
-	
+
 	/**
 	 * 
 	 * @return Isis's default class loader
@@ -65,6 +65,7 @@ public interface IsisContext {
      */
     public static void clear() {
     	_Context.clear();
+    	resetLogging();
     }
     
     // -- DEPRECATIONS
@@ -79,4 +80,19 @@ public interface IsisContext {
     	clear();
     }
 
+	// -- HELPER
+
+	/**
+	 * TODO [andi-huber] not sure if required, initial idea was to force log4j
+	 * re-configuration on an undeploy/deploy cycle
+	 */
+	static void resetLogging() {
+		try {
+			org.apache.log4j.BasicConfigurator.resetConfiguration();
+			org.apache.log4j.Logger.getRootLogger().removeAllAppenders();
+		} catch (Exception e) {
+			// at least we tried
+		}
+	}
+
 }
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 52ac35b..4e8d9fa 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
@@ -41,7 +41,9 @@ import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.factory.InstanceUtil;
 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;
@@ -109,6 +111,20 @@ 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.  
+     * 
+     * @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) {
 
@@ -148,8 +164,9 @@ public class DataNucleusApplicationComponents implements ApplicationScopedCompon
                 datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_TABLES, "true"); // but have DN do everything else...
                 datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_COLUMNS, "true");
                 datanucleusProps.put(PropertyNames.PROPERTY_SCHEMA_AUTOCREATE_CONSTRAINTS, "true");
-
-                persistenceManagerFactory = JDOHelper.getPersistenceManagerFactory(datanucleusProps);
+                
+                persistenceManagerFactory = JDOHelper
+                		.getPersistenceManagerFactory(datanucleusProps,	IsisContext.getClassLoader() );
                 createSchema(persistenceManagerFactory, persistableClassNameSet, datanucleusProps);
 
             } else {
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 8a32edb..ad72168 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
@@ -53,18 +53,12 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
 
     private static final Logger LOG = LoggerFactory.getLogger(PersistenceSessionFactory.class);
 
-    //region > constructor
-
     private final IsisConfigurationDefault configuration;
 
     public PersistenceSessionFactory(final IsisConfigurationDefault isisConfiguration) {
         this.configuration = isisConfiguration;
     }
 
-    //endregion
-
-    //region > init, createDataNucleusApplicationComponents
-
     public static final String JDO_OBJECTSTORE_CONFIG_PREFIX = "isis.persistor.datanucleus";  // specific to the JDO objectstore
     public static final String DATANUCLEUS_CONFIG_PREFIX = "isis.persistor.datanucleus.impl"; // reserved for datanucleus' own config props
 
@@ -93,7 +87,7 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
             final Map<String, String> datanucleusProps = dataNucleusConfig.asMap();
             addDataNucleusPropertiesIfRequired(datanucleusProps);
 
-            final RegisterEntities registerEntities = new RegisterEntities(configuration.asMap(), specificationLoader);
+            final RegisterEntities registerEntities = new RegisterEntities(specificationLoader);
             final Set<String> classesToBePersisted = registerEntities.getEntityTypes();
 
             applicationComponents = new DataNucleusApplicationComponents(jdoObjectstoreConfig, specificationLoader,
@@ -159,19 +153,18 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
             props.put(key, value);
         }
     }
-    //endregion
 
-    //region > shutdown
     @Programmatic
     public final void shutdown() {
-        // no-op
+        if(!isInitialized()) {
+            return;
+        }
+    	if(applicationComponents != null) {
+    		applicationComponents.shutdown();
+            applicationComponents = null;
+    	}
     }
 
-    //endregion
-
-
-    //region > createPersistenceSession
-
     /**
      * Called by {@link org.apache.isis.core.runtime.system.session.IsisSessionFactory#openSession(AuthenticationSession)}.
      */
@@ -190,12 +183,6 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
                 fixturesInstalledFlag);
     }
 
-
-
-    //endregion
-
-    //region > FixturesInstalledFlag impl
-
     private Boolean fixturesInstalled;
 
     @Programmatic
@@ -210,7 +197,5 @@ public class PersistenceSessionFactory implements ApplicationScopedComponent, Fi
         this.fixturesInstalled = fixturesInstalled;
     }
 
-    //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..bc19b81
--- /dev/null
+++ b/core/runtime/src/main/java/org/apache/isis/objectstore/jdo/datanucleus/DataNucleusLifeCycleHelper.java
@@ -0,0 +1,57 @@
+/*
+ *  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 javax.jdo.PersistenceManagerFactory;
+
+import org.apache.isis.core.runtime.system.context.IsisContext;
+import org.datanucleus.enhancer.EnhancementHelper;
+
+/**
+ * 
+ * Purges any state associated with DataNucleus.
+ * <br/><br/>
+ * (requires datanucleus-core 4 or 5 >= 5.1.5)
+ * 
+ * @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();
+			
+			persistenceManagerFactory.close();
+			
+			// for info, on why we do this see
+			// https://github.com/datanucleus/datanucleus-core/issues/272
+			EnhancementHelper.getInstance().unregisterClasses(cl);
+			
+		} catch (Exception e) {
+			// ignore, since it only affects re-deploy-ability, which is nice to have but not critical
+		}
+
+	}
+
+}
diff --git a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication.java b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication.java
index 03426ac..ecf8d4c 100644
--- a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication.java
+++ b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication.java
@@ -30,52 +30,7 @@ import java.util.UUID;
 import java.util.concurrent.Callable;
 import java.util.concurrent.Future;
 
-import com.google.common.base.Function;
-import com.google.common.base.Joiner;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import com.google.common.io.Resources;
-import com.google.inject.Guice;
-import com.google.inject.Injector;
-import com.google.inject.Module;
-
-import org.apache.wicket.Application;
-import org.apache.wicket.Component;
-import org.apache.wicket.ConverterLocator;
-import org.apache.wicket.IConverterLocator;
-import org.apache.wicket.Page;
-import org.apache.wicket.RuntimeConfigurationType;
-import org.apache.wicket.SharedResources;
-import org.apache.wicket.ajax.AjaxRequestTarget;
-import org.apache.wicket.authentication.IAuthenticationStrategy;
-import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy;
-import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication;
-import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
-import org.apache.wicket.core.request.mapper.MountedMapper;
-import org.apache.wicket.devutils.debugbar.DebugBar;
-import org.apache.wicket.devutils.debugbar.InspectorDebugPanel;
-import org.apache.wicket.devutils.debugbar.PageSizeDebugPanel;
-import org.apache.wicket.devutils.debugbar.SessionSizeDebugPanel;
-import org.apache.wicket.devutils.debugbar.VersionDebugContributor;
-import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
-import org.apache.wicket.guice.GuiceComponentInjector;
-import org.apache.wicket.markup.head.IHeaderResponse;
-import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
-import org.apache.wicket.markup.html.IHeaderContributor;
-import org.apache.wicket.markup.html.IHeaderResponseDecorator;
-import org.apache.wicket.markup.html.WebPage;
-import org.apache.wicket.request.cycle.IRequestCycleListener;
-import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
-import org.apache.wicket.request.cycle.RequestCycleListenerCollection;
-import org.apache.wicket.request.resource.CssResourceReference;
-import org.apache.wicket.settings.DebugSettings;
-import org.apache.wicket.settings.RequestCycleSettings;
-import org.apache.wicket.util.IContextProvider;
-import org.apache.wicket.util.time.Duration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.wicketstuff.select2.ApplicationSettings;
-
+import org.apache.isis.applib.internal.context._Context;
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.config.IsisConfigurationDefault;
@@ -91,6 +46,7 @@ import org.apache.isis.core.runtime.system.session.IsisSessionFactory;
 import org.apache.isis.core.runtime.threadpool.ThreadPoolSupport;
 import org.apache.isis.core.webapp.IsisWebAppBootstrapper;
 import org.apache.isis.core.webapp.WebAppConstants;
+import org.apache.isis.core.webapp.WebAppContextSupport;
 import org.apache.isis.schema.utils.ChangesDtoUtils;
 import org.apache.isis.schema.utils.CommandDtoUtils;
 import org.apache.isis.schema.utils.InteractionDtoUtils;
@@ -119,6 +75,51 @@ import org.apache.isis.viewer.wicket.viewer.integration.wicket.ConverterForObjec
 import org.apache.isis.viewer.wicket.viewer.integration.wicket.ConverterForObjectAdapterMemento;
 import org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis;
 import org.apache.isis.viewer.wicket.viewer.settings.IsisResourceSettings;
+import org.apache.wicket.Application;
+import org.apache.wicket.Component;
+import org.apache.wicket.ConverterLocator;
+import org.apache.wicket.IConverterLocator;
+import org.apache.wicket.Page;
+import org.apache.wicket.RuntimeConfigurationType;
+import org.apache.wicket.SharedResources;
+import org.apache.wicket.ajax.AjaxRequestTarget;
+import org.apache.wicket.authentication.IAuthenticationStrategy;
+import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy;
+import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication;
+import org.apache.wicket.authroles.authentication.AuthenticatedWebSession;
+import org.apache.wicket.core.request.mapper.MountedMapper;
+import org.apache.wicket.devutils.debugbar.DebugBar;
+import org.apache.wicket.devutils.debugbar.InspectorDebugPanel;
+import org.apache.wicket.devutils.debugbar.PageSizeDebugPanel;
+import org.apache.wicket.devutils.debugbar.SessionSizeDebugPanel;
+import org.apache.wicket.devutils.debugbar.VersionDebugContributor;
+import org.apache.wicket.devutils.diskstore.DebugDiskDataStore;
+import org.apache.wicket.guice.GuiceComponentInjector;
+import org.apache.wicket.markup.head.IHeaderResponse;
+import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
+import org.apache.wicket.markup.html.IHeaderContributor;
+import org.apache.wicket.markup.html.IHeaderResponseDecorator;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.request.cycle.IRequestCycleListener;
+import org.apache.wicket.request.cycle.PageRequestHandlerTracker;
+import org.apache.wicket.request.cycle.RequestCycleListenerCollection;
+import org.apache.wicket.request.resource.CssResourceReference;
+import org.apache.wicket.settings.DebugSettings;
+import org.apache.wicket.settings.RequestCycleSettings;
+import org.apache.wicket.util.IContextProvider;
+import org.apache.wicket.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wicketstuff.select2.ApplicationSettings;
+
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Resources;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.Module;
 
 import de.agilecoders.wicket.core.Bootstrap;
 import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.BootstrapBaseBehavior;
@@ -333,6 +334,8 @@ public class IsisWicketApplication
         List<Future<Object>> futures = null;
         try {
             super.init();
+            
+            _Context.putSingleton(ClassLoader.class, this.getClass().getClassLoader());
 
             futures = startBackgroundInitializationThreads();
 
@@ -346,6 +349,9 @@ public class IsisWicketApplication
 
             final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder();
             isisConfigurationBuilder.addDefaultConfigurationResourcesAndPrimers();
+            
+            final String webappContextPath = getServletContext().getContextPath();
+            isisConfigurationBuilder.add(WebAppContextSupport.WEB_APP_CONTEXT_PATH, webappContextPath);
 
             final IsisConfigurationDefault configuration = isisConfigurationBuilder.getConfiguration();
 
@@ -859,6 +865,7 @@ public class IsisWicketApplication
             }
             getServletContext().setAttribute(WebAppConstants.ISIS_SESSION_FACTORY, null);
             super.onDestroy();
+            IsisContext.clear();
         } catch(final RuntimeException ex) {
             // symmetry with #init()
             LOG.error("Failed to destroy", ex);

-- 
To stop receiving notification emails like this one, please contact
ahuber@apache.org.