/*
* 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.ignite.startup.servlet;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteIllegalStateException;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgnitionEx;
import org.apache.ignite.internal.processors.resource.GridSpringResourceContext;
import org.apache.ignite.internal.util.typedef.G;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgniteBiTuple;
/**
* This class defines Ignite startup based on servlet context listener.
* This startup can be used to start Ignite inside any web container.
* <p>
* This startup must be defined in {@code web.xml} file.
* <pre name="code" class="xml">
* <listener>
* <listener-class>org.apache.ignite.startup.servlet.ServletContextListenerStartup</listener-class>
* </listener>
*
* <context-param>
* <param-name>IgniteConfigurationFilePath</param-name>
* <param-value>config/default-config.xml</param-value>
* </context-param>
* </pre>
* <p>
* Servlet context listener based startup may be used in any web container like Tomcat, Jetty and etc.
* Depending on the way this startup is deployed the Ignite instance can be accessed
* by either all web applications or by only one. See web container class loading architecture:
* <ul>
* <li><a target=_blank href="http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html">http://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html</a></li>
* <li><a target=_blank href="http://docs.codehaus.org/display/JETTY/Classloading">http://docs.codehaus.org/display/JETTY/Classloading</a></li>
* </ul>
* <p>
* <h2 class="header">Tomcat</h2>
* There are two ways to start Ignite on Tomcat.
* <ul>
* <li>Ignite started when web container starts and Ignite instance is accessible only to all web applications.
* <ol>
* <li>Add Ignite libraries in Tomcat common loader.
* Add in file {@code $TOMCAT_HOME/conf/catalina.properties} for property {@code common.loader}
* the following {@code $IGNITE_HOME/*.jar,$IGNITE_HOME/libs/*.jar}
* (replace {@code $IGNITE_HOME} with absolute path).
* </li>
* <li>Configure this startup in {@code $TOMCAT_HOME/conf/web.xml}
* <pre name="code" class="xml">
* <listener>
* <listener-class>org.apache.ignite.startup.servlet.ServletContextListenerStartup</listener-class>
* </listener>
*
* <context-param>
* <param-name>IgniteConfigurationFilePath</param-name>
* <param-value>config/default-config.xml</param-value>
* </context-param>
* </pre>
* </li>
* </ol>
* </li>
* <li>
* Ignite started from WAR-file and Ignite instance is accessible only to that web application.
* Difference with approach described above is that {@code web.xml} file and all libraries should
* be added in WAR file without changes in Tomcat configuration files.
* </li>
* </ul>
*/
public class ServletContextListenerStartup implements ServletContextListener {
/** Configuration file path parameter name. */
public static final String IGNITE_CFG_FILE_PATH_PARAM = "IgniteConfigurationFilePath";
/** Names of started Ignite instances. */
private final Collection<String> igniteInstanceNames = new ArrayList<>();
/** {@inheritDoc} */
@Override public void contextInitialized(ServletContextEvent evt) {
ServletContext ctx = evt.getServletContext();
String cfgFile = ctx.getInitParameter(IGNITE_CFG_FILE_PATH_PARAM);
Collection<IgniteConfiguration> cfgs;
GridSpringResourceContext rsrcCtx = null;
if (cfgFile != null) {
URL cfgUrl = null;
try {
cfgUrl = evt.getServletContext().getResource("/META-INF/" + cfgFile);
}
catch (MalformedURLException ignored) {
// Ignore, we still need to try with IGNITE_HOME.
}
if (cfgUrl == null)
// Try with IGNITE_HOME and with context class loader.
cfgUrl = U.resolveIgniteUrl(cfgFile);
if (cfgUrl == null)
throw new IgniteException("Failed to find Spring configuration file (path provided should be " +
"either absolute, relative to IGNITE_HOME, or relative to META-INF folder): " + cfgFile);
IgniteBiTuple<Collection<IgniteConfiguration>, ? extends GridSpringResourceContext> t;
try {
t = IgnitionEx.loadConfigurations(cfgUrl);
}
catch (IgniteCheckedException e) {
throw new IgniteException("Failed to load Ignite configuration.", e);
}
cfgs = t.get1();
rsrcCtx = t.get2();
if (cfgs.isEmpty())
throw new IgniteException("Can't find grid factory configuration in: " + cfgUrl);
}
else
cfgs = Collections.<IgniteConfiguration>singleton(new IgniteConfiguration());
try {
assert !cfgs.isEmpty();
for (IgniteConfiguration cfg : cfgs) {
assert cfg != null;
Ignite ignite;
synchronized (ServletContextListenerStartup.class) {
try {
ignite = G.ignite(cfg.getIgniteInstanceName());
}
catch (IgniteIllegalStateException ignored) {
ignite = IgnitionEx.start(new IgniteConfiguration(cfg), rsrcCtx);
}
}
// Check if grid is not null - started properly.
if (ignite != null)
igniteInstanceNames.add(ignite.name());
}
}
catch (IgniteCheckedException e) {
// Stop started grids only.
for (String name : igniteInstanceNames)
G.stop(name, true);
throw new IgniteException("Failed to start Ignite.", e);
}
}
/** {@inheritDoc} */
@Override public void contextDestroyed(ServletContextEvent evt) {
// Stop started grids only.
for (String name: igniteInstanceNames)
G.stop(name, true);
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(ServletContextListenerStartup.class, this);
}
}