Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * 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 com.haulmont.cuba.web.sys.singleapp; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.haulmont.bali.util.ReflectionHelper; import com.haulmont.cuba.core.sys.AppContext; import com.haulmont.cuba.core.sys.CubaClassPathXmlApplicationContext; import com.haulmont.cuba.core.sys.SingleAppResourcePatternResolver; import com.haulmont.cuba.web.sys.CubaApplicationServlet; import com.haulmont.cuba.web.sys.CubaDispatcherServlet; import com.haulmont.cuba.web.sys.CubaHttpFilter; import com.haulmont.cuba.web.sys.WebAppContextLoader; import com.haulmont.idp.sys.CubaIdpServlet; import com.haulmont.idp.sys.SingleAppIdpServlet; import com.haulmont.restapi.sys.CubaRestApiServlet; import com.haulmont.restapi.sys.SingleAppRestApiServlet; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.web.filter.DelegatingFilterProxy; import org.springframework.web.servlet.DispatcherServlet; import javax.servlet.*; import java.io.IOException; import java.net.MalformedURLException; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; /** * {@link AppContext} loader of the web application block packed in a WAR together with the middleware block. */ public class SingleAppWebContextLoader extends WebAppContextLoader { protected static final String IDP_SERVICE_PROVIDERS_URLS = "cuba.idp.serviceProviderUrls"; private final Logger log = LoggerFactory.getLogger(SingleAppWebContextLoader.class); private Set<String> dependencyJars; private ServletContextListener webServletContextListener; protected static final String FRONT_CONTEXT_NAME = "front"; /** * Invoked reflectively by {@link SingleAppWebServletListener}. * * @param jarNames JARs of the core block */ @SuppressWarnings("unused") public void setJarNames(String jarNames) { dependencyJars = new HashSet<>(Splitter.on("\n").omitEmptyStrings().trimResults().splitToList(jarNames)); } /** * Here we create servlets and filters manually, to make sure the classes would be loaded using necessary classloader. */ @Override public void contextInitialized(ServletContextEvent servletContextEvent) { super.contextInitialized(servletContextEvent); ServletContext servletContext = servletContextEvent.getServletContext(); registerAppServlet(servletContext); registerDispatchServlet(servletContext); registerRestApiServlet(servletContext); registerCubaHttpFilter(servletContext); registerFrontAppServlet(servletContext); registerIdpServlet(servletContext); registerClassLoaderFilter(servletContext); initWebServletContextListener(servletContextEvent, servletContext); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { super.contextDestroyed(servletContextEvent); if (webServletContextListener != null) { webServletContextListener.contextDestroyed(servletContextEvent); } } protected void registerAppServlet(ServletContext servletContext) { CubaApplicationServlet cubaServlet = new CubaApplicationServlet(); cubaServlet.setClassLoader(Thread.currentThread().getContextClassLoader()); try { cubaServlet.init(new CubaServletConfig("app_servlet", servletContext)); } catch (ServletException e) { throw new RuntimeException("An error occurred while initializing app_servlet servlet", e); } ServletRegistration.Dynamic cubaServletReg = servletContext.addServlet("app_servlet", cubaServlet); cubaServletReg.setLoadOnStartup(0); cubaServletReg.setAsyncSupported(true); cubaServletReg.addMapping("/*"); } protected void registerDispatchServlet(ServletContext servletContext) { CubaDispatcherServlet cubaDispatcherServlet = new SingleAppDispatcherServlet(dependencyJars); try { cubaDispatcherServlet.init(new CubaServletConfig("dispatcher", servletContext)); } catch (ServletException e) { throw new RuntimeException("An error occurred while initializing dispatcher servlet", e); } ServletRegistration.Dynamic cubaDispatcherServletReg = servletContext.addServlet("dispatcher", cubaDispatcherServlet); cubaDispatcherServletReg.setLoadOnStartup(1); cubaDispatcherServletReg.addMapping("/dispatch/*"); } protected void registerRestApiServlet(ServletContext servletContext) { CubaRestApiServlet cubaRestApiServlet = new SingleAppRestApiServlet(dependencyJars); try { cubaRestApiServlet.init(new CubaServletConfig("rest_api", servletContext)); } catch (ServletException e) { throw new RuntimeException("An error occurred while initializing dispatcher servlet", e); } ServletRegistration.Dynamic cubaRestApiServletReg = servletContext.addServlet("rest_api", cubaRestApiServlet); cubaRestApiServletReg.setLoadOnStartup(2); cubaRestApiServletReg.addMapping("/rest/*"); DelegatingFilterProxy restSpringSecurityFilterChain = new DelegatingFilterProxy(); restSpringSecurityFilterChain .setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.rest_api"); restSpringSecurityFilterChain.setTargetBeanName("springSecurityFilterChain"); FilterRegistration.Dynamic restSpringSecurityFilterChainReg = servletContext .addFilter("restSpringSecurityFilterChain", restSpringSecurityFilterChain); restSpringSecurityFilterChainReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/rest/*"); } protected void registerFrontAppServlet(ServletContext servletContext) { boolean hasFrontApp = false; try { hasFrontApp = servletContext.getResource("/" + FRONT_CONTEXT_NAME) != null; } catch (MalformedURLException e) { //Do nothing } if (hasFrontApp) { String contextPath = servletContext.getContextPath(); String baseUrl = System.getProperty("cuba.front.baseUrl"); if (baseUrl == null || baseUrl.length() == 0) { String path = "/" + FRONT_CONTEXT_NAME + "/"; System.setProperty("cuba.front.baseUrl", "/".equals(contextPath) ? path : contextPath + path); } String apiUrl = System.getProperty("cuba.front.apiUrl"); if (apiUrl == null || apiUrl.length() == 0) { String path = "/rest/"; System.setProperty("cuba.front.apiUrl", "/".equals(contextPath) ? path : contextPath + path); } DispatcherServlet frontServlet; try { Class frontServletClass = ReflectionHelper.getClass("com.haulmont.cuba.web.sys.AppFrontServlet"); frontServlet = (DispatcherServlet) ReflectionHelper.newInstance(frontServletClass, FRONT_CONTEXT_NAME, (Supplier<ApplicationContext>) AppContext::getApplicationContext); } catch (NoSuchMethodException e) { throw new RuntimeException("Unable to instantiate app front servlet", e); } ServletRegistration.Dynamic cubaServletReg = servletContext.addServlet("app_front_servlet", frontServlet); cubaServletReg.setLoadOnStartup(3); cubaServletReg.setAsyncSupported(true); cubaServletReg.addMapping(String.format("/%s/*", FRONT_CONTEXT_NAME)); } } protected void registerIdpServlet(ServletContext servletContext) { String serviceProvidersUrls = AppContext.getProperty(IDP_SERVICE_PROVIDERS_URLS); if (StringUtils.isEmpty(serviceProvidersUrls)) { log.debug("No service providers were found. IDP Servlet will not be started"); return; } CubaIdpServlet idpServlet = new SingleAppIdpServlet(dependencyJars); try { idpServlet.init(new CubaServletConfig("idp", servletContext)); } catch (ServletException e) { throw new RuntimeException("An error occurred while initializing idp servlet", e); } ServletRegistration.Dynamic idpServletRegistration = servletContext.addServlet("idp", idpServlet); idpServletRegistration.setLoadOnStartup(4); idpServletRegistration.addMapping("/idp/*"); DelegatingFilterProxy idpSpringSecurityFilterChain = new DelegatingFilterProxy(); idpSpringSecurityFilterChain .setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.idp"); idpSpringSecurityFilterChain.setTargetBeanName("springSecurityFilterChain"); FilterRegistration.Dynamic idpSpringSecurityFilterChainReg = servletContext .addFilter("idpSpringSecurityFilterChain", idpSpringSecurityFilterChain); idpSpringSecurityFilterChainReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/idp/*"); } protected void registerCubaHttpFilter(ServletContext servletContext) { CubaHttpFilter cubaHttpFilter = new CubaHttpFilter(); FilterRegistration.Dynamic cubaHttpFilterReg = servletContext.addFilter("CubaHttpFilter", cubaHttpFilter); cubaHttpFilterReg.setAsyncSupported(true); cubaHttpFilterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, "/*"); } protected void registerClassLoaderFilter(ServletContext servletContext) { FilterRegistration.Dynamic filterReg = servletContext.addFilter("WebSingleWarHttpFilter", new SetClassLoaderFilter()); filterReg.setAsyncSupported(true); filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*"); filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/dispatch/*"); filterReg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/rest/*"); } @Override protected ClassPathXmlApplicationContext createApplicationContext(String[] locations) { return new CubaClassPathXmlApplicationContext(locations) { /** * Here we create resource resolver which scans only web jars (and avoid putting core beans to web context) * JAR_DEPENDENCIES properties is filled by com.haulmont.cuba.web.sys.singleapp.SingleAppWebServletListener * during application initialization. */ @Override protected ResourcePatternResolver getResourcePatternResolver() { if (dependencyJars == null || dependencyJars.isEmpty()) { throw new RuntimeException("No JARs defined for the 'web' block. " + "Please check that web.dependencies file exists in WEB-INF directory."); } return new SingleAppResourcePatternResolver(this, dependencyJars); } }; } @Override protected String getAppPropertiesConfig(ServletContext sc) { return sc.getInitParameter("appPropertiesConfigWeb"); } protected void initWebServletContextListener(ServletContextEvent servletContextEvent, ServletContext sc) { String className = sc.getInitParameter("webServletContextListener"); if (!Strings.isNullOrEmpty(className)) { try { Class<?> clazz = this.getClass().getClassLoader().loadClass(className); webServletContextListener = (ServletContextListener) clazz.newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException("An error occurred while starting single WAR application", e); } webServletContextListener.contextInitialized(servletContextEvent); } } protected static class SetClassLoaderFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { //do nothing } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); chain.doFilter(request, response); } @Override public void destroy() { //do nothing } } }