AbstractCommand.java
- /*
- * Copyright (C) 2009 Christian Schulte <cs@schulte.it>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * o Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * o Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
- * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * $JOMC: AbstractCommand.java 5299 2016-08-30 01:50:13Z schulte $
- *
- */
- package org.jomc.cli.commands;
- import java.util.List;
- import java.util.Locale;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.ThreadFactory;
- import java.util.concurrent.atomic.AtomicInteger;
- import java.util.logging.Level;
- import org.apache.commons.cli.CommandLine;
- import org.jomc.cli.Command;
- /**
- * Base {@code Command} implementation.
- *
- * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
- */
- public abstract class AbstractCommand implements Command
- {
- /**
- * Default log level.
- */
- private static volatile Level defaultLogLevel;
- /**
- * Log level of the instance.
- */
- private volatile Level logLevel;
- /**
- * The listeners of the instance.
- */
- private volatile List<Listener> listeners = new CopyOnWriteArrayList<Listener>();
- /**
- * The {@code ExecutorService} of the command.
- *
- * @since 1.10
- */
- private volatile ExecutorService executorService;
- /**
- * Creates a new {@code AbstractCommand} instance.
- */
- public AbstractCommand()
- {
- super();
- }
- /**
- * Gets the default log level events are logged at.
- * <p>
- * The default log level is controlled by system property
- * {@code org.jomc.cli.commands.AbstractCommand.defaultLogLevel} holding the log level to log events at by
- * default. If that property is not set, the {@code WARNING} default is returned.
- * </p>
- *
- * @return The log level events are logged at by default.
- *
- * @see #getLogLevel()
- * @see Level#parse(java.lang.String)
- */
- public static Level getDefaultLogLevel()
- {
- if ( defaultLogLevel == null )
- {
- defaultLogLevel = Level.parse( System.getProperty(
- "org.jomc.cli.commands.AbstractCommand.defaultLogLevel", Level.WARNING.getName() ) );
- }
- return defaultLogLevel;
- }
- /**
- * Sets the default log level events are logged at.
- *
- * @param value The new default level events are logged at or {@code null}.
- *
- * @see #getDefaultLogLevel()
- */
- public static void setDefaultLogLevel( final Level value )
- {
- defaultLogLevel = value;
- }
- /**
- * Gets the log level of the instance.
- *
- * @return The log level of the instance.
- *
- * @see #getDefaultLogLevel()
- * @see #setLogLevel(java.util.logging.Level)
- * @see #isLoggable(java.util.logging.Level)
- */
- public final Level getLogLevel()
- {
- if ( this.logLevel == null )
- {
- this.logLevel = getDefaultLogLevel();
- if ( this.isLoggable( Level.CONFIG ) )
- {
- this.log( Level.CONFIG, Messages.getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ),
- null );
- }
- }
- return this.logLevel;
- }
- /**
- * Sets the log level of the instance.
- *
- * @param value The new log level of the instance or {@code null}.
- *
- * @see #getLogLevel()
- * @see #isLoggable(java.util.logging.Level)
- */
- public final void setLogLevel( final Level value )
- {
- this.logLevel = value;
- }
- /**
- * Gets the list of registered listeners.
- * <p>
- * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
- * to the returned list will be present inside the object. This is why there is no {@code set} method for the
- * listeners property.
- * </p>
- *
- * @return The list of registered listeners.
- *
- * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable)
- */
- public final List<Listener> getListeners()
- {
- return this.listeners;
- }
- /**
- * Checks if a message at a given level is provided to the listeners of the instance.
- *
- * @param level The level to test.
- *
- * @return {@code true}, if messages at {@code level} are provided to the listeners of the instance;
- * {@code false}, if messages at {@code level} are not provided to the listeners of the instance.
- *
- * @throws NullPointerException if {@code level} is {@code null}.
- *
- * @see #getLogLevel()
- * @see #setLogLevel(java.util.logging.Level)
- */
- protected boolean isLoggable( final Level level )
- {
- if ( level == null )
- {
- throw new NullPointerException( "level" );
- }
- return level.intValue() >= this.getLogLevel().intValue();
- }
- /**
- * Notifies registered listeners.
- *
- * @param level The level of the event.
- * @param message The message of the event or {@code null}.
- * @param throwable The throwable of the event {@code null}.
- *
- * @throws NullPointerException if {@code level} is {@code null}.
- *
- * @see #getListeners()
- * @see #isLoggable(java.util.logging.Level)
- */
- protected void log( final Level level, final String message, final Throwable throwable )
- {
- if ( level == null )
- {
- throw new NullPointerException( "level" );
- }
- if ( this.isLoggable( level ) )
- {
- for ( final Listener l : this.getListeners() )
- {
- l.onLog( level, message, throwable );
- }
- }
- }
- @Override
- public org.apache.commons.cli.Options getOptions()
- {
- final org.apache.commons.cli.Options options = new org.apache.commons.cli.Options();
- options.addOption( Options.THREADS_OPTION );
- return options;
- }
- /**
- * Gets the {@code ExecutorService} used to run tasks in parallel.
- *
- * @param commandLine The {@code CommandLine} to use for setting up an executor service when not already created.
- *
- * @return The {@code ExecutorService} used to run tasks in parallel or {@code null}.
- *
- * @since 1.10
- */
- protected final ExecutorService getExecutorService( final CommandLine commandLine )
- {
- if ( this.executorService == null )
- {
- final String formular =
- commandLine.hasOption( Options.THREADS_OPTION.getOpt() )
- ? commandLine.getOptionValue( Options.THREADS_OPTION.getOpt() ).toLowerCase( new Locale( "" ) )
- : "1.0c";
- final Double parallelism =
- formular.contains( "c" )
- ? Double.valueOf( formular.replace( "c", "" ) ) * Runtime.getRuntime().availableProcessors()
- : Double.valueOf( formular );
- if ( parallelism.intValue() > 1 )
- {
- this.executorService = Executors.newFixedThreadPool(
- parallelism.intValue(), new ThreadFactory()
- {
- private final ThreadGroup group;
- private final AtomicInteger threadNumber = new AtomicInteger( 1 );
- {
- final SecurityManager s = System.getSecurityManager();
- this.group = s != null
- ? s.getThreadGroup()
- : Thread.currentThread().getThreadGroup();
- }
- @Override
- public Thread newThread( final Runnable r )
- {
- final Thread t =
- new Thread( this.group, r, "jomc-cli-" + this.threadNumber.getAndIncrement(), 0 );
- if ( t.isDaemon() )
- {
- t.setDaemon( false );
- }
- if ( t.getPriority() != Thread.NORM_PRIORITY )
- {
- t.setPriority( Thread.NORM_PRIORITY );
- }
- return t;
- }
- } );
- }
- }
- return this.executorService;
- }
- @Override
- public final int execute( final CommandLine commandLine )
- {
- if ( commandLine == null )
- {
- throw new NullPointerException( "commandLine" );
- }
- int status = STATUS_FAILURE;
- try
- {
- if ( this.isLoggable( Level.INFO ) )
- {
- this.log( Level.INFO, Messages.getMessage( "separator" ), null );
- this.log( Level.INFO, Messages.getMessage( "applicationTitle" ), null );
- this.log( Level.INFO, Messages.getMessage( "separator" ), null );
- this.log( Level.INFO, Messages.getMessage( "commandInfo", this.getName() ), null );
- }
- this.preExecuteCommand( commandLine );
- this.executeCommand( commandLine );
- status = STATUS_SUCCESS;
- }
- catch ( final Throwable t )
- {
- this.log( Level.SEVERE, null, t );
- status = STATUS_FAILURE;
- }
- finally
- {
- try
- {
- this.postExecuteCommand( commandLine );
- }
- catch ( final Throwable t )
- {
- this.log( Level.SEVERE, null, t );
- status = STATUS_FAILURE;
- }
- finally
- {
- if ( this.executorService != null )
- {
- this.executorService.shutdown();
- this.executorService = null;
- }
- }
- }
- if ( this.isLoggable( Level.INFO ) )
- {
- if ( status == STATUS_SUCCESS )
- {
- this.log( Level.INFO, Messages.getMessage( "commandSuccess", this.getName() ), null );
- }
- else if ( status == STATUS_FAILURE )
- {
- this.log( Level.INFO, Messages.getMessage( "commandFailure", this.getName() ), null );
- }
- this.log( Level.INFO, Messages.getMessage( "separator" ), null );
- }
- return status;
- }
- /**
- * Called by the {@code execute} method prior to the {@code executeCommand} method.
- *
- * @param commandLine The command line to execute.
- *
- * @throws NullPointerException if {@code commandLine} is {@code null}.
- * @throws CommandExecutionException if executing the command fails.
- *
- * @see #execute(org.apache.commons.cli.CommandLine)
- */
- protected void preExecuteCommand( final CommandLine commandLine ) throws CommandExecutionException
- {
- if ( commandLine == null )
- {
- throw new NullPointerException( "commandLine" );
- }
- }
- /**
- * Called by the {@code execute} method prior to the {@code postExecuteCommand} method.
- *
- * @param commandLine The command line to execute.
- *
- * @throws CommandExecutionException if executing the command fails.
- *
- * @see #execute(org.apache.commons.cli.CommandLine)
- */
- protected abstract void executeCommand( final CommandLine commandLine ) throws CommandExecutionException;
- /**
- * Called by the {@code execute} method after the {@code preExecuteCommand}/{@code executeCommand} methods even if
- * those methods threw an exception.
- *
- * @param commandLine The command line to execute.
- *
- * @throws NullPointerException if {@code commandLine} is {@code null}.
- * @throws CommandExecutionException if executing the command fails.
- *
- * @see #execute(org.apache.commons.cli.CommandLine)
- */
- protected void postExecuteCommand( final CommandLine commandLine ) throws CommandExecutionException
- {
- if ( commandLine == null )
- {
- throw new NullPointerException( "commandLine" );
- }
- }
- }