org.springfield.lou.application.ApplicationManager.java Source code

Java tutorial

Introduction

Here is the source code for org.springfield.lou.application.ApplicationManager.java

Source

/* 
* ApplicationManager.java
* 
* Copyright (c) 2012 Noterik B.V.
* 
* This file is part of Lou, related to the Noterik Springfield project.
*
* Lou is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Lou is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Lou.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.springfield.lou.application;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.commons.codec.binary.Base64;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.springfield.fs.*;
import org.springfield.lou.homer.LazyHomer;
import org.springfield.lou.screen.Screen;
import org.springfield.lou.servlet.LouServlet;
import org.springfield.marge.Marge;
import org.springfield.marge.MargeObserver;
// org.springfield.lou.maggie.MaggieLoader;
import org.springfield.mojo.interfaces.ServiceInterface;
import org.springfield.mojo.interfaces.ServiceManager;

/**
 * Application manager
 * 
 * @author Daniel Ockeloen
 * @copyright Copyright: Noterik B.V. 2012
 * @package org.springfield.lou.application
 *
 */
public class ApplicationManager extends Thread implements MargeObserver {
    // temp LouServlet.addUrlTrigger("/lou/" + getDomain() + "/" + jumper + ",/lou/domain/" + getDomain() + "/user/admin/html5application/senso?event=" + jumper,"newscreen");

    private static Map<String, Html5ApplicationInterface> runningapps = new HashMap<String, Html5ApplicationInterface>();
    private static Map<String, Html5AvailableApplication> availableapps = null;
    private static ApplicationManager instance;
    private static boolean running = false;
    private static Map<Integer, Html5ApplicationInterface> externalInterfaces = new HashMap<Integer, Html5ApplicationInterface>();
    private static Map<String, Html5ApplicationInterface> router = new HashMap<String, Html5ApplicationInterface>();
    private static Map<String, String> externalMessages = new HashMap<String, String>();
    private static FSList logcollection = null;

    private ApplicationManager() {
        if (!running) {
            running = true;
            if (availableapps == null)
                loadAvailableApps(); // new test for urlmapping
            start();
            // new MaggieLoader();
            Marge.addObserver("/domain/internal/service/lou/apps/*", this);
        }
    }

    public static ApplicationManager instance() {
        if (instance == null)
            instance = new ApplicationManager();
        return instance;
    }

    public void addApplication(Html5ApplicationInterface app) {
        // for now its just one app not static/instance based yet
        runningapps.put(app.getId(), app);
        //update();
    }

    public void removeApplication(String id) {
        //     if(this.getApplication(id)!=null)this.externalInterfaces.remove(this.getApplication(id).getExternalInterfaceId());
        this.runningapps.remove(id);
        //    update();
    }

    public int getEternalInterfaceNumber() {
        Random generator = new Random(System.currentTimeMillis());

        Integer rand = -1;
        while (rand < 0 || externalInterfaces.containsKey(rand)) {
            rand = generator.nextInt(9000) + 1000;
        }
        return rand;
    }

    public Map<String, String> getExternalMessages() {
        return this.externalMessages;
    }

    public void addExternalInterface(Integer id, Html5Application app) {
        this.externalInterfaces.put(id, app);
    }

    public Map<Integer, Html5ApplicationInterface> getExternalInterfaces() {
        return this.externalInterfaces;
    }

    public void removeExternalInterface(Integer id) {
        this.externalInterfaces.remove(id);
    }

    public Html5ApplicationInterface getApplication(String name) {
        Html5ApplicationInterface app = runningapps.get(name);
        if (app != null) {
            return app;
        }
        // lets try to create this app
        app = getLoadedApplication(name);
        if (app != null) {
            app.setFullId(name);
            addApplication(app);
        }

        // maybe they are trying to load a dashboard app ?
        if (name.indexOf("/dashboard") != -1) {
            app = loadDashboardApp(name);
        }
        return app;
    }

    public static Screen getScreenByFullid(String from) {
        int pos = from.indexOf("/screen/");
        if (pos != -1) {
            String fromapp = from.substring(0, pos);
            String fromscreen = from.substring(pos + 8);
            pos = fromapp.lastIndexOf("/");
            fromapp = fromapp.substring(0, pos);
            //System.out.println("APP="+fromapp+" SCR="+fromscreen);
            Html5ApplicationInterface ca = ApplicationManager.instance().getApplication(fromapp);
            //System.out.println("CA="+ca);
            Screen client = ca.getScreenManager().get(from);
            return client;
        }
        return null;
    }

    private Html5ApplicationInterface loadDashboardApp(String tappname) {
        Html5ApplicationInterface app = null;
        String classname = "org.springfield.lou.application.types.";
        int pos = tappname.indexOf("/html5application/");
        if (pos != -1) {
            String apppart = tappname.substring(pos + 18);
            pos = apppart.indexOf("/");
            if (pos != -1) {
                apppart = apppart.substring(0, pos);
            }
            classname += apppart.substring(0, 1).toUpperCase();
            classname += apppart.substring(1) + "Application";
        }
        try {
            Object o = Class.forName(classname).getConstructor(String.class).newInstance(tappname);
            app = (Html5ApplicationInterface) o;
            app.setFullId(tappname);
            addApplication(app);
        } catch (Exception e) {

        }
        return app;
    }

    public Html5ApplicationInterface getLoadedApplication(String name) {

        // so lets see what version is in production !
        ApplicationClassLoader cl = new ApplicationClassLoader();
        try {
            String version = null;
            if (LazyHomer.inDeveloperMode()) {
                version = getDevelopmentId(name);
            } else {
                version = getProductionId(name);
            }
            if (version == null)
                return null;

            // check if we have it not get it ?
            if (!haveAppLocally(name, version)) {
                // load it from remote
                String ips[] = whoHasWar(name, version);
                if (ips != null) {
                    //System.out.println("WHO HAS WAR = "+ips[0]);
                    copyAppFromRemote(ips[0], name, version); // hardcoded
                } else {
                    return null;
                }
            }
            cl.setJarName(name, version);
            Object o = cl.loadClass(name).getConstructor(String.class).newInstance(name);
            Html5ApplicationInterface newapp = (Html5ApplicationInterface) o;
            newapp.setHtmlPath("/springfield/lou/apps/" + newapp.getAppname() + "/" + version + "/");
            // execute the first command list
            //newapp.executeActionlist("init");
            return newapp;
        } catch (Exception e) {
            System.out.println("ApplicationManager ");
            e.printStackTrace();
        }
        return runningapps.get(name);
    }

    public String getProductionId(String appname) {
        Html5AvailableApplication app = getAvailableApplicationByInstance(appname);
        return app.getProductionVersion();
    }

    public String getDevelopmentId(String appname) {
        Html5AvailableApplication app = getAvailableApplicationByInstance(appname);
        return app.getDevelopmentVersion();
    }

    public Html5AvailableApplication getAvailableApplicationByInstance(String url) {
        int pos = url.indexOf("html5application/");
        if (pos != -1) {
            String name = url.substring(pos + 17);
            return getAvailableApplication(name);
        }
        return null;
    }

    public Html5AvailableApplication getAvailableApplication(String name) {
        if (availableapps == null)
            loadAvailableApps();
        return availableapps.get(name);
    }

    public Map<String, Html5ApplicationInterface> getRouter() {
        return this.router;
    }

    public Map<String, Html5ApplicationInterface> getApplications() {
        return runningapps;
    }

    public FSList getOpenApplicationsList() {
        FSList results = new FSList();
        Set<String> keys = ApplicationManager.instance().getApplications().keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String next = (String) it.next();
            Html5ApplicationInterface app = ApplicationManager.instance().getApplication(next);
            FsNode node = new FsNode("applications", app.getId());
            node.setProperty("id", "" + app.getId());
            node.setProperty("screencount", "" + app.getScreenCount());
            node.setProperty("screenidcount", "" + app.getScreenIdCounter());
            node.setProperty("usercount", "" + app.getUserCount());
            results.addNode(node);
        }
        return results;
    }

    public FSList getAvailableApplicationsList() {
        if (availableapps == null)
            loadAvailableApps();
        FSList results = new FSList();
        Set<String> keys = availableapps.keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String next = (String) it.next();
            Html5AvailableApplication vapp = ApplicationManager.instance().getAvailableApplication(next);
            FsNode node = new FsNode("applications", vapp.getId());
            node.setProperty("id", "" + vapp.getId());
            node.setProperty("versioncount", "" + vapp.getVersionsCount());
            node.setProperty("productionversion", "" + vapp.getVersionsCount());
            String pv = vapp.getProductionVersion();
            String dv = vapp.getDevelopmentVersion();
            if (pv == null)
                pv = "";
            if (dv == null)
                dv = "";
            node.setProperty("productionversion", pv + "(" + vapp.getProductionVersionCount() + ")");
            node.setProperty("developmentversion", dv + " (" + vapp.getDevelopmentVersionCount() + ")");
            node.setProperty("status", vapp.getStatus());
            results.addNode(node);

        }
        return results;
    }

    public Map<String, Html5AvailableApplication> getAvailableApplications() {
        if (availableapps == null)
            loadAvailableApps();
        return availableapps;
    }

    /*
    public static void setOpenappCallback(OpenappsComponent c) {
       oac = c;
    }
    */

    /*
    public static void update() {
       if (oac!=null) oac.update();
    }
    */

    public static void startLogger(String url) {
        logcollection = new FSList(url);
    }

    public static String getApplicationWarAsString(String appname, String version) {
        String filename = "/springfield/lou/apps/" + appname + "/" + version + "/war/smt_" + appname + "app.war";
        FileInputStream fileInputStream = null;
        File file = new File(filename);

        byte[] bytes = new byte[(int) file.length()];

        try {
            fileInputStream = new FileInputStream(file);
            fileInputStream.read(bytes);
            fileInputStream.close();

            String result = new String(Base64.encodeBase64(bytes));
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void writeApplicationWarFromString(String appname, String version, String input) {
        byte[] bytes = Base64.decodeBase64(input.getBytes());
        String warname = "/springfield/lou/remotedir/smt_" + appname + "app.war";
        writeBytesToFile(bytes, warname);
    }

    public static void log(Html5ApplicationInterface app, FsNode n) {
        if (logcollection == null)
            return;
        String url = logcollection.getPath();
        if (app.getId().equals(url)) {
            logcollection.addNode(n);
            //   if (oac!=null) oac.logChange(logcollection);
        }
    }

    public void upload(String appname) {
        // lets see what we have in our upload dir
        //TODO: make this configurable or at least windows compatible
        File uploaddir = new File("/springfield/lou/uploaddir");
        if (uploaddir.isDirectory()) {
            File[] files = uploaddir.listFiles();
            if (files != null) {
                for (File uploadfile : files) {
                    //System.out.println("UPLOAD FILE="+uploadfile.toString());
                    processUploadedWar(uploadfile, appname);
                }
            }
        }
        loadAvailableApps();
    }

    public void uploadnew() {
        // lets see what we have in our upload dir
        File uploaddir = new File("/springfield/lou/uploaddir");
        if (uploaddir.isDirectory()) {
            File[] files = uploaddir.listFiles();
            if (files != null) {
                for (File uploadfile : files) {
                    //System.out.println("UPLOAD FILE="+uploadfile.toString());
                    String filename = uploadfile.getName();
                    int pos = filename.indexOf("smt_");
                    if (pos != -1) {
                        filename = filename.substring(pos + 4);
                        pos = filename.indexOf("app.war");
                        //System.out.println("POS2="+pos);
                        if (pos != -1) {
                            filename = filename.substring(0, pos);
                            //System.out.println("APPNAME FILE="+filename);
                            createNewAppEntry(filename);
                            processUploadedWar(uploadfile, filename);
                        }
                    }
                }
            }
        }
    }

    private void createNewAppEntry(String appname) {
        String writepath = "/domain/internal/service/lou/apps/";

        // creare the node      
        String newbody = "<fsxml><properties></properties></fsxml>";
        String postpath = writepath + appname + "/properties";
        //System.out.println("RS="+LazyHomer.sendRequest("PUT",postpath,newbody,"text/xml"));
    }

    private void processUploadedWar(File warfile, String wantedname) {
        // lets first check some vitals to check what it is
        String warfilename = warfile.getName();
        if (warfilename.startsWith("smt_") && warfilename.endsWith("app.war")) {
            // ok so filename checks out is smt_[name]app.war format
            String appname = warfilename.substring(4, warfilename.length() - 7);
            if (wantedname.equals(appname)) {
                // ok found file is the wanted file
                // format "29-Aug-2013-16:55"
                System.out.println("NEW VERSION OF " + appname + " FOUND INSTALLING");
                Date now = new Date();
                SimpleDateFormat df = new SimpleDateFormat("d-MMM-yyyy-HH:mm");
                String datestring = df.format(now);
                String writedir = "/springfield/lou/apps/" + appname + "/" + datestring;

                // create the node
                Html5AvailableApplication vapp = getAvailableApplication(appname);
                String newbody = "<fsxml><properties></properties></fsxml>";

                ServiceInterface smithers = ServiceManager.getService("smithers");
                if (smithers == null)
                    return;
                FsNode tnode = Fs.getNode("/domain/internal/service/lou/apps/" + appname);
                if (tnode == null) {
                    smithers.put("/domain/internal/service/lou/apps/" + appname + "/properties", newbody,
                            "text/xml");
                }
                smithers.put(
                        "/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring + "/properties",
                        newbody, "text/xml");

                // make all the dirs we need
                File md = new File(writedir);
                md.mkdirs();
                md = new File(writedir + "/war");
                md.mkdirs();
                md = new File(writedir + "/jar");
                md.mkdirs();
                md = new File(writedir + "/components");
                md.mkdirs();
                md = new File(writedir + "/css");
                md.mkdirs();
                md = new File(writedir + "/libs");
                md.mkdirs();

                try {
                    JarFile war = new JarFile(warfile);

                    // ok lets first find the jar file !
                    JarEntry entry = war.getJarEntry("WEB-INF/lib/smt_" + appname + "app.jar");
                    if (entry != null) {
                        byte[] bytes = readJarEntryToBytes(war.getInputStream(entry));
                        writeBytesToFile(bytes, writedir + "/jar/smt_" + appname + "app.jar");
                    }
                    // unpack all in eddie dir
                    Enumeration<JarEntry> iter = war.entries();
                    while (iter.hasMoreElements()) {
                        JarEntry lentry = iter.nextElement();
                        //System.out.println("LI="+lentry.getName());
                        String lname = lentry.getName();
                        if (!lname.endsWith("/")) {
                            int pos = lname.indexOf("/" + appname + "/");
                            if (pos != -1) {
                                String nname = lname.substring(pos + appname.length() + 2);
                                String dname = nname.substring(0, nname.lastIndexOf('/'));
                                File de = new File(writedir + "/" + dname);
                                de.mkdirs();
                                byte[] bytes = readJarEntryToBytes(war.getInputStream(lentry));
                                writeBytesToFile(bytes, writedir + "/" + nname);
                            }
                        }
                    }
                    war.close();
                    File ren = new File("/springfield/lou/uploaddir/" + warfilename);
                    File nen = new File(writedir + "/war/smt_" + appname + "app.war");
                    //System.out.println("REN="+warfilename);
                    //System.out.println("REN="+writedir+"/war/smt_"+appname+"app.war");
                    ren.renameTo(nen);

                    loadAvailableApps();
                    // should we make in development or production based on autodeploy ?
                    vapp = getAvailableApplication(appname);
                    if (vapp != null) {
                        String mode = vapp.getAutoDeploy();
                        if (appname.equals("dashboard")) {
                            mode = "development/production";
                        }
                        System.out.println("APPNAME=" + appname + " mode=" + mode);
                        if (mode.equals("production")) {
                            makeProduction(appname, datestring);
                        } else if (mode.equals("development")) {
                            makeDevelopment(appname, datestring);
                        } else if (mode.equals("development/production")) {
                            makeDevelopment(appname, datestring);
                            makeProduction(appname, datestring);
                        }
                    }

                    /*
                    Html5ApplicationInterface app = getApplication("/domain/webtv/html5application/dashboard");
                    if (app!=null) {
                     DashboardApplication dapp = (DashboardApplication)app;
                     dapp.newApplicationFound(appname);
                    }
                    */

                    // lets tell set the available variable to tell the others we have it.
                    FsNode unode = Fs
                            .getNode("/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring);
                    if (unode != null) {
                        String warlist = unode.getProperty("waravailableat");
                        if (warlist == null || warlist.equals("")) {
                            Fs.setProperty(
                                    "/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring,
                                    "waravailableat", LazyHomer.myip);
                        } else {
                            System.out.println("BUG ? Already available war " + warlist + " a=" + appname);
                        }
                    }
                } catch (Exception e) {
                    System.out.println("VERSION NOT READY STILL UPLOADING? RETRY WILL HAPPEN SOON");
                }
            }
        }
    }

    private void processRemoteWar(File warfile, String wantedname, String datestring) {
        // lets first check some vitals to check what it is
        String warfilename = warfile.getName();
        if (warfilename.startsWith("smt_") && warfilename.endsWith("app.war")) {
            // ok so filename checks out is smt_[name]app.war format
            String appname = warfilename.substring(4, warfilename.length() - 7);
            if (wantedname.equals(appname)) {
                // ok found file is the wanted file
                // format "29-Aug-2013-16:55"
                System.out.println("NEW VERSION OF " + appname + " FOUND INSTALLING");

                String writedir = "/springfield/lou/apps/" + appname + "/" + datestring;

                // make all the dirs we need
                File md = new File(writedir);
                md.mkdirs();
                md = new File(writedir + "/war");
                md.mkdirs();
                md = new File(writedir + "/jar");
                md.mkdirs();
                md = new File(writedir + "/components");
                md.mkdirs();
                md = new File(writedir + "/css");
                md.mkdirs();
                md = new File(writedir + "/libs");
                md.mkdirs();

                try {
                    JarFile war = new JarFile(warfile);

                    // ok lets first find the jar file !
                    JarEntry entry = war.getJarEntry("WEB-INF/lib/smt_" + appname + "app.jar");
                    if (entry != null) {
                        byte[] bytes = readJarEntryToBytes(war.getInputStream(entry));
                        writeBytesToFile(bytes, writedir + "/jar/smt_" + appname + "app.jar");
                    }
                    // unpack all in eddie dir
                    Enumeration<JarEntry> iter = war.entries();
                    while (iter.hasMoreElements()) {
                        JarEntry lentry = iter.nextElement();
                        //System.out.println("LI="+lentry.getName());
                        String lname = lentry.getName();
                        if (!lname.endsWith("/")) {
                            int pos = lname.indexOf("/" + appname + "/");
                            if (pos != -1) {
                                String nname = lname.substring(pos + appname.length() + 2);
                                String dname = nname.substring(0, nname.lastIndexOf('/'));
                                File de = new File(writedir + "/" + dname);
                                de.mkdirs();
                                byte[] bytes = readJarEntryToBytes(war.getInputStream(lentry));
                                writeBytesToFile(bytes, writedir + "/" + nname);
                            }
                        }
                    }
                    war.close();
                    File ren = new File("/springfield/lou/uploaddir/" + warfilename);
                    File nen = new File(writedir + "/war/smt_" + appname + "app.war");
                    ren.renameTo(nen);

                    // lets tell set the available variable to tell the others we have it.

                    FsNode unode = Fs
                            .getNode("/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring);
                    if (unode != null) {
                        String warlist = unode.getProperty("waravailableat");
                        if (warlist == null || warlist.equals("")) {
                            Fs.setProperty(
                                    "/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring,
                                    "waravailableat", LazyHomer.myip);
                        } else {
                            Fs.setProperty(
                                    "/domain/internal/service/lou/apps/" + appname + "/versions/" + datestring,
                                    "waravailableat", warlist + "," + LazyHomer.myip);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void makeProduction(String appname, String version) {
        // first we should change smithers and signal the others
        System.out.println("MAKE PRODUCTION CALLED ON " + appname + " " + version);
        // change in memory
        Html5AvailableApplication avapp = getAvailableApplication(appname);
        if (avapp != null) {
            String oldname = avapp.getProductionVersion();
            Html5AvailableApplicationVersion oldv = avapp.getVersion(oldname);
            if (oldv != null) {
                oldv.setProductionState(false);
            }
            Html5AvailableApplicationVersion newv = avapp.getVersion(version);
            if (newv != null) {
                System.out.println("SET PRODUCTION STATE TRUE " + avapp.getId());
                newv.setProductionState(true);
            }

            if (!LazyHomer.inDeveloperMode()) {
                //TODO: make this configurable or at least windows compatible
                String source = "/springfield/lou/apps/" + appname + "/" + version;
                String target = "/springfield/tomcat/webapps/ROOT/eddie/apps/" + appname;
                try {
                    // create dir if needed
                    Runtime.getRuntime().exec("/bin/mkdir " + target);

                    // delete symlinks if available
                    Runtime.getRuntime().exec("/bin/rm " + target + "/css");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/libs");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/img");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/components");

                    // create the sym links
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/css " + target + "/css");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/libs " + target + "/libs");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/img " + target + "/img");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/components " + target + "/components");
                } catch (Exception e) {
                    System.out.println("Can't create symlink");
                    e.printStackTrace();
                }
            }

            // we need to unload the old one from memory
            avapp.deleteCaches();
        } else {
            System.out.println("Appliction not found " + appname);
        }

        // we need to unload the old one from memory
        ArrayList<String> keys = new ArrayList<String>(runningapps.keySet());
        for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
            //   for(Iterator<String> iter = runningapps.keySet().iterator(); iter.hasNext(); ) {
            String appn = (String) iter.next();
            if (appn.indexOf("html5application/" + appname) != -1) {
                Html5ApplicationInterface rapp = runningapps.get(appn);
                rapp.shutdown();
                runningapps.remove(appn);
            }
        }
        // load the new one, will be done on next usage !
        purgeOldVersions();
    }

    public void makeDevelopment(String appname, String version) {
        // change in memory
        Html5AvailableApplication avapp = getAvailableApplication(appname);
        if (avapp != null) {
            String oldname = avapp.getDevelopmentVersion();
            Html5AvailableApplicationVersion oldv = avapp.getVersion(oldname);
            if (oldv != null) {
                oldv.setDevelopmentState(false);
            }
            Html5AvailableApplicationVersion newv = avapp.getVersion(version);
            if (newv != null) {
                newv.setDevelopmentState(true);
            }

            if (LazyHomer.inDeveloperMode()) {
                //TODO: make this configurable or at least windows compatible
                String source = "/springfield/lou/apps/" + appname + "/" + version;
                String target = "/springfield/tomcat/webapps/ROOT/eddie/apps/" + appname;
                try {
                    // create dir if needed
                    Runtime.getRuntime().exec("/bin/mkdir " + target);

                    // delete symlinks if available
                    Runtime.getRuntime().exec("/bin/rm " + target + "/css");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/libs");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/img");
                    Runtime.getRuntime().exec("/bin/rm " + target + "/components");

                    // create the sym links
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/css " + target + "/css");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/libs " + target + "/libs");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/img " + target + "/img");
                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/components " + target + "/components");
                } catch (Exception e) {
                    System.out.println("Can't create symlink");
                    e.printStackTrace();
                }
            }

            avapp.deleteCaches();
        } else {
            System.out.println("Appliction not found " + appname);
        }

        // we need to unload the old one from memory
        avapp.deleteCaches();

        // we need to unload the old one from memory

        ArrayList<String> keys = new ArrayList<String>(runningapps.keySet());
        for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
            String appn = (String) iter.next();
            if (appn.indexOf("html5application/" + appname) != -1) {
                Html5ApplicationInterface rapp = runningapps.get(appn);
                rapp.shutdown();
                runningapps.remove(appn);
            }
        }
        // load the new one, will be done on next usage !
    }

    public void deleteVersion(String appname, String version) {
        // change in memory
        Html5AvailableApplication avapp = getAvailableApplication(appname);
        if (avapp != null) {
            avapp.deleteVersion(version);
        } else {
            System.out.println("Appliction not found " + appname);
        }
    }

    public void setAutoDeploy(String appname, String mode) {
        // change in memory
        Html5AvailableApplication avapp = getAvailableApplication(appname);
        if (avapp != null) {
            avapp.setAutoDeploy(mode);
        } else {
            System.out.println("Appliction not found " + appname);
        }
    }

    public void loadAvailableApps() {
        //System.out.println("ApplicationManager.loadAvailableApps()");
        availableapps = new HashMap<String, Html5AvailableApplication>();
        String xml = "<fsxml><properties><depth>2</depth></properties></fsxml>";
        long starttime = new Date().getTime(); // we track the request time for debugging only
        ServiceInterface smithers = ServiceManager.getService("smithers");
        if (smithers == null)
            return;

        String nodes = smithers.get("/domain/internal/service/lou/apps", xml, "text/xml");
        //System.out.println("APP NODECOUNT="+nodes.length());
        long endtime = new Date().getTime(); // we track the request time for debugging only
        //System.out.println("SMITHERSTIME="+(endtime-starttime));
        try {
            Document result = DocumentHelper.parseText(nodes);
            for (Iterator<Node> iter = result.getRootElement().nodeIterator(); iter.hasNext();) {
                Element child = (Element) iter.next();
                if (!child.getName().equals("properties")) {
                    String id = child.attributeValue("id");
                    Html5AvailableApplication vapp = new Html5AvailableApplication();
                    vapp.setId(id);

                    // get all the versions and nodes
                    String production = null;
                    String development = null;
                    for (Iterator<Node> iter2 = child.nodeIterator(); iter2.hasNext();) {
                        //System.out.println("N2");
                        Node node = iter2.next();
                        if (node instanceof Element) {
                            Element child2 = (Element) node;
                            String nname = child2.getName();
                            if (nname.equals("properties")) {
                                //System.out.println("N3");
                                for (Iterator<Node> iter3 = child2.nodeIterator(); iter3.hasNext();) {
                                    Node node2 = iter3.next();
                                    if (node2 instanceof Element) {
                                        //System.out.println("N4");
                                        Element child3 = (Element) node2;
                                        String pname = child3.getName();
                                        if (pname.equals("autodeploy")) {
                                            vapp.loadAutoDeploy(child3.getText());
                                        }
                                    }
                                }
                            } else if (nname.equals("versions")) {
                                //System.out.println("N5");
                                String version = child2.attributeValue("id");
                                Html5AvailableApplicationVersion v = new Html5AvailableApplicationVersion(vapp);
                                v.setId(version);
                                vapp.addVersion(v);
                                //System.out.println("N5.1");

                            } else if (nname.equals("nodes")) {
                                //System.out.println("N6");
                                String ipnumber = child2.attributeValue("id");
                                String ipversion = child2.attributeValue("referid");
                            } else if (nname.equals("production")) {
                                //System.out.println("N7");
                                production = child2.attributeValue("referid");
                            } else if (nname.equals("development")) {
                                //System.out.println("N7");
                                development = child2.attributeValue("referid");
                            }
                            //System.out.println("N5.2");
                            // ok lets set prod/dev version
                            if (production != null) {
                                Html5AvailableApplicationVersion pv = vapp.getVersionByUrl(production);
                                if (pv != null) {
                                    pv.loadProductionState(true);
                                    // lets scan for triggers !
                                    String scanpath = "/springfield/lou/apps/" + vapp.getId() + "/" + pv.getId()
                                            + "/components/app.xml";
                                    //System.out.println("ACTIONLIST PRESCANNER="+scanpath);
                                    readJumpersForApp(scanpath);
                                    //   if (!LazyHomer.inDeveloperMode()) ActionListManager.readActionListsDirForUrlTriggers(scanpath);
                                }
                            }
                            //System.out.println("N5.2");
                            if (development != null) {
                                Html5AvailableApplicationVersion dv = vapp.getVersionByUrl(development);
                                if (dv != null) {
                                    dv.loadDevelopmentState(true);
                                    String scanpath = "/springfield/lou/apps/" + vapp.getId() + "/" + dv.getId()
                                            + "/actionlists/";
                                    //   if (LazyHomer.inDeveloperMode()) ActionListManager.readActionListsDirForUrlTriggers(scanpath);
                                }
                            }

                            //System.out.println("N5.3");
                        } else {
                            System.out.println("NOT AN ELEMENT!");
                            System.out.println(node);
                        }
                        //System.out.println("N99");
                    }
                    availableapps.put(id, vapp);

                    // parse it again to get the nodes, don't like it but simplest way
                    for (Iterator<Node> iter2 = child.nodeIterator(); iter2.hasNext();) {
                        Element child2 = (Element) iter2.next();
                        String nname = child2.getName();
                        if (nname.equals("nodes")) {
                            String ipnumber = child2.attributeValue("id");
                            String ipversion = child2.attributeValue("referid");
                            //System.out.println("IP="+ipnumber+" VER="+ipversion);
                            Html5AvailableApplicationVersion vv = vapp.getVersionByUrl(ipversion);
                            if (vv != null) {
                                vv.addNode(ipnumber);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("Application manager : ");
            e.printStackTrace();
        }
        if (availableapps.size() == 0) {
            System.out.println("NO APPS FOUND RETURNING NULL");
            availableapps = null;
        }
        //System.out.println("END OF LOADAVAIL");
    }

    private byte[] readJarEntryToBytes(InputStream is) {
        try {
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
            int nextValue = is.read();
            while (-1 != nextValue) {
                byteStream.write(nextValue);
                nextValue = is.read();
            }
            byte[] bytes = byteStream.toByteArray();
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public void run() {
        while (running) {
            try {
                uploadnew();
                //System.out.println("check new upload");
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private boolean haveAppLocally(String appname, String id) {
        int pos = appname.indexOf("html5application/");
        appname = appname.substring(pos + 17);
        String jarname = "/springfield/lou/apps/" + appname + "/" + id + "/jar/smt_" + appname + "app.jar";
        File file = new File(jarname);
        if (file.exists()) {
            return true;
        }
        return false;
    }

    private boolean copyAppFromRemote(String ipnumber, String appname, String id) {
        int pos = appname.indexOf("html5application/");
        appname = appname.substring(pos + 17);
        //System.out.println("REMOTE NAME = "+appname+" ID="+id);
        ServiceInterface lou = ServiceManager.getService("lou", ipnumber);
        if (lou != null) {
            String xml = "<fsxml><properties></properties></fsxml>";
            String result = lou.get("getAppWAR(" + appname + "," + id, xml, "text/xml");
            if (result != null) {
                System.out.println("LOU REMOTE=" + result.length());
                writeApplicationWarFromString(appname, id, result);
                // process it
                File warfile = new File("/springfield/lou/remotedir/smt_" + appname + "app.war");
                if (warfile.exists()) {
                    processRemoteWar(warfile, appname, id);
                } else {
                    System.out.println("FAILED TO LOAD FROM REMOTE");
                }

            } else {
                System.out.println("LOU REMOTE NULL");
            }
        }
        return false;
    }

    public void remoteSignal(String from, String method, String url) {
        try {
            //System.out.println("MULTICAST="+from+" "+method+" "+url);
            if (!method.equals("PUT") || url.indexOf("/versions/") == -1)
                return;

            // first find out who has this app
            int pos = from.indexOf('/');
            if (pos != -1) {
                String ipnumber = from.substring(pos + 1);
                // ok now lets find the app name and version !
                int pos2 = url.indexOf("/apps/");
                if (pos2 != -1) {
                    String appname = url.substring(pos2 + 6);
                    int pos3 = appname.indexOf("/versions/");
                    String version = appname.substring(pos3 + 10);
                    appname = appname.substring(0, pos3);
                    int pos4 = version.indexOf(",");
                    if (pos4 != -1) {
                        version = version.substring(0, pos4);
                        //System.out.println("IP="+ipnumber+" APP="+appname+" ID="+version+" URL="+url+" L="+lastremoteappname);
                        FsNode node = Fs
                                .getNode("/domain/internal/service/lou/apps/" + appname + "/versions/" + version);
                        if (node != null) {
                            String ids = node.getProperty("waravailableat");
                            //System.out.println("REMOTE DONE="+ids+" "+ipnumber);
                            if (ids != null) {

                            } else {
                                // property not set yet
                                return;
                            }
                        } else {
                            // node not found at all
                            return;
                        }

                        if (!haveAppLocally("html5application/" + appname, version)) {
                            copyAppFromRemote(ipnumber, "html5application/" + appname, version);
                            Html5AvailableApplication avapp = getAvailableApplication(appname);
                            if (avapp != null) {
                                String source = "/springfield/lou/apps/" + appname + "/" + version;
                                String target = "/springfield/tomcat/webapps/ROOT/eddie/apps/" + appname;

                                try {
                                    // create dir if needed
                                    Runtime.getRuntime().exec("/bin/mkdir " + target);

                                    // delete symlinks if available
                                    Runtime.getRuntime().exec("/bin/rm " + target + "/css");
                                    Runtime.getRuntime().exec("/bin/rm " + target + "/libs");
                                    Runtime.getRuntime().exec("/bin/rm " + target + "/img");

                                    // create the sym links
                                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/css " + target + "/css");
                                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/libs " + target + "/libs");
                                    Runtime.getRuntime().exec("/bin/ln -s " + source + "/img " + target + "/img");
                                } catch (Exception e) {
                                    System.out.println("Can't create symlink");
                                    e.printStackTrace();
                                }

                                avapp.deleteCaches();
                                loadAvailableApps();
                                Html5ApplicationInterface app = getApplication(
                                        "/domain/webtv/html5application/dashboard");
                                if (app != null) {
                                    //   DashboardApplication dapp = (DashboardApplication)app;
                                    //   dapp.newApplicationFound(appname);
                                }

                                ArrayList<String> keys = new ArrayList<String>(runningapps.keySet());
                                for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
                                    //   for(Iterator<String> iter = runningapps.keySet().iterator(); iter.hasNext(); ) {
                                    String appn = (String) iter.next();
                                    if (appn.indexOf("html5application/" + appname) != -1) {
                                        Html5ApplicationInterface rapp = runningapps.get(appn);
                                        System.out.println("SHUTDOWN OLD APP=" + rapp.getId());
                                        rapp.shutdown();
                                        runningapps.remove(appn);
                                        break;
                                    }
                                }
                                System.out.println("REMOTE INSTALL DONE !!!");

                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            System.out.println("APP not fully send yet.. will retry");
        }
    }

    private static void writeBytesToFile(byte[] bytes, String filename) {
        try {
            FileOutputStream stream = new FileOutputStream(filename);
            try {
                stream.write(bytes);
            } finally {
                stream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String[] whoHasWar(String appname, String version) {
        FsNode node = Fs.getNode("/domain/internal/service/lou/apps/" + appname + "/versions/" + version);
        if (node != null) {
            String ids = node.getProperty("waravailableat");
            if (ids != null) {
                return ids.split(",");
            }
        }
        return null;
    }

    private void purgeOldVersions() {
        int shour = 3600;
        int sday = 24 * 3600;
        int smonth = 30 * 24 * 3600;
        int syear = 365 * 24 * 3600;
        ArrayList<String> copies = null;
        Long now = new Date().getTime() / 1000;
        Set<String> keys = ApplicationManager.instance().getAvailableApplications().keySet();
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String next = (String) it.next();
            copies = new ArrayList<String>();
            Html5AvailableApplication vapp = ApplicationManager.instance().getAvailableApplication(next);
            Iterator<Html5AvailableApplicationVersion> it2 = vapp.getOrderedVersions();
            while (it2.hasNext()) {
                Html5AvailableApplicationVersion version = it2.next();
                String dates = version.getId();
                // 13-May-2014-16:44
                try {
                    DateFormat df = new SimpleDateFormat("dd-MMM-yyyy-HH:mm");
                    Date date = df.parse(dates);
                    long then = date.getTime() / 1000;
                    long delta = now - then;
                    String token = null;
                    if (delta < shour) {
                        // last hour keep all versions !
                        token = "S" + delta;
                    } else if (delta < (sday)) {
                        // in the last 24 hours, keep one per hour max
                        int mod = (int) (delta / shour);
                        token = "H" + mod;
                    } else if (delta < (smonth)) {
                        // in the last 30 days, keep one per day max
                        int mod = (int) (delta / sday);
                        token = "D" + mod;
                    } else if (delta < (syear)) {
                        // in the year, keep one per month max
                        int mod = (int) (delta / smonth);
                        token = "M" + mod;
                    } else {
                        // keep one per year
                        int mod = (int) (delta / syear);
                        token = "Y" + mod;
                    }
                    // check if we already have one
                    if (!copies.contains(token)) {
                        copies.add(token);
                        //System.out.println(token+" NAME="+next+" DATE="+dates+" DELTA="+delta+" KEEP");
                    } else {
                        //System.out.println(token+" NAME="+next+" DATE="+dates+" DELTA="+delta+" DELETE");
                        if (!version.isDevelopmentVersion() && !version.isProductionVersion()) {
                            vapp.deleteVersion(dates);
                        }
                    }
                } catch (Exception e) {
                    System.out.println("PARSE PROBLEM ON DATE IN PURGE DATE = " + dates);
                }

            }
        }
    }

    private void readJumpersForApp(String filename) {
        //      System.out.println("SCANPATH="+filename);
        //      String filename = basepath+File.separator+"apps"+File.separator+part+File.separator+"components"+File.separator+"app.xml";
        File file = new File(filename);
        if (file.exists()) {
            try {
                BufferedReader br = new BufferedReader(new FileReader(filename));
                StringBuffer str = new StringBuffer();
                String line = br.readLine();
                while (line != null) {
                    str.append(line);
                    str.append("\n");
                    line = br.readLine();
                }
                br.close();

                String body = str.toString();
                Document result = DocumentHelper.parseText(body);
                for (Iterator<Node> iter = result.getRootElement().nodeIterator(); iter.hasNext();) {

                    Node child = (Node) iter.next();
                    //System.out.println("C="+child.getName());
                    if (child.getName() != null && child.getName().equals("jumper")) {
                        Element model = (Element) child;
                        String sid = model.attributeValue("id");

                        //FsNode modelnode = new FsNode("model",modelid);
                        for (Iterator<Node> iter2 = model.nodeIterator(); iter2.hasNext();) {
                            Node child2 = (Node) iter2.next();
                            String id = child2.getName();
                            if (id != null) {
                                //System.out.println("JUMPER="+sid+" "+id+" "+child2.getText());
                                LouServlet.addUrlTrigger(sid + "," + child2.getText(), "newscreen");

                            }
                        }
                        //   putNode("/app/component",modelnode);
                        //   System.out.println("MIDELNODE="+modelnode.asXML());
                    } else if (child.getName() != null && child.getName().equals("jumpers")) {
                        Element model = (Element) child;
                        String sid = model.attributeValue("id");

                        //FsNode modelnode = new FsNode("model",modelid);
                        for (Iterator<Node> iter2 = model.nodeIterator(); iter2.hasNext();) {
                            Node child2 = (Node) iter2.next();
                            String id = child2.getName();
                            if (id != null) {
                                System.out.println("JUMPERS=" + sid + " " + id + " " + child2.getText());
                                FSList fslist = FSListManager.get(child2.getText(), false);
                                if (fslist != null) {
                                    List<FsNode> nodes = fslist.getNodes();
                                    if (nodes != null) {
                                        for (Iterator<FsNode> iter3 = nodes.iterator(); iter3.hasNext();) {
                                            FsNode node = (FsNode) iter3.next();
                                            String jumper = node.getId();
                                            String target = node.getProperty("target");
                                            String domain = node.getProperty("domain");
                                            System.out
                                                    .println("JUMPER=/lou/" + domain + "/" + jumper + "," + target);
                                            LouServlet.addUrlTrigger("/lou/" + domain + "/" + jumper + "," + target,
                                                    "newscreen");

                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}