You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2020/07/07 15:43:42 UTC

[sling-org-apache-sling-jcr-repoinit] 01/01: SLING-9572: Adding OSGi WebConsole for executing Sling RepoInit scripts

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

dklco pushed a commit to branch feature/SLING-9572
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git

commit 987974473d92c2cd55f003cc25d8431b6cbd7631
Author: Dan Klco <dk...@apache.org>
AuthorDate: Tue Jul 7 11:43:18 2020 -0400

    SLING-9572: Adding OSGi WebConsole for executing Sling RepoInit scripts
---
 pom.xml                                            |  83 +++++----
 .../jcr/repoinit/impl/RepoInitWebConsole.java      | 185 +++++++++++++++++++++
 src/main/resources/console.html                    |  63 +++++++
 3 files changed, 302 insertions(+), 29 deletions(-)

diff --git a/pom.xml b/pom.xml
index 87044d2..b10101d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,37 +18,37 @@
   under the License.
 -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
+    <modelVersion>4.0.0</modelVersion>
 
-  <parent>
-    <groupId>org.apache.sling</groupId>
-    <artifactId>sling-bundle-parent</artifactId>
-    <version>38</version>
-    <relativePath />
-  </parent>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>38</version>
+        <relativePath />
+    </parent>
 
-  <artifactId>org.apache.sling.jcr.repoinit</artifactId>
-  <version>1.1.29-SNAPSHOT</version>
-  <name>Apache Sling Repoinit JCR</name>
-  <description>
+    <artifactId>org.apache.sling.jcr.repoinit</artifactId>
+    <version>1.1.29-SNAPSHOT</version>
+    <name>Apache Sling Repoinit JCR</name>
+    <description>
 	  Initializes a JCR repository based on the output
 	  of the RepoInit Language parser.
   </description>
 
-  <properties>
-    <jackrabbit.version>2.18.2</jackrabbit.version>
-    <oak.version>1.16.0</oak.version>
-    <org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
-    <!-- To debug the pax process, override this with -D -->
-    <pax.vm.options>-Xmx512M</pax.vm.options>
-  </properties>
+    <properties>
+        <jackrabbit.version>2.18.2</jackrabbit.version>
+        <oak.version>1.16.0</oak.version>
+        <org.ops4j.pax.exam.version>4.13.3</org.ops4j.pax.exam.version>
+        <!-- To debug the pax process, override this with -D -->
+        <pax.vm.options>-Xmx512M</pax.vm.options>
+    </properties>
 
-  <scm>
-    <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git</connection>
-    <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git</developerConnection>
-    <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-repoinit.git</url>
-    <tag>org.apache.sling.jcr.repoinit-1.1.24</tag>
-  </scm>
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-jcr-repoinit.git</url>
+        <tag>org.apache.sling.jcr.repoinit-1.1.24</tag>
+    </scm>
 
     <build>
         <plugins>
@@ -56,7 +56,7 @@
                 <groupId>org.apache.servicemix.tooling</groupId>
                 <artifactId>depends-maven-plugin</artifactId>
             </plugin>
-              <plugin>
+            <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
                 <configuration>
@@ -88,12 +88,12 @@
                     <redirectTestOutputToFile>true</redirectTestOutputToFile>
                     <systemProperties>
                         <property>
-                        <name>bundle.filename</name>
-                        <value>${basedir}/target/${project.build.finalName}.jar</value>
+                            <name>bundle.filename</name>
+                            <value>${basedir}/target/${project.build.finalName}.jar</value>
                         </property>
                         <property>
-                        <name>pax.vm.options</name>
-                        <value>${pax.vm.options}</value>
+                            <name>pax.vm.options</name>
+                            <value>${pax.vm.options}</value>
                         </property>
                     </systemProperties>
                 </configuration>
@@ -298,5 +298,30 @@
             <version>2.6</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.webconsole</artifactId>
+            <version>4.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.3.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.xss</artifactId>
+            <version>2.2.2</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.20.0</version>
+            <scope>provided</scope>
+        </dependency>
+
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoInitWebConsole.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoInitWebConsole.java
new file mode 100644
index 0000000..ac03410
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepoInitWebConsole.java
@@ -0,0 +1,185 @@
+/*
+ * 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.sling.jcr.repoinit.impl;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import javax.jcr.Session;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang3.text.StrSubstitutor;
+import org.apache.felix.webconsole.AbstractWebConsolePlugin;
+import org.apache.felix.webconsole.WebConsoleConstants;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor;
+import org.apache.sling.repoinit.parser.RepoInitParser;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.repoinit.parser.operations.Operation;
+import org.apache.sling.xss.XSSAPI;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = Servlet.class, property = {
+        Constants.SERVICE_DESCRIPTION + "=Apache Sling Service User Manager Web Console Plugin",
+        WebConsoleConstants.PLUGIN_LABEL + "=" + RepoInitWebConsole.LABEL,
+        WebConsoleConstants.PLUGIN_TITLE + "=" + RepoInitWebConsole.TITLE,
+        WebConsoleConstants.PLUGIN_CATEGORY + "=Sling" })
+public class RepoInitWebConsole extends AbstractWebConsolePlugin {
+
+    private static final Logger log = LoggerFactory.getLogger(RepoInitWebConsole.class);
+
+    @Reference(policyOption = ReferencePolicyOption.GREEDY)
+    private XSSAPI xss;
+
+    @Reference
+    private RepoInitParser parser;
+
+    @Reference
+    private JcrRepoInitOpsProcessor processor;
+
+    @Reference(policyOption = ReferencePolicyOption.GREEDY)
+    private ResourceResolverFactory resolverFactory;
+
+    private static final long serialVersionUID = 1L;
+    public static final String LABEL = "repoinit";
+    public static final String TITLE = "RepoInit";
+
+    private static final String PN_STATEMENTS = "statements";
+    private static final String PN_EXECUTE = "execute";
+
+    @Override
+    public String getLabel() {
+        return LABEL;
+    }
+
+    @Override
+    public String getTitle() {
+        return TITLE;
+    }
+
+    @Override
+    protected void renderContent(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        Map<String, String> parameters = readRequest(request);
+        parameters.put("result", "");
+        writeResponse(response, parameters);
+
+    }
+
+    private Map<String, String> readRequest(HttpServletRequest request) {
+
+        Map<String, String> parameters = new HashMap<>();
+        String statements = Optional.ofNullable(request.getParameter(PN_STATEMENTS)).orElse("");
+        parameters.put(PN_STATEMENTS, xss.encodeForHTML(statements));
+        parameters.put(PN_EXECUTE, "true".equals(request.getParameter(PN_EXECUTE)) ? "checked" : "");
+        return parameters;
+    }
+
+    private void writeResponse(HttpServletResponse response, Map<String, String> parameters) throws IOException {
+        String consoleHtml = IOUtils.toString(this.getBundle().getEntry("console.html").openStream(),
+                StandardCharsets.UTF_8);
+        StrSubstitutor sub = new StrSubstitutor(parameters);
+        IOUtils.write(sub.replace(consoleHtml), response.getWriter());
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+            throws ServletException, IOException {
+        Map<String, String> parameters = readRequest(request);
+
+        StringReader statements = Optional.ofNullable(request.getParameter(PN_STATEMENTS)).map(StringReader::new)
+                .orElse(new StringReader(""));
+        List<String> messages = new ArrayList<>();
+        List<Operation> operations = null;
+        try {
+            operations = parser.parse(statements);
+            messages.add("PARSING SUCCEEDED: ");
+            operations.stream().map(Operation::toString).forEach(messages::add);
+        } catch (RepoInitParsingException e) {
+            messages.add("PARSING FAILED: ");
+            messages.add(e.toString());
+        }
+
+        if (operations != null && "true".equals(request.getParameter(PN_EXECUTE))) {
+            messages.add("");
+            Session session;
+            try {
+                session = getResourceResolver(request).adaptTo(Session.class);
+                processor.apply(session, operations);
+                messages.add("EXECUTION SUCCEEDED!");
+            } catch (Exception e) {
+                messages.add("EXECUTION FAILED: ");
+                messages.add(e.toString());
+            }
+        }
+        parameters.put("result", messages.stream().collect(Collectors.joining("\n")));
+
+        PrintWriter pw = startResponse(request, response);
+
+        renderTopNavigation(request, pw);
+
+        pw.println("<div id='content'>");
+        writeResponse(response, parameters);
+        pw.println("</div>");
+
+        endResponse(pw);
+    }
+
+    private boolean needsAdministrativeResolver(HttpServletRequest request) {
+        Object resolver = request.getAttribute("org.apache.sling.auth.core.ResourceResolver");
+        return !(resolver instanceof ResourceResolver);
+    }
+
+    @SuppressWarnings("deprecation")
+    private ResourceResolver getResourceResolver(HttpServletRequest request) throws LoginException {
+        ResourceResolver resolver = null;
+        if (needsAdministrativeResolver(request)) {
+            try {
+                log.warn("Resource resolver not available in request, falling back to adminstrative resource resolver");
+                resolver = resolverFactory.getAdministrativeResourceResolver(null);
+            } catch (LoginException le) {
+                throw new LoginException(
+                        "Unable to get Administrative Resource Resolver, add the bundle org.apache.sling.jcr.repoinit.parser.webconsole in the Apache Sling Login Admin Whitelist",
+                        le);
+            }
+        } else {
+            resolver = (ResourceResolver) request.getAttribute("org.apache.sling.auth.core.ResourceResolver");
+        }
+
+        return resolver;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/resources/console.html b/src/main/resources/console.html
new file mode 100644
index 0000000..7df9b50
--- /dev/null
+++ b/src/main/resources/console.html
@@ -0,0 +1,63 @@
+<!--
+ * 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.
+-->
+<p class="statline ui-state-highlight">
+    RepoInits are statements used to initialize the Sling repository. Use this
+    tool to test these statements.<br/>
+    <a href="https://sling.apache.org/documentation/bundles/repository-initialization.html" target="_blank" rel="noopener noreferrer">
+        Sling RepoInit Documentation
+    </a>
+  </p>
+  <br />
+  <form method="post" action="/system/console/repoinit">
+    <table class="nicetable ui-widget">
+      <thead class="ui-widget-header">
+        <tr>
+          <th colspan="2" class="ui-widget-header">
+            Test RepoInit
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+          <tr>
+              <td style="width: 20%;">RepoInit Statements</td>
+              <td>
+                <textarea name="statements" rows="10" style="width: 100%">${statements}</textarea>
+              </td>
+            </tr>
+            <tr>
+              <td style="width: 20%;">Execute</td>
+              <td>
+                <input type="checkbox" name="execute" value="true" ${execute} />
+              </td>
+            </tr>
+            <tr>
+              <td></td>
+              <td>
+                <input
+                  type="submit"
+                  value="Run"
+                  class="ui-state-default ui-corner-all"
+                />
+              </td>
+            </tr>
+      </tbody>
+    </table>
+  </form>
+  <br/>
+  <div class="statline ui-state-highlight">
+      <pre>${result}</pre>
+  </div>
\ No newline at end of file