All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.neba.core.mvc.MvcContext Maven / Gradle / Ivy

Go to download

Contains the entire NEBA core implementation, i.e. the framework that interprets the NEBA API annotations and provides implementations for the service and lifecycle callback interfaces provided in the NEBA API. This package must not export anything as its implementation details are entirely private.

There is a newer version: 5.2.3
Show newest version
/**
 * Copyright 2013 the original author or authors.
 * 
 * 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 io.neba.core.mvc;

import org.apache.sling.api.SlingHttpServletResponse;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolverComposite;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping;
import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import static org.springframework.beans.factory.BeanFactoryUtils.GENERATED_BEAN_NAME_SEPARATOR;
import static org.springframework.web.servlet.DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME;

/**
 * 

* Configures a bundle-specific {@link ApplicationContext} with the Spring MVC infrastructure beans usually * provided to {@link org.springframework.web.context.WebApplicationContext web application contexts} by the * {@link DispatcherServlet}. Subsequently * {@link DispatcherServlet#initStrategies(org.springframework.context.ApplicationContext) initializes} * a context-specific {@link DispatcherServlet} instance with the so configured context. The resulting * dispatcher servlet is a fully-featured dispatcher servlet for the specific bundle, with the * exception of {@link DispatcherServlet#setPublishEvents(boolean) event publication}, which is disabled * as it requires a {@link org.springframework.web.context.WebApplicationContext}. *

* The configured context may provide custom MVC infrastructure beans, e.g. * by means of a <mvc:.../> XML configuration in their application context XML. * Like the {@link DispatcherServlet}, the {@link MvcContext} will only provide MVC infrastructure * beans if no suitable beans exist in the provided context. * * @author Olaf Otto */ public class MvcContext implements ApplicationListener { private volatile boolean dispatcherServletInitialized = false; private volatile boolean mvcInfrastructureInitialized = false; private ApplicationContext context; /** * Weakens visibility restrictions of the original {@link DispatcherServlet} * and {@link DispatcherServlet#setPublishEvents(boolean) disables event publication} * as event publication requires the presence of a {@link org.springframework.web.context.WebApplicationContext}, * whereas an {@link org.eclipse.gemini.blueprint.context.support.OsgiBundleXmlApplicationContext} is * used by gemini-blueprint. * * @author Olaf Otto */ private static class ContextSpecificDispatcherServlet extends DispatcherServlet { private ServletConfig servletConfig; private ContextSpecificDispatcherServlet() { super(); setPublishEvents(false); setDispatchOptionsRequest(true); setDispatchTraceRequest(true); } @Override public ServletConfig getServletConfig() { return this.servletConfig; } public void setServletConfig(ServletConfig servletConfig) { this.servletConfig = servletConfig; } @Override protected void initStrategies(ApplicationContext context) { super.initStrategies(context); } protected boolean hasHandlerFor(HttpServletRequest request) { try { return super.getHandler(request) != null; } catch (Exception e) { throw new RuntimeException("Unable to lookup a handler for " + request + ".", e); } } } private final ConfigurableListableBeanFactory factory; private final ContextSpecificDispatcherServlet dispatcherServlet; /** * @param factory must not be null. */ public MvcContext(ConfigurableListableBeanFactory factory) { if (factory == null) { throw new IllegalArgumentException("Constructor parameter factory must not be null."); } this.dispatcherServlet = new ContextSpecificDispatcherServlet(); this.factory = factory; } /** * Configures the context's bean factory with MVC infrastructure beans. * * @param event must not be null. */ @Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ContextRefreshedEvent) { synchronized (this) { this.context = ((ContextRefreshedEvent) event).getApplicationContext(); configureMultipartResolver(); configureExceptionResolvers(); configureHandlerAdapters(); registerCustomArgumentResolvers(); configureHandlerMappings(); configureViewResolvers(); this.mvcInfrastructureInitialized = true; } } } /** * @param request must not be null. * * @return true if this context has been * {@link #onApplicationEvent(ApplicationEvent) initialized} * and the {@link DispatcherServlet} contains a * {@link DispatcherServlet#getHandler(javax.servlet.http.HttpServletRequest) handler for the request}. */ public boolean isResponsibleFor(HttpServletRequest request) { return this.mvcInfrastructureInitialized && this.dispatcherServletInitialized && this.dispatcherServlet.hasHandlerFor(request); } /** * Delegates to the context specific {@link DispatcherServlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}. * Must only be called if {@link #isResponsibleFor(javax.servlet.http.HttpServletRequest)} returns true. * * @param request must not be null. * @param response request must not be null. */ public void service(SlingMvcServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException { this.dispatcherServlet.service(request, response); } /** * Registers the custom argument resolvers if a {@link RequestMappingHandlerAdapter} * is present in the factory. */ private void registerCustomArgumentResolvers() { RequestMappingHandlerAdapter requestMappingHandlerAdapter = this.factory.getBean(RequestMappingHandlerAdapter.class); if (requestMappingHandlerAdapter != null) { HandlerMethodArgumentResolverComposite argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers(); if (argumentResolvers == null) { throw new IllegalStateException("No argument resolvers found in " + requestMappingHandlerAdapter + ". It appears the handler was not initialized by the application context."); } // Add Sling-specific argument resolvers first List resolvers = new LinkedList(); resolvers.add(new RequestPathInfoArgumentResolver()); resolvers.add(new ResourceResolverArgumentResolver()); resolvers.add(new ResourceParamArgumentResolver()); // Subsequently add all existing argument resolvers (they are order-sensitive, ending with a catch-all resolver, // thus the custom resolvers have to go first) resolvers.addAll(argumentResolvers.getResolvers()); requestMappingHandlerAdapter.setArgumentResolvers(resolvers); } } /** * Discovers existing {@link org.springframework.web.servlet.HandlerAdapter handler adapters} in the provided * context. Provides the default adapters (see original dispatcher servlet) in case no adapters * exist in the context. */ private void configureHandlerAdapters() { Map handlerAdapters = this.factory.getBeansOfType(HandlerAdapter.class); if (handlerAdapters.isEmpty()) { defineBean(HttpRequestHandlerAdapter.class); defineBean(RequestMappingHandlerAdapter.class); } } /** * Discovers existing {@link org.springframework.web.servlet.HandlerExceptionResolver exception resolvers} in the provided * context. Provides the default resolvers (see original dispatcher servlet) in case no resolvers * exist in the context. */ private void configureExceptionResolvers() { Map resolvers = this.factory.getBeansOfType(HandlerExceptionResolver.class); if (resolvers.isEmpty()) { defineBean(ExceptionHandlerExceptionResolver.class); defineBean(ResponseStatusExceptionResolver.class); DefaultHandlerExceptionResolver defaultResolver = defineBean(DefaultHandlerExceptionResolver.class); defaultResolver.setWarnLogCategory("mvc"); } } /** * Discovers existing {@link org.springframework.web.servlet.HandlerMapping handler mappings} in the provided * context. Provides the default mappings (see original dispatcher servlet) in case no mappings * exist in the context. */ private void configureHandlerMappings() { Map handlerMappings = this.factory.getBeansOfType(HandlerMapping.class); if (handlerMappings.isEmpty()) { defineBean(BeanNameUrlHandlerMapping.class); defineBean(RequestMappingHandlerMapping.class); } } /** * Discovers existing {@link org.springframework.web.servlet.ViewResolver view resolver} in the provided * context. Provides a special {@link NebaViewResolver} in case no resolver * exist in the context. */ private void configureViewResolvers() { Map resolvers = this.factory.getBeansOfType(ViewResolver.class); if (resolvers.isEmpty()) { defineBean(NebaViewResolver.class); } } /** * Discovers existing {@link org.springframework.web.multipart.MultipartResolver multipart resolvers} * in the provided context. Provides a special {@link io.neba.core.mvc.SlingMultipartResolver} * in case no resolver exists in the context. */ private void configureMultipartResolver() { if (!hasBean(MultipartResolver.class)) { defineBean(SlingMultipartResolver.class, MULTIPART_RESOLVER_BEAN_NAME); } } private String generateBeanNameFor(Class type) { return type.getName() + GENERATED_BEAN_NAME_SEPARATOR + "0"; } private T defineBean(Class type) { return defineBean(type, generateBeanNameFor(type)); } private T defineBean(Class type, String beanName) { T bean = this.factory.createBean(type); this.factory.registerSingleton(beanName, bean); return bean; } public synchronized void initializeDispatcherServlet(ServletConfig config) { if (mustInitializeDispatcherServlet()) { this.dispatcherServlet.setServletConfig(config); this.dispatcherServlet.initStrategies(this.context); this.dispatcherServletInitialized = true; } } public boolean mustInitializeDispatcherServlet() { return !this.dispatcherServletInitialized && this.mvcInfrastructureInitialized; } private boolean hasBean(Class type) { return !this.factory.getBeansOfType(type).isEmpty(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy