package org.kisst.gft;
import freemarker.template.Template;
import org.kisst.cfg4j.BooleanSetting;
import org.kisst.cfg4j.CompositeSetting;
import org.kisst.gft.TaskStarter.JmsTaskCreator;
import org.kisst.gft.action.*;
import org.kisst.gft.filetransfer.FileServer;
import org.kisst.gft.poller.Poller;
import org.kisst.gft.ssh.As400SshHost;
import org.kisst.gft.ssh.SshFileServer;
import org.kisst.gft.ssh.WindowsSshHost;
import org.kisst.gft.task.TaskDefinition;
import org.kisst.http4j.BasicHttpHostMap;
import org.kisst.http4j.HttpHost;
import org.kisst.http4j.HttpHostMap;
import org.kisst.jms.JmsMessage;
import org.kisst.jms.JmsSystem;
import org.kisst.jms.MessageHandler;
import org.kisst.props4j.LayeredProps;
import org.kisst.props4j.MultiProps;
import org.kisst.props4j.Props;
import org.kisst.props4j.SimpleProps;
import org.kisst.util.JamonUtil;
import org.kisst.util.JamonUtil.JamonThread;
import org.kisst.util.ReflectionUtil;
import org.kisst.util.TemplateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.lang.reflect.Constructor;
import java.text.SimpleDateFormat;
import java.util.*;
public class GftContainer implements HttpHostMap, ActionCreator, MessageHandler {
final static Logger logger=LoggerFactory.getLogger(GftContainer.class);
public static class Settings extends CompositeSetting {
public Settings(CompositeSetting parent, String name) { super(parent, name); }
public final BooleanSetting continueIfConfigBroken=new BooleanSetting(this, "continueIfConfigBroken", false);
}
private final GftWrapper wrapper;
private final Settings settings;
private final TaskStarter starter = new TaskStarter();
public final Props props;
private final Props topProps;
public final boolean configBroken;
private final BasicHttpHostMap httpHosts;
public final HashMap<String, TaskDefinition> channels= new LinkedHashMap<String, TaskDefinition>();
private final HashMap<String, Class<?>> actions= new LinkedHashMap<String, Class<?>>();
//public final HashMap<String, HttpHost> httphosts= new LinkedHashMap<String, HttpHost>();
public final Map<String, Integer> tags= new TreeMap<>();
public final HashMap<String, SshFileServer> sshhosts= new LinkedHashMap<String, SshFileServer>(); // TODO: make private
//private final HashMap<String, Module > modules=new LinkedHashMap<String, Module>();
private final HashMap<String, Constructor<?>> channelTypes=new LinkedHashMap<String, Constructor<?>>();
private final SimpleProps context = new SimpleProps();
public final HashMap<String, Poller> pollers= new LinkedHashMap<String, Poller>();
private final String tempdir;
private int directorySequenceNumber=0;
private JamonThread jamonThread;
public final Date startupTime = new Date();
public Date getStartupTime() { return startupTime; }
public void addAction(String name, Class<?> cls) {
actions.put(name, cls);
}
public GftContainer(GftWrapper wrapper, String topname, Props config) {
this.wrapper = wrapper;
this.settings=new Settings(null, topname);
context.put(topname, this);
topProps = config;
props=topProps.getProps(wrapper.getTopname());
addAction("local_command", LocalCommandAction.class);
addAction("delete_local_file", DeleteLocalFileAction.class);
addAction("send_message_from_file",SendMessageFromFileAction.class);
wrapper.initGftFromModules(this);
httpHosts = new BasicHttpHostMap(props.getProps("http.host"));
tempdir = props.getString("global.tempdir"); //MAYBE: provide default as working directory+"/temp"
configBroken=init();
}
//public String getHostName() { return hostName; }
public Set<String> getFileServerNames() { return sshhosts.keySet(); }
public FileServer getFileServer(String name) {
SshFileServer result = sshhosts.get(name);
if (result==null)
throw new IllegalArgumentException("Unknown fileserver name "+name);
return result;
}
public SimpleProps getContext() {return context; }
public String getTopname() { return wrapper.getTopname(); }
public String getHostName() { return wrapper.getHostName(); }
public HttpHost getHttpHost(String name) { return httpHosts.getHttpHost(name); }
public Set<String> getHttpHostNames() { return httpHosts.getHttpHostNames(); }
public boolean isConfigBroken() { return configBroken; }
public Props getGlobalProps() { return props.getProps("global"); }
public JmsSystem getQueueSystem(String queueSystemName) { return wrapper.getQueueSystem(queueSystemName);}
public String getMainQueue() { return wrapper.getMainQueue(); }
@Override public boolean handle(JmsMessage msg) { return starter.handle(msg); }
private boolean init() {
boolean configBroken=false;
context.put("global", props.get("global", null));
//String omgeving = getGlobalProps().getString("omgeving",null);
//SimpleProps var=new SimpleProps();
//var.put("omgevingNaar", omgeving);
//var.put("herkomstOmgeving", omgeving);
Object defaultVars = props.get("defaultVars", null);
if (defaultVars instanceof Props)
context.put("var", defaultVars);
if (props.get("ssh.host",null)!=null) {
Props hostProps=props.getProps("ssh.host");
for (String name: hostProps.keys()) {
Props p=hostProps.getProps(name);
String type=p.getString("type",null);
if ("WINDOWS".equals(type))
sshhosts.put(name, new WindowsSshHost(p));
//else if ("UNIX".equals(type))
// sshhosts.put(name, new RemoteFileServer(p));
else if ("AS400".equals(type))
sshhosts.put(name, new As400SshHost(p));
else
throw new RuntimeException("property type for "+name+".ssh.host."+name+" should be WINDOWS, AS400 or UNIX, not "+type);
}
}
Props channelProps=props.getProps("channel");
for (String name: channelProps.keys()) {
try {
addChannel(name, channelProps.getProps(name));
}
catch (RuntimeException e) {
logger.error("Error when loading channel "+name, e);
configBroken=true;
channels.put(name, new BrokenChannel(this, props, e));
}
}
if (props.hasKey("poller")) {
Props pollerProps=props.getProps("poller");
for (String name: pollerProps.keys()) {
try {
//pollers.put(name, new Poller(this, pollerProps.getProps(name)));
pollers.put(name, new Poller(this, name, pollerProps.getProps(name)));
}
catch (RuntimeException e) {
logger.error("Error when loading poller "+name, e);
configBroken=true;
channels.put(name, new BrokenChannel(this, props, e));
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Using props "+props);
for (String name: channels.keySet())
logger.info("Channel {}\t{}",name,channels.get(name));
for (String name: actions.keySet())
logger.info("Action {}\t{}",name,actions.get(name));
for (String name: getHttpHostNames())
logger.info("HttpHost {}\t{}",name,getHttpHost(name));
for (String name: sshhosts.keySet())
logger.info("SshHost {}\t{}",name,sshhosts.get(name));
//for (String name: listeners.keySet())
// logger.info("Listener {}\t{}",name,listeners.get(name));
}
return configBroken;
}
private void addChannel(String name, Props channelprops) {
if (channelprops.nrofKeys()==1 && channelprops.getString("tags",null)!=null) {
logger.warn("Ignoring channel "+name+" because it only has tags");
return;
}
MultiProps lprops=new MultiProps(channelprops,getGlobalProps());
String type=lprops.getString("type","Default");
Constructor<?> cons=channelTypes.get(type);
if (cons==null) {
String typenames="";
String komma="";
for (String name2: channelTypes.keySet()) {
typenames+=komma+name2;
komma=",";
}
throw new RuntimeException("Unknown Channel type in channel "+name+": "+type+" only the following types are allowed "+typenames);
}
TaskDefinition channel = (TaskDefinition) ReflectionUtil.createObject(cons, new Object[] {this, lprops} );
channels.put(name, channel);
}
public TaskDefinition getTaskDefinition(String name) { if (name==null) return null; else return channels.get(name.trim()); }
public String processTemplate(File template, Object context) { return TemplateUtil.processTemplate(template, context); }
public String processTemplate(String templateText, Object context) { return TemplateUtil.processTemplate(templateText, context); }
public String processTemplate(Template template, Object context) { return TemplateUtil.processTemplate(template, context); }
public void start() {
logger.info("Starting GftContainer on host "+getHostName());
logger.info("Trying to log to LogService, if this hangs the database user might be disabled");
LogService.log("info", "StartingContainer", getTopname().toUpperCase()+"-Service", getHostName(), "Starting "+getTopname().toUpperCase()+" Container");
logger.info("Succeeded to log to LogService");
this.jamonThread = new JamonThread(props);
Thread t = new Thread(jamonThread);
t.setDaemon(true);
t.start();
if (logger.isDebugEnabled()){
logger.debug("Starting new GftContainer with props {}", props.toString());
}
for (Poller p : pollers.values())
p.start();
}
public void reset() {
logger.info("Resetting GftContainer on host "+getHostName());
LogService.log("info", "ResettingContainer", getTopname().toUpperCase()+"-Service", getHostName(), "Reset called for "+getTopname().toUpperCase()+" Container");
JamonUtil.jamonLog(props, "RESET called, dumping all statistics");
jamonThread.reset();
for (Poller p: pollers.values())
p.reset();
}
public void stop() {
logger.info("Stopping existing GftContainer on host "+getHostName());
LogService.log("info", "StoppingContainer", getTopname().toUpperCase()+"-Service", getHostName(), "Stopping "+getTopname().toUpperCase()+" Container");
for (Poller p : pollers.values())
p.stop();
}
private synchronized int getUniqueSequenceNumber() {
directorySequenceNumber++;
if (directorySequenceNumber>1000000)
directorySequenceNumber=0;
return directorySequenceNumber;
}
public File createUniqueDir(String subdir){
SimpleDateFormat formatter=new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
String date = formatter.format(new Date());
int volgnummer=getUniqueSequenceNumber();
File file = new File(tempdir +"/"+ subdir +"/"+ date + "-" + volgnummer);
file.mkdirs();
return file;
}
public void registerDefinitionType(Class<?> cls) { registerDefinitionType(cls.getSimpleName(), cls); }
public void registerDefinitionType(String name, Class<?> cls) {
if (channelTypes.get(name)!=null)
throw new RuntimeException("TaskDefinitionType "+name+" is already registerd to class "+channelTypes.get(name)+" when trying to register it for class "+cls);
Constructor<?> cons = ReflectionUtil.getConstructor(cls, new Class<?>[] { GftContainer.class, Props.class} );
channelTypes.put(name, cons);
}
public void appendJmsTaskCreator(JmsTaskCreator creator) {starter.appendCreator(creator); }
private LayeredProps getActionProps(Props channelprops, Class<?> clz, String actionname) {
LayeredProps lprops=new LayeredProps(getGlobalProps());
SimpleProps top=new SimpleProps();
//top.put("action",taskdef.gft.actions.get(name));
top.put("channel",channelprops);
lprops.addLayer(top);
if (channelprops.get(clz.getSimpleName(),null) instanceof Props)
lprops.addLayer(channelprops.getProps(clz.getSimpleName()));
if (actionname!=null && channelprops.get(actionname,null) instanceof Props)
lprops.addLayer(channelprops.getProps(actionname));
lprops.addLayer(channelprops);
//lprops.addLayer(taskdef.gft.props.getProps("gft.global"));
return lprops;
}
public Action createAction(TaskDefinition taskdef, String actionname) {
return createAction(taskdef, actions.get(actionname), actionname);
}
public Action createAction(TaskDefinition taskdef, Class<?> cls) {
return createAction(taskdef, cls, cls.getSimpleName());
}
private Action createAction(TaskDefinition taskdef, Class<?> clz, String actionname) {
if (clz==null)
throw new IllegalArgumentException("Unknown action name "+actionname+" when definining "+taskdef.getName());
Props actionprops=getActionProps(taskdef.getProps(), clz, actionname);
Constructor<?> c = ReflectionUtil.getFirstCompatibleConstructor(clz, new Class<?>[] {TaskDefinition.class, Props.class} );
if (c!=null)
return (Action) ReflectionUtil.createObject(c, new Object[] {taskdef, actionprops} );
c = ReflectionUtil.getConstructor(clz, new Class<?>[] {GftContainer.class, Props.class} );
if (c!=null)
return (Action) ReflectionUtil.createObject(c, new Object[] {this, actionprops} );
c = ReflectionUtil.getConstructor(clz, new Class<?>[] {Props.class} );
if (c!=null)
return (Action) ReflectionUtil.createObject(c, new Object[] {actionprops} );
return (Action) ReflectionUtil.createObject(clz);
}
public void addTags(Object obj, String tags) {
if (tags==null)
return;
tags=tags.trim();
if (tags.length()==0)
return;
for (String t: tags.split("[,]")) {
t = t.trim();
Integer old = this.tags.get(t);
if (old == null)
this.tags.put(t, 1);
else
this.tags.put(t, 1 + old);
}
}
}