Menu

#22 old

1.0
open
nobody
None
2019-05-01
2019-05-01
No
package yl.yljwl;

import yl.ylcommons.YLGenericPair;
import yl.ylcommons.YLMsg;
import yl.ylcommons.YLUtils;
import yl.ylwebcommons.YLWebUtils;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.*;
import java.io.IOException;
import java.math.BigDecimal;

/**
 * Created by yl on 22/11/17.
 */

@MultipartConfig
public class JWLBootstrapServlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        try {
            getApp();
        }
        catch(Exception e) {
            YLUtils.<ServletException>reThrow(e, ServletException.class);
        }
        getServletContext().addListener(new HttpSessionListener() {
            private JWLAppContext appContext;

            @Override
            public void sessionCreated(HttpSessionEvent httpSessionEvent) {
                final HttpSession session = httpSessionEvent.getSession();
                final int maxInactiveInterval = session.getMaxInactiveInterval();
                session.setMaxInactiveInterval(-1);
                synchronized(session) {
                    setAppContext(session, maxInactiveInterval, appContext);
                    httpSessionEvent.getSession().notifyAll();
                }
            }

            @Override
            public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
                //appContext = getAppContext(httpSessionEvent.getSession());
            }
        });
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        process(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        process(req, resp);
    }

    private void process(HttpServletRequest req, HttpServletResponse resp) {
        JWLAppContext appContext;
        YLGenericPair<String, String> pagePath;
        JWLApp app = null;
        try {
            app = getApp();
        }
        catch(Exception ignore) { //cannot throw - already checked in init method
        }
        final HttpSession session = req.getSession();
        boolean redirect = false;
        JWLPageContext pageContext;
        JWLPageResolver pageResolver;
        synchronized(session) {
            if(session.isNew()) {
                appContext = getAppContext(session);
                if(appContext == null) {
                    YLUtils.wait(session, -1);
                }
            }
            appContext = getAppContext(session);
            //}
            app.setAppContext(appContext);
            pagePath = getPagePath(req);
            if(appContext.isMultiTabApp()) {
                if(YLUtils.compare(pagePath.getValue1(), "*") == 0) {
                    pagePath.setValue1(appContext.getTabScopeBean(null).getValue1());
                    redirect = true;
                }
                else {
                    pagePath.setValue1(appContext.getTabScopeBean(pagePath.getValue1()).getValue1());
                }
            }
            else {
                redirect = YLUtils.compare(pagePath.getValue1(), "*") != 0;
                pagePath.setValue1("*");
            }
            app.setTabId(pagePath.getValue1());
            pageContext = new JWLPageContext(req, resp, pagePath.getValue1());
            pageResolver = app.getPageResolver();
        }
        synchronized(app.getTabScopeBean()) {
            try {
                if(!appContext.isInitialized()) {
                    app.onInit();
                }
                final Object exp = req.getAttribute(JWLAbstractErrorPage.ERROR_PAGE_PARAMETER_NAME_EXCEPTION);
                final String[] exp1 = req.getParameterMap().get(JWLAbstractErrorPage.ERROR_PAGE_PARAMETER_NAME_EXCEPTION);
                if(exp != null) {
                    JWLAbstractPage.sendError(app, pageContext, (Exception) exp, req, resp);
                }
                else if(!YLUtils.isEmpty(exp1)) {
                    JWLAbstractPage.sendError(app, pageContext, new JWLExceptionAjax(Integer.parseInt(exp1[0]), exp1[1]), req, resp);
                }
                else if(JWLAbstractErrorPage.isException(app.getTabScopeBean())) {
                    JWLAbstractPage.sendError(app, pageContext, null, req, resp);
                }
                else {
                    if(pagePath.getValue2() == null) {
                        final JWLPageType pageType = pageResolver.getHomePage(app);
                        pageType.setUrlParameters(pageContext.getUrlQueryParams());
                        pageResolver.resolveClass2Path(app, pageType);
                        YLWebUtils.sendRedirect(resp, JWLAbstractPage.getPageURL(pageType, pageContext));
                        return;
                    }
                    JWLPageType pageType = pageResolver.getPageType(app);
                    pageType.setUrlParameters(pageContext.getUrlQueryParams());
                    pageType.setPath(pagePath.getValue2());
                    pageType = pageResolver.resolvePath2Class(app, pageType);
                    if(pageType.getType() == null) {
                        YLWebUtils.sendRedirect(resp, JWLAbstractPage.getPageURL(pageType, pageContext));
                        return;
                    }
                    if(redirect) {
                        pageResolver.resolveClass2Path(app, pageType);
                        YLWebUtils.sendRedirect(resp, JWLAbstractPage.getPageURL(pageType, pageContext));
                        return;
                    }
                    final JWLAbstractPage page = pageType.createPage();
                    page.setApp(app);
                    page.setPageContext(pageContext);
                    req.setAttribute(JWLConstants.getSysName(JWLAbstractPage.class, "curPage"), page);
                    page.process(req, resp);
                }
            }
            catch(Exception e) {
                log(new YLMsg(YLMsg.MsgType.ERR, getClass(), "process", e).toString());
                e.printStackTrace();
                if(resp.getStatus() != HttpServletResponse.SC_FOUND) {
                    JWLAbstractPage.sendError(app, pageContext, e, req, resp);
                }
            }
        }
    }

    private JWLAppContext getAppContext(HttpSession session) {
        return (JWLAppContext) session.getAttribute(JWLConstants.getSysName(JWLAppContext.class, "appContext"));
    }

    private void setAppContext(HttpSession session, int maxInactiveInterval, JWLAppContext appContext) {
        if(appContext == null) {
            final String sMultiTabApp = getServletConfig().getInitParameter(JWLConstants.JWLBootstrapServletInitParam_MULTI_TAB_APP);
            boolean bMultiTabApp = false;
            if(!YLUtils.isEmpty(sMultiTabApp) && YLUtils.compare(Boolean.TRUE.toString().toLowerCase(), sMultiTabApp.toLowerCase()) == 0) {
                bMultiTabApp = true;
            }
            appContext = new JWLAppContext(bMultiTabApp, maxInactiveInterval);
            appContext.setServletContext(getServletContext());
            String s = getServletConfig().getInitParameter(JWLConstants.JWLBootstrapServletInitParam_AJAX_PROGRESS_INDICATOR_CSS_CLASS);
            appContext.setAjaxProgressIndicatorCssClass(YLUtils.isEmpty(s) ? "JWLAjaxProgressIndicator" : s);
            final BigDecimal n = YLUtils.parseNumber(getServletConfig().getInitParameter(JWLConstants.JWLBootstrapServletInitParam_AJAX_TIMEOUT_IN_MILLISECONDS));
            if(n == null || n.intValue() <= 0) {
                appContext.setAjaxTimeoutInMilliseconds(Integer.MAX_VALUE);
            }
            else {
                appContext.setAjaxTimeoutInMilliseconds(n.intValue());
            }
        }
        final JWLAppContext ac = appContext;
        appContext.startGC(() -> {
            JWLApp app = null;
            try {
                app = getApp();
            }
            catch(Exception ignore) {
            }
            app.setAppContext(ac);
            ac.cleanUp(app);
        });
        session.setAttribute(JWLConstants.getSysName(JWLAppContext.class, "appContext"), appContext);
    }

    private JWLApp getApp() throws Exception {
        final String appClass = getServletConfig().getInitParameter(JWLConstants.JWLBootstrapServletInitParam_APP_CLASS);
        final Class clazz = Class.forName(appClass);
        if(!YLUtils.isRelated(JWLApp.class, clazz)) {
            throw new JWLException(YLUtils.formatMsg("class {} should extend class {}", YLUtils.fqn(clazz), YLUtils.fqn(JWLApp.class)));
        }
        return (JWLApp) YLUtils.newInstance(clazz);
    }

    static YLGenericPair<String, String> getPagePath(HttpServletRequest req) {
        if(!req.getServletPath().endsWith(JWLConstants.JWLBootstrapServlet_URL_PATTERN.substring(1)) ||
                YLUtils.compare(req.getServletPath(), "/" + JWLConstants.JWLBootstrapServlet_URL_PATTERN.substring(1)) == 0) {
            return new YLGenericPair<>();
        }
        final int ndx = req.getServletPath().lastIndexOf("/");
        String tabId = req.getServletPath().substring(ndx + 1);
        tabId = tabId.substring(0, tabId.indexOf(JWLConstants.JWLBootstrapServlet_URL_PATTERN.substring(1)));
        return new YLGenericPair<>(tabId, ndx == 0 ? null : req.getServletPath().substring(1, ndx));
    }
}

package yl.yljwl;

import yl.ylcommons.YLGenericBean;
import yl.ylcommons.YLUtils;
import java.net.URL;
import java.util.Map;

/**
 * Created by yl on Nov 22 2017
 */
public abstract class JWLApp {
    private JWLAppContext appContext;
    private String tabId;

    protected void onSessionDestroyed() throws JWLException {
    }

    protected void onInit() throws JWLException {
    }

    public int getAjaxTimeoutInMilliseconds() {
        return appContext.getAjaxTimeoutInMilliseconds();
    }

    public String getAjaxProgressIndicatorCssClass() {
        return appContext.getAjaxProgressIndicatorCssClass();
    }

    public abstract JWLPageResolver getPageResolver();

    public URL getResourceURL(String path) throws JWLException {
        final String p = path.startsWith("/") ? path : "/" + path;
        try {
            URL url = appContext.getServletContext().getResource(p);
            if(url == null) {
                url = YLUtils.getURL(path, Thread.currentThread().getContextClassLoader());
            }
            return url;
        }
        catch(Exception e) {
            YLUtils.<JWLException>reThrow(e, JWLException.class);
            return null;
        }
    }

    public URL getResourceURL(Class parent, String name) throws JWLException {
        String s = parent.getPackage().getName().replace('.', '/');
        if(!YLUtils.isEmpty(s)) {
            s += '/';
        }
        s += name;
        return getResourceURL(s);
    }

    void setAppContext(JWLAppContext appContext) {
        this.appContext = appContext;
    }

    void setTabId(String tabId) {
        this.tabId = tabId;
    }

    public YLGenericBean getAppScopeBean() {
        return appContext.getAppScopeBean();
    }

    public YLGenericBean getTabScopeBean() {
        return appContext.getTabScopeBean(tabId).getValue2();
    }

    YLGenericBean getSysScopeBean() {
        return appContext.getSysScopeBean(tabId);
    }

    public Map<String, String> getContextParams() {
        return appContext.getContextParams();
    }

    public Object lookup(String name) throws JWLException {
        return appContext.lookup(name);
    }
}

package yl.yljwl;

import yl.ylcommons.*;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.Serializable;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by yl on Nov 22 2017
 */
class JWLAppContext implements Serializable {
    private Map<String, String> contextParams = new HashMap<>();
    private YLGenericBean appScopeBean = new YLGenericBeanImpl();
    private Map<String, YLGenericBean> tabScopeBeans = new HashMap<>();
    private boolean multiTabApp;
    private transient ScheduledThreadPoolExecutor gc;
    private boolean initialized;
    private transient Lock lock = new ReentrantLock();
    private transient ServletContext servletContext;
    private boolean gcStarted;
    private int maxInactiveInterval;
    private String ajaxProgressIndicatorCssClass;
    private int ajaxTimeoutInMilliseconds;

    JWLAppContext(boolean multiTabApp, int maxInactiveInterval) {
        this.multiTabApp = multiTabApp;
        this.maxInactiveInterval = maxInactiveInterval;
        createGC();
    }

    void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
        for(Enumeration<String> en = this.servletContext.getInitParameterNames(); en.hasMoreElements(); ) {
            final String name = en.nextElement();
            contextParams.put(name, servletContext.getInitParameter(name));
        }
    }

    ServletContext getServletContext() {
        return servletContext;
    }

    boolean isMultiTabApp() {
        return multiTabApp;
    }

    YLGenericBean getAppScopeBean() {
        return appScopeBean;
    }

    String getAjaxProgressIndicatorCssClass() {
        return ajaxProgressIndicatorCssClass;
    }

    void setAjaxProgressIndicatorCssClass(String ajaxProgressIndicatorCssClass) {
        this.ajaxProgressIndicatorCssClass = ajaxProgressIndicatorCssClass;
    }

    int getAjaxTimeoutInMilliseconds() {
        return ajaxTimeoutInMilliseconds;
    }

    void setAjaxTimeoutInMilliseconds(int ajaxTimeoutInMilliseconds) {
        this.ajaxTimeoutInMilliseconds = ajaxTimeoutInMilliseconds;
    }

    boolean isInitialized() {
        final boolean ret = initialized;
        initialized = true;
        return ret;
    }

    YLGenericPair<String, YLGenericBean> getTabScopeBean(String tabId) {
        final YLGenericPair<String, YLGenericBean> ret = new YLGenericPair<>();
        try {
            lock.lock();
            if(!multiTabApp) {
                ret.setValue2(getAppScopeBean());
            }
            else if(tabId == null) {
                int id = 0;
                final YLGenericPair<Boolean, Integer> v = appScopeBean.get(JWLConstants.getSysName(JWLAppContext.class, "tabId"));
                if(!v.getValue1() || v.getValue2() == null) {
                    id += 1;
                }
                else {
                    id = v.getValue2() + 1;
                }
                appScopeBean.set(JWLConstants.getSysName(JWLAppContext.class, "tabId"), id);
                ret.setValue1(String.valueOf(id));
                ret.setValue2(new YLGenericBeanImpl());
                tabScopeBeans.put(ret.getValue1(), ret.getValue2());
            }
            else {
                ret.setValue1(tabId);
                ret.setValue2(tabScopeBeans.get(ret.getValue1()));
                if(ret.getValue2() == null) {
                    ret.setValue2(new YLGenericBeanImpl());
                    tabScopeBeans.put(ret.getValue1(), ret.getValue2());
                }
            }
        }
        finally {
            ret.getValue2().set(JWLConstants.getSysName(JWLAppContext.class, "lastAccessedTime"), Instant.now());
            lock.unlock();
        }
        return ret;
    }

    YLGenericBean getSysScopeBean(String tabId) {
        final YLGenericBean tabScopeBean = getTabScopeBean(tabId).getValue2();
        final YLGenericPair<Boolean, YLGenericBean> ret = tabScopeBean.get(JWLConstants.getSysName(JWLAppContext.class, "sysScopeBean"));
        if(!ret.getValue1()) {
            ret.setValue2(new YLGenericBeanImpl());
            tabScopeBean.set(JWLConstants.getSysName(JWLAppContext.class, "sysScopeBean"), ret.getValue2());
        }
        return ret.getValue2();
    }

    Map<String, String> getContextParams() {
        return contextParams;
    }

    Object lookup(String name) throws JWLException {
        try {
            return InitialContext.doLookup(name);
        }
        catch(Exception e) {
            YLUtils.<JWLException>reThrow(e, JWLException.class);
            return null;
        }
    }

    private void createGC() {
        gc = new ScheduledThreadPoolExecutor(1, r -> {
            final Thread ret = new Thread(r);
            ret.setDaemon(true);
            return ret;
        });
    }

    void startGC(Runnable r) {
        if(!gcStarted) {
            gcStarted = true;
            gc.scheduleWithFixedDelay(r, maxInactiveInterval, maxInactiveInterval, TimeUnit.SECONDS);
        }
    }

    void cleanUp(JWLApp app) {
        try {
            lock.lock();
            final Instant now = Instant.now();
            if(isMultiTabApp()) {
                for(Iterator<Map.Entry<String, YLGenericBean>> it = tabScopeBeans.entrySet().iterator(); it.hasNext(); ) {
                    final Map.Entry<String, YLGenericBean> en = it.next();
                    final Instant lastAccessedTime = (Instant) en.getValue().get(JWLConstants.getSysName(JWLAppContext.class, "lastAccessedTime")).getValue2();
                    if(lastAccessedTime != null && lastAccessedTime.until(now, ChronoUnit.SECONDS) >= maxInactiveInterval) {
                        app.setTabId(en.getKey());
                        try {
                            app.onSessionDestroyed();
                            it.remove();
                        }
                        catch(JWLException e) {
                            System.err.print(new YLMsg(YLMsg.MsgType.ERR, app.getClass(), "onSessionDestroyed", e).toString());
                        }
                    }
                }
            }
            else {
                final Instant lastAccessedTime = (Instant) getAppScopeBean().get(JWLConstants.getSysName(JWLAppContext.class, "lastAccessedTime")).getValue2();
                if(lastAccessedTime != null && lastAccessedTime.until(now, ChronoUnit.SECONDS) >= maxInactiveInterval) {
                    app.setTabId("*");
                    try {
                        app.onSessionDestroyed();
                        appScopeBean = new YLGenericBeanImpl();
                    }
                    catch(JWLException e) {
                        System.err.print(new YLMsg(YLMsg.MsgType.ERR, app.getClass(), "onSessionDestroyed", e).toString());
                    }
                }
            }
        }
        finally {
            lock.unlock();
        }
    }

    private void writeObject(java.io.ObjectOutputStream out) throws ClassNotFoundException, IOException {
        YLUtils.serializeObject(this, out);
    }

    private void readObject(java.io.ObjectInputStream in) throws ClassNotFoundException, IOException {
        final JWLAppContext o = YLUtils.deserializeObject(getClass(), in);
        contextParams = new HashMap<>(o.contextParams);
        appScopeBean = o.appScopeBean;
        tabScopeBeans = o.tabScopeBeans;
        multiTabApp = o.multiTabApp;
        initialized = o.initialized;
        gcStarted = o.gcStarted;
        maxInactiveInterval = o.maxInactiveInterval;
        ajaxProgressIndicatorCssClass = o.ajaxProgressIndicatorCssClass;
        ajaxTimeoutInMilliseconds = o.ajaxTimeoutInMilliseconds;
        createGC();
        lock = new ReentrantLock();
    }
}

Discussion


Log in to post a comment.