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 <ahu...@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 ahu...@apache.org.