| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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 org.apache.camel.component.servletlistener; |
| |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.servlet.ServletContextEvent; |
| import javax.servlet.ServletContextListener; |
| |
| import org.apache.camel.ManagementStatisticsLevel; |
| import org.apache.camel.RoutesBuilder; |
| import org.apache.camel.builder.ErrorHandlerBuilderRef; |
| import org.apache.camel.builder.RouteBuilder; |
| import org.apache.camel.component.properties.PropertiesComponent; |
| import org.apache.camel.management.DefaultManagementAgent; |
| import org.apache.camel.management.DefaultManagementLifecycleStrategy; |
| import org.apache.camel.management.DefaultManagementStrategy; |
| import org.apache.camel.management.ManagedManagementStrategy; |
| import org.apache.camel.model.RouteDefinition; |
| import org.apache.camel.model.RoutesDefinition; |
| import org.apache.camel.spi.ManagementStrategy; |
| import org.apache.camel.spi.Registry; |
| import org.apache.camel.util.CamelContextHelper; |
| import org.apache.camel.util.CastUtils; |
| import org.apache.camel.util.IOHelper; |
| import org.apache.camel.util.IntrospectionSupport; |
| import org.apache.camel.util.ObjectHelper; |
| import org.apache.camel.util.ResourceHelper; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * A {@link ServletContextListener} which is used to bootstrap |
| * {@link org.apache.camel.CamelContext} in web applications. |
| * |
| * @param <R> the type of the {@link Registry} being {@link #createRegistry() created} |
| */ |
| public abstract class CamelServletContextListener<R extends Registry> implements ServletContextListener { |
| |
| /** |
| * instance is used for testing purpose |
| */ |
| public static ServletCamelContext instance; |
| |
| /** |
| * Key to store the created {@link org.apache.camel.CamelContext} as an attribute on the {@link javax.servlet.ServletContext}. |
| */ |
| public static final String CAMEL_CONTEXT_KEY = "CamelContext"; |
| |
| protected static final Logger LOG = LoggerFactory.getLogger(CamelServletContextListener.class); |
| protected ServletCamelContext camelContext; |
| protected CamelContextLifecycle<R> camelContextLifecycle; |
| protected boolean test; |
| protected R registry; |
| |
| @Override |
| public void contextInitialized(ServletContextEvent sce) { |
| LOG.info("CamelContextServletListener initializing ..."); |
| |
| // create jndi and camel context |
| try { |
| registry = createRegistry(); |
| camelContext = new ServletCamelContext(registry, sce.getServletContext()); |
| } catch (Exception e) { |
| throw new RuntimeException("Error creating CamelContext.", e); |
| } |
| |
| // get the init parameters |
| Map<String, Object> map = extractInitParameters(sce); |
| |
| // special for test parameter |
| String test = (String) map.remove("test"); |
| if (test != null && "true".equalsIgnoreCase(test)) { |
| this.test = true; |
| } |
| LOG.trace("In test mode? {}", this.test); |
| |
| // set properties on the camel context from the init parameters |
| try { |
| initPropertyPlaceholder(camelContext, map); |
| initJmx(camelContext, map); |
| initCamelContext(camelContext, map); |
| if (!map.isEmpty()) { |
| IntrospectionSupport.setProperties(camelContext, map); |
| } |
| } catch (Exception e) { |
| throw new RuntimeException("Error setting init parameters on CamelContext.", e); |
| } |
| |
| // any custom CamelContextLifecycle |
| String lifecycle = (String) map.remove("CamelContextLifecycle"); |
| if (lifecycle != null) { |
| try { |
| Class<CamelContextLifecycle<R>> clazz = CastUtils.cast(camelContext.getClassResolver().resolveMandatoryClass(lifecycle, CamelContextLifecycle.class)); |
| camelContextLifecycle = camelContext.getInjector().newInstance(clazz); |
| } catch (ClassNotFoundException e) { |
| throw new RuntimeException("Error creating CamelContextLifecycle class with name " + lifecycle, e); |
| } |
| } |
| |
| try { |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.beforeAddRoutes(camelContext, registry); |
| } |
| } catch (Exception e) { |
| LOG.error("Error before adding routes to CamelContext.", e); |
| throw new RuntimeException("Error before adding routes to CamelContext.", e); |
| } |
| |
| // get the routes and add to the CamelContext |
| List<Object> routes = extractRoutes(map); |
| for (Object route : routes) { |
| if (route instanceof RouteBuilder) { |
| try { |
| camelContext.addRoutes((RoutesBuilder) route); |
| } catch (Exception e) { |
| throw new RuntimeException("Error adding route " + route, e); |
| } |
| } else if (route instanceof Set) { |
| // its a set of route builders |
| for (Object routesBuilder : (Set<?>) route) { |
| try { |
| camelContext.addRoutes((RoutesBuilder) routesBuilder); |
| } catch (Exception e) { |
| throw new RuntimeException("Error adding route " + routesBuilder, e); |
| } |
| } |
| } else if (route instanceof RoutesDefinition) { |
| try { |
| camelContext.addRouteDefinitions(((RoutesDefinition) route).getRoutes()); |
| } catch (Exception e) { |
| throw new RuntimeException("Error adding route(s) " + route, e); |
| } |
| } else if (route instanceof RouteDefinition) { |
| try { |
| camelContext.addRouteDefinition((RouteDefinition) route); |
| } catch (Exception e) { |
| throw new RuntimeException("Error adding route(s) " + route, e); |
| } |
| } else { |
| throw new IllegalArgumentException("Unsupported route: " + route); |
| } |
| } |
| |
| // just log if we could not use all the parameters, as they may be used by others |
| if (!map.isEmpty()) { |
| LOG.info("There are {} ServletContext init parameters, unknown to Camel. Maybe they are used by other frameworks? [{}]", map.size(), map); |
| } |
| |
| try { |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.afterAddRoutes(camelContext, registry); |
| } |
| } catch (Exception e) { |
| LOG.error("Error after adding routes to CamelContext.", e); |
| throw new RuntimeException("Error after adding routes to CamelContext.", e); |
| } |
| |
| try { |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.beforeStart(camelContext, registry); |
| } |
| camelContext.start(); |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.afterStart(camelContext, registry); |
| } |
| } catch (Exception e) { |
| LOG.error("Error starting CamelContext.", e); |
| throw new RuntimeException("Error starting CamelContext.", e); |
| } |
| |
| if (this.test) { |
| instance = camelContext; |
| } |
| |
| // store the CamelContext as an attribute |
| sce.getServletContext().setAttribute(CAMEL_CONTEXT_KEY, camelContext); |
| |
| LOG.info("CamelContextServletListener initialized"); |
| } |
| |
| @Override |
| public void contextDestroyed(ServletContextEvent sce) { |
| LOG.info("CamelContextServletListener destroying ..."); |
| if (camelContext != null) { |
| try { |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.beforeStop(camelContext, registry); |
| } |
| camelContext.stop(); |
| if (camelContextLifecycle != null) { |
| camelContextLifecycle.afterStop(camelContext, registry); |
| } |
| } catch (Exception e) { |
| LOG.warn("Error stopping CamelContext. This exception will be ignored.", e); |
| } |
| } |
| camelContext = null; |
| registry = null; |
| instance = null; |
| |
| // store the CamelContext as an attribute |
| sce.getServletContext().removeAttribute(CAMEL_CONTEXT_KEY); |
| |
| LOG.info("CamelContextServletListener destroyed"); |
| } |
| |
| /** |
| * Creates the {@link Registry} implementation to use. |
| */ |
| protected abstract R createRegistry() throws Exception; |
| |
| /** |
| * Extracts all the init parameters, and will do reference lookup in {@link #createRegistry() registry} |
| * in case the value starts with a {@code #} sign. |
| */ |
| private Map<String, Object> extractInitParameters(ServletContextEvent sce) { |
| // configure CamelContext with the init parameter |
| Map<String, Object> map = new LinkedHashMap<String, Object>(); |
| Enumeration<?> names = sce.getServletContext().getInitParameterNames(); |
| while (names.hasMoreElements()) { |
| String name = (String) names.nextElement(); |
| String value = sce.getServletContext().getInitParameter(name); |
| |
| if (ObjectHelper.isNotEmpty(value)) { |
| Object target = value; |
| if (value.startsWith("#")) { |
| // a reference lookup in registry |
| value = value.substring(1); |
| target = lookupRegistryByName(value); |
| LOG.debug("Resolved the servlet context's initialization parameter {} to {}", value, target); |
| } |
| map.put(name, target); |
| } |
| } |
| return map; |
| } |
| |
| /** |
| * Initializes the property placeholders by registering the {@link PropertiesComponent} with |
| * the configuration from the given init parameters. |
| */ |
| private void initPropertyPlaceholder(ServletCamelContext camelContext, Map<String, Object> parameters) throws Exception { |
| // setup property placeholder first |
| Map<String, Object> properties = IntrospectionSupport.extractProperties(parameters, "propertyPlaceholder."); |
| if (properties != null && !properties.isEmpty()) { |
| PropertiesComponent pc = new PropertiesComponent(); |
| IntrospectionSupport.setProperties(pc, properties); |
| // validate we could set all parameters |
| if (!properties.isEmpty()) { |
| throw new IllegalArgumentException("Error setting propertyPlaceholder parameters on CamelContext." |
| + " There are " + properties.size() + " unknown parameters. [" + properties + "]"); |
| } |
| // register the properties component |
| camelContext.addComponent("properties", pc); |
| } |
| } |
| |
| /** |
| * Initializes JMX on {@link ServletCamelContext} with the configuration from the given init parameters. |
| */ |
| private void initJmx(ServletCamelContext camelContext, Map<String, Object> parameters) throws Exception { |
| // setup jmx |
| Map<String, Object> properties = IntrospectionSupport.extractProperties(parameters, "jmx."); |
| if (properties != null && !properties.isEmpty()) { |
| String disabled = (String) properties.remove("disabled"); |
| boolean disableJmx = CamelContextHelper.parseBoolean(camelContext, disabled != null ? disabled : "false"); |
| if (disableJmx) { |
| // disable JMX which is a bit special to do |
| LOG.info("JMXAgent disabled"); |
| // clear the existing lifecycle strategies define by the DefaultCamelContext constructor |
| camelContext.getLifecycleStrategies().clear(); |
| // no need to add a lifecycle strategy as we do not need one as JMX is disabled |
| camelContext.setManagementStrategy(new DefaultManagementStrategy()); |
| } else { |
| LOG.info("JMXAgent enabled"); |
| DefaultManagementAgent agent = new DefaultManagementAgent(camelContext); |
| IntrospectionSupport.setProperties(agent, properties); |
| |
| ManagementStrategy managementStrategy = new ManagedManagementStrategy(camelContext, agent); |
| camelContext.setManagementStrategy(managementStrategy); |
| |
| // clear the existing lifecycle strategies defined by the DefaultCamelContext constructor |
| camelContext.getLifecycleStrategies().clear(); |
| camelContext.addLifecycleStrategy(new DefaultManagementLifecycleStrategy(camelContext)); |
| // set additional configuration from agent |
| boolean onlyId = agent.getOnlyRegisterProcessorWithCustomId() != null && agent.getOnlyRegisterProcessorWithCustomId(); |
| camelContext.getManagementStrategy().onlyManageProcessorWithCustomId(onlyId); |
| |
| String statisticsLevel = (String) properties.remove("statisticsLevel"); |
| if (statisticsLevel != null) { |
| camelContext.getManagementStrategy().setStatisticsLevel(ManagementStatisticsLevel.valueOf(statisticsLevel)); |
| } |
| |
| String loadStatisticsEnabled = (String) properties.remove("loadStatisticsEnabled"); |
| Boolean statisticsEnabled = CamelContextHelper.parseBoolean(camelContext, loadStatisticsEnabled != null ? loadStatisticsEnabled : "true"); |
| if (statisticsEnabled != null) { |
| camelContext.getManagementStrategy().setLoadStatisticsEnabled(statisticsEnabled); |
| } |
| } |
| // validate we could set all parameters |
| if (!properties.isEmpty()) { |
| throw new IllegalArgumentException("Error setting jmx parameters on CamelContext." |
| + " There are " + properties.size() + " unknown parameters. [" + properties + "]"); |
| } |
| } |
| } |
| |
| /** |
| * Initializes the {@link ServletCamelContext} by setting the supported init parameters. |
| */ |
| private void initCamelContext(ServletCamelContext camelContext, Map<String, Object> parameters) throws Exception { |
| String messageHistory = (String) parameters.remove("messageHistory"); |
| if (messageHistory != null) { |
| camelContext.setMessageHistory(CamelContextHelper.parseBoolean(camelContext, messageHistory)); |
| } |
| String streamCache = (String) parameters.remove("streamCache"); |
| if (streamCache != null) { |
| camelContext.setStreamCaching(CamelContextHelper.parseBoolean(camelContext, streamCache)); |
| } |
| String trace = (String) parameters.remove("trace"); |
| if (trace != null) { |
| camelContext.setTracing(CamelContextHelper.parseBoolean(camelContext, trace)); |
| } |
| String delayer = (String) parameters.remove("delayer"); |
| if (delayer != null) { |
| camelContext.setDelayer(CamelContextHelper.parseLong(camelContext, delayer)); |
| } |
| String handleFault = (String) parameters.remove("handleFault"); |
| if (handleFault != null) { |
| camelContext.setHandleFault(CamelContextHelper.parseBoolean(camelContext, handleFault)); |
| } |
| String errorHandlerRef = (String) parameters.remove("errorHandlerRef"); |
| if (errorHandlerRef != null) { |
| camelContext.setErrorHandlerBuilder(new ErrorHandlerBuilderRef(errorHandlerRef)); |
| } |
| String autoStartup = (String) parameters.remove("autoStartup"); |
| if (autoStartup != null) { |
| camelContext.setAutoStartup(CamelContextHelper.parseBoolean(camelContext, autoStartup)); |
| } |
| String useMDCLogging = (String) parameters.remove("useMDCLogging"); |
| if (useMDCLogging != null) { |
| camelContext.setUseMDCLogging(CamelContextHelper.parseBoolean(camelContext, useMDCLogging)); |
| } |
| String useBreadcrumb = (String) parameters.remove("useBreadcrumb"); |
| if (useBreadcrumb != null) { |
| camelContext.setUseBreadcrumb(CamelContextHelper.parseBoolean(camelContext, useBreadcrumb)); |
| } |
| String managementNamePattern = (String) parameters.remove("managementNamePattern"); |
| if (managementNamePattern != null) { |
| camelContext.getManagementNameStrategy().setNamePattern(managementNamePattern); |
| } |
| String threadNamePattern = (String) parameters.remove("threadNamePattern"); |
| if (threadNamePattern != null) { |
| camelContext.getExecutorServiceManager().setThreadNamePattern(threadNamePattern); |
| } |
| |
| // extract any additional properties. prefixes |
| Map<String, Object> properties = IntrospectionSupport.extractProperties(parameters, "properties."); |
| if (properties != null && !properties.isEmpty()) { |
| for (Map.Entry<String, Object> entry : properties.entrySet()) { |
| camelContext.getProperties().put(entry.getKey(), "" + entry.getValue()); |
| } |
| } |
| } |
| |
| /** |
| * Extract the routes from the parameters. |
| * |
| * @param map parameters |
| * @return a list of routes, which can be of different types. See source code for more details. |
| */ |
| private List<Object> extractRoutes(Map<String, Object> map) { |
| List<Object> answer = new ArrayList<Object>(); |
| List<String> names = new ArrayList<String>(); |
| |
| for (Map.Entry<String, Object> entry : map.entrySet()) { |
| if (entry.getKey().toLowerCase(Locale.UK).startsWith("routebuilder")) { |
| names.add(entry.getKey()); |
| // we can have multiple values assigned, separated by comma, so create an iterator |
| String value = (String) entry.getValue(); |
| Iterator<Object> it = ObjectHelper.createIterator(value); |
| while (it.hasNext()) { |
| value = (String) it.next(); |
| if (ObjectHelper.isNotEmpty(value)) { |
| // trim value before usage, as people can indent the values |
| value = value.trim(); |
| Object target = null; |
| |
| if (value.startsWith("#")) { |
| // a reference lookup in jndi |
| value = value.substring(1); |
| target = lookupRegistryByName(value); |
| } else if (ResourceHelper.hasScheme(value)) { |
| // XML resource from classpath or file system |
| InputStream is = null; |
| try { |
| is = ResourceHelper.resolveMandatoryResourceAsInputStream(camelContext, value); |
| target = camelContext.loadRoutesDefinition(is); |
| } catch (Exception e) { |
| throw new RuntimeException("Error loading routes from resource: " + value, e); |
| } finally { |
| IOHelper.close(is, entry.getKey(), LOG); |
| } |
| } else if (value.startsWith("packagescan:")) { |
| // using package scanning |
| String path = value.substring(12); |
| Set<Class<?>> classes = camelContext.getPackageScanClassResolver().findImplementations(RouteBuilder.class, path); |
| if (!classes.isEmpty()) { |
| Set<RouteBuilder> builders = new LinkedHashSet<RouteBuilder>(); |
| target = builders; |
| for (Class<?> clazz : classes) { |
| try { |
| RouteBuilder route = (RouteBuilder) camelContext.getInjector().newInstance(clazz); |
| builders.add(route); |
| } catch (Exception e) { |
| throw new RuntimeException("Error creating RouteBuilder " + clazz, e); |
| } |
| } |
| } |
| } else { |
| // assume its a FQN classname for a RouteBuilder class |
| try { |
| Class<RouteBuilder> clazz = camelContext.getClassResolver().resolveMandatoryClass(value, RouteBuilder.class); |
| target = camelContext.getInjector().newInstance(clazz); |
| } catch (Exception e) { |
| throw new RuntimeException("Error creating RouteBuilder " + value, e); |
| } |
| } |
| if (target != null) { |
| answer.add(target); |
| } |
| } |
| } |
| } |
| } |
| |
| // after adding the route builders we should remove them from the map |
| for (String name : names) { |
| map.remove(name); |
| } |
| |
| return answer; |
| } |
| |
| private Object lookupRegistryByName(String name) { |
| return registry.lookupByName(name); |
| } |
| |
| } |