[spring-extensions] branch master updated: JSE-38 - Provide a command line wrapper for Spring contexts

Scott Cantor cantor.2 at osu.edu
Wed Jul 1 16:23:20 UTC 2020


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch master
in repository spring-extensions.

View the commit online:
http://git.shibboleth.net/view/?p=spring-extensions.git;a=commit;h=b4c3367900b18c4e5720ab88d982421b0c1b6863

The following commit(s) were added to refs/heads/master by this push:
       new  b4c3367   JSE-38 - Provide a command line wrapper for Spring contexts
b4c3367 is described below

commit b4c3367900b18c4e5720ab88d982421b0c1b6863
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Wed Jul 1 12:23:15 2020 -0400

    JSE-38 - Provide a command line wrapper for Spring contexts
    
    https://issues.shibboleth.net/jira/browse/JSE-38
---
 pom.xml                                            |   6 +
 .../ext/spring/cli/AbstractCommandLine.java        | 202 +++++++++++++++++++++
 .../spring/cli/AbstractCommandLineArguments.java   | 135 ++++++++++++++
 .../ext/spring/cli/CommandLineArguments.java       |  89 +++++++++
 .../shibboleth/ext/spring/cli/package-info.java    |  22 +++
 src/main/resources/logger-normal.xml               |  15 ++
 src/main/resources/logger-quiet.xml                |  15 ++
 src/main/resources/logger-verbose.xml              |  15 ++
 8 files changed, 499 insertions(+)

diff --git a/pom.xml b/pom.xml
index 4220e04..13ce697 100644
--- a/pom.xml
+++ b/pom.xml
@@ -82,6 +82,12 @@
             <!-- Required for the DomDocumentFactoryBean and Duration conversion classes. -->
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>com.beust</groupId>
+            <artifactId>jcommander</artifactId>
+            <!-- Required for command line classes. -->
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>${spring.groupId}</groupId>
             <artifactId>spring-context</artifactId>
diff --git a/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLine.java b/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLine.java
new file mode 100644
index 0000000..664a936
--- /dev/null
+++ b/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLine.java
@@ -0,0 +1,202 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.ext.spring.cli;
+
+import java.lang.reflect.Constructor;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+import org.springframework.context.support.GenericApplicationContext;
+import org.springframework.core.io.Resource;
+
+import com.beust.jcommander.JCommander;
+
+import net.shibboleth.ext.spring.resource.PreferFileSystemResourceLoader;
+import net.shibboleth.ext.spring.util.ApplicationContextBuilder;
+import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
+
+/**
+ * A simple driver for a Spring-based CLI.
+ * 
+ * This class handles a single parameters, the primary Spring configuration resource. Additional parameters may be
+ * handled by subclasses.
+ * 
+ * All logging is done in accordance with the logback.xml file included in the library. If you wish to use a
+ * different logging configuration you may do so using the <code>-Dlogback.configurationFile=/path/to/logback.xml</code>
+ * JVM configuration option.
+ * 
+ * @param <T> argument object type
+ */
+public abstract class AbstractCommandLine<T extends CommandLineArguments> {
+
+    /** Name of system property for command line argument class. */
+    @Nonnull @NotEmpty public static final String ARGS_PROPERTY = "net.shibboleth.ext.spring.cli.arguments";
+
+    /** Return code indicating command completed successfully, {@value} . */
+    public static final int RC_OK = 0;
+
+    /** Return code indicating an initialization error, {@value} . */
+    public static final int RC_INIT = 1;
+
+    /** Return code indicating an error reading files, {@value} . */
+    public static final int RC_IO = 2;
+
+    /** Return code indicating an unknown error occurred, {@value} . */
+    public static final int RC_UNKNOWN = -1;
+    
+    /** Spring context. */
+    @Nullable private GenericApplicationContext applicationContext;
+        
+    /**
+     * Get the Spring context.
+     * 
+     * @return Spring context
+     */
+    @Nonnull protected GenericApplicationContext getApplicationContext() {
+        if (applicationContext == null) {
+            throw new IllegalStateException("No application context installed");
+        }
+        return applicationContext;
+    }
+
+    /**
+     * Run method.
+     * 
+     * @param args command line arguments
+     * 
+     * @return exit code
+     */
+    protected int run(@Nonnull final String[] args) {
+
+        final T argObject;
+        
+        try {
+            final Constructor<T> construct = getArgumentClass().getConstructor();
+            argObject = construct.newInstance();
+            final JCommander jc = new JCommander(argObject);
+            jc.parse(args);
+            if (argObject.isHelp()) {
+                argObject.printHelp(System.out);
+                return RC_OK;
+            } else if (argObject.isVersion()) {
+                System.out.println(getVersion());
+                return RC_OK;
+            }
+            
+            if (argObject.getOtherArgs().size() == 0) {
+                error("Missing Spring config argument");
+                return RC_INIT;
+            }
+            
+            initLogging(argObject);
+
+            argObject.validate();
+
+        } catch (final Exception e) {
+            error(e.getMessage());
+            return RC_INIT;
+        }
+
+        return doRun(argObject);
+    }
+    
+    /**
+     * Initialize the logging subsystem.
+     * 
+     * @param args command line arguments
+     */
+    protected void initLogging(@Nonnull final T args) {
+        if (args.getLoggingConfiguration() != null) {
+            System.setProperty("logback.configurationFile", args.getLoggingConfiguration());
+        } else if (args.isVerboseOutput()) {
+            System.setProperty("logback.configurationFile", "logger-verbose.xml");
+        } else if (args.isQuietOutput()) {
+            System.setProperty("logback.configurationFile", "logger-quiet.xml");
+        } else {
+            System.setProperty("logback.configurationFile", "logger-normal.xml");
+        }
+    }
+
+    /**
+     * The execution method to override.
+     * 
+     * The default implementation handles Spring context creation.
+     * 
+     * @param args input arguments
+     * 
+     * @return exit code
+     */
+    protected int doRun(@Nonnull final T args) {
+        try {
+            final Resource config = new PreferFileSystemResourceLoader().getResource(args.getOtherArgs().get(0));
+            
+            getLogger().debug("Initializing Spring context with configuration file {}", config.getURI());
+            
+            applicationContext = new ApplicationContextBuilder().setServiceConfiguration(config).build();
+            
+            // Register a shutdown hook for the context, so that beans will be
+            // correctly destroyed before the CLI exits.
+            applicationContext.registerShutdownHook();
+        } catch (final Exception e) {
+            if (args.isVerboseOutput()) {
+                getLogger().error("Unable to initialize Spring context", e);
+            } else {
+                getLogger().error("Unable to initialize Spring context", e.getMessage());
+            }
+            return RC_INIT;
+        }
+        
+        return RC_OK;
+    }
+    
+    /**
+     * Get the class of the argument object to instantiate.
+     * 
+     * @return argument class
+     */
+    @Nonnull protected abstract Class<T> getArgumentClass();
+    
+    /**
+     * Return an appropriate version value.
+     * 
+     * @return a version string
+     */
+    @Nonnull @NotEmpty protected abstract String getVersion();
+       
+    /**
+     * Get logger.
+     * 
+     * @return logger
+     */
+    @Nonnull protected abstract Logger getLogger();
+    
+    /**
+     * Prints the error message to STDERR.
+     * 
+     * @param error the error message
+     */
+    private static void error(@Nonnull @NotEmpty final String error) {
+        System.err.println(error);
+        System.err.flush();
+        System.out.println();
+        System.out.flush();
+    }
+    
+}
\ No newline at end of file
diff --git a/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLineArguments.java b/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLineArguments.java
new file mode 100644
index 0000000..a3d04d5
--- /dev/null
+++ b/src/main/java/net/shibboleth/ext/spring/cli/AbstractCommandLineArguments.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.ext.spring.cli;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import com.beust.jcommander.Parameter;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+
+/** Command line arguments base class for the {@link AbstractCommandLine} class. */
+public abstract class AbstractCommandLineArguments implements CommandLineArguments {
+
+    // Non-option arguments
+    
+    /**
+     * Command-line arguments which are not part of options.
+     */
+    @Parameter
+    @Nonnull private List<String> otherArgs = new ArrayList<>();
+    
+    // Logging
+    
+    /**
+     * Verbose logging has been requested.
+     */
+    @Parameter(names = "--verbose")
+    private boolean verbose;
+
+    /**
+     * Quiet logging has been requested.
+     */
+    @Parameter(names = "--quiet")
+    private boolean quiet;
+
+    /**
+     * Name of a specific logging configuration, if one has been requested.
+     */
+    @Parameter(names = "--logConfig")
+    @Nullable private String logConfig;
+
+    // Help
+    
+    /**
+     * Help has been requested.
+     */
+    @Parameter(names = "--help", help=true)
+    private boolean help;
+
+    // Version
+
+    /**
+     * Version has been requested.
+     */
+    @Parameter(names = "--version")
+    private boolean version;
+
+    /** {@inheritDoc} */
+    public boolean isVerboseOutput() {
+        return verbose;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isQuietOutput() {
+        return quiet;
+    }
+
+    /** {@inheritDoc} */
+    @Nullable public String getLoggingConfiguration() {
+        return logConfig;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isHelp() {
+        return help;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isVersion() {
+        return version;
+    }
+    
+    /** {@inheritDoc} */
+    @Nonnull @Unmodifiable @NotLive public List<String> getOtherArgs() {
+        return otherArgs;
+    }
+
+    /** {@inheritDoc} */
+    public void validate() throws IllegalArgumentException {
+        if (isVerboseOutput() && isQuietOutput()) {
+            throw new IllegalArgumentException("Verbose and quiet output are mutually exclusive");
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void printHelp(final PrintStream out) {
+        out.println();
+        out.println("==== Command Line Options ====");
+        out.println();
+        out.println(String.format("  --%-20s %s", "help", "Prints this help information"));
+        out.println(String.format("  --%-20s %s", "version", "Prints version"));
+        out.println();
+
+        out.println("Logging Options - these options are mutually exclusive");
+        out.println(String.format("  --%-20s %s", "verbose", "Turn on verbose messages."));
+        out.println(String.format("  --%-20s %s", "quiet",
+                "Restrict output messages to errors and warnings."));
+        out.println();
+        out.println(String.format("  --%-20s %s", "logConfig",
+                "Specifies a logback configuration file to use to configure logging."));
+        out.println();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/shibboleth/ext/spring/cli/CommandLineArguments.java b/src/main/java/net/shibboleth/ext/spring/cli/CommandLineArguments.java
new file mode 100644
index 0000000..e859c3c
--- /dev/null
+++ b/src/main/java/net/shibboleth/ext/spring/cli/CommandLineArguments.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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 net.shibboleth.ext.spring.cli;
+
+import java.io.PrintStream;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import net.shibboleth.utilities.java.support.annotation.constraint.NotLive;
+import net.shibboleth.utilities.java.support.annotation.constraint.Unmodifiable;
+
+/** Command line arguments interface for the {@link CLI} command line tool. */
+public interface CommandLineArguments {
+
+    /**
+     * Indicates the presence of the <code>--verbose</code> option.
+     * 
+     * @return <code>true</code> if the user requested verbose logging.
+     */
+    boolean isVerboseOutput();
+
+    /**
+     * Indicates the presence of the <code>--quiet</code> option.
+     * 
+     * @return <code>true</code> if the user requested quiet logging.
+     */
+    boolean isQuietOutput();
+
+    /**
+     * Gets the name of the requested logging configuration file
+     * from the command line.
+     * 
+     * @return the logging configuration file name, or <code>null</code>.
+     */
+    @Nullable String getLoggingConfiguration();
+
+    /**
+     * Indicates the presence of the <code>--help</code> option.
+     * 
+     * @return <code>true</code> if the user requested help.
+     */
+    boolean isHelp();
+
+    /**
+     * Indicates the presence of the <code>--version</code> option.
+     *
+     * @return <code>true</code> if the user requested the version be printed.
+     */
+    boolean isVersion();
+
+    /**
+     * Get unparsed arguments.
+     * 
+     * @return unparsed arguments
+     */
+    @Nonnull @Unmodifiable @NotLive public List<String> getOtherArgs();
+
+    /**
+     * Validate the parameter set.
+     * 
+     * @throws IllegalArgumentException if the parameters are invalid
+     */
+    void validate() throws IllegalArgumentException;
+
+    /**
+     * Print default command line help instructions.
+     * 
+     * @param out location where to print the output
+     */
+    void printHelp(@Nonnull final PrintStream out);
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/shibboleth/ext/spring/cli/package-info.java b/src/main/java/net/shibboleth/ext/spring/cli/package-info.java
new file mode 100644
index 0000000..a4a077f
--- /dev/null
+++ b/src/main/java/net/shibboleth/ext/spring/cli/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements.  See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID 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.
+ */
+
+/**
+ * Command line functionality.
+ */
+
+package net.shibboleth.ext.spring.cli;
\ No newline at end of file
diff --git a/src/main/resources/logger-normal.xml b/src/main/resources/logger-normal.xml
new file mode 100644
index 0000000..89bd0bb
--- /dev/null
+++ b/src/main/resources/logger-normal.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%-5level - %msg%n</Pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+    
+</configuration>
diff --git a/src/main/resources/logger-quiet.xml b/src/main/resources/logger-quiet.xml
new file mode 100644
index 0000000..22eb3af
--- /dev/null
+++ b/src/main/resources/logger-quiet.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%-5level - %msg%n</Pattern>
+        </encoder>
+    </appender>
+    
+    <root level="WARN">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+    
+</configuration>
diff --git a/src/main/resources/logger-verbose.xml b/src/main/resources/logger-verbose.xml
new file mode 100644
index 0000000..362d1b7
--- /dev/null
+++ b/src/main/resources/logger-verbose.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+            <charset>UTF-8</charset>
+            <Pattern>%date{ISO8601} - %level [%logger:%line] - %msg%n</Pattern>
+        </encoder>
+    </appender>
+    
+    <root level="DEBUG">
+        <appender-ref ref="CONSOLE"/>
+    </root>
+    
+</configuration>

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list