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 51ab3e1e28faf8d63d4a8dcf9285309f677f6595 Author: Andi Huber <ahu...@apache.org> AuthorDate: Fri Aug 31 14:37:46 2018 +0200 ISIS-1895: optimizations and cleanup cleaning up IsisWicketApplication reuse configuration instances when applicable Task-Url: https://issues.apache.org/jira/browse/ISIS-1895 --- .../configbuilder/IsisConfigurationBuilder.java | 6 +- .../isis/core/webapp/IsisWebAppConfigProvider.java | 35 +- .../core/webapp/IsisWebAppContextListener.java | 12 +- .../apache/isis/core/webapp/modules/WebModule.java | 21 - .../core/webapp/modules/WebModule_RestEasy.java | 4 +- .../isis/core/webapp/modules/WebModule_Wicket.java | 11 +- .../wicket/viewer/IsisWicketApplication.java | 635 +++++++++------------ .../viewer/IsisWicketApplication_Experimental.java | 107 ++++ 8 files changed, 427 insertions(+), 404 deletions(-) diff --git a/core/metamodel/src/main/java/org/apache/isis/core/commons/configbuilder/IsisConfigurationBuilder.java b/core/metamodel/src/main/java/org/apache/isis/core/commons/configbuilder/IsisConfigurationBuilder.java index 9b3e5c2..a8790a0 100644 --- a/core/metamodel/src/main/java/org/apache/isis/core/commons/configbuilder/IsisConfigurationBuilder.java +++ b/core/metamodel/src/main/java/org/apache/isis/core/commons/configbuilder/IsisConfigurationBuilder.java @@ -366,7 +366,9 @@ public final class IsisConfigurationBuilder { } } - + public String peekAt(String key) { + return configuration.getString(key); + } // -- dumpResourcesToLog, toString @@ -397,6 +399,4 @@ public final class IsisConfigurationBuilder { return toString.toString(this); } - - } diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppConfigProvider.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppConfigProvider.java index c36a9ae..5b22087 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppConfigProvider.java +++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppConfigProvider.java @@ -18,6 +18,8 @@ */ package org.apache.isis.core.webapp; +import static org.apache.isis.commons.internal.base._With.ifPresentElse; +import static org.apache.isis.commons.internal.base._With.requires; import static org.apache.isis.commons.internal.context._Context.getOrThrow; import static org.apache.isis.commons.internal.context._Context.putSingleton; @@ -26,7 +28,6 @@ import javax.servlet.ServletContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.isis.core.commons.config.IsisConfiguration; import org.apache.isis.core.commons.configbuilder.IsisConfigurationBuilder; import org.apache.isis.core.commons.resource.ResourceStreamSourceContextLoaderClassPath; import org.apache.isis.core.commons.resource.ResourceStreamSourceCurrentClassClassPath; @@ -53,28 +54,20 @@ public class IsisWebAppConfigProvider { /** * If available reuses the IsisConfigurationBuilder instance that is already on the ServletContext or - * creates a new one. + * creates a new one and puts it on the ServletContext. * * @param servletContext * @return */ - public IsisConfigurationBuilder getConfigurationBuilder(final ServletContext servletContext) { + public synchronized IsisConfigurationBuilder getConfigurationBuilder(final ServletContext servletContext) { IsisConfigurationBuilder isisConfigurationBuilder = (IsisConfigurationBuilder) servletContext.getAttribute(WebAppConstants.CONFIGURATION_BUILDER_KEY); if(isisConfigurationBuilder == null) { isisConfigurationBuilder = newIsisConfigurationBuilder(servletContext); + servletContext.setAttribute(WebAppConstants.CONFIGURATION_BUILDER_KEY, isisConfigurationBuilder); } return isisConfigurationBuilder; } - - /** - * Shortcut for {@code getConfigurationBuilder(servletContext).peekConfiguration()} - * @param servletContext - * @return a configuration copy - */ - public IsisConfiguration peekConfiguration(final ServletContext servletContext) { - return getConfigurationBuilder(servletContext).peekConfiguration(); - } /** * Returns a new IsisConfigurationBuilder populated with all currently available configuration values. @@ -88,6 +81,24 @@ public class IsisWebAppConfigProvider { return isisConfigurationBuilder; } + // -- PEEKING + + /** + * Try to fetch the value from config stored under {@code key} else fallback to {@code defaultValue} + * @param servletContext + * @param key + * @param defaultValue + * @return non-null + */ + public String peekAtOrDefault(ServletContext servletContext, String key, String defaultValue) { + requires(servletContext, "servletContext"); + requires(key, "key"); + requires(defaultValue, "defaultValue"); + + final String configValue = getConfigurationBuilder(servletContext).peekAt(key); + return ifPresentElse(configValue, defaultValue); + } + // -- LOOKUP /** diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppContextListener.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppContextListener.java index 0f41369..a0bc0ee 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppContextListener.java +++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/IsisWebAppContextListener.java @@ -61,16 +61,16 @@ public class IsisWebAppContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent event) { - + final ServletContext servletContext = event.getServletContext(); + LOG.info("=== PHASE 1 === Setting up ServletContext parameters"); + setDefaultClassLoader(this.getClass().getClassLoader(), true); + putContextPathIfPresent(servletContext.getContextPath()); final IsisWebAppConfigProvider configProvider = new IsisWebAppConfigProvider(); IsisWebAppConfigProvider.register(configProvider); - - // phase 1 - setting up context specific properties before bootstrapping - putContextPathIfPresent(servletContext.getContextPath()); final List<WebModule> webModules = WebModule.discoverWebModules() @@ -82,13 +82,15 @@ public class IsisWebAppContextListener implements ServletContextListener { // invalidate config such that next IsisConfigurationBuilder that gets obtained is reinitialized configProvider.invalidate(servletContext); - // phase 2 - initializing the ServletContext + LOG.info("=== PHASE 2 === Initializing the ServletContext"); webModules.stream() .filter(module->module.isApplicable(servletContext)) // filter those WebModules that are applicable .forEach(module->addListener(servletContext, module)); activeListeners.forEach(listener->listener.contextInitialized(event)); + + LOG.info("=== DONE === ServletContext initialized."); } @Override diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule.java index a837fe4..eab3bbd 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule.java +++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule.java @@ -19,8 +19,6 @@ package org.apache.isis.core.webapp.modules; import static org.apache.isis.commons.internal.base._Strings.isEmpty; -import static org.apache.isis.commons.internal.base._With.ifPresentElse; -import static org.apache.isis.commons.internal.base._With.requires; import java.util.stream.Stream; @@ -32,8 +30,6 @@ import javax.servlet.ServletException; import javax.servlet.annotation.WebListener; import org.apache.isis.commons.internal.base._Strings; -import org.apache.isis.core.commons.config.IsisConfiguration; -import org.apache.isis.core.webapp.IsisWebAppConfigProvider; import org.apache.isis.core.webapp.IsisWebAppContextListener; /** @@ -169,23 +165,6 @@ public interface WebModule { final String list = (String) ctx.getAttribute("isis.protected"); return _Strings.splitThenStream(list, ","); } - - /** - * Try to fetch the value from config stored under {@code key} else fallback to {@code defaultValue} - * @param ctx - * @param key - * @param defaultValue - * @return non-null - */ - public static String getConfigOrDefault(ServletContext ctx, String key, String defaultValue) { - requires(key, "key"); - requires(defaultValue, "defaultValue"); - - final IsisConfiguration webXmlConfig = - IsisWebAppConfigProvider.getInstance().peekConfiguration(ctx); - - return ifPresentElse(webXmlConfig.getString(key), defaultValue); - } } diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_RestEasy.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_RestEasy.java index 462b6ad..de06abd 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_RestEasy.java +++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_RestEasy.java @@ -32,6 +32,7 @@ import javax.servlet.ServletContextListener; import javax.servlet.ServletException; import org.apache.isis.core.webapp.IsisSessionFilter; +import org.apache.isis.core.webapp.IsisWebAppConfigProvider; /** * Package private mixin for WebModule implementing WebModule. @@ -64,7 +65,8 @@ final class WebModule_RestEasy implements WebModule { // try to fetch restfulPath from config else fallback to default final String restfulPath = - ContextUtil.getConfigOrDefault(ctx, KEY_RESTFUL_BASE_PATH, KEY_RESTFUL_BASE_PATH_DEFAULT); + IsisWebAppConfigProvider.getInstance() + .peekAtOrDefault(ctx, KEY_RESTFUL_BASE_PATH, KEY_RESTFUL_BASE_PATH_DEFAULT); putRestfulPath(restfulPath); diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Wicket.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Wicket.java index b2076b5..3e5b2ad 100644 --- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Wicket.java +++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Wicket.java @@ -27,6 +27,9 @@ import static org.apache.isis.commons.internal.exceptions._Exceptions.unexpected import javax.servlet.Filter; import javax.servlet.FilterRegistration.Dynamic; + +import org.apache.isis.core.webapp.IsisWebAppConfigProvider; + import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; @@ -58,14 +61,16 @@ final class WebModule_Wicket implements WebModule { return; } + final IsisWebAppConfigProvider configProvider = IsisWebAppConfigProvider.getInstance(); + pathConfigValue = - ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.basePath", "/wicket"); + configProvider.peekAtOrDefault(ctx, "isis.viewer.wicket.basePath", "/wicket"); modeConfigValue = - ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.mode", "deployment"); + configProvider.peekAtOrDefault(ctx, "isis.viewer.wicket.mode", "deployment"); appConfigValue = - ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.app", + configProvider.peekAtOrDefault(ctx, "isis.viewer.wicket.app", "org.apache.isis.viewer.wicket.viewer.IsisWicketApplication"); ContextUtil.registerBootstrapper(ctx, this); 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 69c8ecf..8a0c9a0 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 @@ -19,11 +19,12 @@ package org.apache.isis.viewer.wicket.viewer; +import static java.util.Objects.requireNonNull; + import java.io.IOException; import java.nio.charset.Charset; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.UUID; @@ -33,21 +34,18 @@ import java.util.concurrent.Future; import java.util.function.Function; import com.google.common.base.Joiner; -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.ajax.AjaxRequestTarget.IListener; import org.apache.wicket.authentication.IAuthenticationStrategy; import org.apache.wicket.authentication.strategy.DefaultAuthenticationStrategy; import org.apache.wicket.authroles.authentication.AuthenticatedWebApplication; @@ -187,7 +185,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi private final IsisLoggingConfigurer loggingConfigurer = new IsisLoggingConfigurer(); /** - * Convenience locator, downcasts inherited functionality. + * Convenience locator, down-casts inherited functionality. */ public static IsisWicketApplication get() { return (IsisWicketApplication) AuthenticatedWebApplication.get(); @@ -229,20 +227,13 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. */ @com.google.inject.Inject - private IsisConfiguration configuration; - - /** - * {@link com.google.inject.Inject Inject}ed when {@link #init() initialized}. - */ - @com.google.inject.Inject private DeploymentCategory deploymentCategory; @com.google.inject.Inject private WicketViewerSettings settings; - - private boolean determiningConfigurationType; - private DeploymentTypeWicketAbstract deploymentType; + private IsisConfigurationDefault isisConfiguration; + private final IsisWicketApplication_Experimental experimental; // ///////////////////////////////////////////////// @@ -250,6 +241,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi // ///////////////////////////////////////////////// public IsisWicketApplication() { + experimental = new IsisWicketApplication_Experimental(this); } @@ -266,11 +258,30 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi setResourceSettings(new IsisResourceSettings(this)); // this doesn't seem to accomplish anything - // addListenerToStripRemovedComponentsFromAjaxTargetResponse(); + // experimental.addListenerToStripRemovedComponentsFromAjaxTargetResponse(); + // --- prepare the configuration prior to init() + + isisConfiguration = prepareConfiguration(); + deploymentType = determineDeploymentType(isisConfiguration); + super.internalInit(); } + + private IsisConfigurationDefault prepareConfiguration() { + + final String isisConfigDir = getServletContext().getInitParameter("isis.config.dir"); + configureLogging(isisConfigDir); + + final IsisConfigurationBuilder isisConfigurationBuilder = IsisWebAppConfigProvider.getInstance() + .getConfigurationBuilder(getServletContext()); + isisConfigurationBuilder.addDefaultConfigurationResourcesAndPrimers(); + + final IsisConfigurationDefault configuration = isisConfigurationBuilder.getConfiguration(); + + return configuration; + } private static AjaxRequestTarget decorate(final AjaxRequestTarget ajaxRequestTarget) { ajaxRequestTarget.registerRespondListener( new TargetRespondListenerToResetQueryResultCache() ); @@ -285,44 +296,6 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi return application; } - // idea here is to avoid XmlPartialPageUpdate spitting out warnings, eg: - // - // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = copyLink]]' with markupid: 'copyLink94c' not rendered because it was already removed from page - // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[SimpleClipboardModalWindow [Component id = simpleClipboardModalWindow]]' with markupid: 'simpleClipboardModalWindow94e' not rendered because it was already removed from page - // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link951' not rendered because it was already removed from page - // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link952' not rendered because it was already removed from page - // 13:08:36,655 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = clearBookmarkLink]]' with markupid: 'clearBookmarkLink953' not rendered because it was already removed from page - // - // however, doesn't seem to work (even though the provided map is mutable). - // must be some other sort of side-effect which causes the enqueued component(s) to be removed from page between - // this listener firing and XmlPartialPageUpdate actually attempting to render the change components - // - private boolean addListenerToStripRemovedComponentsFromAjaxTargetResponse() { - return getAjaxRequestTargetListeners().add(new IListener(){ - - @Override - public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target) { - - System.out.println("====================================="); - System.out.println("=== on before respond"); - System.out.println("map="+map); - System.out.println("====================================="); - System.out.println("=== removals"); - map.entrySet().removeIf(entry->{ - final Component component = entry.getValue(); - final Page page = component.findParent(Page.class); - if(page==null) { - System.out.println("id: "+entry.getKey()+": page="+page); - } - return page==null; - }); - - System.out.println("====================================="); - - } - }); - } - /** * Initializes the application; in particular, bootstrapping the Isis * backend, and initializing the {@link ComponentFactoryRegistry} to be used @@ -336,22 +309,12 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi futures = startBackgroundInitializationThreads(); - String isisConfigDir = getServletContext().getInitParameter("isis.config.dir"); - - configureLogging(isisConfigDir); + final IsisConfigurationDefault configuration = requireNonNull(isisConfiguration, + "Configuration must be prepared prior to init()."); getRequestCycleSettings().setRenderStrategy(RequestCycleSettings.RenderStrategy.REDIRECT_TO_RENDER); - getResourceSettings().setParentFolderPlaceholder("$up$"); - final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder(); - isisConfigurationBuilder.addDefaultConfigurationResourcesAndPrimers(); - - final IsisConfigurationDefault configuration = isisConfigurationBuilder.getConfiguration(); - - DeploymentTypeWicketAbstract deploymentType = - determineDeploymentType(configuration.getString("isis.deploymentType")); - RequestCycleListenerCollection requestCycleListeners = getRequestCycleListeners(); IRequestCycleListener requestCycleListenerForIsis = newWebRequestCycleForIsis(); requestCycleListeners.add(requestCycleListenerForIsis); @@ -387,7 +350,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi filterJavascriptContributions(); configureWicketSourcePluginIfNecessary(); - + // TODO ISIS-987 Either make the API better (no direct access to the map) or use DB records int maxEntries = 1000; setMetaData(AccountConfirmationMap.KEY, new AccountConfirmationMap(maxEntries, Duration.days(1))); @@ -439,7 +402,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi ThreadPoolSupport.join(futures); } } - + protected List<Future<Object>> startBackgroundInitializationThreads() { return ThreadPoolSupport.getInstance().invokeAll(_Lists.<Callable<Object>>of( @@ -501,7 +464,9 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi } protected void configureWicketSourcePluginIfNecessary() { - if(isWicketSourcePluginEnabled(this.configuration)) { + final IsisConfigurationDefault configuration = requireNonNull(isisConfiguration, + "Configuration must be prepared prior to init()."); + if(isWicketSourcePluginEnabled(configuration)) { configureWicketSourcePlugin(); } } @@ -587,52 +552,25 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi /** * Made protected visibility for easy (informal) pluggability. */ - protected void determineDeploymentTypeIfRequired() { - if(deploymentType != null) { - return; - } - - determiningConfigurationType = true; - try { - final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder(); - - final IsisConfiguration configuration = isisConfigurationBuilder.peekConfiguration(); - String deploymentTypeFromConfig = configuration.getString("isis.deploymentType"); - deploymentType = determineDeploymentType(deploymentTypeFromConfig); - } finally { - determiningConfigurationType = false; - } - } - - /** - * Made protected visibility for easy (informal) pluggability. - */ - protected DeploymentTypeWicketAbstract determineDeploymentType(String deploymentTypeFromConfig) { - final DeploymentTypeWicketAbstract prototype = new WicketServerPrototype(); - final DeploymentTypeWicketAbstract deployment = new WicketServer(); - - if(deploymentTypeFromConfig != null) { - final DeploymentType deploymentType = DeploymentType.lookup(deploymentTypeFromConfig); - return !deploymentType.getDeploymentCategory().isProduction() - ? prototype - : deployment; + protected DeploymentTypeWicketAbstract determineDeploymentType(IsisConfigurationDefault configuration) { + + //TODO[ahuber] we need to get rid of this static processing, + // once wicket is only one of multiple viewer options + final String deploymentTypeConfigValue = configuration.getString("isis.deploymentType"); + final String wicketModeConfigValue = configuration.getString("isis.viewer.wicket.mode"); + + final boolean isPrototype; + + if(wicketModeConfigValue!=null) { + isPrototype = "development".equalsIgnoreCase(wicketModeConfigValue); + } else if(deploymentTypeConfigValue!=null) { + final DeploymentType deploymentType = DeploymentType.lookup(deploymentTypeConfigValue); + isPrototype = !deploymentType.getDeploymentCategory().isProduction(); } else { - return usesDevelopmentConfig() - ? prototype - : deployment; + isPrototype = false; // defaulting to production } - } - - - // ////////////////////////////////////// - - private IsisConfigurationBuilder isisConfigurationBuilder; - - protected IsisConfigurationBuilder obtainConfigBuilder() { - return isisConfigurationBuilder != null - ? isisConfigurationBuilder - : (isisConfigurationBuilder = IsisWebAppConfigProvider.getInstance() - .getConfigurationBuilder(getServletContext())); + + return isPrototype ? new WicketServerPrototype() : new WicketServer(); } // ////////////////////////////////////// @@ -650,16 +588,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi * Made protected visibility for easy (informal) pluggability. */ protected void buildCssBundle() { - // get the css for all components built by component factories - final Set<CssResourceReference> references = cssResourceReferencesForAllComponents(); - - // some additional special cases. - addSpecialCasesToCssBundle(references); - - // create the bundle - getResourceBundles().addCssBundle( - IsisWicketApplication.class, "isis-wicket-viewer-bundle.css", - references.toArray(new CssResourceReference[]{})); + experimental.buildCssBundle(); } /** @@ -698,280 +627,268 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi Collections.<CssResourceReference>emptyList(); }; + // ////////////////////////////////////// - protected Set<CssResourceReference> cssResourceReferencesForAllComponents() { - // TODO mgrigorov: ISIS-537 temporary disabled to not mess up with Bootstrap styles - // Collection<ComponentFactory> componentFactories = getComponentFactoryRegistry().listComponentFactories(); - return Sets.newLinkedHashSet( - // Iterables.concat( - // Iterables.transform( - // componentFactories, - // getCssResourceReferences)) - ); - } + /** + * filters Javascript header contributions so rendered to bottom of page. + * + * <p> + * Factored out for easy (informal) pluggability. + * </p> + */ + protected void filterJavascriptContributions() { - // ////////////////////////////////////// - - /** - * filters Javascript header contributions so rendered to bottom of page. - * - * <p> - * Factored out for easy (informal) pluggability. - * </p> - */ - protected void filterJavascriptContributions() { - - setHeaderResponseDecorator(response -> { - return new ResourceAggregator(new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS")); - }); - - //[ahuber] no longer supported since wicket 8 - // setHeaderResponseDecorator(new IHeaderResponseDecorator() - // { - // @Override - // public IHeaderResponse decorate(IHeaderResponse response) - // { - // // use this header resource decorator to load all JavaScript resources in the page - // // footer (after </body>) - // return new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"); - // } - // }); - } + setHeaderResponseDecorator(response -> { + return new ResourceAggregator(new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS")); + }); - // ////////////////////////////////////// + //[ahuber] no longer supported since wicket 8 + // setHeaderResponseDecorator(new IHeaderResponseDecorator() + // { + // @Override + // public IHeaderResponse decorate(IHeaderResponse response) + // { + // // use this header resource decorator to load all JavaScript resources in the page + // // footer (after </body>) + // return new JavaScriptFilteredIntoFooterHeaderResponse(response, "footerJS"); + // } + // }); + } - /** - * Map entity and action to provide prettier URLs. - * - * <p> - * Factored out for easy (informal) pluggability. - * </p> - */ - protected void mountPages() { + // ////////////////////////////////////// - mountPage("/signin", PageType.SIGN_IN); - mountPage("/signup", PageType.SIGN_UP); - mountPage("/signup/verify", PageType.SIGN_UP_VERIFY); - mountPage("/password/reset", PageType.PASSWORD_RESET); + /** + * Map entity and action to provide prettier URLs. + * + * <p> + * Factored out for easy (informal) pluggability. + * </p> + */ + protected void mountPages() { - mountPage("/entity/#{objectOid}", PageType.ENTITY); + mountPage("/signin", PageType.SIGN_IN); + mountPage("/signup", PageType.SIGN_UP); + mountPage("/signup/verify", PageType.SIGN_UP_VERIFY); + mountPage("/password/reset", PageType.PASSWORD_RESET); - // nb: action mount cannot contain {actionArgs}, because the default - // parameters encoder doesn't seem to be able to handle multiple args - mountPage("/action/${objectOid}/${actionOwningSpec}/${actionId}/${actionType}", PageType.ACTION_PROMPT); + mountPage("/entity/#{objectOid}", PageType.ENTITY); - mountPage("/logout", WicketLogoutPage.class); - } + // nb: action mount cannot contain {actionArgs}, because the default + // parameters encoder doesn't seem to be able to handle multiple args + mountPage("/action/${objectOid}/${actionOwningSpec}/${actionId}/${actionType}", PageType.ACTION_PROMPT); - protected void mountPage(final String mountPath, final PageType pageType) { - final Class<? extends Page> pageClass = this.pageClassRegistry.getPageClass(pageType); - mount(new MountedMapper(mountPath, pageClass)); - } + mountPage("/logout", WicketLogoutPage.class); + } + protected void mountPage(final String mountPath, final PageType pageType) { + final Class<? extends Page> pageClass = this.pageClassRegistry.getPageClass(pageType); + mount(new MountedMapper(mountPath, pageClass)); + } - // ////////////////////////////////////// - private void logError(String validationError) { - log(validationError); - } + // ////////////////////////////////////// - private static void logBanner() { - String msg = "################################################ ISIS METAMODEL VALIDATION ERRORS ################################################################"; - log(msg); - } + private void logError(String validationError) { + log(validationError); + } - private static void log(String msg) { - System.err.println(msg); - LOG.error(msg); - } + private static void logBanner() { + String msg = "################################################ ISIS METAMODEL VALIDATION ERRORS ################################################################"; + log(msg); + } - // ////////////////////////////////////// - - /** - * Whether Wicket tags should be stripped from the markup, as specified by configuration settings.. - * - * <p> - * If the <tt>isis.viewer.wicket.stripWicketTags</tt> is set, then this is used, otherwise the default is to strip - * the tags because they may break some CSS rules. - */ - private boolean determineStripWicketTags(IsisConfiguration configuration) { - final boolean strip = configuration.getBoolean(STRIP_WICKET_TAGS_KEY, STRIP_WICKET_TAGS_DEFAULT); - return strip; - } + private static void log(String msg) { + System.err.println(msg); + LOG.error(msg); + } - // ////////////////////////////////////// - - /** - * Whether the Ajax debug should be shown, as specified by configuration settings. - * - * <p> - * If the <tt>isis.viewer.wicket.ajaxDebugMode</tt> is set, then this is used, otherwise the default is to disable. - */ - private boolean determineAjaxDebugModeEnabled(IsisConfiguration configuration) { - final boolean debugModeEnabled = configuration.getBoolean(AJAX_DEBUG_MODE_KEY, AJAX_DEBUG_MODE_DEFAULT); - return debugModeEnabled; - } + // ////////////////////////////////////// - /** - * Whether the Wicket source plugin should be enabled, as specified by configuration settings. - * - * <p> - * If the <tt>isis.viewer.wicket.wicketSourcePlugin</tt> is set, then this is used, otherwise the default is to disable. - */ - private boolean isWicketSourcePluginEnabled(IsisConfiguration configuration) { - final boolean pluginEnabled = configuration.getBoolean(WICKET_SOURCE_PLUGIN_KEY, WICKET_SOURCE_PLUGIN_DEFAULT); - return pluginEnabled; - } + /** + * Whether Wicket tags should be stripped from the markup, as specified by configuration settings.. + * + * <p> + * If the <tt>isis.viewer.wicket.stripWicketTags</tt> is set, then this is used, otherwise the default is to strip + * the tags because they may break some CSS rules. + */ + private boolean determineStripWicketTags(IsisConfiguration configuration) { + final boolean strip = configuration.getBoolean(STRIP_WICKET_TAGS_KEY, STRIP_WICKET_TAGS_DEFAULT); + return strip; + } - // ////////////////////////////////////// + // ////////////////////////////////////// - @Override - protected void onDestroy() { - try { - if (isisSessionFactory != null) { - isisSessionFactory.destroyServicesAndShutdown(); - } - getServletContext().setAttribute(WebAppConstants.ISIS_SESSION_FACTORY, null); - super.onDestroy(); - IsisContext.clear(); - } catch(final RuntimeException ex) { - // symmetry with #init() - LOG.error("Failed to destroy", ex); - throw ex; - } - } + /** + * Whether the Ajax debug should be shown, as specified by configuration settings. + * + * <p> + * If the <tt>isis.viewer.wicket.ajaxDebugMode</tt> is set, then this is used, otherwise the default is to disable. + */ + private boolean determineAjaxDebugModeEnabled(IsisConfiguration configuration) { + final boolean debugModeEnabled = configuration.getBoolean(AJAX_DEBUG_MODE_KEY, AJAX_DEBUG_MODE_DEFAULT); + return debugModeEnabled; + } - // ////////////////////////////////////// + /** + * Whether the Wicket source plugin should be enabled, as specified by configuration settings. + * + * <p> + * If the <tt>isis.viewer.wicket.wicketSourcePlugin</tt> is set, then this is used, otherwise the default is to disable. + */ + private boolean isWicketSourcePluginEnabled(IsisConfiguration configuration) { + final boolean pluginEnabled = configuration.getBoolean(WICKET_SOURCE_PLUGIN_KEY, WICKET_SOURCE_PLUGIN_DEFAULT); + return pluginEnabled; + } - @Override - public RuntimeConfigurationType getConfigurationType() { - if(determiningConfigurationType) { - // avoiding an infinite loop; have already passed through here once before - // this time around, just delegate to web-inf - return super.getConfigurationType(); - } - determineDeploymentTypeIfRequired(); - return deploymentType.getConfigurationType(); - } + // ////////////////////////////////////// - protected IsisInjectModule newIsisModule( - final DeploymentCategory deploymentCategory, - final IsisConfigurationDefault isisConfiguration) { - return new IsisInjectModule(deploymentCategory, isisConfiguration); - } - - // ////////////////////////////////////// + @Override + protected void onDestroy() { + try { + if (isisSessionFactory != null) { + isisSessionFactory.destroyServicesAndShutdown(); + } + getServletContext().setAttribute(WebAppConstants.ISIS_SESSION_FACTORY, null); + super.onDestroy(); + IsisContext.clear(); + } catch(final RuntimeException ex) { + // symmetry with #init() + LOG.error("Failed to destroy", ex); + throw ex; + } + } + // ////////////////////////////////////// - protected void initWicketComponentInjection(final Injector injector) { - getComponentInstantiationListeners().add(new GuiceComponentInjector(this, injector, false)); - } + private DeploymentTypeWicketAbstract deploymentType; + + @Override + public RuntimeConfigurationType getConfigurationType() { + requireNonNull(deploymentType, + "Applications needs to be prepared before accessing this field."); + return deploymentType.getConfigurationType(); + } + + // ////////////////////////////////////// - // ///////////////////////////////////////////////// - // Wicket Hooks - // ///////////////////////////////////////////////// - - /** - * Installs a {@link AuthenticatedWebSessionForIsis custom implementation} - * of Wicket's own {@link AuthenticatedWebSession}, effectively associating - * the Wicket session with the Isis's equivalent session object. - * - * <p> - * In general, it shouldn't be necessary to override this method. - */ - @Override - protected Class<? extends AuthenticatedWebSession> getWebSessionClass() { - return AuthenticatedWebSessionForIsis.class; - } + protected IsisInjectModule newIsisModule( + final DeploymentCategory deploymentCategory, + final IsisConfigurationDefault isisConfiguration) { + return new IsisInjectModule(deploymentCategory, isisConfiguration); + } - /** - * Installs a {@link ConverterLocator} preconfigured with a number of - * implementations to support Isis specific objects. - */ - @Override - protected IConverterLocator newConverterLocator() { - final ConverterLocator converterLocator = new ConverterLocator(); - converterLocator.set(ObjectAdapter.class, new ConverterForObjectAdapter()); - converterLocator.set(ObjectAdapterMemento.class, new ConverterForObjectAdapterMemento()); - return converterLocator; - } + // ////////////////////////////////////// - // ///////////////////////////////////////////////// - // Component Factories - // ///////////////////////////////////////////////// - @Override - public final ComponentFactoryRegistry getComponentFactoryRegistry() { - return componentFactoryRegistry; - } + protected void initWicketComponentInjection(final Injector injector) { + getComponentInstantiationListeners().add(new GuiceComponentInjector(this, injector, false)); + } - // ///////////////////////////////////////////////// - // Page Registry - // ///////////////////////////////////////////////// + // ///////////////////////////////////////////////// + // Wicket Hooks + // ///////////////////////////////////////////////// - /** - * Access to other page types. - * - * <p> - * Non-final only for testing purposes; should not typically be overridden. - */ - @Override - public PageClassRegistry getPageClassRegistry() { - return pageClassRegistry; - } + /** + * Installs a {@link AuthenticatedWebSessionForIsis custom implementation} + * of Wicket's own {@link AuthenticatedWebSession}, effectively associating + * the Wicket session with the Isis's equivalent session object. + * + * <p> + * In general, it shouldn't be necessary to override this method. + */ + @Override + protected Class<? extends AuthenticatedWebSession> getWebSessionClass() { + return AuthenticatedWebSessionForIsis.class; + } - /** - * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. - */ - @Override - public Class<? extends Page> getHomePage() { - return getPageClassRegistry().getPageClass(PageType.HOME); - } + /** + * Installs a {@link ConverterLocator} preconfigured with a number of + * implementations to support Isis specific objects. + */ + @Override + protected IConverterLocator newConverterLocator() { + final ConverterLocator converterLocator = new ConverterLocator(); + converterLocator.set(ObjectAdapter.class, new ConverterForObjectAdapter()); + converterLocator.set(ObjectAdapterMemento.class, new ConverterForObjectAdapterMemento()); + return converterLocator; + } - /** - * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. - */ - @SuppressWarnings("unchecked") - @Override - public Class<? extends WebPage> getSignInPageClass() { - return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_IN); - } + // ///////////////////////////////////////////////// + // Component Factories + // ///////////////////////////////////////////////// - /** - * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. - */ - @SuppressWarnings("unchecked") - public Class<? extends WebPage> getSignUpPageClass() { - return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP); - } + @Override + public final ComponentFactoryRegistry getComponentFactoryRegistry() { + return componentFactoryRegistry; + } - /** - * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. - */ - @SuppressWarnings("unchecked") - public Class<? extends WebPage> getSignUpVerifyPageClass() { - return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP_VERIFY); - } + // ///////////////////////////////////////////////// + // Page Registry + // ///////////////////////////////////////////////// - /** - * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. - */ - @SuppressWarnings("unchecked") - public Class<? extends WebPage> getForgotPasswordPageClass() { - return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.PASSWORD_RESET); - } + /** + * Access to other page types. + * + * <p> + * Non-final only for testing purposes; should not typically be overridden. + */ + @Override + public PageClassRegistry getPageClassRegistry() { + return pageClassRegistry; + } - public AuthenticationSession getAuthenticationSession() { - return isisSessionFactory.getCurrentSession().getAuthenticationSession(); - } + /** + * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. + */ + @Override + public Class<? extends Page> getHomePage() { + return getPageClassRegistry().getPageClass(PageType.HOME); + } - public DeploymentCategory getDeploymentCategory() { - return deploymentCategory; - } + /** + * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. + */ + @SuppressWarnings("unchecked") + @Override + public Class<? extends WebPage> getSignInPageClass() { + return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_IN); + } - @Override - public WicketViewerSettings getSettings() { - return settings; - } + /** + * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. + */ + @SuppressWarnings("unchecked") + public Class<? extends WebPage> getSignUpPageClass() { + return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP); + } + + /** + * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. + */ + @SuppressWarnings("unchecked") + public Class<? extends WebPage> getSignUpVerifyPageClass() { + return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.SIGN_UP_VERIFY); + } + + /** + * Delegates to the {@link #getPageClassRegistry() PageClassRegistry}. + */ + @SuppressWarnings("unchecked") + public Class<? extends WebPage> getForgotPasswordPageClass() { + return (Class<? extends WebPage>) getPageClassRegistry().getPageClass(PageType.PASSWORD_RESET); + } + + public AuthenticationSession getAuthenticationSession() { + return isisSessionFactory.getCurrentSession().getAuthenticationSession(); + } + + public DeploymentCategory getDeploymentCategory() { + return deploymentCategory; + } + + @Override + public WicketViewerSettings getSettings() { + return settings; + } } diff --git a/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication_Experimental.java b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication_Experimental.java new file mode 100644 index 0000000..c0f0449 --- /dev/null +++ b/core/viewer-wicket-impl/src/main/java/org/apache/isis/viewer/wicket/viewer/IsisWicketApplication_Experimental.java @@ -0,0 +1,107 @@ +/* + * 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.viewer.wicket.viewer; + +import java.util.Map; +import java.util.Set; + +import com.google.common.collect.Sets; + +import org.apache.wicket.Component; +import org.apache.wicket.Page; +import org.apache.wicket.ajax.AjaxRequestTarget; +import org.apache.wicket.ajax.AjaxRequestTarget.IListener; +import org.apache.wicket.request.resource.CssResourceReference; + +/** + * package private mixin for IsisWicketApplication; + * to move experimental code out of IsisWicketApplication + */ +final class IsisWicketApplication_Experimental { + + private final IsisWicketApplication holder; + + IsisWicketApplication_Experimental(IsisWicketApplication holder) { + this.holder = holder; + } + + // idea here is to avoid XmlPartialPageUpdate spitting out warnings, eg: + // + // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = copyLink]]' with markupid: 'copyLink94c' not rendered because it was already removed from page + // 13:08:36,642 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[SimpleClipboardModalWindow [Component id = simpleClipboardModalWindow]]' with markupid: 'simpleClipboardModalWindow94e' not rendered because it was already removed from page + // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link951' not rendered because it was already removed from page + // 13:08:36,643 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxFallbackLink [Component id = link]]' with markupid: 'link952' not rendered because it was already removed from page + // 13:08:36,655 [XmlPartialPageUpdate qtp1988859660-18 WARN ] Component '[AjaxLink [Component id = clearBookmarkLink]]' with markupid: 'clearBookmarkLink953' not rendered because it was already removed from page + // + // however, doesn't seem to work (even though the provided map is mutable). + // must be some other sort of side-effect which causes the enqueued component(s) to be removed from page between + // this listener firing and XmlPartialPageUpdate actually attempting to render the change components + // + boolean addListenerToStripRemovedComponentsFromAjaxTargetResponse() { + + return holder.getAjaxRequestTargetListeners().add(new IListener(){ + + @Override + public void onBeforeRespond(Map<String, Component> map, AjaxRequestTarget target) { + + System.out.println("====================================="); + System.out.println("=== on before respond"); + System.out.println("map="+map); + System.out.println("====================================="); + System.out.println("=== removals"); + map.entrySet().removeIf(entry->{ + final Component component = entry.getValue(); + final Page page = component.findParent(Page.class); + if(page==null) { + System.out.println("id: "+entry.getKey()+": page="+page); + } + return page==null; + }); + + System.out.println("====================================="); + + } + }); + } + + private Set<CssResourceReference> cssResourceReferencesForAllComponents() { + // TODO mgrigorov: ISIS-537 temporary disabled to not mess up with Bootstrap styles + // Collection<ComponentFactory> componentFactories = getComponentFactoryRegistry().listComponentFactories(); + return Sets.newLinkedHashSet( + // Iterables.concat( + // Iterables.transform( + // componentFactories, + // getCssResourceReferences)) + ); + } + + void buildCssBundle() { + // get the css for all components built by component factories + final Set<CssResourceReference> references = cssResourceReferencesForAllComponents(); + + // some additional special cases. + holder.addSpecialCasesToCssBundle(references); + + // create the bundle + holder.getResourceBundles().addCssBundle( + IsisWicketApplication.class, "isis-wicket-viewer-bundle.css", + references.toArray(new CssResourceReference[]{})); + } + +}