/*
* (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.content.internal;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.WebApplicationInitializer;
import com.kurento.kmf.common.exception.internal.ReflectionUtils;
import com.kurento.kmf.content.HttpPlayerHandler;
import com.kurento.kmf.content.HttpPlayerService;
import com.kurento.kmf.content.HttpRecorderHandler;
import com.kurento.kmf.content.HttpRecorderService;
import com.kurento.kmf.content.RtpContentHandler;
import com.kurento.kmf.content.RtpContentService;
import com.kurento.kmf.content.WebRtcContentHandler;
import com.kurento.kmf.content.WebRtcContentService;
import com.kurento.kmf.content.internal.player.PlayerHandlerServlet;
import com.kurento.kmf.content.internal.recorder.RecorderHandlerServlet;
import com.kurento.kmf.content.internal.rtp.RtpMediaHandlerServlet;
import com.kurento.kmf.content.internal.webrtc.WebRtcMediaHandlerServlet;
import com.kurento.kmf.spring.KurentoApplicationContextUtils;
/**
* TODO: review & improve javadoc
*
* This class performs the initialization of the implemented web applications,
* searching the declared handlers with corresponding annotations (
* {@link HttpPlayerService}, {@link HttpRecorderService},
* {@link RtpContentService}, {@link RtpContentService}).
*
* @see HttpPlayerService
* @see HttpRecorderService
* @see RtpContentService
* @see WebRtcContentService
*
* @author Luis López (llopez@gsyc.es)
* @author Boni García (bgarcia@gsyc.es)
* @version 1.0.0
*/
public class ContentApiWebApplicationInitializer implements
WebApplicationInitializer {
/**
* Logger.
*/
private static final Logger log = LoggerFactory
.getLogger(ContentApiWebApplicationInitializer.class);
/**
* Identifier for the declared handlers.
*/
public static final String HANDLER_CLASS_PARAM_NAME = ContentApiWebApplicationInitializer.class
.getName() + "HandlerClassParamName";
/**
* Web initialization is performed in this method, calling every handler
* initilizator (player, recorder, webrtc, rtp).
*/
@Override
public void onStartup(ServletContext sc) throws ServletException {
// At this stage we cannot create KurentoApplicationContext given that
// we don't know y App developer wants to instantiate a Spring root
// WebApplicationContext
// ... so we need to live without Spring
initializeRecorders(sc);
initializePlayers(sc);
initializeWebRtcMediaServices(sc);
initializeRtpMediaServices(sc);
// Register Kurento ServletContextListener
KurentoApplicationContextUtils
.registerKurentoServletContextListener(sc);
}
/**
* Player initializator: this method search classes in the classpath using
* the annotation {@link HttpPlayerService}, and it register a servlet for
* each handler found.
*
* @param sc
* Servlet Context in which register servlets for each handler
* @throws ServletException
* Exception raised when a reflection problem occurs, typically
* when a class has not been found in the classpath
*/
private void initializePlayers(ServletContext sc) throws ServletException {
for (String ph : findServices(HttpPlayerHandler.class,
HttpPlayerService.class)) {
try {
HttpPlayerService playerService = Class.forName(ph)
.getAnnotation(HttpPlayerService.class);
if (playerService != null) {
String name = playerService.name().isEmpty() ? ph
: playerService.name();
String path = playerService.path();
log.info("Registering HttpPlayerHandler with name " + name
+ " at path " + path);
ServletRegistration.Dynamic sr = sc.addServlet(name,
PlayerHandlerServlet.class);
if (sr == null) {
throw new ServletException(
"Duplicated handler named "
+ name
+ " found. You must check your handlers' annotations to assert that no name duplications are declared.");
}
sr.addMapping(path);
sr.setInitParameter(HANDLER_CLASS_PARAM_NAME, ph);
sr.setAsyncSupported(true);
}
} catch (ClassNotFoundException e) {
log.error(
"Error: could not find class " + ph + " in classpath",
e);
throw new ServletException(e);
}
}
}
/**
* Recorder initializator: this method search classes in the classpath using
* the annotation {@link HttpRecorderService}, and it register a servlet for
* each handler found.
*
* @param sc
* Servlet Context in which registering servlets for each handler
* @throws ServletException
* Exception raised when a reflection problem occurs, typically
* when a class has not been found in the classpath
*/
private void initializeRecorders(ServletContext sc) throws ServletException {
for (String rh : findServices(HttpRecorderHandler.class,
HttpRecorderService.class)) {
try {
HttpRecorderService recorderService = Class.forName(rh)
.getAnnotation(HttpRecorderService.class);
if (recorderService != null) {
String name = recorderService.name().isEmpty() ? rh
: recorderService.name();
String path = recorderService.path();
log.debug("Registering HttpRecorderHandler with name "
+ name + " at path " + path);
ServletRegistration.Dynamic sr = sc.addServlet(name,
RecorderHandlerServlet.class);
sr.addMapping(path);
sr.setInitParameter(HANDLER_CLASS_PARAM_NAME, rh);
sr.setAsyncSupported(true);
}
} catch (ClassNotFoundException e) {
log.error(
"Error: could not find class " + rh + " in classpath",
e);
throw new ServletException(e);
}
}
}
/**
* WebRtc initializator: this method search classes in the classpath using
* the annotation {@link WebRtcContentService}, and it register a servlet
* for each handler found.
*
* @param sc
* Servlet Context in which register servlets for each handler
* @throws ServletException
* Exception raised when a reflection problem occurs, typically
* when a class has not been found in the classpath
*/
private void initializeWebRtcMediaServices(ServletContext sc)
throws ServletException {
for (String wh : findServices(WebRtcContentHandler.class,
WebRtcContentService.class)) {
try {
WebRtcContentService mediaService = Class.forName(wh)
.getAnnotation(WebRtcContentService.class);
if (mediaService != null) {
String name = mediaService.name().isEmpty() ? wh
: mediaService.name();
String path = mediaService.path();
log.debug("Registering WebRtcContentHandler with name "
+ name + " at path " + path);
ServletRegistration.Dynamic sr = sc.addServlet(name,
WebRtcMediaHandlerServlet.class);
sr.addMapping(path);
sr.setInitParameter(HANDLER_CLASS_PARAM_NAME, wh);
sr.setAsyncSupported(true);
}
} catch (ClassNotFoundException e) {
log.error(
"Error: could not find class " + wh + " in classpath",
e);
throw new ServletException(e);
}
}
}
/**
* RtpMedia initializator: this method search classes in the classpath using
* the annotation {@link RtpContentService}, and it register a servlet for
* each handler found.
*
* @param sc
* Servlet Context in which register servlets for each handler
* @throws ServletException
* Exception raised when a reflection problem occurs, typically
* when a class has not been found in the classpath
*/
private void initializeRtpMediaServices(ServletContext sc)
throws ServletException {
for (String rh : findServices(RtpContentHandler.class,
RtpContentService.class)) {
try {
RtpContentService mediaService = Class.forName(rh)
.getAnnotation(RtpContentService.class);
if (mediaService != null) {
String name = mediaService.name().isEmpty() ? rh
: mediaService.name();
String path = mediaService.path();
log.debug("Registering RtpContentHandler with name " + name
+ " at path " + path);
ServletRegistration.Dynamic sr = sc.addServlet(name,
RtpMediaHandlerServlet.class);
sr.addMapping(path);
sr.setInitParameter(HANDLER_CLASS_PARAM_NAME, rh);
sr.setAsyncSupported(true);
}
} catch (ClassNotFoundException e) {
log.error(
"Error: could not find class " + rh + " in classpath",
e);
throw new ServletException(e);
}
}
}
/**
* It seeks declared handlers in the classpath by using reflections.
*
* @param handlerClass
* Handler class ({@link HttpPlayerHandler},
* {@link HttpRecorderHandler}, {@link WebRtcContentHandler},
* {@link RtpContentHandler})
* @param serviceAnnotation
* Service annotation ({@link HttpPlayerService},
* {@link HttpRecorderService}, {@link WebRtcContentService},
* {@link RtpContentService})
* @return List of services
* @throws ServletException
* Exception raised when an incorrect implementation of handler
* is detected (mismatching between annotation and inheritance
* in handler)
*/
private List<String> findServices(Class<?> handlerClass,
Class<? extends Annotation> serviceAnnotation)
throws ServletException {
Set<Class<?>> annotatedList = ReflectionUtils
.getTypesAnnotatedWith(serviceAnnotation);
List<String> handlerList = new ArrayList<String>();
for (Class<?> clazz : annotatedList) {
if (handlerClass.isAssignableFrom(clazz)) {
handlerList.add(clazz.getName());
} else {
String error = "Incorrect implementation of handler: class "
+ clazz.getCanonicalName() + " is annotated with "
+ serviceAnnotation.getSimpleName() + " (instead, "
+ clazz.getSimpleName() + " should extend "
+ handlerClass.getSimpleName()
+ " or use the correct annotation)";
log.error(error);
throw new ServletException(error);
}
}
return handlerList;
}
}