You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/09/19 11:18:58 UTC

[commons-vfs] branch master updated: VFS-841: copying Jackrabbit standalone Main class not to depend on wh… (#409)

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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-vfs.git


The following commit(s) were added to refs/heads/master by this push:
     new 16b16c39 VFS-841: copying Jackrabbit standalone Main class not to depend on wh… (#409)
16b16c39 is described below

commit 16b16c39c1378b262044f91a90e520fd17d59e86
Author: Woonsan Ko <wo...@users.noreply.github.com>
AuthorDate: Tue Sep 19 20:18:53 2023 +0900

    VFS-841: copying Jackrabbit standalone Main class not to depend on wh… (#409)
    
    * VFS-841: copying Jackrabbit standalone Main class not to depend on what's not supposed to be exposed by Jackrabbit2
    
    * VFS-841: extract constants to snooze checkstyle complaints
    
    * Update Webdav4ProviderTestCase.java
    
    ---------
    
    Co-authored-by: Gary Gregory <ga...@users.noreply.github.com>
---
 .../vfs2/provider/webdav4/test/JackrabbitMain.java | 332 +++++++++++++++++++++
 .../webdav4/test/Webdav4ProviderTestCase.java      |   8 +-
 .../org/apache/commons/vfs2/util/URIUtils.java     |  12 +-
 3 files changed, 345 insertions(+), 7 deletions(-)

diff --git a/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/JackrabbitMain.java b/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/JackrabbitMain.java
new file mode 100644
index 00000000..93e3ee64
--- /dev/null
+++ b/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/JackrabbitMain.java
@@ -0,0 +1,332 @@
+/*
+ * 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.
+ */
+
+// COPIED FROM JACKRABBIT 2.20.11 (No additional NOTICE required, see VFS-841)
+
+package org.apache.commons.vfs2.provider.webdav4.test;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.chain.Context;
+import org.apache.commons.chain.impl.ContextBase;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.commons.JcrUtils;
+import org.apache.jackrabbit.core.RepositoryCopier;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.servlet.jackrabbit.JackrabbitRepositoryServlet;
+import org.apache.jackrabbit.standalone.Main;
+import org.apache.jackrabbit.standalone.cli.CommandException;
+import org.apache.jackrabbit.standalone.cli.CommandHelper;
+import org.apache.jackrabbit.standalone.cli.JcrClient;
+import org.eclipse.jetty.server.NCSARequestLog;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.RequestLogHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.webapp.WebAppContext;
+
+/**
+ * For testing purpose, copied from org.apache.jackrabbit.standalone.Main with renaming the classname,
+ * except of the part calling <CODE>Main.class.getProtectionDomain().getCodeSource().getLocation()</CODE>,
+ * which reads some files from the jackrabbit-standalone-component.jar file directly.
+ */
+public class JackrabbitMain {
+
+    /**
+     * @param args
+     */
+    public static void main(String[] args) throws Exception {
+        new JackrabbitMain(args).run();
+    }
+
+    private final Options options = new Options();
+
+    private final CommandLine command;
+
+    private final RequestLogHandler accessLog = new RequestLogHandler();
+
+    private final WebAppContext webapp = new WebAppContext();
+
+    private final Server server = new Server();
+
+    private final ServerConnector connector = new ServerConnector(server);
+
+    /**
+     * Construct Main application instance.
+     * <P>
+     * <EM>Note:</EM> Constructor is protected because other projects such as Commons VFS can extend this for some reasons
+     *       (e.g, unit testing against Jackrabbit WebDAV).
+     */
+    protected JackrabbitMain(String[] args) throws ParseException {
+        options.addOption("?", "help", false, "print this message");
+        options.addOption("n", "notice", false, "print copyright notices");
+        options.addOption("l", "license", false, "print license information");
+        options.addOption(
+                "b", "backup", false, "create a backup of the repository");
+        options.addOption(
+                "i", "cli", true, "command line access to a remote repository");
+
+        options.addOption("q", "quiet", false, "disable console output");
+        options.addOption("d", "debug", false, "enable debug logging");
+
+        options.addOption("h", "host", true, "IP address of the HTTP server");
+        options.addOption("p", "port", true, "TCP port of the HTTP server (8080)");
+        options.addOption("f", "file", true, "location of this jar file");
+        options.addOption("r", "repo", true, "repository directory (jackrabbit)");
+        options.addOption("c", "conf", true, "repository configuration file");
+        options.addOption(
+                "R", "backup-repo", true,
+                "backup repository directory (jackrabbit-backupN)");
+        options.addOption(
+                "C", "backup-conf", true,
+                "backup repository configuration file");
+
+        command = new DefaultParser().parse(options, args);
+    }
+
+    /**
+     * Run this Main application.
+     * <P>
+     * <EM>Note:</EM> this is public because this can be used by other projects in unit tests. e.g, Commons-VFS.
+     * @throws Exception if any exception occurs
+     */
+    public void run() throws Exception {
+        String defaultFile = "jackrabbit-standalone.jar";
+        URL location =
+            Main.class.getProtectionDomain().getCodeSource().getLocation();
+        if (location != null && "file".equals(location.getProtocol())) {
+            File file = new File(location.getPath());
+            if (file.isFile()) {
+                defaultFile = location.getPath();
+            }
+        }
+        File file = new File(command.getOptionValue("file", defaultFile));
+
+        if (command.hasOption("help")) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp("java -jar " + file.getName(), options, true);
+        } else if (command.hasOption("notice")) {
+            copyToOutput("/META-INF/NOTICE.txt");
+        } else if (command.hasOption("license")) {
+            copyToOutput("/META-INF/LICENSE.txt");
+        } else if (command.hasOption("cli")) {
+            System.setProperty("logback.configurationFile", "logback-cli.xml");
+
+            String uri = command.getOptionValue("cli");
+            Repository repository = JcrUtils.getRepository(uri);
+
+            Context context = new ContextBase();
+            CommandHelper.setRepository(context, repository, uri);
+            try {
+                Session session = repository.login();
+                CommandHelper.setSession(context, session);
+                CommandHelper.setCurrentNode(context, session.getRootNode());
+            } catch (RepositoryException ignore) {
+                // anonymous login not possible
+            }
+
+            new JcrClient(context).runInteractive();
+
+            try {
+                CommandHelper.getSession(context).logout();
+            } catch (CommandException ignore) {
+                // already logged out
+            }
+        } else {
+            message("Welcome to Apache Jackrabbit!");
+            message("-------------------------------");
+
+            File repository =
+                new File(command.getOptionValue("repo", "jackrabbit"));
+            message("Using repository directory " + repository);
+            repository.mkdirs();
+            File tmp = new File(repository, "tmp");
+            tmp.mkdir();
+            File log = new File(repository, "log");
+            log.mkdir();
+
+            message("Writing log messages to " + log);
+            prepareServerLog(log);
+
+            if (command.hasOption("backup")) {
+                backup(repository);
+            } else {
+                message("Starting the server...");
+                prepareWebapp(file, repository, tmp);
+                accessLog.setHandler(webapp);
+                prepareAccessLog(log);
+                server.setHandler(accessLog);
+                prepareConnector();
+                server.addConnector(connector);
+                prepareShutdown();
+
+                try {
+                    server.start();
+
+                    String host = connector.getHost();
+                    if (host == null) {
+                        host = "localhost";
+                    }
+                    message("Apache Jackrabbit is now running at "
+                            +"http://" + host + ":" + connector.getPort() + "/");
+                } catch (Throwable t) {
+                    System.err.println(
+                            "Unable to start the server: " + t.getMessage());
+                    System.exit(1);
+                }
+            }
+        }
+    }
+
+    /**
+     * Shutdown this Main application.
+     * <P>
+     * <EM>Note:</EM> this is public because this can be used by other projects in unit tests for graceful shutdown.
+     * e.g, Commons-VFS. If this is not invoked properly, some unexpected exceptions may occur on shutdown hook
+     * due to an unexpected, invalid state for org.apache.lucene.index.IndexFileDeleter for instance.
+     */
+    public void shutdown() {
+        try {
+            message("Shutting down the server...");
+            server.stop();
+            server.join();
+            message("-------------------------------");
+            message("Goodbye from Apache Jackrabbit!");
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void backup(File sourceDir) throws Exception {
+        RepositoryConfig source;
+        if (command.hasOption("conf")) {
+            source = RepositoryConfig.create(
+                    new File(command.getOptionValue("conf")), sourceDir);
+        } else {
+            source = RepositoryConfig.create(sourceDir);
+        }
+
+        File targetDir;
+        if (command.hasOption("backup-repo")) {
+            targetDir = new File(command.getOptionValue("backup-repo"));
+        } else {
+            int i = 1;
+            do {
+                targetDir = new File("jackrabbit-backup" + i++);
+            } while (targetDir.exists());
+        }
+
+        RepositoryConfig target;
+        if (command.hasOption("backup-conf")) {
+            target = RepositoryConfig.install(
+                    new File(command.getOptionValue("backup-conf")), targetDir);
+        } else {
+            target = RepositoryConfig.install(targetDir);
+        }
+
+        message("Creating a repository copy in " + targetDir);
+        RepositoryCopier.copy(source, target);
+        message("The repository has been successfully copied.");
+    }
+
+    private void prepareServerLog(File log)
+            throws IOException {
+        System.setProperty(
+                "jackrabbit.log", new File(log, "jackrabbit.log").getPath());
+        System.setProperty(
+                "jetty.log", new File(log, "jetty.log").getPath());
+
+        if (command.hasOption("debug")) {
+            System.setProperty("log.level", "DEBUG");
+        } else {
+            System.setProperty("log.level", "INFO");
+        }
+
+        System.setProperty(
+                "derby.stream.error.file",
+                new File(log, "derby.log").getPath());
+    }
+
+    private void prepareAccessLog(File log) {
+        NCSARequestLog ncsa = new NCSARequestLog(
+                new File(log, "access.log.yyyy_mm_dd").getPath());
+        ncsa.setFilenameDateFormat("yyyy-MM-dd");
+        accessLog.setRequestLog(ncsa);
+    }
+
+    private void prepareWebapp(File file, File repository, File tmp) {
+        webapp.setContextPath("/");
+        webapp.setWar(file.getPath());
+        webapp.setExtractWAR(true);
+        webapp.setTempDirectory(tmp);
+
+        ServletHolder servlet =
+            new ServletHolder(JackrabbitRepositoryServlet.class);
+        servlet.setInitOrder(1);
+        servlet.setInitParameter("repository.home", repository.getPath());
+        String conf = command.getOptionValue("conf");
+        if (conf != null) {
+            servlet.setInitParameter("repository.config", conf);
+        }
+        webapp.addServlet(servlet, "/repository.properties");
+    }
+
+    private void prepareConnector() {
+        String port = command.getOptionValue("port", "8080");
+        connector.setPort(Integer.parseInt(port));
+        String host = command.getOptionValue("host");
+        if (host != null) {
+            connector.setHost(host);
+        }
+    }
+
+    private void prepareShutdown() {
+        Runtime.getRuntime().addShutdownHook(new Thread() {
+            public void run() {
+                shutdown();
+            }
+        });
+    }
+
+    private void message(String message) {
+        if (!command.hasOption("quiet")) {
+            System.out.println(message);
+        }
+    }
+
+    private void copyToOutput(String resource) throws IOException {
+        InputStream stream = JackrabbitMain.class.getResourceAsStream(resource);
+        try {
+            IOUtils.copy(stream, System.out);
+        } finally {
+            stream.close();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/Webdav4ProviderTestCase.java b/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/Webdav4ProviderTestCase.java
index 2518b895..93fa66d6 100644
--- a/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/Webdav4ProviderTestCase.java
+++ b/commons-vfs2-jackrabbit2/src/test/java/org/apache/commons/vfs2/provider/webdav4/test/Webdav4ProviderTestCase.java
@@ -47,12 +47,12 @@ import org.apache.commons.vfs2.provider.webdav4.Webdav4FileProvider;
 import org.apache.commons.vfs2.provider.webdav4.Webdav4FileSystemConfigBuilder;
 import org.apache.commons.vfs2.util.FreeSocketPortUtil;
 import org.apache.jackrabbit.core.TransientRepository;
-import org.apache.jackrabbit.standalone.Main;
 
 import junit.framework.Test;
 
 /**
  * Test cases for the WebDAV4 provider.
+ * Do NOT use org.apache.jackrabbit.standalone.Main.
  *
  * @since 2.5.0
  */
@@ -68,7 +68,7 @@ public class Webdav4ProviderTestCase extends AbstractProviderTestConfig {
 
     private static final String TEST_URI = "test.webdav4.uri";
 
-    private static Main jrMain;
+    private static JackrabbitMain jrMain;
 
     /**
      * Use %40 for @ in URLs
@@ -212,7 +212,7 @@ public class Webdav4ProviderTestCase extends AbstractProviderTestConfig {
         dump(RepoDirectory);
         // Start server with temp repo
         startJackrabbit(RepoDirectory);
-        message("Returned from org.apache.jackrabbit.standalone.Main " + SocketPort);
+        message("Returned from " + JackrabbitMain.class.getName() +  " " + SocketPort);
     }
 
     /**
@@ -228,7 +228,7 @@ public class Webdav4ProviderTestCase extends AbstractProviderTestConfig {
             quiet = true;
         }
 
-        jrMain = new Main(new String[] { "--port", Integer.toString(SocketPort), "--repo", repoDirectory.toString(),
+        jrMain = new JackrabbitMain(new String[] { "--port", Integer.toString(SocketPort), "--repo", repoDirectory.toString(),
                 quiet ? "--quiet" : "" }) {
         };
 
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIUtils.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIUtils.java
index 5999b8d6..df529a79 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIUtils.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/util/URIUtils.java
@@ -107,6 +107,12 @@ public class URIUtils {
 
         private static final byte ESCAPE_CHAR = '%';
 
+        private static final int EIGHT_BIT_CHARSET_SIZE = 256;
+
+        private static final int FOUR_BITS = 4;
+
+        private static final int UNSIGNED_BYTE_MASK = 0xF;
+
         // @formatter:off
         private static final FluentBitSet WWW_FORM_URL_SAFE = URIBitSets.bitSet()
             // alpha characters
@@ -140,7 +146,7 @@ public class URIUtils {
             for (final byte c : bytes) {
                 int b = c;
                 if (b < 0) {
-                    b = 256 + b;
+                    b = EIGHT_BIT_CHARSET_SIZE + b;
                 }
                 if (urlsafe.get(b)) {
                     if (b == ' ') {
@@ -149,7 +155,7 @@ public class URIUtils {
                     buffer.write(b);
                 } else {
                     buffer.write(ESCAPE_CHAR);
-                    final char hex1 = hexDigit(b >> 4);
+                    final char hex1 = hexDigit(b >> FOUR_BITS);
                     final char hex2 = hexDigit(b);
                     buffer.write(hex1);
                     buffer.write(hex2);
@@ -159,7 +165,7 @@ public class URIUtils {
         }
 
         private static char hexDigit(final int b) {
-            return Character.toUpperCase(Character.forDigit(b & 0xF, RADIX));
+            return Character.toUpperCase(Character.forDigit(b & UNSIGNED_BYTE_MASK, RADIX));
         }
     }