package org.kisst.gft; import org.kisst.cfg4j.BooleanSetting; import org.kisst.cfg4j.CompositeSetting; import org.kisst.gft.admin.*; import org.kisst.gft.admin.status.*; import org.kisst.jms.*; import org.kisst.props4j.Props; import org.kisst.props4j.SimpleProps; import org.kisst.servlet4j.AbstractServlet; import org.kisst.util.*; import org.kisst.util.JamonUtil.JamonThread; import org.kisst.util.JarLoader.ModuleInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.UnknownHostException; import java.util.*; public class GftWrapper implements MessageHandler { final static Logger logger = LoggerFactory.getLogger(GftWrapper.class); public final Props props; public Props getProps() {return props; } public static class Settings extends CompositeSetting { public Settings(CompositeSetting parent, String name) { super(parent, name); } public final JarLoader.Settings modules = new JarLoader.Settings(this, "modules"); public final BooleanSetting continueIfConfigBroken = new BooleanSetting(this, "continueIfConfigBroken", false); } private final Settings settings; private final String topname; private final AdminServer admin; //public final Props props; //private final SimpleProps topProps; GftContainer gft=null; private final HashMap<String, Module> modules = new LinkedHashMap<String, Module>(); private JamonThread jamonThread; private final JarLoader jarloader; private final String hostName; private final File configfile; public final Date startupTime = new Date(); public final HashMap<String, MultiListener> listeners= new LinkedHashMap<String, MultiListener>(); private final HashMap<String,JmsSystem> queueSystem = new LinkedHashMap<String,JmsSystem>(); private final ArrayList<StatusItem> statusItems =new ArrayList<StatusItem>(); public GftWrapper(String topname, File configfile, Class<? extends Module>[] moduleClasses) { this.topname = topname; this.settings = new Settings(null, topname); this.configfile = configfile; this.hostName = determineHostName(); TemplateUtil.init(configfile.getParentFile()); Props topProps = new SimpleProps(this.configfile); props = topProps.getProps(this.topname); setLocale(props); admin = new AdminServer(props); jarloader = new JarLoader(settings.modules, topProps); addDynamicModules(props); for (Class<? extends Module> mod: moduleClasses) addModule(mod, props); loadModuleSpecificCryptoKey(); for (Module mod : modules.values()) mod.init(this, props); loadQueuesSystems(props); loadListeners(props); boolean continueIfConfigBroken = settings.continueIfConfigBroken.get(topProps); String message = loadGft(continueIfConfigBroken); if (message!=null) { message = "Errors during initialization, using property " + settings.continueIfConfigBroken.getFullName() + "=" + continueIfConfigBroken+": "+message; if (continueIfConfigBroken) logger.error(message); else throw new RuntimeException("FATAL " + message); } addServlets(props); startJamonThread(props); } public GftContainer getCurrentGft() { return gft; } public Date getStartupTime() { return startupTime; } //public ClassLoader getSpecialClassLoader() { return jarloader.getClassLoader(); } public String getTopname() { return topname; } public String getHostName() { return hostName; } @Override public boolean handle(JmsMessage msg) { return gft.handle(msg);} private String determineHostName() { try { return java.net.InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { throw new RuntimeException(e); } } private void setLocale(Props props) { if (props.getString("timezone", null) != null) TimeZone.setDefault(TimeZone.getTimeZone(props.getString("timezone"))); if (props.getString("locale", null) != null) Locale.setDefault(new Locale(props.getString("locale"))); } private void addServlets(Props props) { MultiListener listener = listeners.get("main"); statusItems.add(new QueueStatus(this, listener, "input")); statusItems.add(new QueueStatus(this, listener, "error")); statusItems.add(new ProblematicPollerFiles(this)); statusItems.add(new InProgressPollerFiles(this)); statusItems.add(new NotListeningListenerThreads(this,listeners)); addServlet("/listener", new ListenerServlet(listeners, props)); addServlet("/channel", new ChannelServlet(this)); addServlet("/poller", new PollerServlet(this)); addServlet("/dir", new DirectoryServlet(this)); addServlet("/message", new JmsMessageServlet(this, props)); addServlet("/config", new ConfigServlet(this)); //handlerMap.put("/restart", new RestartServlet(gft)); addServlet("/reset", new ResetServlet(this)); addServlet("/reload", new ReloadServlet(this)); //handlerMap.put("/shutdown", new ShutdownServlet(gft)); addServlet("/encrypt", new EncryptServlet(this)); addServlet("default", new HomeServlet(this)); for (StatusItem item: statusItems) addServlet("/"+item.getUrl(), item); } private void loadListeners(Props props) { SimpleProps context = new SimpleProps(); context.put(topname, this); for (String lname : props.getProps("listener").keys()) { Props listenerprops = props.getProps("listener." + lname); String queueSystemName = listenerprops.getString("queueSystem", "main"); MultiListener listener = new MultiListener(getQueueSystem(queueSystemName), this, listenerprops, context); listeners.put(lname, listener); } } private void loadQueuesSystems(Props props) { Props qmhostProps = props.getProps("mq.host"); for (String name : qmhostProps.keys()) { Props qmprops = qmhostProps.getProps(name); String type = qmprops.getString("type"); if ("ActiveMq".equals(type)) queueSystem.put(name, new ActiveMqSystem(qmprops)); else if ("Jms".equals(type)) queueSystem.put(name, new JmsSystem(qmprops)); else throw new RuntimeException("Unknown type of queueing system " + type); } } public void addContext(HashMap<String, Object> root) { root.put("wrapper", this); root.put("gft", getCurrentGft()); root.put("channels", getCurrentGft().channels); root.put("pollers", getCurrentGft().pollers); root.put("listeners", listeners); root.put("modules", getModuleInfo()); root.put("statusItems", statusItems); root.put("tags", getCurrentGft().tags); for (StatusItem item :statusItems) item.refresh(); } public String getMainQueue() { for (MultiListener l : listeners.values()) return l.getInputQueue(); throw new RuntimeException("No main queue defined"); } public JmsSystem getQueueSystem(String name) { JmsSystem result = queueSystem.get(name); if (result==null) throw new RuntimeException("Unknown queueSystem "+name); return result; } public String reload() { if (gft!=null) { LogService.log("info", "ReloadingContainer", getTopname().toUpperCase() + "-Service", getHostName(), "Reloading " + getTopname().toUpperCase()); logger.info("Reloading GftContainer on host "+getHostName()); } return loadGft(false); } private synchronized String loadGft(boolean continueIfBroken) { GftContainer oldGft=gft; Props topProps = new SimpleProps(this.configfile); Props props = topProps.getProps(this.topname); GftContainer newGft=new GftContainer(this, topname, topProps); if (newGft.configBroken && ! continueIfBroken) { return "TODO: reason"; } gft=newGft; gft.start(); if (oldGft!=null) { LogService.log("info", "ReloadingContainer", getTopname().toUpperCase() + "-Service", getHostName(), "Start of new Container successful, now stopping old Container"); oldGft.stop(); } return null; } public void addServlet(String url, AbstractServlet servlet) { admin.addServlet(url, servlet); } public void start() { logger.info("Starting GftWrapper on host " + getHostName()); for (MultiListener q : listeners.values() ) q.start(); admin.startListening(); } private void startJamonThread(Props props) { this.jamonThread = new JamonThread(props); Thread t = new Thread(jamonThread); t.setDaemon(true); t.start(); } public void join() { admin.join(); } public void reset() { logger.info("Resetting GftContainer on host " + getHostName()); LogService.log("info", "ResettingContainer", getTopname().toUpperCase() + "-Service", getHostName(), "Reset called for " + getTopname().toUpperCase() + " Service"); JamonUtil.jamonLog(gft.props, "RESET called, dumping all statistics"); jamonThread.reset(); } public void stop() { logger.info("Stopping GftContainer on host " + getHostName()); LogService.log("info", "StoppingContainer", getTopname().toUpperCase() + "-Service", getHostName(), "Stopping " + getTopname().toUpperCase() + " Service"); JamonUtil.jamonLog(gft.props, "STOP called, dumping all statistics"); jamonThread.stop(); for (MultiListener q : listeners.values() ) q.stop(); for (JmsSystem q : queueSystem.values()) q.stop(); admin.stopListening(); } // This method is called from the constructor of the GFT, before it reads the channels etc void initGftFromModules(GftContainer gft) { for (Module mod: modules.values()) mod.initGft(gft); } @SuppressWarnings("unchecked") private void addDynamicModules(Props props) { //addModule(FileTransferModule.class, props); for (Class<?> cls: jarloader.getMainClasses()) { addModule((Class<? extends Module>) cls, props); } } private void addModule(Class<? extends Module> cls, Props props) { boolean disabled = props.getBoolean("module."+cls.getSimpleName()+".disabled", false); try { if (disabled) return; Module mod=null; Constructor<?> cons=ReflectionUtil.getConstructor(cls, new Class<?>[] {GftWrapper.class, Props.class}); if (cons!=null) mod= (Module) ReflectionUtil.createObject(cons, new Object[] {this, props}); else { // use default constructor cons=ReflectionUtil.getConstructor(cls, new Class<?>[] {}); mod= (Module) ReflectionUtil.createObject(cons, new Object[] {}); } modules.put(mod.getName(), mod); } catch (Exception e) { throw new RuntimeException("Could not load module class "+cls.getSimpleName(), e); } } // This method is to give modules a way to set a cryptographic key very early in startup process. // especially before the Host list with encrypted passwords is initialized private void loadModuleSpecificCryptoKey() { for (Module mod: modules.values()) { CryptoUtil.checkKeySetter(mod); } } public List<ModuleInfo> getModuleInfo() { return jarloader.getModuleInfo(); } public String getVersion() { InputStream in = GftContainer.class.getResourceAsStream("/version.properties"); if (in == null) return "unknown-version"; Properties props = new Properties(); try { props.load(in); } catch (IOException e) { throw new RuntimeException(e); } return props.getProperty("project.version"); } }