/*
* (C) Copyright 2013 Kurento (http://kurento.org/)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl-2.1.html
*
* 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 GNU
* Lesser General Public License for more details.
*
*/
package com.kurento.kmf.spring;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.beans.factory.config.PropertyOverrideConfigurer;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.util.Assert;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.ServletContextResource;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.kurento.kmf.common.exception.KurentoException;
public final class KurentoApplicationContextUtils {
private static final Logger log = LoggerFactory
.getLogger(KurentoApplicationContextUtils.class);
private static final String KURENTO_SERVLET_CONTEXT_LISTENER_ATTRIBUTE_NAME = KurentoApplicationContextUtils.class
+ "AttributeName";
// Is there any better mechanisms for enabling direct recovery of beans
// (e.g. configurations)
private static AnnotationConfigApplicationContext kurentoApplicationContextInternalReference;
private static ConcurrentHashMap<String, AnnotationConfigApplicationContext> childContexts;
private KurentoApplicationContextUtils() {
}
/**
* This class returns the Spring KurentoApplicationContext, which is the
* parent context for all specific Kurento Servlet contexts. In case a
* pre-exiting Spring root WebApplicationContext if found, the returned
* KurentoApplicationContext will be made child of this root context. When
* necessary, this method creates the KurentoApplicationContext, so it
* should never return null.
*
* This method MUST NOT be called in ServletContextListeners, given that at
* that stage there might not be information about the presence of a root
* Spring root WebApplicationConext.
*
* @param ctx
* @return the context
*
*/
public static synchronized AnnotationConfigApplicationContext createKurentoApplicationContext(
ServletContext ctx) {
Assert.notNull(ctx,
"Cannot recover KurentoApplicationContext from a null ServletContext");
Assert.isNull(kurentoApplicationContextInternalReference,
"Pre-existing Kurento ApplicationContext found. Cannot create a new instance.");
kurentoApplicationContextInternalReference = new AnnotationConfigApplicationContext();
// We can't scan whole com.kurento.kmf package because there are classes
// in classpath not designed to work with content-api
kurentoApplicationContextInternalReference
.scan("com.kurento.kmf.spring");
kurentoApplicationContextInternalReference
.scan("com.kurento.kmf.content");
kurentoApplicationContextInternalReference
.scan("com.kurento.kmf.repository");
// Recover root WebApplicationContext context just in case
// application developer is using Spring
WebApplicationContext rootContext = WebApplicationContextUtils
.getWebApplicationContext(ctx);
if (rootContext != null) {
kurentoApplicationContextInternalReference.setParent(rootContext);
}
final String jbossServerConfigDir = System
.getProperty("jboss.server.config.dir");
final String kurentoPropertiesDir = System
.getProperty("kurento.properties.dir");
final String kurentoProperties = "/kurento.properties";
InputStream inputStream = null;
try {
if (jbossServerConfigDir != null
&& new File(jbossServerConfigDir + kurentoProperties)
.exists()) {
// First, look for JVM argument "jboss.server.config.dir"
log.info("Found custom properties in 'jboss.server.config.dir': "
+ jbossServerConfigDir);
inputStream = new FileInputStream(jbossServerConfigDir
+ kurentoProperties);
} else if (kurentoPropertiesDir != null
&& new File(kurentoPropertiesDir + kurentoProperties)
.exists()) {
// Second, look for JVM argument "kurento.properties.dir"
log.info("Found custom properties in 'kurento.properties.dir': "
+ kurentoPropertiesDir);
inputStream = new FileInputStream(kurentoPropertiesDir
+ kurentoProperties);
} else {
// Third, look for properties in Servlet Context
ServletContextResource servletContextResource = new ServletContextResource(
ctx, "/WEB-INF" + kurentoProperties);
if (servletContextResource.exists()) {
log.info("Found custom properties in Servlet Context: /WEB-INF"
+ kurentoProperties);
inputStream = servletContextResource.getInputStream();
}
}
if (inputStream != null) {
Properties properties = new Properties();
properties.load(inputStream);
PropertyOverrideConfigurer propertyOverrideConfigurer = new PropertyOverrideConfigurer();
propertyOverrideConfigurer.setProperties(properties);
kurentoApplicationContextInternalReference
.addBeanFactoryPostProcessor(propertyOverrideConfigurer);
inputStream.close();
}
} catch (IOException e) {
throw new KurentoException("Exception loading custom properties", e);
}
kurentoApplicationContextInternalReference.refresh();
return kurentoApplicationContextInternalReference;
}
public static boolean kurentoApplicationContextExists() {
return kurentoApplicationContextInternalReference != null;
}
public static AnnotationConfigApplicationContext getKurentoApplicationContext() {
return kurentoApplicationContextInternalReference;
}
/**
* Returns a specific application context associated to a Kurento handler
* servlet. This method returns null if the context does not exist.
*
* @param servletClass
* @param servletName
* @return the context
*/
public static AnnotationConfigApplicationContext getKurentoServletApplicationContext(
Class<?> servletClass, String servletName) {
Assert.notNull(servletClass,
"Cannot recover KurentoServletApplicationContext from a null Servlet class");
if (childContexts == null) {
return null;
}
AnnotationConfigApplicationContext childAppContext = childContexts
.get(servletClass.getName() + ":" + servletName);
if (childAppContext == null) {
return null;
}
return childAppContext;
}
public static synchronized AnnotationConfigApplicationContext createKurentoHandlerServletApplicationContext(
Class<?> servletClass, String servletName, ServletContext sc,
String handlerClassName) {
Assert.notNull(sc,
"Cannot create Kurento ServletApplicationContext from null ServletContext");
Assert.notNull(servletClass,
"Cannot create KurentoServletApplicationContext from a null Servlet class");
Assert.notNull(servletClass,
"Cannot create KurentoServletApplicationContext from a null Hanlder class");
if (childContexts == null) {
childContexts = new ConcurrentHashMap<>();
}
AnnotationConfigApplicationContext childContext = childContexts
.get(servletClass.getName() + ":" + servletName);
Assert.isNull(childContext,
"Pre-existing context found associated to servlet class "
+ servletClass.getName() + " and servlet name "
+ servletName);
childContext = new AnnotationConfigApplicationContext();
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(handlerClassName);
childContext.registerBeanDefinition(handlerClassName, beanDefinition);
if (!kurentoApplicationContextExists()) {
createKurentoApplicationContext(sc);
}
childContext.setParent(getKurentoApplicationContext());
childContext.refresh();
childContexts.put(servletClass.getName(), childContext);
return childContext;
}
public static synchronized void closeAllKurentoApplicationContexts(
ServletContext ctx) {
Assert.notNull(ctx, "Cannot close contexts from a null ServletContext");
if (childContexts != null) {
for (AnnotationConfigApplicationContext childContext : childContexts
.values()) {
log.info("Closing Kurento Servlet Application Context "
+ childContext);
childContext.close();
}
}
childContexts = null;
if (kurentoApplicationContextInternalReference != null) {
log.info("Closing Kurento Application Context "
+ kurentoApplicationContextInternalReference);
kurentoApplicationContextInternalReference.close();
}
kurentoApplicationContextInternalReference = null;
}
public static void processInjectionBasedOnApplicationContext(Object bean,
AnnotationConfigApplicationContext appContext) {
Assert.notNull(
appContext,
"Cannot process bean injection. Reason the specified ApplicationContext is null");
Assert.notNull(bean,
"Cannot process bean injection into null bean reference");
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(appContext.getAutowireCapableBeanFactory());
bpp.processInjection(bean);
}
public static void processInjectionBasedOnKurentoApplicationContext(
Object bean) {
Assert.notNull(
kurentoApplicationContextInternalReference,
"Cannot process bean injection. Reason Kurento ApplicationContext has not been initialized");
Assert.notNull(bean,
"Cannot process bean injection into null bean reference");
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(kurentoApplicationContextInternalReference
.getAutowireCapableBeanFactory());
bpp.processInjection(bean);
}
public static <T> T getConfiguration(Class<T> configurationClass) {
Assert.notNull(kurentoApplicationContextInternalReference,
"Cannot access configuration before creating Kurento Application Context");
T result = kurentoApplicationContextInternalReference
.getBean(configurationClass);
Assert.notNull(result,
"No configuration has been found associated to type "
+ configurationClass.getName());
return result;
}
public static Object getBean(String name, Object... args) {
Assert.notNull(
kurentoApplicationContextInternalReference,
"Cannot get bean for the following reason: Kurento ApplicationContext has not been initlized.");
return kurentoApplicationContextInternalReference.getBean(name, args);
}
public static Object getBean(String name) {
Assert.notNull(
kurentoApplicationContextInternalReference,
"Cannot get bean for the following reason: Kurento ApplicationContext has not been initlized.");
return kurentoApplicationContextInternalReference.getBean(name);
}
public static synchronized void registerKurentoServletContextListener(
ServletContext ctx) {
// Add listener for closing Kurento ApplicationContexts on container
// shutdown
if (ctx.getAttribute(KURENTO_SERVLET_CONTEXT_LISTENER_ATTRIBUTE_NAME) != null) {
log.info("Kurento ServletContextListener already registered, we don't register it again ...");
return;
}
log.info("Registering Kurento ServletContextListener ...");
ctx.setAttribute(KURENTO_SERVLET_CONTEXT_LISTENER_ATTRIBUTE_NAME,
"initialized");
try {
ctx.addListener(KurentoServletContextListener.class);
} catch (NullPointerException e) {
// TODO: Workaround to make it compatible with/without SpringBoot.
// This exception is thrown when this class is used with SpringBoot.
// As workaround, SpringBoot application has to define the following
// @Bean in a @Configuration class:
//
// @Bean
// public ServletListenerRegistrationBean
// <KurentoServletContextListener> listener(){
// return new ServletListenerRegistrationBean<>(
// new KurentoServletContextListener());
// }
}
}
public static synchronized AnnotationConfigApplicationContext debugOnlyCreateKurentoApplicationContext() {
Assert.isNull(kurentoApplicationContextInternalReference,
"Pre-existing Kurento ApplicationContext found. Cannot create a new instance.");
kurentoApplicationContextInternalReference = new AnnotationConfigApplicationContext();
// Add or remove packages when required
kurentoApplicationContextInternalReference.scan("com.kurento.kmf");
kurentoApplicationContextInternalReference.refresh();
return kurentoApplicationContextInternalReference;
}
}