com.zotoh.maedr.device.JettyIO.java Source code

Java tutorial

Introduction

Here is the source code for com.zotoh.maedr.device.JettyIO.java

Source

/*??
 * COPYRIGHT (C) 2011 CHERIMOIA LLC. ALL RIGHTS RESERVED.
 *
 * THIS IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR
 * MODIFY IT UNDER THE TERMS OF THE APACHE LICENSE, 
 * VERSION 2.0 (THE "LICENSE").
 *
 * THIS LIBRARY 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 LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS 
 * AND LIMITATIONS UNDER THE LICENSE.
 *
 * You should have received a copy of the Apache License
 * along with this distribution; if not, you may obtain a copy of the 
 * License at 
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 ??*/

package com.zotoh.maedr.device;

import static com.zotoh.core.util.CoreUte.asList;
import static com.zotoh.core.util.CoreUte.getCZldr;
import static com.zotoh.core.util.CoreUte.getResourceStr;
import static com.zotoh.core.util.CoreUte.niceFPath;
import static com.zotoh.core.util.CoreUte.tstEStrArg;
import static com.zotoh.core.util.CoreUte.tstObjArg;
import static com.zotoh.core.util.LangUte.MP;
import static com.zotoh.core.util.StrUte.isEmpty;
import static com.zotoh.core.util.StrUte.nsb;
import static com.zotoh.core.util.StrUte.trim;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;

import javax.net.ssl.SSLContext;

import org.eclipse.jetty.http.ssl.SslContextFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.NCSARequestLog;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.server.nio.SelectChannelConnector;
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.webapp.WebAppContext;
import org.json.JSONArray;
import org.json.JSONObject;

import com.zotoh.core.io.CmdLineMandatory;
import com.zotoh.core.io.CmdLineQuestion;
import com.zotoh.core.io.CmdLineSequence;
import com.zotoh.core.util.Tuple;

/**
 * Http IO using Jetty as an embedded web server, using Jetty's continuation to create asynchronicity when handling requests.
 * 
 * The set of properties:
 * 
 * <b>contextpath</b>
 * The application context path.
 * <b>waitmillis</b>
 * The time this request will be put on hold until a result is ready from the downstream application - default is 5 mins.
 * <b>workers</b>
 * The number of worker threads allocated to Jetty, default is 10.
 * <b>warfile</b>
 * The full path pointing to a web WAR file.  The WAR application must use the servlet provided by this framework.
 * If a WAR is defined, then the following properties are ignored. 
 * <b>resbase</b>
 * The full path pointing to the resource base directory.
 * <b>urlpatterns</b>
 * A list of servlet path patterns.
 * <b>filters</b>
 * A list of Filter definitions.  Each Filter definition is another Map of name-value pairs, such as:
 * ----> <b>urlpattern</b> the filter path.
 * ----> <b>class</b> the class for the filter object.
 * ----> <b>params</b> a map of parameters for this filter (key-values).
 * 
 * @see com.zotoh.maedr.device.HttpIOTrait
 * 
 * @author kenl
 * 
 */
public class JettyIO extends BaseHttpIO implements Weblet {

    //    private static final SMap<JettyIO> _devMap= new SMap<JettyIO>();
    private String _warPtr, _resDir, _logDir, _contextPath;
    private List<Tuple> _filters;
    private List<String> _urls;
    private Server _jetty;

    /**
     * @param mgr
     */
    public JettyIO(DeviceManager<?, ?> mgr) {
        super(mgr);
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.Weblet#setContextPath(java.lang.String)
     */
    public void setContextPath(String path) {
        _contextPath = nsb(path);
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.Weblet#getContextPath()
     */
    public String getContextPath() {
        return _contextPath;
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.HttpIOTrait#inizWithProperties(org.json.JSONObject)
     */
    @SuppressWarnings("serial")
    @Override
    protected void inizWithProperties(JSONObject deviceProperties) throws Exception {
        super.inizWithProperties(deviceProperties);

        String cpath = trim(deviceProperties.optString("contextpath"));
        _logDir = trim(deviceProperties.optString("logdir"));
        _resDir = trim(deviceProperties.optString("resbase"));
        _warPtr = trim(deviceProperties.optString("warfile"));
        _contextPath = isEmpty(cpath) ? "" : cpath;

        if (isEmpty(_warPtr)) {
            final JSONArray arr = deviceProperties.optJSONArray("urlpatterns");
            tstObjArg("servlet-url-patterns", arr);
            _urls = new ArrayList<String>() {
                {
                    for (int i = 0; i < arr.length(); ++i) {
                        add(nsb(arr.get(i)));
                    }
                }
            };

            final JSONArray fils = deviceProperties.optJSONArray("filters");
            _filters = new ArrayList<Tuple>() {
                {
                    if (fils != null)
                        for (int i = 0; i < fils.length(); ++i) {
                            add(toFilter(fils.optJSONObject(i)));
                        }
                }
            };
        }

    }

    /**/
    private Tuple toFilter(JSONObject ftr) throws Exception {
        tstObjArg("filter-definition", ftr);
        JSONObject ps = ftr.optJSONObject("params");
        String url = ftr.optString("urlpattern");
        String s, z = ftr.optString("class");
        JSONObject m = new JSONObject();
        tstEStrArg("filter-url-pattern", url);
        tstEStrArg("filter-class", z);
        if (ps != null) {
            for (Iterator<?> it = ps.keys(); it.hasNext();) {
                s = (String) it.next();
                m.put(s, nsb(ps.get(s)));
            }
        }
        return new Tuple(z, url, m);
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.Device#onStart()
     */
    @Override
    protected void onStart() throws Exception {
        SelectChannelConnector cc;
        Server svr = new Server();
        if (isSSL()) {
            SslSelectChannelConnector c = new SslSelectChannelConnector();
            Tuple t = cfgSSL(true, getSSLType(), getKeyURL(), getKeyPwd());
            SslContextFactory fac = c.getSslContextFactory();
            fac.setSslContext((SSLContext) t.get(1));
            fac.setWantClientAuth(false);
            fac.setNeedClientAuth(false);
            cc = c;
        } else {
            cc = new SelectChannelConnector();
        }

        cc.setName(this.getId());

        if (!isEmpty(getHost())) {
            cc.setHost(getHost());
        }
        cc.setPort(getPort());

        cc.setThreadPool(new QueuedThreadPool(getWorkers()));
        cc.setMaxIdleTime(30000); // from jetty examples
        cc.setRequestHeaderSize(8192); // from jetty examples
        svr.setConnectors(new Connector[] { cc });

        if (isEmpty(_warPtr)) {
            onStart_Servlet(svr);
        } else {
            onStart_War(svr);
        }

        _jetty = svr;
        _jetty.start();
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.Device#onStop()
     */
    @Override
    protected void onStop() {
        if (_jetty != null)
            try {
                _jetty.stop();
            } catch (Throwable t) {
            }
        _jetty = null;
    }

    /**/
    @SuppressWarnings("unchecked")
    private void onStart_Servlet(Server svr) throws Exception {
        //ServletContextHandler x = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.NO_SECURITY);
        ServletContextHandler x = new ServletContextHandler(ServletContextHandler.SESSIONS);
        x.setContextPath(_contextPath);
        x.setClassLoader(getCZldr());
        x.setDisplayName(this.getId());
        x.setResourceBase(isEmpty(_resDir) ? "." : _resDir);

        ContextHandlerCollection contexts = new ContextHandlerCollection();
        HandlerCollection handlers = new HandlerCollection();
        Handler[] hs;

        /*
        ResourceHandler rh= new ResourceHandler();
        rh.setDirectoriesListed(false);
        rh.setAliases(false);
        rh.setResourceBase(isEmpty(_resDir) ? "." : _resDir);
        rh.setWelcomeFiles(new String[]{ "index.html" });
        */

        hs = asList(true, contexts, new DefaultHandler(), maybeREQLog()).toArray(new Handler[0]);
        handlers.setHandlers(hs);

        ServletHolder so = new ServletHolder(new DefaultServlet());
        FilterHolder ho;
        Map<String, String> pms = new HashMap<String, String>();
        //pms.put("org.eclipse.jetty.servlet.Default.dirAllowed","false");
        //pms.put("org.eclipse.jetty.servlet.Default.resourceBase", isEmpty(_resDir)?".":_resDir);
        pms.put("resourceBase", isEmpty(_resDir) ? "." : _resDir);
        so.setInitParameters(pms);
        x.addServlet(so, "/*");

        // add url patterns
        for (String u : _urls) {
            so = new ServletHolder(new WEBServlet(this));
            pms = MP();
            pms.put("server-info", Server.getVersion());
            so.setInitParameters(pms);
            x.addServlet(so, u);
        }

        // filters
        for (Tuple t : _filters) {
            ho = x.addFilter(nsb(t.get(0)), nsb(t.get(1)), 0 /* Handler.DEFAULT*/);
            ho.setInitParameters((Map<String, String>) t.get(2));
        }

        contexts.addHandler(x);

        svr.setHandler(handlers);
    }

    /**/
    private void onStart_War(Server svr) throws Exception {

        WebAppContext webapp = new WebAppContext();

        webapp.setAttribute("_#version#_", Server.getVersion());
        webapp.setAttribute("_#device#_", this);
        webapp.setContextPath(_contextPath);
        webapp.setWar(_warPtr);
        webapp.setExtractWAR(true);

        svr.setHandler(webapp);
    }

    /**/
    private RequestLogHandler maybeREQLog() throws Exception {
        if (isEmpty(_logDir)) {
            return null;
        }
        RequestLogHandler h = new RequestLogHandler();
        File dir = new File(_logDir);
        //dir.mkdirs();
        String path = niceFPath(dir) + "/jetty-yyyy_mm_dd.log";

        tlog().debug("JettyIO: request-log output path {} ", path);

        NCSARequestLog requestLog = new NCSARequestLog(path);
        requestLog.setRetainDays(90);
        requestLog.setAppend(true);
        requestLog.setExtended(false);
        requestLog.setLogTimeZone("GMT");

        h.setRequestLog(requestLog);
        return h;
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.HttpIOTrait#supportsConfigMenu()
     */
    public boolean supportsConfigMenu() {
        return true;
    }

    /* (non-Javadoc)
     * @see com.zotoh.maedr.device.HttpIOTrait#getCmdSeq(java.util.ResourceBundle, java.util.Properties)
     */
    @SuppressWarnings("unchecked")
    protected CmdLineSequence getCmdSeq(ResourceBundle rcb, Properties props) throws Exception {
        props.put("filters", new ArrayList<Map<String, Object>>());
        props.put("urlpatterns", new ArrayList<String>());
        CmdLineQuestion q7 = new CmdLineMandatory("fcz", getResourceStr(rcb, "cmd.http.fcz")) {
            protected String onAnswerSetOutput(String answer, Properties props) {
                List<Map<String, Object>> a = (List<Map<String, Object>>) props.get("filters");
                Map<String, Object> m = MP();
                String uri = nsb(props.remove("f_uri"));
                m.put("urlpattern", uri);
                m.put("class", answer);
                m.put("params", new HashMap<String, String>());
                a.add(m);
                return "fpath";
            }
        };
        CmdLineQuestion q6 = new CmdLineMandatory("fpath", getResourceStr(rcb, "cmd.http.fpath")) {
            protected String onAnswerSetOutput(String answer, Properties props) {
                if (isEmpty(answer)) {
                    props.remove("f_uri");
                    return "";
                }
                props.put("f_uri", answer);
                return "fcz";
            }
        };
        CmdLineQuestion q5 = new CmdLineQuestion("filters", getResourceStr(rcb, "cmd.http.filters"), "y/n", "n") {
            protected String onAnswerSetOutput(String answer, Properties props) {
                return "Yy".indexOf(answer) >= 0 ? "fpath" : "";
            }
        };
        CmdLineQuestion q4 = new CmdLineMandatory("spath", getResourceStr(rcb, "cmd.http.spath")) {
            protected String onAnswerSetOutput(String answer, Properties props) {
                if (isEmpty(answer))
                    return "filters";
                List<String> lst = (List<String>) props.get("urlpatterns");
                lst.add(answer);
                return "spath";
            }
        };
        CmdLineQuestion q2 = new CmdLineQuestion("base", getResourceStr(rcb, "cmd.http.resbase"), "", ".") {
            protected String onAnswerSetOutput(String answer, Properties props) {
                props.put("resbase", answer);
                return "spath";
            }
        };
        final CmdLineQuestion q1 = new CmdLineQuestion("ctx", getResourceStr(rcb, "cmd.http.ctx")) {
            protected String onAnswerSetOutput(String answer, Properties props) {
                props.put("contextpath", answer);
                return "base";
            }
        };
        return new CmdLineSequence(super.getCmdSeq(rcb, props), q1, q2, q4, q5, q6, q7) {
            protected String onStart() {
                return q1.getId();
            }
        };
    }

}