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/08/30 12:39:00 UTC

[isis] 04/04: ISIS-1895: splitting web-module initialization into 2 phases

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 9e1eecadcb83e5a28b18b0db95584e1ebf5505af
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Aug 30 14:37:38 2018 +0200

    ISIS-1895: splitting web-module initialization into 2 phases
    
    to allow modules to share information on the ServletContext
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-1895
---
 .../isis/commons/internal/resources/_Resource.java |  16 +++
 .../services/swagger/SwaggerServiceDefault.java    |  19 +---
 .../core/webapp/IsisWebAppContextListener.java     |  29 +++--
 .../apache/isis/core/webapp/modules/WebModule.java | 117 ++++++++++++++++++++-
 ...et.java => WebModule_FallbackBootstrapper.java} |  13 +--
 .../modules/WebModule_LogOnExceptionLogger.java    |  33 ++----
 .../core/webapp/modules/WebModule_RestEasy.java    |  37 ++++++-
 .../isis/core/webapp/modules/WebModule_Shiro.java  |   6 +-
 .../isis/core/webapp/modules/WebModule_Wicket.java |  39 +++++--
 .../wicket/viewer/IsisWicketApplication.java       |  95 ++++++++---------
 10 files changed, 283 insertions(+), 121 deletions(-)

diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resource.java b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resource.java
index 32be534..c36b667 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resource.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/resources/_Resource.java
@@ -19,6 +19,7 @@
 
 package org.apache.isis.commons.internal.resources;
 
+import static org.apache.isis.commons.internal.base._With.ifPresentElseThrow;
 import static org.apache.isis.commons.internal.base._With.requires;
 
 import java.io.IOException;
@@ -128,6 +129,19 @@ public final class _Resource {
     }
 
     /**
+     * 
+     * @return restful-path resource as stored previously by {@link #putRestfulPath(String)}
+     *  or throws if resource not found
+     * @throws NullPointerException if resource not found
+     */
+    public final static String getRestfulPathOrThrow() {
+        return ifPresentElseThrow(getRestfulPathIfAny(), 
+                ()->new NullPointerException(
+                        "Could not find BasePath for the REST Service "
+                        + "config value on the context."));
+    }
+    
+    /**
      * Stores the {@code restfulPath} as an application scoped resource-object.
      * @param restfulPath
      * @throws IllegalArgumentException if the restfulPath is empty or is the root-path.
@@ -163,5 +177,7 @@ public final class _Resource {
         return name;
     }
 
+    
+
 
 }
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 f4b0ec1..4462911 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,16 +22,16 @@ 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.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.resources._Resource;
 import org.apache.isis.core.metamodel.services.swagger.internal.SwaggerSpecGenerator;
 import org.apache.isis.core.metamodel.specloader.SpecificationLoader;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 @DomainService(
         nature = NatureOfService.DOMAIN,
@@ -42,22 +42,13 @@ public class SwaggerServiceDefault implements SwaggerService {
     @SuppressWarnings("unused")
     private final static Logger LOG = LoggerFactory.getLogger(SwaggerServiceDefault.class);
 
-    public static final String KEY_RESTFUL_BASE_PATH = "isis.services.swagger.restfulBasePath";
-    public static final String KEY_RESTFUL_BASE_PATH_DEFAULT = "/restful";
-
     private String basePath;
 
     @PostConstruct
     public void init(final Map<String,String> properties) {
 
-        // ----------------------------------------------------------------------------------------------------------
-        // TODO [ahuber] this initialization must be done once before accessing _Resource.prependContextPathIfPresent
-        // could be done anywhere during bootstrapping
-        final String restfulPath =
-                _NullSafe.getOrDefault(properties, KEY_RESTFUL_BASE_PATH, KEY_RESTFUL_BASE_PATH_DEFAULT);
-        _Resource.putRestfulPath(restfulPath);
-        // ----------------------------------------------------------------------------------------------------------
-
+        final String restfulPath = _Resource.getRestfulPathOrThrow(); 
+        
         this.basePath = _Resource.prependContextPathIfPresent(restfulPath);
     }
 
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 8991c64..54808ae 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
@@ -19,9 +19,12 @@
 package org.apache.isis.core.webapp;
 
 import static org.apache.isis.commons.internal.base._With.acceptIfPresent;
+import static org.apache.isis.commons.internal.context._Context.setDefaultClassLoader;
+import static org.apache.isis.commons.internal.resources._Resource.putContextPathIfPresent;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextEvent;
@@ -54,16 +57,31 @@ public class IsisWebAppContextListener implements ServletContextListener {
     
     private final List<ServletContextListener> activeListeners = new ArrayList<>();
 
-    // -- IMPLEMENTATION
+    // -- INTERFACE IMPLEMENTATION
     
     @Override
     public void contextInitialized(ServletContextEvent event) {
         
-        final ServletContext context = event.getServletContext();
+        final ServletContext servletContext = event.getServletContext();
         
-        WebModule.discoverWebModules()
-        .filter(module->module.isAvailable(context)) // filter those WebModules that are applicable
-        .forEach(module->addListener(context, module));
+        setDefaultClassLoader(this.getClass().getClassLoader(), true);
+        
+        // setting up context specific properties before bootstrapping
+        putContextPathIfPresent(servletContext.getContextPath());
+
+        // phase 1
+        final List<WebModule> webModules =
+                 WebModule.discoverWebModules()
+                 .peek(module->module.prepare(servletContext)) // prepare context
+                 .collect(Collectors.toList());
+
+        // put the list of viewer names "isis.viewers" into a context parameter
+        WebModule.ContextUtil.commitViewers(servletContext); 
+        
+        // phase 2
+        webModules.stream()
+        .filter(module->module.isApplicable(servletContext)) // filter those WebModules that are applicable
+        .forEach(module->addListener(servletContext, module));
         
         activeListeners.forEach(listener->listener.contextInitialized(event));
     }
@@ -92,6 +110,5 @@ public class IsisWebAppContextListener implements ServletContextListener {
             LOG.error(String.format("Failed to shutdown WebListener '%s'.", listener.getClass().getName()), e);
         }
     }
-    
 
 }
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 d24d0f4..3f4178e 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
@@ -18,6 +18,10 @@
  */
 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;
 
 import javax.servlet.Filter;
@@ -27,6 +31,9 @@ import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebListener;
 
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.core.commons.config.IsisConfigurationDefault;
+import org.apache.isis.core.webapp.IsisWebAppBootstrapper;
 import org.apache.isis.core.webapp.IsisWebAppContextListener;
 
 /**
@@ -48,6 +55,16 @@ public interface WebModule {
     public String getName();
     
     /**
+     * Before initializing any WebModule we call each WebModule's prepare method 
+     * to allow for a WebModule to leave information useful for other modules on 
+     * the shared ServletContext.
+     * 
+     * @param ctx ServletContext
+     */
+    default public void prepare(ServletContext ctx) {}
+    
+    /**
+     * Expected to be called after all WebModules had a chance to prepare the ServletContext.
      * Sets this WebModule's {@link Filter}s, {@link Servlet}s or {@link WebListener}s 
      * up and registers them with the given {@link ServletContext} {@code ctx}.
      * @param ctx ServletContext
@@ -55,10 +72,11 @@ public interface WebModule {
     public ServletContextListener init(ServletContext ctx) throws ServletException;
     
     /**
+     * Expected to be called after all WebModules had a chance to prepare the ServletContext.
      * @param ctx ServletContext
-     * @return whether this module is available (on the class-path) and also applicable 
+     * @return whether this module is applicable/usable
      */
-    public boolean isAvailable(ServletContext ctx);
+    public boolean isApplicable(ServletContext ctx);
     
     // -- DISCOVERY 
     
@@ -75,10 +93,103 @@ public interface WebModule {
         return Stream.of(
                 new WebModule_Shiro(), // filters before all others
                 new WebModule_Wicket(),
-                new WebModule_NoWicket(), // not required if the Wicket viewer is in use
+                new WebModule_FallbackBootstrapper(), // not required if the Wicket module is in use
                 new WebModule_RestEasy(), // default REST provider
                 new WebModule_LogOnExceptionLogger() // log any logon exceptions, filters after all others
                 );
     }
     
+    // -- UTILITY
+    
+    static final class ContextUtil {
+        
+        /**
+         * Tell other modules that a bootstrapper is present.
+         * @param ctx
+         * @param bootstrapper
+         */
+        public static void registerBootstrapper(ServletContext ctx, WebModule bootstrapper) {
+            ctx.setAttribute("isis.bootstrapper", bootstrapper);    
+        }
+        
+        /**
+         * @param ctx
+         * @return whether this context has a bootstrapper registered
+         */
+        public static boolean hasBootstrapper(ServletContext ctx) {
+            return ctx.getAttribute("isis.bootstrapper")!=null;    
+        }
+        
+        /**
+         *  Adds to the list of viewer names "isis.viewers"
+         * @param ctx
+         * @param viewerName
+         */
+        public static void registerViewer(ServletContext ctx, String viewerName) {
+            String viewers = (String) ctx.getAttribute("isis.viewers");
+            if(isEmpty(viewers)) {
+                viewers = viewerName;
+            } else {
+                viewers = viewers + "," + viewerName;
+            }
+            ctx.setAttribute("isis.viewers", viewers);
+        }
+        
+        /**
+         * Puts the list of viewer names "isis.viewers" into a context parameter.
+         * @param ctx
+         */
+        public static void commitViewers(ServletContext ctx) {
+            String viewers = (String) ctx.getAttribute("isis.viewers");
+            if(!isEmpty(viewers)) {
+                ctx.setInitParameter("isis.viewers", viewers);    
+            }
+        }
+        
+        /**
+         *  Adds to the list of protected path names "isis.protected"
+         * @param ctx
+         * @param path
+         */
+        public static void registerProtectedPath(ServletContext ctx, String path) {
+            String list = (String) ctx.getAttribute("isis.protected");
+            if(isEmpty(list)) {
+                list = path;
+            } else {
+                list = list + "," + path;
+            }
+            ctx.setAttribute("isis.protected", list);
+        }
+        
+        /**
+         * Streams the protected path names "isis.protected".
+         * @param ctx
+         */
+        public static Stream<String> streamProtectedPaths(ServletContext ctx) {
+            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 IsisConfigurationDefault webXmlConfig = 
+                    IsisWebAppBootstrapper.lookupIsisConfigurationBuilder(ctx)
+                    .getConfiguration();
+            
+            return ifPresentElse(webXmlConfig.getProperty(key), defaultValue);
+        }
+        
+        
+    }
+    
+    
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_NoWicket.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_FallbackBootstrapper.java
similarity index 81%
rename from core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_NoWicket.java
rename to core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_FallbackBootstrapper.java
index aea5509..e686b16 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_NoWicket.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_FallbackBootstrapper.java
@@ -28,23 +28,24 @@ import org.apache.isis.core.webapp.IsisWebAppBootstrapper;
  * Package private mixin for WebModule implementing WebModule.
  * @since 2.0.0
  */
-final class WebModule_NoWicket implements WebModule  {
+final class WebModule_FallbackBootstrapper implements WebModule  {
     
     @Override
     public String getName() {
-        return "No-Wicket";
+        return "Fallback Bootstrapper";
     }
 
     @Override
     public ServletContextListener init(ServletContext ctx) throws ServletException {
         ctx.setInitParameter("deploymentType", "SERVER_PROTOTYPE");
-        ctx.setInitParameter("isis.viewers", "restfulobjects");
         return ctx.createListener(IsisWebAppBootstrapper.class);
     }
 
     @Override
-    public boolean isAvailable(ServletContext ctx) {
-        // not required if the Wicket viewer is in use.
-        return !new WebModule_Wicket().isAvailable(ctx);
+    public boolean isApplicable(ServletContext ctx) {
+        // not required if another bootstrapper module is on the context 
+        // e.g. the Wicket module
+        return ctx.getAttribute("bootstrapper")!=null;
     }
+    
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_LogOnExceptionLogger.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_LogOnExceptionLogger.java
index 3f18e7e..e92cc70 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_LogOnExceptionLogger.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_LogOnExceptionLogger.java
@@ -19,12 +19,8 @@
 package org.apache.isis.core.webapp.modules;
 
 import static org.apache.isis.commons.internal.base._Casts.uncheckedCast;
-import static org.apache.isis.commons.internal.base._Strings.prefix;
-import static org.apache.isis.commons.internal.base._Strings.suffix;
-import static org.apache.isis.commons.internal.base._With.ifPresentElse;
 import static org.apache.isis.commons.internal.context._Context.getDefaultClassLoader;
 import static org.apache.isis.commons.internal.exceptions._Exceptions.unexpectedCodeReach;
-import static org.apache.isis.commons.internal.resources._Resource.getRestfulPathIfAny;
 
 import javax.servlet.Filter;
 import javax.servlet.FilterRegistration.Dynamic;
@@ -32,6 +28,8 @@ import javax.servlet.ServletContext;
 import javax.servlet.ServletContextListener;
 import javax.servlet.ServletException;
 
+import org.apache.isis.commons.internal.collections._Arrays;
+
 /**
  * Package private mixin for WebModule implementing WebModule.
  * @since 2.0.0
@@ -66,13 +64,13 @@ final class WebModule_LogOnExceptionLogger implements WebModule  {
             return null; // filter was already registered somewhere else (eg web.xml)
         }
 
-        reg.addMappingForUrlPatterns(null, true, getUrlPatterns() ); // filter is forced last
+        reg.addMappingForUrlPatterns(null, true, getProtectedUrlPatterns(ctx) ); // filter is forced last
 
         return null; // does not provide a listener
     }
 
     @Override
-    public boolean isAvailable(ServletContext ctx) {
+    public boolean isApplicable(ServletContext ctx) {
         try {
             getDefaultClassLoader().loadClass(LOGONLOGGER_FILTER_CLASS_NAME);
             return true;
@@ -83,29 +81,12 @@ final class WebModule_LogOnExceptionLogger implements WebModule  {
 
     // -- HELPER
     
-    private String[] getUrlPatterns() {
-        return new String[] {
-                getRestfulUrlPattern(),
-                getWicketUrlPattern()
-            };
+    private String[] getProtectedUrlPatterns(ServletContext ctx) {
+        return ContextUtil.streamProtectedPaths(ctx)
+        .collect(_Arrays.toArray(String.class));
     }
 
-    private String getWicketUrlPattern() {
-        final String wicketPath = getWicketPath();
-        final String wicketPathEnclosedWithSlashes = suffix(prefix(wicketPath, "/"), "/");
-        return wicketPathEnclosedWithSlashes + "*";
-    }
 
-    private String getWicketPath() {
-        // TODO[ISIS-1895] should be provided by a config value
-        return "wicket";
-    }
-
-    private String getRestfulUrlPattern() {
-        final String restfulPath = ifPresentElse(getRestfulPathIfAny(), "restful");
-        final String restfulPathEnclosedWithSlashes = suffix(prefix(restfulPath, "/"), "/");
-        return restfulPathEnclosedWithSlashes + "*";
-    }
     
 
 }
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 3ad268c..7069461 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
@@ -18,13 +18,13 @@
  */
 package org.apache.isis.core.webapp.modules;
 
+import static java.util.Objects.requireNonNull;
 import static org.apache.isis.commons.internal.base._Casts.uncheckedCast;
 import static org.apache.isis.commons.internal.base._Strings.prefix;
 import static org.apache.isis.commons.internal.base._Strings.suffix;
-import static org.apache.isis.commons.internal.base._With.ifPresentElse;
 import static org.apache.isis.commons.internal.context._Context.getDefaultClassLoader;
 import static org.apache.isis.commons.internal.exceptions._Exceptions.unexpectedCodeReach;
-import static org.apache.isis.commons.internal.resources._Resource.getRestfulPathIfAny;
+import static org.apache.isis.commons.internal.resources._Resource.putRestfulPath;
 
 import javax.servlet.ServletContext;
 import javax.servlet.ServletContextListener;
@@ -37,15 +37,40 @@ import javax.servlet.ServletException;
  */
 final class WebModule_RestEasy implements WebModule  {
     
+    public static final String KEY_RESTFUL_BASE_PATH = "isis.viewer.restfulobjects.basePath";
+    public static final String KEY_RESTFUL_BASE_PATH_DEFAULT = "/restful";
+    
     private final static String RESTEASY_BOOTSTRAPPER = 
             "org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap";
     
     private final static String RESTEASY_DISPATCHER = "RestfulObjectsRestEasyDispatcher";
     
+    String restfulPathConfigValue;
+    
     @Override
     public String getName() {
         return "RestEasy";
     }
+    
+    @Override
+    public void prepare(ServletContext ctx) {
+        
+        if(!isApplicable(ctx)) {
+            return;
+        }
+        
+        // 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); 
+                
+        putRestfulPath(restfulPath);
+        
+        this.restfulPathConfigValue = restfulPath; // store locally for reuse
+        
+        // register this module as a viewer
+        ContextUtil.registerViewer(ctx, "restfulobjects");
+        ContextUtil.registerProtectedPath(ctx, suffix(prefix(restfulPath, "/"), "/") + "*" );
+    }
 
     @Override
     public ServletContextListener init(ServletContext ctx) throws ServletException {
@@ -72,7 +97,7 @@ final class WebModule_RestEasy implements WebModule  {
     }
 
     @Override
-    public boolean isAvailable(ServletContext ctx) {
+    public boolean isApplicable(ServletContext ctx) {
         try {
             getDefaultClassLoader().loadClass(RESTEASY_BOOTSTRAPPER);
             return true;
@@ -88,10 +113,12 @@ final class WebModule_RestEasy implements WebModule  {
     }
 
     private String getRestfulPath() {
-        final String restfulPath = ifPresentElse(getRestfulPathIfAny(), "restful");
-        final String restfulPathEnclosedWithSlashes = suffix(prefix(restfulPath, "/"), "/");
+        requireNonNull(restfulPathConfigValue, "This web-module needs to be prepared first.");
+        final String restfulPathEnclosedWithSlashes = suffix(prefix(restfulPathConfigValue, "/"), "/");
         return restfulPathEnclosedWithSlashes;
     }
     
     
+
+    
 }
diff --git a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Shiro.java b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Shiro.java
index 48cdadd..87aa79a 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Shiro.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/webapp/modules/WebModule_Shiro.java
@@ -46,12 +46,10 @@ final class WebModule_Shiro implements WebModule  {
     public String getName() {
         return "Shiro";
     }
-
+    
     @Override
     public ServletContextListener init(ServletContext ctx) throws ServletException {
         
-        ctx.setInitParameter("isis.viewers", "wicket,restfulobjects");
-
         final Filter filter;
         try {
             final Class<?> filterClass = getDefaultClassLoader().loadClass(SHIRO_FILTER_CLASS_NAME);
@@ -81,7 +79,7 @@ final class WebModule_Shiro implements WebModule  {
     }
 
     @Override
-    public boolean isAvailable(ServletContext ctx) {
+    public boolean isApplicable(ServletContext ctx) {
         try {
             getDefaultClassLoader().loadClass(SHIRO_LISTENER_CLASS_NAME);
             return true;
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 b406a97..8241dca 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
@@ -18,6 +18,7 @@
  */
 package org.apache.isis.core.webapp.modules;
 
+import static java.util.Objects.requireNonNull;
 import static org.apache.isis.commons.internal.base._Casts.uncheckedCast;
 import static org.apache.isis.commons.internal.base._Strings.prefix;
 import static org.apache.isis.commons.internal.base._Strings.suffix;
@@ -40,11 +41,37 @@ final class WebModule_Wicket implements WebModule  {
             "org.apache.wicket.protocol.http.WicketFilter";
 
     private final static String WICKET_FILTER_NAME = "WicketFilter";
+    
+    private String pathConfigValue;
+    private String modeConfigValue;
+    private String appConfigValue;
 
     @Override
     public String getName() {
         return "Wicket";
     }
+    
+    @Override
+    public void prepare(ServletContext ctx) {
+        
+        if(!isApplicable(ctx)) {
+            return;
+        }
+        
+        pathConfigValue = 
+                ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.basePath", "/wicket");
+        
+        modeConfigValue = 
+                ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.mode", "deployment");
+        
+        appConfigValue = 
+                ContextUtil.getConfigOrDefault(ctx, "isis.viewer.wicket.app", 
+                        "org.apache.isis.viewer.wicket.viewer.IsisWicketApplication");
+        
+        ContextUtil.registerBootstrapper(ctx, this);
+        ContextUtil.registerViewer(ctx, "wicket");
+        ContextUtil.registerProtectedPath(ctx, suffix(prefix(pathConfigValue, "/"), "/") + "*" );
+    }
 
     @Override
     public ServletContextListener init(ServletContext ctx) throws ServletException {
@@ -75,7 +102,7 @@ final class WebModule_Wicket implements WebModule  {
     }
 
     @Override
-    public boolean isAvailable(ServletContext ctx) {
+    public boolean isApplicable(ServletContext ctx) {
         try {
             getDefaultClassLoader().loadClass(WICKET_FILTER_CLASS_NAME);
             return true;
@@ -94,18 +121,16 @@ final class WebModule_Wicket implements WebModule  {
     }
 
     private String getWicketPath() {
-        // TODO[ISIS-1895] should be provided by a config value
-        return "wicket";
+        return requireNonNull(pathConfigValue, "This web-module needs to be prepared first.");
     }
 
     private String getWicketMode() {
-        // TODO[ISIS-1895] should be provided by a config value
-        return "development";
+        return requireNonNull(modeConfigValue, "This web-module needs to be prepared first.");
     }
 
     private String getApplicationClassName() {
-        // TODO[ISIS-1895] should be provided by a config value
-        return "domainapp.webapp.DomainApplication";
+        return requireNonNull(appConfigValue, "This web-module needs to be prepared first.");
+
     }
 
 }
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 115ca87..ec1d105 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
@@ -32,9 +32,51 @@ import java.util.concurrent.Executors;
 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;
+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.ResourceAggregator;
+import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
+import org.apache.wicket.markup.html.IHeaderContributor;
+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.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.wicketstuff.select2.ApplicationSettings;
+
 import org.apache.isis.commons.internal.collections._Lists;
-import org.apache.isis.commons.internal.context._Context;
-import org.apache.isis.commons.internal.resources._Resource;
 import org.apache.isis.core.commons.authentication.AuthenticationSession;
 import org.apache.isis.core.commons.config.IsisConfiguration;
 import org.apache.isis.core.commons.config.IsisConfigurationDefault;
@@ -78,49 +120,6 @@ 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.ajax.AjaxRequestTarget.IListener;
-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.ResourceAggregator;
-import org.apache.wicket.markup.head.filter.JavaScriptFilteredIntoFooterHeaderResponse;
-import org.apache.wicket.markup.html.IHeaderContributor;
-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.time.Duration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.wicketstuff.select2.ApplicationSettings;
-
-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 de.agilecoders.wicket.core.Bootstrap;
 import de.agilecoders.wicket.core.markup.html.bootstrap.behavior.BootstrapBaseBehavior;
@@ -335,8 +334,6 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi
         try {
             super.init();
 
-            _Context.setDefaultClassLoader(this.getClass().getClassLoader(), true);
-
             futures = startBackgroundInitializationThreads();
 
             String isisConfigDir = getServletContext().getInitParameter("isis.config.dir");
@@ -350,8 +347,6 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi
             final IsisConfigurationBuilder isisConfigurationBuilder = obtainConfigBuilder();
             isisConfigurationBuilder.addDefaultConfigurationResourcesAndPrimers();
 
-            _Resource.putContextPathIfPresent(getServletContext().getContextPath());
-
             final IsisConfigurationDefault configuration = isisConfigurationBuilder.getConfiguration();
 
             DeploymentTypeWicketAbstract deploymentType =
@@ -863,7 +858,7 @@ implements ComponentFactoryRegistryAccessor, PageClassRegistryAccessor, WicketVi
                     final IsisConfigurationDefault isisConfiguration) {
                 return new IsisInjectModule(deploymentCategory, isisConfiguration);
             }
-
+            
             // //////////////////////////////////////