org.rzo.yajsw.app.WrapperManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.rzo.yajsw.app.WrapperManagerImpl.java

Source

/*******************************************************************************
 * Copyright  2015 rzorzorzo@users.sf.net
 *
 * 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 org.rzo.yajsw.app;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.SimpleLoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.logging.LogFactory;
import org.rzo.yajsw.Constants;
import org.rzo.yajsw.YajswVersion;
import org.rzo.yajsw.action.Action;
import org.rzo.yajsw.action.ActionFactory;
import org.rzo.yajsw.config.ConfigUtils;
import org.rzo.yajsw.config.YajswConfiguration;
import org.rzo.yajsw.config.YajswConfigurationImpl;
import org.rzo.yajsw.controller.Message;
import org.rzo.yajsw.controller.jvm.MessageDecoder;
import org.rzo.yajsw.controller.jvm.MessageEncoder;
import org.rzo.yajsw.io.CyclicBufferFileInputStream;
import org.rzo.yajsw.io.CyclicBufferFilePrintStream;
import org.rzo.yajsw.io.TeeInputStream;
import org.rzo.yajsw.io.TeeOutputStream;
import org.rzo.yajsw.nettyutils.SystemOutLoggingFilter;
import org.rzo.yajsw.os.OperatingSystem;
import org.rzo.yajsw.script.Script;
import org.rzo.yajsw.script.ScriptFactory;
import org.rzo.yajsw.util.Cycler;
import org.rzo.yajsw.util.DaemonThreadFactory;
import org.rzo.yajsw.util.Utils;
import org.rzo.yajsw.wrapper.AlphaNumericComparator;

import com.sun.management.HotSpotDiagnosticMXBean;

// TODO: Auto-generated Javadoc
/**
 * The Class WrapperManagerImpl.
 */
public class WrapperManagerImpl implements WrapperManager, Constants, WrapperManagerImplMBean {

    /** The _port. */
    int _port = DEFAULT_PORT;

    /** The _debug. */
    int _debug = 3;
    boolean _debugComm = false;

    /** The log. */
    final InternalLogger log = SimpleLoggerFactory.getInstance("WrapperManager");

    /** The _started. */
    volatile boolean _started = false;

    /** The _key. */
    String _key;

    /** The _ping interval. */
    int _pingInterval = 5000;

    /** The connector. */
    Bootstrap connector;

    /** The _session. */
    volatile Channel _session;

    /** The _stopping. */
    volatile boolean _stopping = false;

    /** The _config. */
    Configuration _config;

    /** The instance. */
    static WrapperManagerImpl instance;

    /** The _exit code. */
    int _exitCode = 0;

    /** The main method. */
    Method mainMethod = null;

    /** The main method args. */
    String[] mainMethodArgs = null;

    /** The exit on main terminate. */
    int exitOnMainTerminate = -1;
    private int exitOnException = 999;

    /** The _my pid. */
    volatile int _myPid = -1;

    boolean _externalStop = false;

    String _groovyScript = null;

    Cycler _pinger;

    OutputStream _outStream;
    OutputStream _errStream;

    volatile boolean _appearHanging = false;

    boolean _overrideStdErr = false;

    boolean _haltAppOnWrapper = false;

    Lock _lock = new ReentrantLock();
    Condition _connectEnd = _lock.newCondition();
    Executor executor = Executors.newCachedThreadPool(new DaemonThreadFactory("yajsw-pool", Thread.MAX_PRIORITY));

    long _startupTimeout = 0;

    volatile String shutdownScript = null;

    Properties _properties;

    volatile boolean _dumpingHeap = false;

    volatile String _stopReason = null;

    float currentPercentHeap = -1;
    long minorGCDuration = -1;
    long fullGCDuration = -1;
    final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    final long maxHeap = memoryBean.getHeapMemoryUsage().getMax();
    final Object _heapDataLock = new Object();
    boolean _sendHeapData = false;

    private long lastMinorCollectionCount;
    private long lastMinorCollectionTime;

    private long lastFullCollectionCount;
    private long lastFullCollectionTime;

    Long usedHeap = null;
    Long timeMinorGC = null;
    Long timeFullGC = null;
    Long lastUsedHeap = null;

    GarbageCollectorMXBean minorGCBean;
    GarbageCollectorMXBean fullGCBean;

    MessageFormat gcFormat = null;

    boolean _initGCBeans = false;

    volatile Runnable _shutdownListener;

    private String getSystemProperty(String key) {
        String result = System.getProperty(key);
        if (result != null && result.contains("\""))
            result.replaceAll("\"", "");
        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#init(java.lang.String[],
     * java.lang.ClassLoader)
     */
    public void init(String[] args, ClassLoader wrapperClassLoader) {
        /*
         * System.out.println(Scheduler.class.getClassLoader());
         * System.out.println(Configuration.class.getClassLoader());
         * System.out.flush(); try { Thread.sleep(10000); } catch
         * (InterruptedException e1) { // TODO Auto-generated catch block
         * e1.printStackTrace(); }
         */
        System.out.println("YAJSW: " + YajswVersion.YAJSW_VERSION);
        System.out.println("OS   : " + YajswVersion.OS_VERSION);
        System.out.println("JVM  : " + YajswVersion.JAVA_VERSION);
        // set commons logging for vfs -> avoid using default java logger
        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(wrapperClassLoader);
        // String commonsLog =
        // getSystemProperty("org.apache.commons.logging.Log");
        // System.setProperty("org.apache.commons.logging.Log",
        // "org.apache.commons.logging.impl.SimpleLog");
        LogFactory.getFactory().setAttribute("org.apache.commons.logging.Log",
                "org.apache.commons.logging.impl.SimpleLog");
        instance = this;
        String outFile = getSystemProperty("wrapper.teeName");
        String outPath = getSystemProperty("wrapper.tmp.path");
        if (outPath != null)
            outPath = outPath.replaceAll("\"", "");
        String vStr = getSystemProperty("wrapper.console.visible");
        boolean visible = vStr != null && vStr.equals("true");
        if (outFile != null) {
            teeSystemStreams(outFile, outPath, visible);
        }

        String preScript = getSystemProperty("wrapper.app.pre.script");
        if (preScript != null & !"".equals(preScript))
            try {
                if (_debug > 2)
                    System.out.println("wrapped process: executing pre script " + preScript);
                Script script = ScriptFactory.createScript(preScript, "wrapper.app.pre.script", null, new String[0],
                        log, 0, "UTF-8", false, _debug, 1);
                if (script != null)
                    script.execute();
                else
                    System.out.println("wrapped process: executing pre script error: could not open script");
            } catch (Throwable ex) {
                ex.printStackTrace();
            }

        YajswConfigurationImpl config = new YajswConfigurationImpl();
        // config.setDebug(false);
        config.init();
        boolean dbg = config.getBoolean("wrapper.debug", false);
        int debugLevel = config.getInt("wrapper.debug.level", 3);
        _debug = dbg ? debugLevel : 0;
        _debugComm = config.getBoolean("wrapper.debug.comm", false);
        logJavaInfo(args);

        try {
            _overrideStdErr = config.getBoolean("wrapper.java.dump.override", false);
        } catch (Exception ex) {
            System.out.println("Error getting wrapper.java.dump.override " + ex.getMessage());
        }
        try {
            String mainClassName = config.getString("wrapper.java.app.mainclass");
            String jarName = config.getString("wrapper.java.app.jar");
            String groovyScript = config.getString("wrapper.groovy");
            if (mainClassName == null && jarName == null && groovyScript == null)
                mainClassName = config.getString("wrapper.app.parameter.1");
            if (_debug > 1)
                System.out.println("mainClass/jar/script: " + mainClassName + "/" + jarName + "/" + groovyScript);
            if (jarName == null && mainClassName == null && groovyScript == null) {
                System.out
                        .println("missing main class name or jar file or groovy file. please check configuration");
                return;
            }
            if (jarName != null) {
                mainMethod = loadJar(jarName);
            } else if (mainClassName != null)
                try {
                    Class cls = ClassLoader.getSystemClassLoader().loadClass(mainClassName);// Class.forName(mainClassName,
                    // currentContext);
                    mainMethod = cls.getMethod("main", new Class[] { String[].class });
                } catch (Exception e) {
                    System.out.println(
                            "error finding main method in class: " + mainClassName + " : " + e.getMessage());
                    // log.throwing(WrapperMain.class.getName(), "main", e);
                    e.printStackTrace();
                    return;
                }
            else
                _groovyScript = groovyScript;

            String stopConfig = config.getString("wrapper.stop.conf");
            if (stopConfig != null) {
                File f = new File(stopConfig);
                _externalStop = true;
            }
            if (_debug > 1)
                System.out.println("external stop " + _externalStop);

            exitOnMainTerminate = config.getInt("wrapper.exit_on_main_terminate", DEFAULT_EXIT_ON_MAIN_TERMINATE);

            exitOnException = config.getInt("wrapper.exit_on_main_exception", DEFAULT_EXIT_ON_MAIN_EXCEPTION);

            mainMethodArgs = getAppParam((Configuration) config);
            setConfiguration((Configuration) config);
            if (_config.getBoolean("wrapper.java.jmx", false))
                registerMBean(config);

            String control = _config.getString("wrapper.control", DEFAULT_CONTROL);
            if ("TIGHT".equals(control) || "APPLICATION".equals(control))
                _haltAppOnWrapper = true;

            setKey(_config.getString("wrapper.key"));
            // setDebug(true);
            setPort(_config.getInt("wrapper.port"));
            setPingInterval(_config.getInt("wrapper.ping.interval", Constants.DEFAULT_PING_INTERVAL));

            _startupTimeout = _config.getInt("wrapper.startup.timeout", DEFAULT_STARTUP_TIMEOUT) * 1000;

            shutdownScript = _config.getString("wrapper.app.shutdown.script", null);
            if (shutdownScript != null && !"".equals(shutdownScript)) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        executeShutdownScript();
                    }
                });

            }

            try {
                _sendHeapData = config.getBoolean("wrapper.java.monitor.gc.restart", false)
                        || config.getBoolean("wrapper.java.monitor.heap", false);
            } catch (Exception ex) {
                System.out.println("error reading wrapper.java.monitor.*.restart");
            }

            monitorDeadLocks(config);
            monitorHeap(config);
            monitorGc(config);

            if (_debug > 1)
                System.out.println("terminated WrapperManager.init()");
            // if (commonsLog != null)
            // System.setProperty("org.apache.commons.logging.Log", commonsLog);
            LogFactory.getFactory().removeAttribute("org.apache.commons.logging.Log");

            Utils.verifyIPv4IsPreferred(null);
        } catch (Throwable ex) {
            ex.printStackTrace();
        }
        if (currentClassLoader != null)
            Thread.currentThread().setContextClassLoader(currentClassLoader);

    }

    private void monitorDeadLocks(YajswConfigurationImpl config) {
        if (config.getBoolean("wrapper.java.monitor.deadlock", false)) {
            final long cycle = config.getLong("wrapper.java.monitor.deadlock.interval", 30) * 1000;
            final ThreadMXBean bean = ManagementFactory.getThreadMXBean();
            if (_debug > 2)
                System.out.println("monitor deadlock: start");
            executor.execute(new Runnable() {

                public void run() {
                    while (!_stopping) {
                        long[] ids = bean.findDeadlockedThreads();
                        if (ids != null && ids.length > 0) {
                            System.err.println("wrapper.java.monitor.deadlock: DEADLOCK IN THREADS: ");
                            threadDump(ids);
                            // exit loop once we find a deadlock
                            return;
                        }
                        try {
                            Thread.sleep(cycle);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }

                }

            });

        }
    }

    volatile boolean _heapNotified = false;

    private void monitorHeap(YajswConfigurationImpl config) {
        if (config.getBoolean("wrapper.java.monitor.heap", false)) {
            final long cycle = config.getLong("wrapper.java.monitor.heap.interval", 30) * 1000;
            final int thresholdPercent = config.getInt("wrapper.java.monitor.heap.threshold.percent", 95);
            if (_debug > 2)
                System.out.println("monitor heap: start");
            executor.execute(new Runnable() {

                public void run() {
                    while (!_stopping) {
                        synchronized (_heapDataLock) {
                            // currentPercentHeap = ((float)
                            // memoryBean.getHeapMemoryUsage().getUsed() /
                            // maxHeap) * 100;
                            long usedMem = memoryBean.getHeapMemoryUsage().getUsed();
                            currentPercentHeap = (float) ((((double) usedMem) / maxHeap) * 100d);
                            if (currentPercentHeap > thresholdPercent) {
                                if (!_heapNotified) {
                                    // System.err.println("wrapper.java.monitor.heap: HEAP SIZE EXCEEDS THRESHOLD: "
                                    // +
                                    // memoryBean.getHeapMemoryUsage().getUsed()
                                    // + "/" + maxHeap);
                                    System.err.println("wrapper.java.monitor.heap: HEAP SIZE EXCEEDS THRESHOLD: "
                                            + usedMem + "/" + maxHeap + " => " + currentPercentHeap + "%");
                                    _heapNotified = true;
                                }
                            } else if (_heapNotified) {
                                /*
                                 * System.err .println(
                                 * "wrapper.java.monitor.heap: HEAP SIZE OK: " +
                                 * memoryBean .getHeapMemoryUsage() .getUsed() +
                                 * "/" + maxHeap);
                                 */
                                System.err.println("wrapper.java.monitor.heap: HEAP SIZE OK: " + usedMem + "/"
                                        + maxHeap + " => " + currentPercentHeap + "%");

                                _heapNotified = false;

                            }
                        }
                        try {
                            Thread.sleep(cycle);
                        } catch (InterruptedException e) {
                            return;
                        }
                    }

                }

            });

        }
    }

    private void monitorGc(YajswConfigurationImpl config) {
        initGCBeans();
        String mFormat = config.getString("wrapper.java.monitor.gc", null);
        boolean gcRestart = config.getBoolean("wrapper.java.monitor.gc.restart", false);
        if (mFormat != null || gcRestart)
            try {
                if (_debug > 2)
                    System.out.println("monitor GC: " + mFormat);
                if (mFormat != null)
                    gcFormat = new MessageFormat(mFormat);
                final long cycle = config.getLong("wrapper.java.monitor.gc.interval", 1) * 1000;

                if (_debug > 2) {
                    System.out.println("monitor gc: minorGCBean/fullGCBean: " + minorGCBean.getName() + "/"
                            + fullGCBean.getName());

                    System.out.println("monitor gc: start cycle " + cycle + "ms");
                }
                executor.execute(new Runnable() {
                    public void run() {
                        if (minorGCBean == null) {
                            System.err.println("monitor gc: could not find minorGCBean -> abort monitor");
                            return;
                        }
                        if (fullGCBean == null) {
                            System.err.println("monitor gc: could not find fullGCBean -> abort monitor");
                            return;
                        }
                        try {
                            while (!_stopping) {
                                getGCData();
                                // Sleep a little bit before the next poll
                                Thread.sleep(cycle);
                            }
                        } catch (Exception ex) {
                            // Do nothing except exit this thread
                            ex.printStackTrace();
                        }
                        System.err.println("monitor gc: end");

                    }
                });
            } catch (Exception ex) {
                System.err.println("monitor gc: exception: " + ex);
                ex.printStackTrace();
            }
    }

    private void initGCBeans() {
        if (_initGCBeans)
            return;
        GarbageCollectorMXBean minorGCBeanX = null;
        GarbageCollectorMXBean fullGCBeanX = null;

        try {
            List<GarbageCollectorMXBean> gcMBeans = ManagementFactory.getGarbageCollectorMXBeans();
            for (GarbageCollectorMXBean gcBean : gcMBeans) {
                if (gcBean.getName().toLowerCase().contains("copy")) {
                    minorGCBeanX = gcBean;
                } else if ("ParNew".equals(gcBean.getName())) {
                    minorGCBeanX = gcBean;
                } else if (gcBean.getName().toLowerCase().contains("scavenge")) {
                    minorGCBeanX = gcBean;
                } else if (gcBean.getName().toLowerCase().contains("marksweep")) {
                    fullGCBeanX = gcBean;
                } else {
                    System.err.println("Unable to classify GarbageCollectorMXBean [" + gcBean.getName() + "]");
                }
            }
        } catch (Throwable e) {
            System.out.println("error getting GC beans");
        }
        minorGCBean = minorGCBeanX;
        fullGCBean = fullGCBeanX;
        _initGCBeans = true;

    }

    private void getGCData() {
        initGCBeans();
        if (minorGCBean == null || fullGCBean == null)
            return;
        if (minorGCBean.getCollectionCount() != lastMinorCollectionCount) {
            long diffCount = minorGCBean.getCollectionCount() - lastMinorCollectionCount;
            long diffTime = minorGCBean.getCollectionTime() - lastMinorCollectionTime;
            if (diffCount != 0 && diffCount != 1)
                timeMinorGC = diffTime / diffCount;
            else
                timeMinorGC = diffTime;
            usedHeap = memoryBean.getHeapMemoryUsage().getUsed();

            lastMinorCollectionCount = minorGCBean.getCollectionCount();
            lastMinorCollectionTime = minorGCBean.getCollectionTime();
        }

        if (fullGCBean.getCollectionCount() != lastFullCollectionCount) {
            long diffCount = fullGCBean.getCollectionCount() - lastFullCollectionCount;
            long diffTime = fullGCBean.getCollectionTime() - lastFullCollectionTime;
            if (diffCount != 0 && diffCount != 1)
                timeFullGC = diffTime / diffCount;
            else
                timeFullGC = diffTime;

            lastFullCollectionCount = fullGCBean.getCollectionCount();
            lastFullCollectionTime = fullGCBean.getCollectionTime();
        }
        usedHeap = memoryBean.getHeapMemoryUsage().getUsed();
        if (usedHeap != null) {
            if (timeMinorGC == null)
                timeMinorGC = 0L;
            if (timeFullGC == null)
                timeFullGC = 0L;

            // remember data to be sent by ping
            synchronized (_heapDataLock) {
                currentPercentHeap = ((float) usedHeap / maxHeap) * 100;
                if (minorGCDuration == -1)
                    minorGCDuration = 0;
                if (fullGCDuration == -1)
                    fullGCDuration = 0;
                minorGCDuration += timeMinorGC;
                fullGCDuration += timeFullGC;
            }

            lastUsedHeap = usedHeap;

            if (gcFormat != null)
                System.err.println(gcFormat.format(new Object[] { usedHeap, timeMinorGC, timeFullGC }));
            usedHeap = null;
            timeMinorGC = null;
            timeFullGC = null;
        }

    }

    private void registerMBean(YajswConfiguration config) {
        MBeanServer server = null;
        ArrayList servers = MBeanServerFactory.findMBeanServer(null);
        try {
            if (servers != null && servers.size() > 0)
                server = (MBeanServer) servers.get(0);
            if (server != null) {
                String name = config.getString("wrapper.console.title");
                if (name == null)
                    name = config.getString("wrapper.ntservice.name");
                if (name == null)
                    name = "yajsw.noname";
                ObjectName oName = new ObjectName("Wrapper", "name", name);
                server.registerMBean(this, oName);
                // System.out.println("found mbean server: " +
                // server.toString());
            } else
                System.out.println("ERROR: no mbean server found ");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#getMainMethod()
     */
    public Method getMainMethod() {
        return mainMethod;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#getMainMethodArgs()
     */
    public Object[] getMainMethodArgs() {
        return mainMethodArgs;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#isExitOnMainTerminate()
     */
    public int getExitOnMainTerminate() {
        if (_debug > 2)
            System.out.println("exit on main terminate " + exitOnMainTerminate);
        return exitOnMainTerminate;
    }

    public int getExitOnException() {
        if (_debug > 2)
            System.out.println("exit on main exception " + exitOnException);
        return exitOnException;
    }

    /**
     * Load jar.
     * 
     * @param jarName
     *            the jar name
     * 
     * @return the method
     */
    private Method loadJar(String jarName) {
        URL url = null;
        try {
            url = new File(jarName).toURI().toURL();
        } catch (MalformedURLException e2) {
            e2.printStackTrace();
            return null;
        }
        Manifest manifest;
        try {
            manifest = new JarFile(new File(jarName)).getManifest();
        } catch (IOException e1) {
            e1.printStackTrace();
            return null;
        }
        Attributes attr = manifest.getMainAttributes();

        String cl = attr.getValue("Class-Path");
        ClassLoader loader = null;
        /*
         * if (cl != null) { ArrayList classpath = new ArrayList(); String[]
         * clArr = cl.split(" "); for (int i = 0; i < clArr.length; i++) {
         * String file = clArr[i]; File myFile; try { myFile = new File(file);
         * classpath.add(myFile); } catch (Exception e) { e.printStackTrace(); }
         * }
         * 
         * URL[] urlsArr = new URL[classpath.size()]; int i = 0; for (Iterator
         * it = classpath.iterator(); it.hasNext(); i++) try { urlsArr[i] =
         * ((File) it.next()).toURI().toURL(); } catch (Exception e) {
         * e.printStackTrace(); }
         * 
         * loader = new URLClassLoader(urlsArr,
         * ClassLoader.getSystemClassLoader()); }
         */if (loader == null)
            loader = ClassLoader.getSystemClassLoader();

        String mainClassName = attr.getValue("Main-Class");
        if (mainClassName == null)
            return null;
        Method mainMethod = null;
        try {
            Class cls = loader.loadClass(mainClassName);// cl.loadClass(mainClassName);
            mainMethod = cls.getMethod("main", new Class[] { String[].class });
        } catch (Exception ex) {
            ex.printStackTrace();
            System.err
                    .println("ERROR: could not load main method from class/jar: " + mainClassName + "/" + jarName);
        }

        return mainMethod;
    }

    private File createRWfile(String path, String fname) {
        File pFile = new File(path);
        try {
            if (!pFile.exists())
                pFile.mkdirs();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        File result = new File(path, fname);

        if (OperatingSystem.instance().isPosix()) {
            String absPath = result.getAbsolutePath();
            System.out.println("createRWfile " + absPath);
            try {
                if (!result.exists()) {
                    result.createNewFile();
                }
                result.deleteOnExit();
                Process p = Runtime.getRuntime().exec("chmod 777 " + absPath);
                // p.waitFor();
                Thread.sleep(500);
                p.destroy();
            } catch (Exception ex) {
                ex.printStackTrace(System.out);
            }
        }

        return result;
    }

    /**
     * Tee system streams.
     * 
     * @param outFile
     *            the out file
     * @param path
     *            the path
     * @param visible
     *            the visible
     */
    private void teeSystemStreams(String outFile, String path, boolean visible) {
        File fOut = createRWfile(path, "out_" + outFile);
        // if (fOut.exists())
        // fOut.delete();
        fOut.deleteOnExit();
        File fErr = createRWfile(path, "err_" + outFile);
        // if (fErr.exists())
        // fErr.delete();
        fErr.deleteOnExit();
        File fIn = createRWfile(path, "in_" + outFile);
        fIn.deleteOnExit();

        try {
            PrintStream wrapperOut = (PrintStream) new CyclicBufferFilePrintStream(fOut);
            TeeOutputStream newOut = (TeeOutputStream) new TeeOutputStream();
            newOut.connect(wrapperOut);
            // pipe output to console only if it is visible
            if (visible)
                newOut.connect(System.out);
            _outStream = wrapperOut;
            System.setOut(new PrintStream(newOut));
        } catch (Throwable e) {
            e.printStackTrace(System.out);
        }

        try {

            PrintStream wrapperErr = (PrintStream) new CyclicBufferFilePrintStream(fErr);
            TeeOutputStream newErr = (TeeOutputStream) new TeeOutputStream();
            newErr.connect(wrapperErr);
            // pipe output to console only if it is visible
            if (visible)
                newErr.connect(System.err);
            _errStream = newErr;
            System.setErr(new PrintStream(newErr));
        } catch (Throwable e) {
            e.printStackTrace();
        }

        try {
            CyclicBufferFileInputStream wrapperIn = new CyclicBufferFileInputStream(fIn, "r");
            TeeInputStream newIn = (TeeInputStream) new TeeInputStream();
            newIn.connect(wrapperIn);
            newIn.connect(System.in);
            System.setIn(newIn);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * Log java info.
     * 
     * @param args
     *            the args
     */
    private void logJavaInfo(String[] args) {
        if (_debug > 1) {
            System.out.println("APP user name=" + getSystemProperty("user.name"));
            System.out.println("APP working dir=" + getSystemProperty("user.dir"));
            System.out.println("APP java version=" + getSystemProperty("java.version"));
            System.out.println("APP class path=" + getSystemProperty("java.class.path"));
            System.out.println("APP library path=" + getSystemProperty("java.library.path"));
        }
        String[] files = getSystemProperty("java.class.path").split(File.pathSeparator);
        for (int i = 0; i < files.length; i++) {
            File f = new File(files[i]);
            if (!f.exists())
                System.err.println("Classpath File not found: " + files[i]);
        }

        if (_debug > 1) {
            String argsStr = "Application args: ";
            if (args != null && args.length > 0)
                for (int i = 0; i < args.length; i++) {
                    argsStr += args[i] + " ";
                }
            else
                argsStr += "no args";
            System.out.println(argsStr);
        }

    }

    /**
     * Gets the app param.
     * 
     * @param config
     *            the config
     * 
     * @return the app param
     */
    private String[] getAppParam(Configuration config) {
        ArrayList result = new ArrayList();
        ArrayList keys = new ArrayList();
        for (Iterator it = config.getKeys("wrapper.app.parameter"); it.hasNext();) {
            keys.add(it.next());
        }
        Collections.sort(keys, new AlphaNumericComparator());
        for (Iterator it = keys.listIterator(); it.hasNext();) {
            String arg = config.getString((String) it.next());
            if (arg != null) {
                arg = arg.trim();
                if (arg.length() > 0)
                    result.add(arg);
            }
        }
        String[] args = new String[result.size()];
        int i = 0;
        for (Iterator it = result.iterator(); it.hasNext(); i++) {
            args[i] = (String) it.next();
        }
        if (_debug > 1) {
            System.out.println("args: ");
            for (String arg : args)
                System.out.println(arg);
        }
        return args;
    }

    /**
     * Sets the configuration.
     * 
     * @param config
     *            the new configuration
     */
    void setConfiguration(Configuration config) {
        _config = config;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#start()
     */
    public void start() {
        try {
            // hack: avoid netty hangs on connect
            if (_config.getBoolean("wrapper.console.pipestreams", false)) {
                Socket dummy = new Socket("127.0.0.1", _port);
                OutputStream out = dummy.getOutputStream();
                out.close();
                dummy.close();
            }
        } catch (Throwable e1) {
            if (_debug > 1)
                e1.printStackTrace();
        }

        connector = new Bootstrap();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        // workerGroup.setIoRatio(99);
        connector.group(workerGroup);
        connector.channel(NioSocketChannel.class);

        connector.remoteAddress(new InetSocketAddress("127.0.0.1", _port));
        connector.option(ChannelOption.SO_REUSEADDR, true);
        connector.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000);
        connector.option(ChannelOption.TCP_NODELAY, true);

        if (_debugComm)
            connector.handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline()
                            .addLast(new io.netty.channel.ChannelHandler[] {
                                    new SystemOutLoggingFilter("WrapperManager"),
                                    new DelimiterBasedFrameDecoder(8192, true, Delimiters.nulDelimiter()),
                                    new MessageEncoder(), new MessageDecoder(), new WrapperHandler() }

                    );
                }

            });
        else
            connector.handler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline()
                            .addLast(new io.netty.channel.ChannelHandler[] {
                                    new DelimiterBasedFrameDecoder(8192, true, Delimiters.nulDelimiter()),
                                    new MessageEncoder(), new MessageDecoder(), new WrapperHandler() }

                    );
                }

            });

        // pinger is a cycler with high priority threads
        // sends ping messages within a ping interval
        _pinger = new Cycler(getPingInterval(), 0,
                Executors.newCachedThreadPool(new DaemonThreadFactory("pinger", Thread.MAX_PRIORITY)),
                new Runnable() {
                    long start = System.currentTimeMillis();

                    public void run() {
                        ChannelFuture future;
                        if (_session != null && _session.isActive() && !_stopping && !_appearHanging) {
                            synchronized (_heapDataLock) {
                                if (_sendHeapData) {
                                    if (minorGCDuration == -1)
                                        getGCData();
                                    future = _session.writeAndFlush(
                                            new Message(Constants.WRAPPER_MSG_PING, "" + currentPercentHeap + ";"
                                                    + minorGCDuration + ";" + fullGCDuration + ";" + lastUsedHeap));
                                    currentPercentHeap = -1;
                                    minorGCDuration = -1;
                                    fullGCDuration = -1;
                                } else {
                                    future = _session.writeAndFlush(new Message(Constants.WRAPPER_MSG_PING, ""));
                                }
                            }
                            try {
                                future.await(10000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            start = System.currentTimeMillis();
                        } else if ((_haltAppOnWrapper && (System.currentTimeMillis() - start) > _startupTimeout)
                                && !_stopping) {
                            System.out.println("no connection to wrapper during " + (_startupTimeout / 1000)
                                    + " seconds -> System.exit(-1)");
                            System.out.println(
                                    "if this is due to server overload consider increasing yajsw configuration property wrapper.startup.timeout");
                            System.exit(-1);
                        }
                    }
                });
        _pinger.start();

        // connect
        // handler should process messages in a separate thread

        reconnect();
        /*
         * try { if (_config.getInt("wrapper.action.port") > 0) { _actionHandler
         * = new ActionHandler(this, _config); _actionHandler.start(); } } catch
         * (Exception ex) { log.info("could not start action handler " +
         * ex.getMessage()); }
         */

    }

    /**
     * Reconnect.
     */
    private void reconnect() {
        // try connecting, if we could not sleep then retry
        while (!_started) {
            if (_debug > 1)
                // log.fine("connecting to port " + _port);
                System.out.println("connecting to port " + _port);
            final ChannelFuture future1 = connector.connect();
            try {
                Thread.yield();
                // System.out.println("connecting wait future ");
                future1.await(10000);
                // System.out.println("after connecting wait future ");
                _started = future1.isSuccess();
            } catch (Exception e1) {
                // TODO Auto-generated catch block
                System.out.println("error connecting to wrapper: " + e1);
                e1.printStackTrace();
            }
            /*
             * executor.execute(new Runnable() {
             * 
             * public void run() { future1.addListener(new
             * ChannelFutureListener() { public void
             * operationComplete(ChannelFuture future) throws Exception {
             * _lock.lock(); System.out.println("future" + future.isSuccess());
             * _started = future.isSuccess(); _connectEnd.signal();
             * _lock.unlock();
             * 
             * } }); }
             * 
             * });
             * 
             * _lock.lock(); try { _connectEnd.await(); } catch
             * (InterruptedException e1) { // TODO Auto-generated catch block
             * e1.printStackTrace(); } _lock.unlock();
             * System.out.println("started "+_started);
             */

            if (_started) {
                if (_debug > 2)
                    System.out.println("WrapperManager: channel connected, sending key");
                future1.channel().writeAndFlush(new Message(Constants.WRAPPER_MSG_KEY, _key));
            } else
                try {
                    if (_debug > 0)
                        // log.fine("connection failed -> sleep then retry");
                        System.out.println("connection failed -> sleep then retry");
                    _started = false;
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
        }
    }

    /**
     * The Class WrapperHandler.
     */
    class WrapperHandler extends ChannelInboundHandlerAdapter {

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            if (_debug > 1)
                System.out.println("session closed");
            _started = false;
            if (_session != null)
                try {
                    _session.close();
                } catch (Throwable ex) {
                    ex.printStackTrace();
                }
            _session = null;
            if (!_stopping) {
                if (_debug > 0)
                    System.out.println("try reconnect");
                executor.execute(new Runnable() {
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        reconnect();
                    }
                });
            }
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object message) {
            if (_stopping)
                return;
            Channel session = ctx.channel();
            Message msg = (Message) message;
            if (msg.getCode() == Constants.WRAPPER_MSG_STOP)
                try {
                    System.out.println("wrapper manager received stop command");
                    _stopping = true;

                    if (session != null)
                        session.close();
                    // Thread.sleep(100);
                    if (msg.getMessage() != null && msg.getMessage().length() > 0)
                        try {
                            String[] txt = msg.getMessage().split(":");
                            if (txt[0].length() > 0)
                                _exitCode = Integer.parseInt(txt[0]);
                            if (txt.length > 1 && txt[1].length() > 0)
                                _stopReason = txt[1];
                        } catch (Exception ex) {
                            // DO NOTHING
                        }
                    executeShutdownScript();
                    executeShutdownListener();
                    if (!_externalStop) {
                        System.exit(_exitCode);
                    }
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            else if (msg.getCode() == Constants.WRAPPER_MSG_OKKEY) {
                _session = session;
                try {
                    _myPid = Integer.parseInt(msg.getMessage());
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            } else if (msg.getCode() == Constants.WRAPPER_MSG_THREAD_DUMP) {
                threadDump();
            } else if (msg.getCode() == Constants.WRAPPER_MSG_GC) {
                gc();
            } else if (msg.getCode() == Constants.WRAPPER_MSG_DUMP_HEAP) {
                dumpHeap(msg.getMessage());
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) throws Exception {
            /*
             * if (_debug) // log.log(Level.FINE, "exceptionCaught",
             * e.getCause()); e.getCause().printStackTrace();
             */
            if (_debug > 1) {
                Throwable th = e;
                if (th == null)
                    return;
                String msg = th.getMessage();
                if (msg == null)
                    return;
                System.err.println(msg);
                if (!msg.toLowerCase().contains("connection refused"))
                    th.printStackTrace(System.err);
            }

        }

    }

    /**
     * Gets the port.
     * 
     * @return the port
     */
    int getPort() {
        return _port;
    }

    /**
     * Sets the port.
     * 
     * @param port
     *            the new port
     */
    public void setPort(int port) {
        _port = port;
    }

    /**
     * Checks if is debug.
     * 
     * @return true, if is debug
     */
    int getDebug() {
        return _debug;
    }

    /**
     * Sets the debug.
     * 
     * @param debug
     *            the new debug
     */
    void setDebug(int debug) {
        _debug = debug;
    }

    /**
     * Gets the key.
     * 
     * @return the key
     */
    String getKey() {
        return _key;
    }

    /**
     * Sets the key.
     * 
     * @param key
     *            the new key
     */
    public void setKey(String key) {
        _key = key;
    }

    /**
     * Checks if is started.
     * 
     * @return true, if is started
     */
    boolean isStarted() {
        return _started;
    }

    /**
     * Gets the ping interval.
     * 
     * @return the ping interval
     */
    int getPingInterval() {
        return _pingInterval;
    }

    /**
     * Sets the ping interval.
     * 
     * @param pingInterval
     *            the new ping interval
     */
    void setPingInterval(int pingInterval) {
        _pingInterval = pingInterval * 1000;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#stop()
     */
    public void stop() {
        if (_session != null)
            while (_session != null && !_stopping) {
                _session.writeAndFlush(new Message(Constants.WRAPPER_MSG_STOP, null));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        else
            System.exit(0);

    }

    /**
     * Stop timer.
     */
    public void stopTimer() {
        if (_session != null)
            while (_session != null && !_stopping) {
                _session.writeAndFlush(new Message(Constants.WRAPPER_MSG_STOP_TIMER, null));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    }

    /**
     * Restart.
     */
    public void restart() {
        if (_session != null)
            while (_session != null && !_stopping) {
                _session.writeAndFlush(new Message(Constants.WRAPPER_MSG_RESTART, null));
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        else
            System.out.println("not connected to wrapper -> cannot send restart command");
    }

    /**
     * Instance.
     * 
     * @return the wrapper manager impl
     */
    public static WrapperManagerImpl instance() {
        return instance;
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.rzo.yajsw.WrapperManager#getPid()
     */
    public int getPid() {
        return _myPid;
    }

    public boolean isControlledByWrapper() {
        return _started;
    }

    public boolean isLaunchedAsService() {
        return _config.getBoolean("wrapper.service", false);
    }

    public String getGroovyScript() {
        return _groovyScript;
    }

    public void threadDump() {
        System.out.println("yajsw: thread dump requested");
        threadDump(null);
    }

    public void threadDump(long[] ids) {
        Message m = new Message(Constants.WRAPPER_MSG_THREAD_DUMP, null);
        Action a = ActionFactory.getAction(m);
        try {
            if (_overrideStdErr)
                a.execute(m, _session, new PrintStream(_errStream), ids);
            else
                a.execute(m, _session, System.err, ids);

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

    public void gc() {
        System.out.println("yajsw: gc requested");
        System.gc();
    }

    public void dumpHeap(final String fileName) {
        if (_dumpingHeap)
            return;
        System.out.println("yajsw: dumpHeap requested " + fileName);
        _dumpingHeap = true;
        executor.execute(new Runnable() {

            public void run() {
                try {
                    File file;
                    if (fileName == null || fileName.length() < 1)
                        file = new File(".");
                    else
                        file = new File(fileName);
                    File parent;
                    if (file.isDirectory()) {
                        parent = file;
                        file = new File(parent, "dump" + "_"
                                + new SimpleDateFormat("yyyy_MM_dd-hh_mm").format(new Date()) + ".hprof");
                    } else
                        parent = file.getParentFile();

                    if (!parent.exists())
                        parent.mkdirs();

                    // com.sun.management.HotSpotDiagnosticMXBean mb =
                    // sun.management.ManagementFactory.getDiagnosticMXBean();
                    com.sun.management.HotSpotDiagnosticMXBean mb = ManagementFactory.newPlatformMXBeanProxy(
                            ManagementFactory.getPlatformMBeanServer(), HOTSPOT_BEAN_NAME,
                            HotSpotDiagnosticMXBean.class);
                    File dumpFile = new File(parent, file.getName());
                    mb.dumpHeap(dumpFile.getAbsolutePath(), true);
                    System.out.println("yajsw: dumpHeap done " + dumpFile.getAbsolutePath());
                } catch (Throwable ex) {
                    ex.printStackTrace();
                } finally {
                    _dumpingHeap = false;
                }
            }

        });

    }

    private static final String HOTSPOT_BEAN_NAME = "com.sun.management:type=HotSpotDiagnostic";

    public static void main(String[] args) throws MalformedURLException {
        String name = "c:/test test/start.jar";
        System.out.println(new File(name).exists());
        System.out.println(new File(new File(name).toURI().getPath()).exists());
        WrapperManager wm = new WrapperManagerImpl();
        ((WrapperManagerImpl) wm).loadJar("c:/test test/start.jar");
        synchronized (wm) {
            wm.threadDump();
        }
    }

    public boolean isAppearHanging() {
        return _appearHanging;
    }

    public void setAppearHanging(boolean appearHanging) {
        _appearHanging = appearHanging;
    }

    public void reportServiceStartup() {
        boolean reported = false;
        while (!reported && !_stopping) {
            if (_session == null || !_session.isActive())
                try {
                    Thread.sleep(500);
                } catch (Exception ex) {

                }
            else {
                _session.writeAndFlush(new Message(Constants.WRAPPER_MSG_SERVICE_STARTUP, null));
                reported = true;
            }
        }
    }

    private void executeShutdownScript() {
        if (shutdownScript != null & !"".equals(shutdownScript)) {
            if (_debug > 1)
                System.out.println("executing shutdown script " + shutdownScript);
            Script script = ScriptFactory.createScript(shutdownScript, "wrapper.app.shutdown.script", null,
                    new String[0], log, 0, _config.getString("wrapper.script.encoding"),
                    _config.getBoolean("wrapper.script.reload", false), _debug, 1);
            // make sure it is invoked only once
            // stop may be invoke multiple times
            shutdownScript = null;
            if (script != null) {
                script.execute();
                if (_debug > 2)
                    System.out.println("terminated shutdown script ");
            } else
                System.out.println("Error " + shutdownScript + "not initialized");
        }

    }

    private void executeShutdownListener() {
        if (_shutdownListener == null)
            return;
        new Thread(_shutdownListener).start();
    }

    public void executeScript(String scriptFileName, ClassLoader wrapperClassLoader) {
        System.out.println("initializing script " + scriptFileName);
        ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(wrapperClassLoader);
        try {
            Script script = ScriptFactory.createScript(scriptFileName, "", null, (String[]) getMainMethodArgs(),
                    null, 0, _config.getString("wrapper.script.encoding"),
                    _config.getBoolean("wrapper.script.reload", false), _debug, 1);
            if (script != null)
                script.execute();
            else
                System.err.println("error opening script script: " + scriptFileName);
        } catch (Throwable ex) {
            System.err.println("error executing script: " + scriptFileName);
            ex.printStackTrace();
        }
        Thread.currentThread().setContextClassLoader(currentClassLoader);

    }

    public void signalStopping(int timeoutHint) {
        try {
            if (_session == null || !_session.isActive()) {
                final ChannelFuture future1 = connector.connect();
                future1.await();
                future1.isSuccess();
                _session = future1.channel();
            }
            ChannelFuture wFuture = _session
                    .writeAndFlush(new Message(Constants.WRAPPER_MSG_STOP_PENDING, String.valueOf(timeoutHint)));
            wFuture.await();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            _session.close();
            _session = null;
        }

    }

    public Properties getProperties() {
        if (_properties == null)
            _properties = ConfigUtils.asProperties(_config);
        return _properties;
    }

    public String getStopReason() {
        return _stopReason;
    }

    @Override
    public void setShutdownListener(Runnable listener) {
        _shutdownListener = listener;
    }

}