All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.continual.util.console.ConsoleProgram Maven / Gradle / Ivy

There is a newer version: 0.3.14
Show newest version
/*
 *	Copyright 2019, Continual.io
 *
 *	Licensed 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 io.continual.util.console;

import java.io.File;
import java.io.FilenameFilter;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.continual.util.nv.NvReadable;
import io.continual.util.nv.NvReadable.InvalidSettingValueException;
import io.continual.util.nv.NvReadable.LoadException;
import io.continual.util.nv.NvReadable.MissingReqdSettingException;
import io.continual.util.nv.NvWriteable;
import io.continual.util.nv.impl.nvEnvProperties;
import io.continual.util.nv.impl.nvInstallTypeWrapper;
import io.continual.util.nv.impl.nvReadableStack;
import io.continual.util.nv.impl.nvWriteableTable;

/**
 * A console program runs on the command line.
 * 

* The console class expects the program's main() routine to call its * runFromMain() method to start the system. */ public class ConsoleProgram { public static class UsageException extends Exception { public UsageException ( String correctUsage ) { super(correctUsage); } public UsageException ( Exception cause ) { super(cause); } private static final long serialVersionUID = 1L; } public static class StartupFailureException extends Exception { public StartupFailureException ( Exception x ) { super(x); } public StartupFailureException ( String msg ) { super(msg); } public StartupFailureException ( String msg, Exception x ) { super(msg,x); } private static final long serialVersionUID = 1L; } /** * A looper is an object that is run repeatedly (in a loop). This class is * what the main thread of the program does between startup and exit. */ public interface Looper { /** * setup the looper and return true to continue. Called once. * @param prefs the preferences structure * @param cmdLine command line preferences * @return true/false */ boolean setup ( NvReadable prefs, CmdLinePrefs cmdLine ); /** * Run a loop iteration, return true to continue, false to exit. (Note * that nothing requires this implementation to do a small amount of * work vs. lengthy processing.) * @param prefs the preferences structure * @return true to continue, false to exit */ boolean loop ( NvReadable prefs ); /** * teardown the looper. called once. * @param prefs the preferences structure */ void teardown ( NvReadable prefs ); } protected CmdLineParser getCmdLineParser () { return fCmdLineParser; } protected ConsoleProgram () { fHostInfo = new nvWriteableTable (); fDefaults = new nvWriteableTable (); fCmdLineParser = new CmdLineParser (); } public void runFromMain ( String[] args ) throws UsageException, LoadException, MissingReqdSettingException, InvalidSettingValueException, StartupFailureException { // get setup installShutdownHook (); setupHostInfo (); setupDefaults ( fDefaults ); setupOptions ( fCmdLineParser ); // parse the command line final CmdLinePrefs cmdLine = fCmdLineParser.processArgs ( args ); // build a preferences stack final nvReadableStack stack = new nvReadableStack (); stack.push ( fHostInfo ); // sets 'hostname' stack.push ( new nvEnvProperties() ); // makes system environment available stack.push ( fDefaults ); // app defaults stack.push ( cmdLine ); // settings from command line // wrap the settings stack with the install-type suffix check, // which lets you set -Drr.installation=foo on the Java cmd line // and then have settings like mySetting[foo]=bar as specialization // over mySetting=theUsualValue. final nvInstallTypeWrapper wrapper = new nvInstallTypeWrapper ( stack ); // optionally load more configuration from the app. If provided, // it fits in the stack below command line settings final NvReadable config = loadAdditionalConfig ( wrapper ); if ( config != null ) { stack.pushBelow ( config, cmdLine ); wrapper.rescan (); } // init and get the run loop final Looper l = init ( wrapper, cmdLine ); if ( l != null ) { if ( l.setup ( wrapper, cmdLine ) ) { while ( l.loop ( wrapper ) ) {} l.teardown ( wrapper ); } } cleanup (); } /** * Override this to handle an abrupt shutdown. This method is called when the system exits. */ protected void onShutdown () { } /** * Override this to setup default settings for the program. * @param pt a writeable preferences table in which to add defaults * @return this console program */ protected ConsoleProgram setupDefaults ( NvWriteable pt ) { return this; } /** * Override this to setup recognized command line options. Note that default values * provided to the command line reader are NOT available from init's settings instance. * That's because the settings system doesn't have a way to differentiate between having * a key and having a key as a default value. When stacked, if the command line parser * states that it has a key, then any explicit setting further down the stack will not * be used. * @param p a command line parser * @return this console program */ protected ConsoleProgram setupOptions ( CmdLineParser p ) { return this; } /** * Override this to load additional configuration. If a non-null config is returned, * it's inserted into the preferences stack between the default settings and the command line * settings. That way, the command line arguments have precedence. * @param currentPrefs the current preferences * @return the updated configuration * @throws NvReadable.LoadException if the system cannot load additional preferences * @throws NvReadable.MissingReqdSettingException if a required setting is missing */ protected NvReadable loadAdditionalConfig ( NvReadable currentPrefs ) throws LoadException, MissingReqdSettingException { return null; } /** * Init the program and return a loop instance if the program should continue. The base * class returns null, so you have to override this to do anything beyond init. * @param p settings * @param cmdLine command line values * @return non-null to continue, null to exit * @throws NvReadable.MissingReqdSettingException if a required setting is missing * @throws NvReadable.InvalidSettingValueException if a setting is invalid * @throws StartupFailureException if the program couldn't start */ protected Looper init ( NvReadable p, CmdLinePrefs cmdLine ) throws MissingReqdSettingException, InvalidSettingValueException, StartupFailureException { return null; } /** * Override this to run any cleanup code after the main loop. */ protected void cleanup () {} /** * expand a file argument ("*" matches, etc.) * @param arg a file argument, possibly with wildcards * @return a list of files that match the argument */ protected List expandFileArg ( String arg ) { final LinkedList fileList= new LinkedList (); final File file = new File ( arg ); final File parentDir = file.getParentFile (); if ( parentDir != null ) { final String matchPart = file.getName ().replace ( "*", ".*" ); // cmd line regex to java regex final Pattern p = Pattern.compile ( matchPart ); final File[] files = parentDir.listFiles ( new FilenameFilter () { @Override public boolean accept ( File dir, String name ) { return p.matcher ( name ).matches (); } } ); if ( files != null ) { for ( File f : files ) { fileList.add ( f ); } } } return fileList; } private final CmdLineParser fCmdLineParser; private final nvWriteableTable fDefaults; private final nvWriteableTable fHostInfo; private static final Logger log = LoggerFactory.getLogger ( ConsoleProgram.class ); private void setupHostInfo () { try { fHostInfo.set ( "hostname", InetAddress.getLocalHost().getHostName() ); } catch ( UnknownHostException e ) { log.warn ( "Couldn't establish hostname.", e ); } } private void installShutdownHook () { Runtime.getRuntime ().addShutdownHook ( new Thread () { @Override public void run () { onShutdown (); } } ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy