
de.tsl2.nano.incubation.terminal.SIShell Maven / Gradle / Ivy
/*
* File: $HeadURL$
* Id : $Id$
*
* created by: Tom
* created on: 13.12.2014
*
* Copyright: (c) Thomas Schneider 2014, all rights reserved
*/
package de.tsl2.nano.incubation.terminal;
import static de.tsl2.nano.incubation.terminal.TextTerminal.BLOCK_BAR;
import static de.tsl2.nano.incubation.terminal.TextTerminal.SCREEN_HEIGHT;
import static de.tsl2.nano.incubation.terminal.TextTerminal.SCREEN_WIDTH;
import static de.tsl2.nano.incubation.terminal.TextTerminal.getTextFrame;
import java.io.File;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.apache.commons.logging.Log;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.DefaultType;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementMap;
import org.simpleframework.xml.core.Commit;
import de.tsl2.nano.core.AppLoader;
import de.tsl2.nano.core.ENV;
import de.tsl2.nano.core.Finished;
import de.tsl2.nano.core.ManagedException;
import de.tsl2.nano.core.Messages;
import de.tsl2.nano.core.classloader.NetworkClassLoader;
import de.tsl2.nano.core.cls.PrivateAccessor;
import de.tsl2.nano.core.cls.UnboundAccessor;
import de.tsl2.nano.core.execution.CompatibilityLayer;
import de.tsl2.nano.core.execution.SystemUtil;
import de.tsl2.nano.core.log.LogFactory;
import de.tsl2.nano.core.serialize.XmlUtil;
import de.tsl2.nano.core.util.ConcurrentUtil;
import de.tsl2.nano.core.util.FileUtil;
import de.tsl2.nano.core.util.NetUtil;
import de.tsl2.nano.core.util.StringUtil;
import de.tsl2.nano.core.util.Util;
import de.tsl2.nano.incubation.platform.PlatformManagement;
import de.tsl2.nano.incubation.terminal.IItem.Type;
import de.tsl2.nano.incubation.terminal.item.Container;
import de.tsl2.nano.util.SchedulerUtil;
/**
* Structured Input Shell (SIShell) showing a textual manual. The configuration can be read through an xml file
* (standard name: {@link #DEFAULT_NAME}). For further informations, see {@link IItem}.
*
*
* Features:
* - simple input and output on small screens
* - input constraints check
* - configuration over xml
* - result will be written to a property map
* - tree nodes can be selected through numbers or names (the first unique characters are enough)
* - batch mode possible
* - macro recording and replaying
* - simplified java method calls
* - variable output sizes and styles
* - workflow conditions: items are active if an optional condition is true
* - if an item container (a tree) has only one visible item (perhaps filtered through conditions), it delegates directly to that item
* - actions get the entire environment properties (including system properties) on calling run().
* - sequential mode: if true, all tree items will be asked for in a sequential mode.
* - show ascii-pictures (transformed from pixel-images) for an item (description must point to an image file)
* - extends itself downloading required jars from network (if {@link #useNetworkExtension} ist true)
* - schedule mode: starts a scheduler for an action
* - selectors for files, content of files, class members, properties, csv-files etc.
* - administration modus to create/change/remove items, change terminal properties
*
*
* @author Tom
* @version $Revision$
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
@Default(value = DefaultType.FIELD, required = false)
public class SIShell implements IItemHandler, Serializable {
/** serialVersionUID */
private static final long serialVersionUID = -5767124822662015899L;
transient private static final Log LOG;
static {
LogFactory.setPrintToConsole(false);
LOG = LogFactory.getLog(SIShell.class);
}
/** used as file name */
@Attribute
String name = DEFAULT_NAME;
/** utility to read user input */
transient Scanner input;
transient InputStream in;
transient PrintStream out;
@Attribute
int width = SCREEN_WIDTH;
/** if height < 1, the height will be dynamic to the items height */
@Attribute
int height = SCREEN_HEIGHT;
@Attribute
int style = BLOCK_BAR;
@Attribute
boolean bars = true;
/** item properties */
transient Properties env;
/** default: false. if true, on each terminal save, the terminals xml serialization file will be stored. */
@Attribute(required = false)
boolean refreshConfig = false;
transient Exception lastException;
/**
* predefined variables (not changable through user input) copied to the {@link #env} but not saved in property
* file. mostly technical definitions.
*/
@ElementMap(entry = "definition", attribute = true, inline = true, keyType = String.class, key = "name", required = false, value = "value", valueType = Object.class)
Map definitions;
/** batch file name. the batch file contains input instructions (numbers or names) separated by '\n'. */
@Element(required = false)
String batch;
/** base item - should be a Selection */
@Element
IItem root;
/** useNetworkExtension */
@Attribute
boolean useNetworkExtension = true;
/**
* true, if macro recording was started through {@link #KEY_MACRO_RECORD} and not yet stoped with
* {@link #KEY_MACRO_STOP}
*/
transient boolean isRecording;
/** if true, all tree items will be accessed directly and sequentially */
@Attribute(required = false)
boolean sequential = false;
@Element(required = false)
String clearScreenCmd = AppLoader.isUnix() ? "clear" : "cls";
/** command identifier */
static final String KEY_COMMAND = ":";
/*
* available commands
*/
static final String KEY_HELP = "help";
/** prints system informations */
static final String KEY_INFO = "info";
/** prints content of all platform MBeans (JMX) */
static final String KEY_PLATFORMINFO = "platform";
/** prints all system properties */
static final String KEY_PROPERTIES = "properties";
/** starts macro recording. user input will be stored to {@link #batch} and saved on terminal end. */
static final String KEY_MACRO_RECORD = "record";
/** stops macro recording */
static final String KEY_MACRO_STOP = "stop";
/** starts a scheduler for the given action */
static final String KEY_SCHEDULE = "schedule";
/** starts the given reflection expression on a given property */
static final String KEY_REFLECT = "reflect";
static final String KEY_ADMIN = "admin";
public static final String KEY_SEQUENTIAL0 = "sequential";
static final String KEY_USENETWORKEXTENSION = "network";
public static final String KEY_LASTEXCEPTION = "exception";
/** saves the current state to xml and property files */
static final String KEY_SAVE = "save";
/** quits the terminal */
static final String KEY_QUIT = "quit";
public static final String PREFIX = "sishell.";
public static final String KEY_NAME = PREFIX + "name";
public static final String KEY_WIDTH = PREFIX + "width";
public static final String KEY_HEIGHT = PREFIX + "height";
public static final String KEY_SEQUENTIAL = PREFIX + "sequential";
/** default script file name */
public static final String DEFAULT_NAME = PREFIX + "xml";
static final String ASK_ENTER = ENV.translate("ask.enter", false);
private static final String LOGO = "tsl2nano.logo.png";
public SIShell() {
initDeserialization();
}
public SIShell(IItem root) {
this(root, System.in, System.out);
}
public SIShell(IItem root, InputStream in, PrintStream out) {
this(root, in, out, SCREEN_WIDTH, SCREEN_HEIGHT, BLOCK_BAR, null);
}
public SIShell(IItem root, InputStream in, PrintStream out, int width, int height, int style) {
this(root, in, out, width, height, style, null);
}
/**
* constructor
*
* @param root
* @param input
* @param in
* @param out
*/
public SIShell(IItem root,
InputStream in,
PrintStream out,
int width,
int height,
int style,
Map defintions) {
super();
this.root = root;
this.in = in;
this.out = out;
this.width = width;
this.height = height;
this.style = style;
this.definitions = defintions;
this.env = createEnvironment(name, defintions);
}
static Properties createEnvironment(String name, Map definitions) {
String p = name + ".properties";
Properties env = new File(p).getAbsoluteFile().canRead() ? FileUtil.loadPropertiesFromFile(p) : new Properties();
if (definitions != null) {
env.putAll(definitions);
}
return env;
}
public static SIShell create(String file) {
File ffile = new File(file);
//do a pre-check on the toolbox configuration file which needs ant to be available
if (file.contains(DEFAULT_NAME) && ffile.getAbsoluteFile().exists() && !NetUtil.isOnline()
&& !new CompatibilityLayer().isAvailable("org.apache.tools.ant.Task"))
throw new ManagedException("error.ant.missing", file);
SIShell t =
ffile.exists() ? XmlUtil.loadXml(file, SIShell.class) : new SIShell(new Container(file, null));
t.name = file;
return t;
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
try {
LOG.info("starting SI-Shell " + name);
input = new Scanner(in);
prepareEnvironment(env, root);
// //welcome screen
if (!isInBatchMode()) {
printAsciiImage(LOGO, new PrintWriter(out), width, height > 0 ? height : 22, true, bars);
nextLine(in);
}
//if only one tree-item available, go to that item
if (root instanceof Container) {
root = ((Container) root).delegateToUniqueChild(root, in, out, env);
}
serve(root, in, out, env);
shutdown();
} catch (Finished ex) {
shutdown();
} catch (Exception ex) {
ManagedException.forward(ex);
} finally {
if (input != null) {
input.close();
}
}
}
public static void printAsciiImage(String name,
PrintWriter out,
int width,
int height,
boolean resource,
boolean bars) {
try {
if (resource) {
new AsciiImage(bars ? AsciiImage.BARS : AsciiImage.CHARS, AsciiImage.RGB).convertToAscii(
ImageIO.read(FileUtil.getResource(name)), out, width, height).flush();
} else {
new AsciiImage().convertToAscii(name, out, width, height).flush();
}
} catch (Exception e) {
//it's only a logo, no problem (perhaps on android)
LOG.error(e.toString());
}
}
/**
* see {@link #definitions}
*
* @return Returns the definitions.
*/
public Map getDefinitions() {
return definitions;
}
/**
* see {@link #definitions}
*
* @param definitions The definitions to set.
*/
public void setDefinitions(Map definitions) {
this.definitions = definitions;
}
protected void shutdown() {
save(refreshConfig || !new File(name).getAbsoluteFile().exists());
String shutdownInfo =
ENV.translate("message.shutdown", false, name);
printScreen(shutdownInfo, in, out, null, width, height, style, true, isInBatchMode());
LOG.info("si-shell " + name + " ended");
}
protected void save(boolean saveConfiguration) {
if (saveConfiguration) {
XmlUtil.saveXml(name, this);
}
Set
© 2015 - 2025 Weber Informatics LLC | Privacy Policy