com.sonicle.webtop.core.app.ContextLoader.java Source code

Java tutorial

Introduction

Here is the source code for com.sonicle.webtop.core.app.ContextLoader.java

Source

/*
 * WebTop Services is a Web Application framework developed by Sonicle S.r.l.
 * Copyright (C) 2014 Sonicle S.r.l.
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License version 3 as published by
 * the Free Software Foundation with the addition of the following permission
 * added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
 * WORK IN WHICH THE COPYRIGHT IS OWNED BY SONICLE, SONICLE DISCLAIMS THE
 * WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
 *
 * This program 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 Affero General Public License
 * along with this program; if not, see http://www.gnu.org/licenses or write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA.
 *
 * You can contact Sonicle S.r.l. at email address sonicle@sonicle.com
 *
 * The interactive user interfaces in modified source and object code versions
 * of this program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU Affero General Public License version 3.
 *
 * In accordance with Section 7(b) of the GNU Affero General Public License
 * version 3, these Appropriate Legal Notices must retain the display of the
 * Sonicle logo and Sonicle copyright notice. If the display of the logo is not
 * reasonably feasible for technical reasons, the Appropriate Legal Notices must
 * display the words "Copyright (C) 2014 Sonicle S.r.l.".
 */
package com.sonicle.webtop.core.app;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.Loader;
import ch.qos.logback.core.util.StatusPrinter;
import com.sonicle.commons.PropUtils;
import com.sonicle.commons.web.ContextUtils;
import com.sonicle.webtop.core.app.servlet.RestApi;
import com.sonicle.webtop.core.app.shiro.filter.JWTSignatureVerifier;
import com.sonicle.webtop.core.app.util.LogbackHelper;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Properties;
import javax.servlet.ServletContext;
import javax.servlet.ServletRegistration.Dynamic;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.MessageFormatter;

/**
 *
 * @author malbinola
 */
public class ContextLoader {
    private static final Logger logger = WT.getLogger(ContextLoader.class);
    public static final String WEBAPPNAME_ATTRIBUTE_KEY = "wtwebappname";
    public static final String WEBTOPAPP_ATTRIBUTE_KEY = "wtapp";

    public static String getWabappName(ServletContext servletContext) {
        return (String) servletContext.getAttribute(WEBAPPNAME_ATTRIBUTE_KEY);
    }

    public void initLogging(ServletContext servletContext) {
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        String webappFullName = ContextUtils.getWebappFullName(servletContext, false); // Like <context-name>##<context-version>
        ClassLoader classLoader = Loader.getClassLoaderOfObject(this);
        Properties systemProps = System.getProperties();

        // Locates logback configuration file: try custom (webappConfig) first, then standard ones
        URL logbackFileUrl = LogbackHelper
                .findURLOfCustomConfigurationFile(WebTopProps.getWebappsConfigDir(systemProps), webappFullName);
        if (logbackFileUrl == null) {
            logbackFileUrl = LogbackHelper.findURLOfDefaultConfigurationFile(classLoader);
        }

        // Preparing logback props
        String logDir = WebTopProps.getLogDir(systemProps);
        logDir = expandLogDirVariables(logDir, webappFullName);
        String logFileBasename = PropUtils.isDefined(systemProps, WebTopProps.LOG_FILE_BASENAME)
                ? WebTopProps.getLogFileBasename()
                : null;
        if (StringUtils.isBlank(logFileBasename))
            logFileBasename = webappFullName;
        String logAppender = WebTopProps.getLogAppender(systemProps);

        try {
            // https://stackoverflow.com/questions/32595740/how-to-specify-file-path-dynamically-in-logback-xml
            // Fill props and write to logback file
            Properties logbackProps = new Properties();
            logbackProps.setProperty(LogbackHelper.PROP_APPENDER, logAppender);
            logbackProps.setProperty(LogbackHelper.PROP_LOG_DIR, logDir);
            logbackProps.setProperty(LogbackHelper.PROP_LOG_FILE_BASENAME, logFileBasename);
            LogbackHelper.writeProperties(Loader.getClassLoaderOfObject(this), logbackProps);

            printToSystemOut("[{}] Logback: using {} = {}", webappFullName, LogbackHelper.PROP_APPENDER,
                    logbackProps.getProperty(LogbackHelper.PROP_APPENDER));
            printToSystemOut("[{}] Logback: using {} = {}", webappFullName, LogbackHelper.PROP_LOG_DIR,
                    logbackProps.getProperty(LogbackHelper.PROP_LOG_DIR));
            printToSystemOut("[{}] Logback: using {} = {}", webappFullName, LogbackHelper.PROP_LOG_FILE_BASENAME,
                    logbackProps.getProperty(LogbackHelper.PROP_LOG_FILE_BASENAME));

            // Reload configuration
            LogbackHelper.loadConfiguration(loggerContext, logbackFileUrl);
            printToSystemOut("[{}] Logback: using configuration file at '{}'", webappFullName,
                    logbackFileUrl.toString());

        } catch (IOException | URISyntaxException ex) {
            printToSystemOut("[{}] Unable to write logback properties file", webappFullName);
            printToSystemOut("{}", ex);
        } catch (JoranException ex) {
            printToSystemOut("[{}] Unable to reload logback configuration", webappFullName);
        }
        StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
    }

    public void initApp(ServletContext servletContext) throws IllegalStateException {
        String webappName = ContextUtils.getWebappFullName(servletContext, false);
        servletContext.setAttribute(WEBAPPNAME_ATTRIBUTE_KEY, webappName);
        if (servletContext.getAttribute(WEBTOPAPP_ATTRIBUTE_KEY) != null) {
            throw new IllegalStateException(
                    "There is already a WebTop application associated with the current ServletContext.");
        }

        try {
            WebTopApp wta = new WebTopApp(servletContext);
            wta.boot();
            servletContext.setAttribute(WEBTOPAPP_ATTRIBUTE_KEY, wta);
            servletContext.setAttribute(JWTSignatureVerifier.SECRET_CONTEXT_ATTRIBUTE,
                    wta.getDocumentServerSecretIn());

            Dynamic atmosphereServlet = servletContext.addServlet("AtmosphereServlet",
                    com.sonicle.webtop.core.app.atmosphere.AtmosphereServlet.class);
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.AtmosphereFramework.analytics", "false");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcasterCacheClass",
                    "com.sonicle.webtop.core.app.atmosphere.UUIDBroadcasterCache");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcasterLifeCyclePolicy",
                    "BroadcasterLifeCyclePolicy.EMPTY");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.asyncSupport",
                    "org.atmosphere.container.JSR356AsyncSupport");
            //atmosphereServlet.setInitParameter("org.atmosphere.cpr.asyncSupport", "org.atmosphere.container.Tomcat7CometSupport");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.sessionSupport", "true");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.sessionCreate", "false");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.broadcaster.shareableThreadPool", "true");
            atmosphereServlet.setInitParameter("org.atmosphere.cpr.AtmosphereInterceptor",
                    "org.atmosphere.interceptor.ShiroInterceptor");
            atmosphereServlet.setLoadOnStartup(1);
            atmosphereServlet.setAsyncSupported(true);
            atmosphereServlet.addMapping(com.sonicle.webtop.core.app.atmosphere.AtmosphereServlet.URL + "/*");

            Dynamic resourcesServlet = servletContext.addServlet("ResourcesServlet",
                    com.sonicle.webtop.core.app.servlet.ResourceRequest.class);
            resourcesServlet.setLoadOnStartup(1);
            resourcesServlet.addMapping(com.sonicle.webtop.core.app.servlet.ResourceRequest.URL + "/*");

            Dynamic loginServlet = servletContext.addServlet("LoginServlet",
                    com.sonicle.webtop.core.app.servlet.Login.class);
            loginServlet.setLoadOnStartup(1);
            loginServlet.addMapping(com.sonicle.webtop.core.app.servlet.Login.URL + "/*");

            Dynamic logoutServlet = servletContext.addServlet("LogoutServlet",
                    com.sonicle.webtop.core.app.servlet.Logout.class);
            logoutServlet.setLoadOnStartup(1);
            logoutServlet.addMapping(com.sonicle.webtop.core.app.servlet.Logout.URL + "/*");

            Dynamic otpServlet = servletContext.addServlet("OtpServlet",
                    com.sonicle.webtop.core.app.servlet.Otp.class);
            otpServlet.setLoadOnStartup(1);
            otpServlet.addMapping(com.sonicle.webtop.core.app.servlet.Otp.URL + "/*");

            Dynamic uiPrivateServlet = servletContext.addServlet("UIPrivateServlet",
                    com.sonicle.webtop.core.app.servlet.UIPrivate.class);
            uiPrivateServlet.setLoadOnStartup(1);
            uiPrivateServlet.addMapping(com.sonicle.webtop.core.app.servlet.UIPrivate.URL + "/*");

            Dynamic privateRequestServlet = servletContext.addServlet("PrivateRequestServlet",
                    com.sonicle.webtop.core.app.servlet.PrivateRequest.class);
            privateRequestServlet.setLoadOnStartup(1);
            privateRequestServlet.addMapping(com.sonicle.webtop.core.app.servlet.PrivateRequest.URL + "/*");
            privateRequestServlet.addMapping(com.sonicle.webtop.core.app.servlet.PrivateRequest.URL_LEGACY + "/*");

            Dynamic publicRequestServlet = servletContext.addServlet("PublicRequestServlet",
                    com.sonicle.webtop.core.app.servlet.PublicRequest.class);
            publicRequestServlet.setLoadOnStartup(1);
            publicRequestServlet.addMapping(com.sonicle.webtop.core.app.servlet.PublicRequest.URL + "/*");

            Dynamic docEditorServlet = servletContext.addServlet("DocEditorServlet",
                    com.sonicle.webtop.core.app.servlet.DocEditor.class);
            docEditorServlet.setLoadOnStartup(1);
            docEditorServlet.addMapping(com.sonicle.webtop.core.app.servlet.DocEditor.URL + "/*");

            // Adds RestApiServlets dynamically
            ServiceManager svcMgr = wta.getServiceManager();
            for (String serviceId : svcMgr.listRegisteredServices()) {
                ServiceDescriptor desc = svcMgr.getDescriptor(serviceId);

                if (desc.hasOpenApiDefinitions() || desc.hasRestApiEndpoints()) {
                    addRestApiServlet(servletContext, desc);
                }
            }

        } catch (Throwable t) {
            servletContext.removeAttribute(WEBTOPAPP_ATTRIBUTE_KEY);
            logger.error("Error initializing WTA [{}]", webappName, t);
        }
    }

    private void addRestApiServlet(ServletContext servletContext, ServiceDescriptor desc) {
        String serviceId = desc.getManifest().getId();
        String name = serviceId + "@RestApiServlet";
        String path = com.sonicle.webtop.core.app.servlet.RestApi.URL + "/" + serviceId + "/*";

        logger.debug("Adding RestApi servlet [{}] -> [{}]", name, path);
        Dynamic servlet = servletContext.addServlet(name, com.sonicle.webtop.core.app.servlet.RestApi.class);
        servlet.setInitParameter("javax.ws.rs.Application", "com.sonicle.webtop.core.app.JaxRsServiceApplication");
        servlet.setInitParameter("com.sun.jersey.config.feature.DisableWADL", "true");
        servlet.setInitParameter(RestApi.INIT_PARAM_WEBTOP_SERVICE_ID, serviceId);
        servlet.setLoadOnStartup(2);
        servlet.setAsyncSupported(true);
        servlet.addMapping(path);
    }

    protected void destroyApp(ServletContext servletContext) {
        final String appname = getWabappName(servletContext);
        try {
            WebTopApp.get(servletContext).shutdown();

        } catch (Throwable t) {
            logger.error("Error destroying WTA [{}]", appname, t);
        } finally {
            servletContext.removeAttribute(WEBTOPAPP_ATTRIBUTE_KEY);
        }
    }

    private void printToSystemOut(String message, Object... arguments) {
        System.out.println(MessageFormatter.arrayFormat(message, arguments).getMessage());
    }

    private String expandLogDirVariables(String logDir, String webappFullName) {
        if (logDir == null)
            return logDir;
        String s = StringUtils.replace(logDir, "${WEBAPP_FULLNAME}", webappFullName);
        s = StringUtils.replace(s, "${WEBAPP_NAME}", ContextUtils.stripWebappVersion(webappFullName));
        return s;
    }
}