blob: 6ade5bc21b695f912e9cdd64523f7cf7bbcfd474 [file] [log] [blame]
/*
* 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.jackrabbit.j2ee;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.felix.connect.launch.PojoServiceRegistry;
import org.apache.felix.webconsole.WebConsoleSecurityProvider;
import org.apache.jackrabbit.api.JackrabbitRepository;
import org.apache.jackrabbit.commons.repository.RepositoryFactory;
import org.apache.jackrabbit.oak.run.osgi.OakOSGiRepositoryFactory;
import org.apache.jackrabbit.oak.run.osgi.ServiceRegistryProvider;
import org.apache.jackrabbit.rmi.server.RemoteAdapterFactory;
import org.apache.jackrabbit.rmi.server.ServerAdapterFactory;
import org.apache.jackrabbit.servlet.AbstractRepositoryServlet;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.rmi.AlreadyBoundException;
import java.rmi.Naming;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.RMIServerSocketFactory;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* The RepositoryStartupServlet starts a jackrabbit repository and registers it
* to the JNDI environment and optional to the RMI registry.
* <p id="registerAlgo">
* <b>Registration with RMI</b>
* <p>
* Upon successfull creation of the repository in the {@link #init()} method,
* the repository is registered with an RMI registry if the web application is
* so configured. To register with RMI, the following web application
* <code>init-params</code> are considered: <code>rmi-port</code> designating
* the port on which the RMI registry is listening, <code>rmi-host</code>
* designating the interface on the local host on which the RMI registry is
* active, <code>repository-name</code> designating the name to which the
* repository is to be bound in the registry, and <code>rmi-uri</code>
* designating an RMI URI complete with host, optional port and name to which
* the object is bound.
* <p>
* If the <code>rmi-uri</code> parameter is configured with a non-empty value,
* the <code>rmi-port</code> and <code>rmi-host</code> parameters are ignored.
* The <code>repository-name</code> parameter is only considered if a non-empty
* <code>rmi-uri</code> parameter is configured if the latter does not contain
* a name to which to bind the repository.
* <p>
* This is the algorithm used to find out the host, port and name for RMI
* registration:
* <ol>
* <li>If neither a <code>rmi-uri</code> nor a <code>rmi-host</code> nor a
* <code>rmi-port</code> parameter is configured, the repository is not
* registered with any RMI registry.
* <li>If a non-empty <code>rmi-uri</code> parameter is configured extract the
* host name (or IP address), port number and name to bind to from the
* URI. If the URI is not valid, host defaults to <code>0.0.0.0</code>
* meaning all interfaces on the local host, port defaults to the RMI
* default port (<code>1099</code>) and the name defaults to the value
* of the <code>repository-name</code> parameter.
* <li>If a non-empty <code>rmi-uri</code> is not configured, the host is taken
* from the <code>rmi-host</code> parameter, the port from the
* <code>rmi-port</code> parameter and the name to bind the repository to
* from the <code>repository-name</code> parameter. If the
* <code>rmi-host</code> parameter is empty or not configured, the host
* defaults to <code>0.0.0.0</code> meaning all interfaces on the local
* host. If the <code>rmi-port</code> parameter is empty, not configured,
* zero or a negative value, the default port for the RMI registry
* (<code>1099</code>) is used.
* </ol>
* <p>
* After finding the host and port of the registry, the RMI registry itself
* is acquired. It is assumed, that host and port primarily designate an RMI
* registry, which should be active on the local host but has not been started
* yet. In this case, the <code>LocateRegistry.createRegistry</code> method is
* called to create a registry on the local host listening on the host and port
* configured. If creation fails, the <code>LocateRegistry.getRegistry</code>
* method is called to get a remote instance of the registry. Note, that
* <code>getRegistry</code> does not create an actual registry on the given
* host/port nor does it check, whether an RMI registry is active.
* <p>
* When the registry has been retrieved, either by creation or by just creating
* a remote instance, the repository is bound to the configured name in the
* registry.
* <p>
* Possible causes for registration failures include:
* <ul>
* <li>The web application is not configured to register with an RMI registry at
* all.
* <li>The registry is expected to be running on a remote host but does not.
* <li>The registry is expected to be running on the local host but cannot be
* accessed. Reasons include another application which does not act as an
* RMI registry is running on the configured port and thus blocks creation
* of a new RMI registry.
* <li>An object may already be bound to the same name as is configured to be
* used for the repository.
* </ul>
* <p>
* <b>Note:</b> if a <code>bootstrap-config</code> init parameter is specified the
* servlet tries to read the respective resource, either as context resource or
* as file. The properties specified in this file override the init params
* specified in the <code>web.xml</code>.
* <p>
* <p>
* <b>Setup Wizard Functionality</b><br>
* When using the first time, the configuraition can miss the relevant
* repository parameters in the web.xml. if so, it must contain a
* <code>bootstrap-config</code> parameter that referrs to a propertiy file.
* This file must exsit for proper working. If not, the repository is not
* started.<br>
* If the servlet is not configured correctly and accessed via http, it will
* provide a simple wizard for the first time configuration. It propmpts for
* a new (or existing) repository home and will copy the templates of the
* repository.xml and bootstrap.properties to the respective location.
*/
public class RepositoryStartupServlet extends AbstractRepositoryServlet {
/**
* the default logger
*/
private static final Logger log = LoggerFactory.getLogger(RepositoryStartupServlet.class);
/**
* the context attribute name foe 'this' instance.
*/
private final static String CTX_PARAM_THIS = "repository.startup.servet";
/**
* initial param name for the bootstrap config location
*/
public final static String INIT_PARAM_BOOTSTRAP_CONFIG = "bootstrap-config";
/**
* Ugly hack to override the bootstrap file location in the test cases
*/
static String bootstrapOverride = null;
/**
* the registered repository
*/
private Repository repository;
/**
* the jndi context; created based on configuration
*/
private InitialContext jndiContext;
private Registry rmiRegistry = null;
/**
* Keeps a strong reference to the server side RMI repository instance to
* prevent the RMI distributed Garbage Collector from collecting the
* instance making the repository unaccessible though it should still be.
* This field is only set to a non-<code>null</code> value, if registration
* of the repository to an RMI registry succeeded in the
* {@link #registerRMI()} method.
*
* @see #registerRMI()
* @see #unregisterRMI()
*/
private Remote rmiRepository;
/**
* the file to the bootstrap config
*/
private File bootstrapConfigFile;
/**
* The bootstrap configuration
*/
private BootstrapConfig config;
/**
* Initializes the servlet.<br>
* Please note that only one repository startup servlet may exist per
* webapp. it registers itself as context attribute and acts as singleton.
*
* @throws ServletException if a same servlet is already registered or of
* another initialization error occurs.
*/
public void init() throws ServletException {
// check if servlet is defined twice
if (getServletContext().getAttribute(CTX_PARAM_THIS) != null) {
throw new ServletException("Only one repository startup servlet allowed per web-app.");
}
getServletContext().setAttribute(CTX_PARAM_THIS, this);
startup();
}
/**
* Returns an instance of this servlet. Please note, that only 1
* repository startup servlet can exist per webapp.
*
* @param context the servlet context
* @return this servlet
*/
public static RepositoryStartupServlet getInstance(ServletContext context) {
return (RepositoryStartupServlet) context.getAttribute(CTX_PARAM_THIS);
}
/**
* Configures and starts the repository. It registers it then to the
* RMI registry and bind is to the JNDI context if so configured.
* @throws ServletException if an error occurs.
*/
public void startup() throws ServletException {
if (repository != null) {
log.error("Startup: Repository already running.");
throw new ServletException("Repository already running.");
}
log.info("RepositoryStartupServlet initializing...");
try {
if (configure()) {
initRepository();
registerRMI();
registerJNDI();
}
//Once repository is initialized get its instances bounded to ServletContext
//via super class init
if (repository != null){
super.init();
}
log.info("RepositoryStartupServlet initialized.");
} catch (ServletException e) {
// shutdown repository
shutdownRepository();
log.error("RepositoryStartupServlet initializing failed: " + e, e);
}
}
/**
* Does a shutdown of the repository and deregisters it from the RMI
* registry and unbinds if from the JNDI context if so configured.
*/
public void shutdown() {
if (repository == null) {
log.info("Shutdown: Repository already stopped.");
} else {
log.info("RepositoryStartupServlet shutting down...");
unregisterOSGi();
shutdownRepository();
unregisterRMI();
unregisterJNDI();
log.info("RepositoryStartupServlet shut down.");
}
}
/**
* Restarts the repository.
* @throws ServletException if an error occurs.
* @see #shutdown()
* @see #startup()
*/
public void restart() throws ServletException {
if (repository != null) {
shutdown();
}
startup();
}
/**
* destroy the servlet
*/
public void destroy() {
super.destroy();
shutdown();
}
/**
* Returns the started repository or <code>null</code> if not started
* yet.
* @return the JCR repository
*/
public Repository getRepository() {
return repository;
}
/**
* Returns a repository factory that returns the repository if available
* or throws an exception if not.
*
* @return repository factory
*/
public RepositoryFactory getRepositoryFactory() {
return new RepositoryFactory() {
public Repository getRepository() throws RepositoryException {
Repository r = repository;
if (r != null) {
return repository;
} else {
throw new RepositoryException("Repository not available");
}
}
};
}
/**
* Reads the configuration and initializes the {@link #config} field if
* successful.
* @throws ServletException if an error occurs.
*/
private boolean configure() throws ServletException {
// check if there is a loadable bootstrap config
Properties bootstrapProps = new Properties();
String bstrp = bootstrapOverride;
if (bstrp == null) {
bstrp = getServletConfig().getInitParameter(INIT_PARAM_BOOTSTRAP_CONFIG);
}
if (bstrp != null) {
// check if it's a web-resource
InputStream in = getServletContext().getResourceAsStream(bstrp);
if (in == null) {
// check if it's a file
bootstrapConfigFile = new File(bstrp);
if (bootstrapConfigFile.canRead()) {
try {
in = new FileInputStream(bootstrapConfigFile);
} catch (FileNotFoundException e) {
throw new ServletExceptionWithCause(
"Bootstrap configuration not found: " + bstrp, e);
}
}
}
if (in != null) {
try {
bootstrapProps.load(in);
} catch (IOException e) {
throw new ServletException(
"Bootstrap configuration failure: " + bstrp, e);
} finally {
try {
in.close();
} catch (IOException e) {
// ignore
}
}
}
}
// read bootstrap config
config = new BootstrapConfig();
config.init(getServletConfig());
config.init(bootstrapProps);
config.validate();
if (!config.isValid()
|| config.getRepositoryHome() == null) {
if (bstrp == null) {
log.error("Repository startup configuration is not valid.");
} else {
log.error("Repository startup configuration is not valid but a bootstrap config is specified.");
log.error("Either create the {} file or", bstrp);
log.error("use the '/config/index.jsp' for easy configuration.");
}
return false;
} else {
config.logInfos();
return true;
}
}
/**
* Creates a new Repository based on the configuration and initializes the
* {@link #repository} field if successful.
*
* @throws ServletException if an error occurs
*/
private void initRepository() throws ServletException {
// get repository config
File repHome;
try {
repHome = new File(config.getRepositoryHome()).getCanonicalFile();
} catch (IOException e) {
throw new ServletExceptionWithCause(
"Repository configuration failure: " + config.getRepositoryHome(), e);
}
String repConfig = config.getRepositoryConfig();
if (repConfig != null) {
File configJson = new File(repHome, repConfig);
if (!configJson.exists()){
InputStream in = getServletContext().getResourceAsStream(repConfig);
if (in == null){
throw new ServletException("No config file found in classpath " + repConfig);
}
OutputStream os = null;
try {
os = FileUtils.openOutputStream(configJson);
IOUtils.copy(in, os);
} catch (IOException e1) {
throw new ServletExceptionWithCause(
"Error copying the repository config json", e1);
} finally {
IOUtils.closeQuietly(os);
IOUtils.closeQuietly(in);
}
}
try {
repository = createRepository(configJson, repHome);
if (getBootstrapConfig().isRepositoryCreateDefaultIndexes()){
new IndexInitializer(repository).initialize();
}
} catch (RepositoryException e) {
throw new ServletExceptionWithCause("Error while creating repository", e);
}
}
}
/**
* Shuts down the repository. If the repository is an instanceof
* {@link JackrabbitRepository} it's {@link JackrabbitRepository#shutdown()}
* method is called. in any case, the {@link #repository} field is
* <code>nulled</code>.
*/
private void shutdownRepository() {
if (repository instanceof JackrabbitRepository) {
((JackrabbitRepository) repository).shutdown();
}
repository = null;
}
/**
* Creates the repository instance for the given config and homedir.
* Subclasses may override this method of providing own implementations of
* a {@link Repository}.
*
* @param configJson input source of the repository config
* @param homedir the repository home directory
* @return a new jcr repository.
* @throws RepositoryException if an error during creation occurs.
*/
protected Repository createRepository(File configJson, File homedir)
throws RepositoryException {
Map<String,Object> config = new HashMap<String, Object>();
config.put(OakOSGiRepositoryFactory.REPOSITORY_HOME, homedir.getAbsolutePath());
config.put(OakOSGiRepositoryFactory.REPOSITORY_CONFIG_FILE, configJson.getAbsolutePath());
config.put(OakOSGiRepositoryFactory.REPOSITORY_BUNDLE_FILTER, getBootstrapConfig().getBundleFilter());
config.put(OakOSGiRepositoryFactory.REPOSITORY_SHUTDOWN_ON_TIMEOUT, getBootstrapConfig().isShutdownOnTimeout());
config.put(OakOSGiRepositoryFactory.REPOSITORY_TIMEOUT_IN_SECS, getBootstrapConfig().getStartupTimeout());
configureActivator(config);
//TODO oak-jcr also provides a dummy RepositoryFactory. Hence this
//cannot be used
//return JcrUtils.getRepository(config);
Repository repository = new OakOSGiRepositoryFactory().getRepository(config);
configWebConsoleSecurityProvider(repository);
return repository;
}
private void configWebConsoleSecurityProvider(Repository repository) {
if (repository instanceof ServiceRegistryProvider){
PojoServiceRegistry registry = ((ServiceRegistryProvider) repository).getServiceRegistry();
registry.registerService(WebConsoleSecurityProvider.class.getName(),
new RepositorySecurityProvider(repository), null);
}
}
private void configureActivator(Map<String, Object> config) {
try{
config.put(BundleActivator.class.getName(), new BundleActivator() {
@Override
public void start(BundleContext bundleContext) throws Exception {
registerOSGi(bundleContext);
}
@Override
public void stop(BundleContext bundleContext) throws Exception {
unregisterOSGi();
}
});
} catch (Throwable t){
log.warn("OSGi support not present", t);
}
}
/**
* Binds the repository to the JNDI context
* @throws ServletException if an error occurs.
*/
private void registerJNDI() throws ServletException {
JNDIConfig jc = config.getJndiConfig();
if (jc.isValid() && jc.enabled()) {
try {
jndiContext = new InitialContext(jc.getJndiEnv());
jndiContext.bind(jc.getJndiName(), repository);
log.info("Repository bound to JNDI with name: " + jc.getJndiName());
} catch (NamingException e) {
throw new ServletExceptionWithCause(
"Unable to bind repository using JNDI: " + jc.getJndiName(), e);
}
}
}
/**
* Unbinds the repository from the JNDI context.
*/
private void unregisterJNDI() {
if (jndiContext != null) {
try {
jndiContext.unbind(config.getJndiConfig().getJndiName());
} catch (NamingException e) {
log("Error while unbinding repository from JNDI: " + e);
}
}
}
/**
* Registers the repository to an RMI registry configured in the web
* application. See <a href="#registerAlgo">Registration with RMI</a> in the
* class documentation for a description of the algorithms used to register
* the repository with an RMI registry.
* @throws ServletException if an error occurs.
*/
private void registerRMI() {
RMIConfig rc = config.getRmiConfig();
if (!rc.isValid() || !rc.enabled()) {
return;
}
// try to create remote repository
Remote remote;
try {
Class<?> clazz = Class.forName(getRemoteFactoryDelegaterClass());
RemoteFactoryDelegater rmf = (RemoteFactoryDelegater) clazz.newInstance();
remote = rmf.createRemoteRepository(repository);
} catch (RemoteException e) {
log.warn("Unable to create RMI repository.", e);
return;
} catch (Throwable t) {
log.warn("Unable to create RMI repository."
+ " The jcr-rmi jar might be missing.", t);
return;
}
try {
System.setProperty("java.rmi.server.useCodebaseOnly", "true");
Registry reg = null;
// first try to create the registry, which will fail if another
// application is already running on the configured host/port
// or if the rmiHost is not local
try {
// find the server socket factory: use the default if the
// rmiHost is not configured
RMIServerSocketFactory sf;
if (rc.getRmiHost().length() > 0) {
log.debug("Creating RMIServerSocketFactory for host " + rc.getRmiHost());
InetAddress hostAddress = InetAddress.getByName(rc.getRmiHost());
sf = getRMIServerSocketFactory(hostAddress);
} else {
// have the RMI implementation decide which factory is the
// default actually
log.debug("Using default RMIServerSocketFactory");
sf = null;
}
// create a registry using the default client socket factory
// and the server socket factory retrieved above. This also
// binds to the server socket to the rmiHost:rmiPort.
reg = LocateRegistry.createRegistry(rc.rmiPort(), null, sf);
rmiRegistry = reg;
} catch (UnknownHostException uhe) {
// thrown if the rmiHost cannot be resolved into an IP-Address
// by getRMIServerSocketFactory
log.info("Cannot create Registry", uhe);
} catch (RemoteException e) {
// thrown by createRegistry if binding to the rmiHost:rmiPort
// fails, for example due to rmiHost being remote or another
// application already being bound to the port
log.info("Cannot create Registry", e);
}
// if creation of the registry failed, we try to access an
// potentially active registry. We do not check yet, whether the
// registry is actually accessible.
if (reg == null) {
log.debug("Trying to access existing registry at " + rc.getRmiHost()
+ ":" + rc.getRmiPort());
try {
reg = LocateRegistry.getRegistry(rc.getRmiHost(), rc.rmiPort());
} catch (RemoteException re) {
log.warn("Cannot create the reference to the registry at "
+ rc.getRmiHost() + ":" + rc.getRmiPort(), re);
}
}
// if we finally have a registry, register the repository with the
// rmiName
if (reg != null) {
log.debug("Registering repository as " + rc.getRmiName()
+ " to registry " + reg);
reg.bind(rc.getRmiName(), remote);
// when successfull, keep references
this.rmiRepository = remote;
log.info("Repository bound via RMI with name: " + rc.getRmiUri());
} else {
log.info("RMI registry missing, cannot bind repository via RMI");
}
} catch (RemoteException e) {
log.warn("Unable to bind repository via RMI: " + rc.getRmiUri(), e);
} catch (AlreadyBoundException e) {
log.warn("Unable to bind repository via RMI: " + rc.getRmiUri(), e);
}
}
/**
* Unregisters the repository from the RMI registry, if it has previously
* been registered.
*/
private void unregisterRMI() {
if (rmiRepository != null) {
// Forcibly unexport the repository;
try {
UnicastRemoteObject.unexportObject(rmiRepository, true);
} catch (NoSuchObjectException e) {
log.warn("Odd, the RMI repository was not exported", e);
}
// drop strong reference to remote repository
rmiRepository = null;
// unregister repository
try {
Naming.unbind(config.getRmiConfig().getRmiUri());
} catch (Exception e) {
log("Error while unbinding repository from JNDI: " + e);
}
}
if (rmiRegistry != null) {
try {
UnicastRemoteObject.unexportObject(rmiRegistry, true);
} catch (NoSuchObjectException e) {
log.warn("Odd, the RMI registry was not exported", e);
}
rmiRegistry = null;
}
}
/**
* Set the BundleContext reference with ServletContext. This is then used by
* Felix Proxy Servlet. Kept the type as object to allow logic to work in
* absence of OSGi classes also.
* @param bundleContext
*/
private void registerOSGi(Object bundleContext) {
getServletContext().setAttribute("org.osgi.framework.BundleContext", bundleContext);
}
private void unregisterOSGi() {
getServletContext().removeAttribute("org.osgi.framework.BundleContext");
}
/**
* Returns the config that was used to bootstrap this servlet.
* @return the bootstrap config or <code>null</code>.
*/
public BootstrapConfig getBootstrapConfig() {
return config;
}
/**
* Return the fully qualified name of the class providing the remote
* repository. The class whose name is returned must implement the
* {@link RemoteFactoryDelegater} interface.
* <p>
* Subclasses may override this method for providing a name of a own
* implementation.
*
* @return getClass().getName() + "$RMIRemoteFactoryDelegater"
*/
protected String getRemoteFactoryDelegaterClass() {
return getClass().getName() + "$RMIRemoteFactoryDelegater";
}
/**
* Returns an <code>RMIServerSocketFactory</code> used to create the server
* socket for a locally created RMI registry.
* <p>
* This implementation returns a new instance of a simple
* <code>RMIServerSocketFactory</code> which just creates instances of
* the <code>java.net.ServerSocket</code> class bound to the given
* <code>hostAddress</code>. Implementations may overwrite this method to
* provide factory instances, which provide more elaborate server socket
* creation, such as SSL server sockets.
*
* @param hostAddress The <code>InetAddress</code> instance representing the
* the interface on the local host to which the server sockets are
* bound.
* @return A new instance of a simple <code>RMIServerSocketFactory</code>
* creating <code>java.net.ServerSocket</code> instances bound to
* the <code>rmiHost</code>.
*/
protected RMIServerSocketFactory getRMIServerSocketFactory(
final InetAddress hostAddress) {
return new RMIServerSocketFactory() {
public ServerSocket createServerSocket(int port) throws IOException {
return new ServerSocket(port, -1, hostAddress);
}
};
}
/**
* optional class for RMI, will only be used, if RMI server is present
*/
protected static abstract class RemoteFactoryDelegater {
public abstract Remote createRemoteRepository(Repository repository)
throws RemoteException;
}
/**
* optional class for RMI, will only be used, if RMI server is present
*/
protected static class RMIRemoteFactoryDelegater extends RemoteFactoryDelegater {
private static final RemoteAdapterFactory FACTORY =
new ServerAdapterFactory();
public Remote createRemoteRepository(Repository repository)
throws RemoteException {
return FACTORY.getRemoteRepository(repository);
}
}
//-------------------------------------------------< Installer Routines >---
/**
* {@inheritDoc}
*/
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (repository == null) {
redirect(req, resp, "/bootstrap/missing.jsp");
} else {
redirect(req, resp, "/bootstrap/running.jsp");
}
}
/**
* {@inheritDoc}
*/
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
if (repository != null) {
redirect(req, resp, "/bootstrap/reconfigure.jsp");
} else {
int rc = new Installer(bootstrapConfigFile,
getServletContext()).installRepository(req);
switch (rc) {
case Installer.C_INSTALL_OK:
// restart rep
restart();
if (repository == null) {
redirect(req, resp, "/bootstrap/error.jsp");
} else {
redirect(req, resp, "/bootstrap/success.jsp");
}
break;
case Installer.C_INVALID_INPUT:
redirect(req, resp, "/bootstrap/missing.jsp");
break;
case Installer.C_CONFIG_EXISTS:
case Installer.C_BOOTSTRAP_EXISTS:
case Installer.C_HOME_EXISTS:
redirect(req, resp, "/bootstrap/exists.jsp");
break;
case Installer. C_HOME_MISSING:
case Installer.C_CONFIG_MISSING:
redirect(req, resp, "/bootstrap/notexists.jsp");
break;
case Installer.C_INSTALL_ERROR:
redirect(req, resp, "/bootstrap/error.jsp");
break;
}
}
}
/**
* Helper function to send a redirect response respecting the context path.
*
* @param req the request
* @param resp the response
* @param loc the location for the redirect
* @throws ServletException if an servlet error occurs.
* @throws IOException if an I/O error occurs.
*/
private void redirect(HttpServletRequest req,
HttpServletResponse resp, String loc)
throws ServletException, IOException {
String cp = req.getContextPath();
if (cp == null || cp.equals("/")) {
cp = "";
}
resp.sendRedirect(cp + loc);
}
}