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/28 16:32:27 UTC

[isis] branch master updated: ISIS-1901: Swagger-UI: Replace index.html with index.template.html

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


The following commit(s) were added to refs/heads/master by this push:
     new bb7eec7  ISIS-1901: Swagger-UI: Replace index.html with index.template.html
bb7eec7 is described below

commit bb7eec7d8d0fe4850537878d2a1628e2b9af3338
Author: Andi Huber <ah...@apache.org>
AuthorDate: Tue Aug 28 18:28:15 2018 +0200

    ISIS-1901: Swagger-UI: Replace index.html with index.template.html
    
    Also let the ResourceServlet handle resources that end with
    '.template.html' specifically:
    
    These templates may include placeholders ${placholder-name}.
    
    These are replaced with actual values by the ResourceServlet as
    configured.
    
    In this particular case we make the swagger-ui aware of the actual
    restful resource path configuration.
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-1901
---
 .../services/swagger/SwaggerServiceMenu.java       |  2 +-
 .../isis/commons/internal/base/_Strings.java       |  9 +++
 .../internal/base/_Strings_KeyValuePair.java       | 60 ++++++++++++++
 .../isis/core/commons/lang/ResourceUtil.java       | 23 +++++-
 .../core/webapp/content/ResourceCachingFilter.java | 63 ++-------------
 .../isis/core/webapp/content/ResourceServlet.java  | 91 +++++++++++++++-------
 .../ResourceServlet_HtmlTemplateVariables.java     | 64 +++++++++++++++
 .../swagger-ui/{index.html => index.template.html} |  8 +-
 core/plugins/eventbus-axon/pom.xml                 |  4 +
 9 files changed, 231 insertions(+), 93 deletions(-)

diff --git a/core/applib/src/main/java/org/apache/isis/applib/services/swagger/SwaggerServiceMenu.java b/core/applib/src/main/java/org/apache/isis/applib/services/swagger/SwaggerServiceMenu.java
index 655a755..c840300 100644
--- a/core/applib/src/main/java/org/apache/isis/applib/services/swagger/SwaggerServiceMenu.java
+++ b/core/applib/src/main/java/org/apache/isis/applib/services/swagger/SwaggerServiceMenu.java
@@ -55,7 +55,7 @@ public class SwaggerServiceMenu {
             )
     @MemberOrder(sequence="500.600.1")
     public LocalResourcePath openSwaggerUi() {
-        return new LocalResourcePath("/swagger-ui/index.html");
+        return new LocalResourcePath("/swagger-ui/index.template.html");
     }
 
     public static class OpenRestApiDomainEvent extends ActionDomainEvent { private static final long serialVersionUID = 1L; }
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
index 273d44a..ea764f7 100644
--- a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings.java
@@ -24,6 +24,7 @@ import static org.apache.isis.commons.internal.base._With.mapIfPresentElse;
 import static org.apache.isis.commons.internal.base._With.requires;
 
 import java.nio.charset.Charset;
+import java.util.Map;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.function.UnaryOperator;
@@ -61,6 +62,14 @@ public final class _Strings {
      * (a duplicate of in {@link _Constants.emptyStringArray} )
      */
     public final static String[] emptyArray = new String[0];
+    
+    // -- PAIR OF STRINGS
+    
+    public static interface KeyValuePair extends Map.Entry<String, String> { }
+    
+    public static KeyValuePair pair(final String key, final String value){
+        return _Strings_KeyValuePair.of(key, value);
+    }
 
     // -- BASIC PREDICATES
 
diff --git a/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings_KeyValuePair.java b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings_KeyValuePair.java
new file mode 100644
index 0000000..7696a48
--- /dev/null
+++ b/core/commons/src/main/java/org/apache/isis/commons/internal/base/_Strings_KeyValuePair.java
@@ -0,0 +1,60 @@
+/*
+ *  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.commons.internal.base;
+
+import static org.apache.isis.commons.internal.exceptions._Exceptions.notImplemented;
+
+import org.apache.isis.commons.internal.base._Strings.KeyValuePair;
+
+/**
+ *
+ * package private mixin for utility class {@link _Strings}
+ *
+ */
+final class _Strings_KeyValuePair implements _Strings.KeyValuePair {
+
+    static KeyValuePair of(String key, String value) {
+        return new _Strings_KeyValuePair(key, value);
+    }
+    
+    private final String key;
+    private final String value;
+
+    private _Strings_KeyValuePair(String key, String value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    @Override
+    public String getKey() {
+        return key;
+    }
+
+    @Override
+    public String getValue() {
+        return value;
+    }
+
+    @Override
+    public String setValue(String value) {
+        throw notImplemented();
+    }
+
+}
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ResourceUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ResourceUtil.java
index c92eb89..3b31c98 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ResourceUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/commons/lang/ResourceUtil.java
@@ -19,13 +19,14 @@
 
 package org.apache.isis.core.commons.lang;
 
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.InputStream;
 
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.isis.commons.internal.context._Context;
 
-/**
- * Adapted from Ibatis Common, now with some additional guava stuff.
- */
 public class ResourceUtil {
 
     private ResourceUtil(){}
@@ -62,5 +63,21 @@ public class ResourceUtil {
             return null;
         }
     }
+    
+    /**
+     * @param request
+     * @return real-path resource from file-system, if any
+     */
+    public static InputStream getResourceAsStream(final HttpServletRequest request) {
+        final String realPath = request.getSession().getServletContext().getRealPath(request.getServletPath());
+        if (realPath == null) {
+            return null;
+        }
+        try {
+            return new FileInputStream(realPath);
+        } catch (final FileNotFoundException e) {
+            return null;
+        }
+    }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceCachingFilter.java b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceCachingFilter.java
index f95bd2a..ceb7758 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceCachingFilter.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceCachingFilter.java
@@ -33,67 +33,18 @@ import javax.servlet.FilterConfig;
 import javax.servlet.ServletException;
 import javax.servlet.ServletRequest;
 import javax.servlet.ServletResponse;
+import javax.servlet.annotation.WebFilter;
+import javax.servlet.annotation.WebInitParam;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.isis.commons.internal.exceptions._Exceptions.FluentException;
 
-/**
- * Adapted from {@link http
- * ://www.digitalsanctuary.com/tech-blog/java/jboss/setting
- * -cache-headers-from-jboss.html}
- *
- * <p>
- * Usage:
- *
- * <pre>
- * &lt;filter>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;filter-class>org.apache.isis.core.webapp.content.ResourceCachingFilter&lt;/filter-class>
- *   &lt;init-param>
- *     &lt;param-name>CacheTime&lt;/param-name>
- *     &lt;param-value>86400&lt;/param-value>
- *   &lt;/init-param>
- * &lt;/filter>
- * ...
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.css&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.png&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.jpg&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.jpeg&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.gif&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.svg&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.js&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.html&lt;/url-pattern>
- * &lt;/filter-mapping>
- * &lt;filter-mapping>
- *   &lt;filter-name>ResourceCachingFilter&lt;/filter-name>
- *   &lt;url-pattern>*.swf&lt;/url-pattern>
- * &lt;/filter-mapping>
- * </pre>
- */
+@WebFilter(
+        initParams = { @WebInitParam(name = "CacheTime", value = "86400") }, 
+        urlPatterns = { 
+                "*.css", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.svg", "*.js", "*.html", "*.swf" }
+)
 public class ResourceCachingFilter implements Filter {
 
     /**
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet.java b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet.java
index a5ef21c..69107ec 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet.java
@@ -19,12 +19,17 @@
 
 package org.apache.isis.core.webapp.content;
 
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import static org.apache.isis.commons.internal.base._Strings.pair;
+import static org.apache.isis.commons.internal.base._With.ifPresentElse;
+import static org.apache.isis.commons.internal.base._With.ifPresentElseGet;
+
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 
+import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
@@ -32,14 +37,34 @@ import javax.servlet.http.HttpServletResponse;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import org.apache.isis.commons.internal.base._Bytes;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.resources._Resource;
 import org.apache.isis.core.commons.lang.InputStreamExtensions;
 import org.apache.isis.core.commons.lang.ResourceUtil;
 import org.apache.isis.core.commons.lang.StringExtensions;
 
+/**
+ * Serves static web-resources by class-path or file-system lookup.
+ * Also handles HTML-templates, where template's placeholders get replaced by their values.
+ */
+@WebServlet(
+        urlPatterns = { 
+                "*.css", "*.png", "*.jpg", "*.jpeg", "*.gif", "*.svg", "*.js", "*.html", "*.swf" }
+)
 public class ResourceServlet extends HttpServlet {
 
     private static final Logger LOG = LoggerFactory.getLogger(ResourceServlet.class);
     private static final long serialVersionUID = 1L;
+    private ResourceServlet_HtmlTemplateVariables templateVariables;
+    
+    @Override
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+        
+        final String restfulPath = ifPresentElse(_Resource.getRestfulPathIfAny(), "restful"); 
+        templateVariables = new ResourceServlet_HtmlTemplateVariables(pair("restful", restfulPath));
+    }
 
     @Override
     protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
@@ -50,34 +75,53 @@ public class ResourceServlet extends HttpServlet {
     protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
         processRequest(request, response);
     }
+    
+    // -- HELPER
 
     private void processRequest(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
         final String servletPath = StringExtensions.stripLeadingSlash(request.getServletPath());
         LOG.debug("request: {}", servletPath);
 
-        // try to load from filesystem
-        final InputStream is2 = getRealPath(request);
-        if (is2 != null) {
-            LOG.debug("request: {} loaded from filesystem", servletPath);
-            writeContentType(request, response);
-            InputStreamExtensions.copyTo(is2, response.getOutputStream());
-            is2.close();
-            return;
-        }
-
-        // otherwise, try to load from classpath
-        final InputStream is = ResourceUtil.getResourceAsStream(servletPath);
+        final InputStream is = ifPresentElseGet(
+                ResourceUtil.getResourceAsStream(request), // try to load from file-system first 
+                ()->ResourceUtil.getResourceAsStream(servletPath)); // otherwise, try to load from class-path  
+        
         if (is != null) {
             LOG.debug("request: {} loaded from classpath", servletPath );
-            writeContentType(request, response);
-            InputStreamExtensions.copyTo(is, response.getOutputStream());
-            is.close();
-            return;
+            
+            try {
+                writeContentType(request, response);
+                processContent(is, request, response);
+                return;
+            } finally {
+                is.close();    
+            }
         }
 
         LOG.warn("failed to load resource from classpath or file system: {}", servletPath);
     }
 
+    private void processContent(
+            final InputStream is, 
+            final HttpServletRequest request, 
+            final HttpServletResponse response) 
+                    throws IOException {
+        
+        if(request.getServletPath().endsWith(".template.html")) {
+            
+            final String templateContent = _Strings.ofBytes(_Bytes.of(is), StandardCharsets.UTF_8);
+            final String htmlContent = templateVariables.applyTo(templateContent);
+                        
+            response.getWriter().append(htmlContent);
+            
+        } else {
+            
+            // direct copy
+            InputStreamExtensions.copyTo(is, response.getOutputStream());
+            
+        }
+    }
+
     private static void writeContentType(final HttpServletRequest request, final HttpServletResponse response) {
         final String contentType = guessContentType(request.getServletPath());
         if(contentType != null) {
@@ -108,15 +152,4 @@ public class ResourceServlet extends HttpServlet {
         return null;
     }
 
-    private FileInputStream getRealPath(final HttpServletRequest request) {
-        final String realPath = request.getSession().getServletContext().getRealPath(request.getServletPath());
-        if (realPath == null) {
-            return null;
-        }
-        try {
-            return new FileInputStream(realPath);
-        } catch (final FileNotFoundException e) {
-            return null;
-        }
-    }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet_HtmlTemplateVariables.java b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet_HtmlTemplateVariables.java
new file mode 100644
index 0000000..cc2a122
--- /dev/null
+++ b/core/metamodel/src/main/java/org/apache/isis/core/webapp/content/ResourceServlet_HtmlTemplateVariables.java
@@ -0,0 +1,64 @@
+/*
+ *  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.content;
+
+import static org.apache.isis.commons.internal.base._NullSafe.stream;
+import static org.apache.isis.commons.internal.base._With.requires;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.isis.commons.internal.base._Strings.KeyValuePair;
+
+/**
+ * Package private mixin for ResourceServlet
+ * @since 2.0.0
+ */
+final class ResourceServlet_HtmlTemplateVariables {
+
+    final Map<String, String> placeholders = new HashMap<>();
+    
+    public ResourceServlet_HtmlTemplateVariables(KeyValuePair ... kvPairs) {
+        requires(kvPairs, "placeholders");
+        
+        stream(kvPairs)
+        .forEach(kvPair->placeholders.put(kvPair.getKey(), kvPair.getValue()));
+    }
+    
+    /**
+     * @param template HTML template containing placeholders
+     * @return HTML post-processed template with all the placeholders replaced by their values
+     */
+    public String applyTo(final String template) {
+        
+        String acc = template;        
+        
+        for ( Entry<String, String> entry : placeholders.entrySet()) {
+            final String placeholderLiteral = "${" + entry.getKey() + "}";  
+            final String placeholderValue = entry.getValue();
+            
+            acc = acc.replace(placeholderLiteral, placeholderValue);
+        }
+
+        return acc;
+    }
+
+}
diff --git a/core/metamodel/src/main/resources/swagger-ui/index.html b/core/metamodel/src/main/resources/swagger-ui/index.template.html
similarity index 96%
rename from core/metamodel/src/main/resources/swagger-ui/index.html
rename to core/metamodel/src/main/resources/swagger-ui/index.template.html
index 46247d0..38e48d2 100644
--- a/core/metamodel/src/main/resources/swagger-ui/index.html
+++ b/core/metamodel/src/main/resources/swagger-ui/index.template.html
@@ -152,15 +152,15 @@
 				API Select:
 
                 <select id="input_baseUrl" name="baseUrl" style="height: 26px;">
-                    <option id="private" label="private" value="../../restful/swagger/private">private</option>
-                    <option id="public" label="public" value="../../restful/swagger/public">public</option>
-                    <option id="prototyping" label="prototyping" value="../../restful/swagger/prototyping">prototyping</option>
+                    <option id="private" label="private" value="../../${restful}/swagger/private">private</option>
+                    <option id="public" label="public" value="../../${restful}/swagger/public">public</option>
+                    <option id="prototyping" label="prototyping" value="../../${restful}/swagger/prototyping">prototyping</option>
                 </select>
             </div>
             <div class='input' style="display:none;"><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text"/></div>
             <div class='input'><a id="explore" href="#" data-sw-translate>Reload</a></div>
             
-            <a href="../restful/" target="blank" title="You might need to login.">Direct API Link</a>
+            <a href="../${restful}/" target="blank" title="You might need to login.">Direct API Link</a>
             
         </form>
 
diff --git a/core/plugins/eventbus-axon/pom.xml b/core/plugins/eventbus-axon/pom.xml
index 8c85d54..bef0454 100644
--- a/core/plugins/eventbus-axon/pom.xml
+++ b/core/plugins/eventbus-axon/pom.xml
@@ -59,6 +59,10 @@
 			<groupId>org.axonframework</groupId>
 			<artifactId>axon-core</artifactId>
 			<version>${axon-core.version}</version>
+			<!-- <classifier>ee-compat</classifier> 
+			seems not available yet
+			see https://github.com/AxonFramework/AxonFramework/issues/534
+			-->
 		</dependency>
 
 		<dependency>