From commits-return-3523-apmail-jackrabbit-commits-archive=jackrabbit.apache.org@jackrabbit.apache.org Fri Jan 12 09:39:52 2007 Return-Path: Delivered-To: apmail-jackrabbit-commits-archive@www.apache.org Received: (qmail 75563 invoked from network); 12 Jan 2007 09:39:49 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 12 Jan 2007 09:39:49 -0000 Received: (qmail 89633 invoked by uid 500); 12 Jan 2007 09:39:55 -0000 Delivered-To: apmail-jackrabbit-commits-archive@jackrabbit.apache.org Received: (qmail 89608 invoked by uid 500); 12 Jan 2007 09:39:55 -0000 Mailing-List: contact commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@jackrabbit.apache.org Delivered-To: mailing list commits@jackrabbit.apache.org Received: (qmail 89599 invoked by uid 99); 12 Jan 2007 09:39:55 -0000 Received: from herse.apache.org (HELO herse.apache.org) (140.211.11.133) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 12 Jan 2007 01:39:55 -0800 X-ASF-Spam-Status: No, hits=-8.6 required=10.0 tests=ALL_TRUSTED,INFO_TLD,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 12 Jan 2007 01:39:45 -0800 Received: by eris.apache.org (Postfix, from userid 65534) id 978711A981A; Fri, 12 Jan 2007 01:38:44 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r495531 [1/2] - in /jackrabbit/trunk/jackrabbit-webapp/src/main: java/org/apache/jackrabbit/j2ee/ webapp/ webapp/WEB-INF/ webapp/WEB-INF/bootstrap/ webapp/WEB-INF/repository/ webapp/WEB-INF/templates/ webapp/bootstrap/ Date: Fri, 12 Jan 2007 09:38:43 -0000 To: commits@jackrabbit.apache.org From: tripod@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20070112093844.978711A981A@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: tripod Date: Fri Jan 12 01:38:40 2007 New Revision: 495531 URL: http://svn.apache.org/viewvc?view=rev&rev=495531 Log: JCR-697: .war distribution should be configurable, prompting you to setup JNDI with the Repository Home and Config locations. Added: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/AbstractConfig.java (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/BootstrapConfig.java jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/bootstrap/ jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/ jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/repository.xml - copied, changed from r494040, jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/repository/repository.xml jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/ jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/error.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/exists.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/missing.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/notexists.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/reconfigure.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/running.html (with props) jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/bootstrap/success.html (with props) Removed: jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/repository/ Modified: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryAccessServlet.java jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/web.xml jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/index.jsp Added: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/AbstractConfig.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/AbstractConfig.java?view=auto&rev=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/AbstractConfig.java (added) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/AbstractConfig.java Fri Jan 12 01:38:40 2007 @@ -0,0 +1,110 @@ +/* + * 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.collections.BeanMap; +import org.apache.jackrabbit.util.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +/** + * Abstract configuration class that is based on a bean map. + */ +public abstract class AbstractConfig { + + /** + * default logger + */ + private static final Logger log = LoggerFactory.getLogger(AbstractConfig.class); + + protected boolean valid; + + private BeanMap map = new BeanMap(this); + + /** + * Initializes the config with values from the given properties + * @param props the config properties + */ + public void init(Properties props) throws ServletException { + Iterator iter = props.keySet().iterator(); + while (iter.hasNext()) { + String name = (String) iter.next(); + String mapName = toMapName(name, '.'); + try { + if (map.containsKey(mapName)) { + map.put(mapName, props.getProperty(name)); + } + } catch (Exception e) { + log.error("Error in configuration: {}", e.toString()); + throw new ServletException("Error in configuration: " + e.toString()); + } + } + } + + public void init(ServletConfig ctx) throws ServletException { + Enumeration enum = ctx.getInitParameterNames(); + while (enum.hasMoreElements()) { + String name = (String) enum.nextElement(); + String mapName = toMapName(name, '-'); + try { + if (map.containsKey(mapName)) { + map.put(mapName, ctx.getInitParameter(name)); + } + } catch (Exception e) { + log.error("Error in configuration: {}", e.toString()); + throw new ServletException("Error in configuration: " + e.toString()); + } + } + } + + public String toMapName(String name, char delim) { + StringBuffer ret = new StringBuffer(); + String[] elems = Text.explode(name, delim); + ret.append(elems[0]); + for (int i=1; i + * +-------------------+-------------------+ + * | Property Name | Init-Param Name | + * +-------------------+-------------------+ + * | repository.home | repository-home | + * | repository.config | repository-config | + * | repository.name | repository-name | + * +-------------------+-------------------+ + * + */ +public class BootstrapConfig extends AbstractConfig { + + private String repositoryHome; + + private String repositoryConfig; + + private String repositoryName; + + private JNDIConfig jndiConfig = new JNDIConfig(this); + + private RMIConfig rmiConfig = new RMIConfig(this); + + public void init(Properties props) throws ServletException { + super.init(props); + jndiConfig.init(props); + rmiConfig.init(props); + } + + public void init(ServletConfig ctx) throws ServletException { + super.init(ctx); + jndiConfig.init(ctx); + rmiConfig.init(ctx); + } + + public String getRepositoryHome() { + return repositoryHome; + } + + public void setRepositoryHome(String repositoryHome) { + this.repositoryHome = repositoryHome; + } + + public String getRepositoryConfig() { + return repositoryConfig; + } + + public void setRepositoryConfig(String repositoryConfig) { + this.repositoryConfig = repositoryConfig; + } + + public String getRepositoryName() { + return repositoryName; + } + + public void setRepositoryName(String repositoryName) { + this.repositoryName = repositoryName; + } + + public JNDIConfig getJndiConfig() { + return jndiConfig; + } + + public RMIConfig getRmiConfig() { + return rmiConfig; + } + + public void validate() { + valid = repositoryName != null; + jndiConfig.validate(); + rmiConfig.validate(); + } + + + public void logInfos() { + super.logInfos(); + if (jndiConfig.isValid()) { + jndiConfig.logInfos(); + } + if (rmiConfig.isValid()) { + rmiConfig.logInfos(); + } + } +} \ No newline at end of file Added: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java?view=auto&rev=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java (added) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java Fri Jan 12 01:38:40 2007 @@ -0,0 +1,214 @@ +/* + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Properties; + +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +/** + * Provides very basic installation capabilities. + */ +public class Installer { + + /** + * the default logger + */ + private static final Logger log = LoggerFactory.getLogger(Installer.class); + + /** + * Return code for installation succeeded + */ + public static final int C_INSTALL_OK = 0; + + /** + * Return code for invalid input parameter + */ + public static final int C_INVALID_INPUT = 1; + + /** + * Return code for repository home already exists + */ + public static final int C_HOME_EXISTS = 2; + + /** + * Return code for repository home is missing + */ + public static final int C_HOME_MISSING = 3; + + /** + * Return code for repository config already exists + */ + public static final int C_CONFIG_EXISTS = 4; + + /** + * Return code for repository config is missing + */ + public static final int C_CONFIG_MISSING = 5; + + /** + * Return code for bootstrap config already exists + */ + public static final int C_BOOTSTRAP_EXISTS = 6; + + /** + * Return code for a general install error + */ + public static final int C_INSTALL_ERROR = 7; + + /** + * place to store the config file + */ + private final File bootstrapConfigFile; + + /** + * the servlet context + */ + private final ServletContext context; + + /** + * the place for the repository config template + * todo: to be configured + */ + private final String configTemplate = "/WEB-INF/templates/repository.xml"; + + /** + * the place for the bootstrap properties template + * todo: to be configured + */ + private final String bootstrapTemplate = "/WEB-INF/templates/bootstrap.properties"; + + /** + * Creates a new installer + * @param bootstrapConfigFile the location for the config file + * @param context the servlet context for accessing resources + */ + public Installer(File bootstrapConfigFile, ServletContext context) { + this.bootstrapConfigFile = bootstrapConfigFile; + this.context = context; + } + + /** + * Handles the installation. + * + * @param req the servlet request with the input parameters + * @return the installation return code + * + * @throws ServletException if a servlet error occurs. + * @throws IOException if an I/O error occurs. + */ + public int installRepository(HttpServletRequest req) + throws ServletException, IOException { + String repHome = req.getParameter("repository_home"); + String repXml = req.getParameter("repository_xml"); + String mode = req.getParameter("mode"); + if (repHome == null || mode == null) { + return C_INVALID_INPUT; + } + if (repXml == null || repXml.length() == 0) { + repXml = repHome + "/repository.xml"; + } + File home = new File(repHome); + File config = new File(repXml); + if ("new".equals(mode)) { + if (home.exists()) { + log.error("Trying to install new repository home '{}' but already exists", repHome); + return C_HOME_EXISTS; + } + if (config.exists()) { + log.error("Trying to install new repository config '{}' but already exists", repXml); + return C_CONFIG_EXISTS; + } + log.info("Creating new repository home '{}'", repHome); + home.mkdirs(); + // install repository xml + try { + installRepositoryConfig(config); + } catch (IOException e) { + log.error("Error while installing new repository config '{}': {}", repXml, e.toString()); + return C_BOOTSTRAP_EXISTS; + } + } else { + if (!home.exists()) { + log.error("Trying to use exisintg repository home '{}' but does not exists", repHome); + return C_HOME_MISSING; + } + if (!config.exists()) { + log.error("Trying to use existing repository config '{}' but does not exists", repXml); + return C_CONFIG_MISSING; + } + } + // install bootstrap.properties + try { + installBootstrap(bootstrapConfigFile, repHome, repXml); + } catch (IOException e) { + log.error("Error while installing '{}': {}", bootstrapConfigFile.getPath(), e.toString()); + return C_INSTALL_ERROR; + } + return C_INSTALL_OK; + } + + /** + * Installs the repository config file from the template + * @param dest the destination location + * @throws IOException if an I/O error occurs. + */ + private void installRepositoryConfig(File dest) throws IOException { + log.info("Creating new repository config: {}", dest.getPath()); + InputStream in = context.getResourceAsStream(configTemplate); + OutputStream out = new FileOutputStream(dest); + byte[] buffer = new byte[8192]; + int read; + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + in.close(); + out.close(); + } + + /** + * Installs the bootstrap config file from the template + * @param dest the destination location + * @param repHome the repository home location + * @param repXml the repository xml location + * @throws IOException if an I/O error occurs + */ + private void installBootstrap(File dest, String repHome, String repXml) + throws IOException { + log.info("Creating new bootstrap properties: {}", dest.getPath()); + InputStream in = context.getResourceAsStream(bootstrapTemplate); + Properties props = new Properties(); + props.load(in); + props.setProperty("repository.home", repHome); + props.setProperty("repository.config", repXml); + in.close(); + OutputStream out = new FileOutputStream(dest); + props.store(out, "bootstrap properties for the repository startup servlet."); + out.close(); + } + +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/Installer.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url rev Added: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java?view=auto&rev=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java (added) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java Fri Jan 12 01:38:40 2007 @@ -0,0 +1,112 @@ +/* + * 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 java.util.Enumeration; +import java.util.Iterator; +import java.util.Properties; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +/** + * The JNDI config hold information about JNDI connection details. + * + * It supports the following properties and init parameters: + * + * +-------------------+--------------------+ + * | Property Name | Init-Param Name | + * +-------------------+--------------------+ + * | jndi.enable | {provider spec.} | + * | java.naming.* | java.naming.* | + * +-------------------+--------------------+ + * + */ +public class JNDIConfig extends AbstractConfig { + + private boolean jndiEnabled; + + private String jndiName; + + private final BootstrapConfig parentConfig; + + private Properties jndiEnv = new Properties(); + + + public JNDIConfig(BootstrapConfig parentConfig) { + this.parentConfig = parentConfig; + } + + + public String getJndiName() { + return jndiName; + } + + public void setJndiName(String jndiName) { + this.jndiName = jndiName; + } + + public boolean enabled() { + return jndiEnabled; + } + + public String getJndiEnabled() { + return String.valueOf(jndiEnabled); + } + + public void setJndiEnabled(String jndiEnabled) { + this.jndiEnabled = Boolean.valueOf(jndiEnabled).booleanValue(); + } + + public Properties getJndiEnv() { + return jndiEnv; + } + + public void init(Properties props) throws ServletException { + super.init(props); + // add all props whose name starts with 'java.namming.' to the env + Iterator iter = props.keySet().iterator(); + while (iter.hasNext()) { + String name = (String) iter.next(); + if (name.startsWith("java.naming.")) { + jndiEnv.put(name, props.getProperty(name)); + } + } + } + + public void init(ServletConfig ctx) throws ServletException { + super.init(ctx); + // add all params whose name starts with 'java.namming.' to the env + Enumeration enum = ctx.getInitParameterNames(); + while (enum.hasMoreElements()) { + String name = (String) enum.nextElement(); + if (name.startsWith("java.naming.")) { + jndiEnv.put(name, ctx.getInitParameter(name)); + } + } + // enable jndi if url is specified + jndiEnabled = jndiEnv.containsKey("java.naming.provider.url"); + } + + + public void validate() { + if (jndiName == null) { + jndiName = parentConfig.getRepositoryName(); + } + valid = true; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/JNDIConfig.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url rev Added: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java?view=auto&rev=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java (added) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java Fri Jan 12 01:38:40 2007 @@ -0,0 +1,175 @@ +/* + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.URI; +import java.net.URISyntaxException; +import java.rmi.registry.Registry; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; + +/** + * The RMI config hold information about RMI connection details. + * + * It supports the following properties and init parameters: + * + * +-------------------+--------------------+ + * | Property Name | Init-Param Name | + * +-------------------+--------------------+ + * | rmi.enable | {rmi-port sepc.} | + * | rmi.host | rmi-host | + * | rmi.port | rmi-port | + * | rmi.name | {repository name} | + * | rmi.url | rmi-url | + * +-------------------+--------------------+ + * + */ +public class RMIConfig extends AbstractConfig { + + /** + * default logger + */ + private static final Logger log = LoggerFactory.getLogger(RMIConfig.class); + + private boolean rmiEnabled; + + private int rmiPort = -1; + + private String rmiHost; + + private String rmiName; + + private String rmiUri; + + private final BootstrapConfig parentConfig; + + + public RMIConfig(BootstrapConfig parentConfig) { + this.parentConfig = parentConfig; + } + + public void init(ServletConfig ctx) throws ServletException { + super.init(ctx); + // enable rmi if port was set + rmiEnabled = rmiPort >= 0; + } + + public String getRmiName() { + return rmiName; + } + + public void setRmiName(String rmiName) { + this.rmiName = rmiName; + } + + public boolean enabled() { + return rmiEnabled; + } + + public String getRmiEnabled() { + return String.valueOf(rmiEnabled); + } + + public void setRmiEnabled(String rmiEnabled) { + this.rmiEnabled = Boolean.valueOf(rmiEnabled).booleanValue(); + } + + public int rmiPort() { + return rmiPort; + } + + public String getRmiPort() { + return String.valueOf(rmiPort); + } + + public void setRmiPort(String rmiPort) { + this.rmiPort = Integer.decode(rmiPort).intValue(); + } + + public String getRmiHost() { + return rmiHost; + } + + public void setRmiHost(String rmiHost) { + this.rmiHost = rmiHost; + } + + public String getRmiUri() { + return rmiUri; + } + + public void setRmiUri(String rmiUri) { + this.rmiUri = rmiUri; + } + + public void validate() { + if (!rmiEnabled) { + return; + } + + if (rmiUri != null && rmiUri.length() > 0) { + // URI takes precedences, so check whether the configuration has to + // be set from the URI + try { + URI uri = new URI(rmiUri); + + // extract values from the URI, check later + rmiHost = uri.getHost(); + rmiPort = uri.getPort(); + rmiName = uri.getPath(); + + } catch (URISyntaxException e) { + log.warn("Cannot parse RMI URI '" + rmiUri + "'.", e); + rmiUri = null; // clear RMI URI use another one + rmiHost = null; // use default host, ignore rmi-host param + } + + // cut of leading slash from name if defined at all + if (rmiName != null && rmiName.startsWith("/")) { + rmiName = rmiName.substring(1); + } + } + + // check RMI port + if (rmiPort == -1 || rmiPort == 0) { + // accept -1 or 0 as a hint to use the default + rmiPort = Registry.REGISTRY_PORT; + } else if (rmiPort < -1 || rmiPort > 0xFFFF) { + // emit a warning if out of range, use defualt in this case + log.warn("Invalid port in rmi-port param " + rmiPort + ". using default port."); + rmiPort = Registry.REGISTRY_PORT; + } + + // check host - use an empty name if null (i.e. not configured) + if (rmiHost == null) { + rmiHost = ""; + } + + // check name - use repositoryName if empty or null + if (rmiName == null || rmiName.length() ==0) { + rmiName = parentConfig.getRepositoryName(); + } + + // reconstruct the rmiURI now because values might have been changed + rmiUri = "//" + rmiHost + ":" + rmiPort + "/" + rmiName; + valid = true; + } +} \ No newline at end of file Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RMIConfig.java ------------------------------------------------------------------------------ svn:keywords = author date id revision url rev Modified: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryAccessServlet.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryAccessServlet.java?view=diff&rev=495531&r1=495530&r2=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryAccessServlet.java (original) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryAccessServlet.java Fri Jan 12 01:38:40 2007 @@ -20,21 +20,26 @@ 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.net.MalformedURLException; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.util.Properties; + import javax.jcr.Repository; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; -import java.net.MalformedURLException; -import java.rmi.NotBoundException; -import java.rmi.RemoteException; -import java.util.Enumeration; -import java.util.Properties; /** * This Class implements a servlet that is used as unified mechanism to retrieve - * a jcr repository either through JNID, RMI or JCRWebdavServer. + * a jcr repository either through JNDI or RMI. */ public class RepositoryAccessServlet extends HttpServlet { @@ -44,92 +49,147 @@ private static final Logger log = LoggerFactory.getLogger(RepositoryAccessServlet.class); /** - * the 'repository-name' init parameter + * initial param name for the bootstrap config location */ - public final static String INIT_PARAM_REPOSITORY_NAME = "repository-name"; + public final static String INIT_PARAM_BOOTSTRAP_CONFIG = "bootstrap-config"; /** - * the 'rmi-uri' init parameter + * Context parameter name for 'this' instance. */ - public final static String INIT_PARAM_RMI_URI = "rmi-uri"; + private final static String CTX_PARAM_THIS = "repository.access.servlet"; /** - * the 'missing-auth-mapping' init parameter + * the bootstrap config */ - //public final static String INIT_PARAM_MISSING_AUTH_MAPPING = "missing-auth-mapping"; - - private static final String CTX_ATTR_REPOSITORY = "jcr.repository"; + private BootstrapConfig config; - private static final String CTX_ATTR_REPOSITORY_NAME = "jcr.repository.name"; - - private static final String CTX_ATTR_REPOSITORY_RMI_URI = "jcr.repository.rmiURI"; + /** + * the initialized initial context + */ + private InitialContext jndiContext; - private static final String CTX_ATTR_REPOSITORY_JNDI_CONTEXT = "jcr.repository.jndiContext"; + /** + * the repository + */ + private Repository repository; /** - * Initializes this servlet + * Initializes the servlet.
+ * Please note that only one repository startup servlet may exist per + * webapp. it registers itself as context attribute and acts as singleton. * - * @throws javax.servlet.ServletException + * @throws ServletException if a same servlet is already registered or of + * another initialization error occurs. */ public void init() throws ServletException { - log.info("RepositoryAccessServlet initializing..."); - // fetching the name - String repositoryName = getServletConfig().getInitParameter(INIT_PARAM_REPOSITORY_NAME); - if (repositoryName == null) { - repositoryName = "default"; + // check if servlet is defined twice + if (getServletContext().getAttribute(CTX_PARAM_THIS) != null) { + throw new ServletException("Only one repository access servlet allowed per web-app."); } - getServletContext().setAttribute(CTX_ATTR_REPOSITORY_NAME, repositoryName); - - // fetching the rmiuri - getServletContext().setAttribute(CTX_ATTR_REPOSITORY_RMI_URI, getRMIUri()); - - // setup initial context - getServletContext().setAttribute(CTX_ATTR_REPOSITORY_JNDI_CONTEXT, getInitialContext()); - + getServletContext().setAttribute(CTX_PARAM_THIS, this); log.info("RepositoryAccessServlet initialized."); } - private InitialContext getInitialContext() { - // retrieve JNDI Context environment - try { - Properties env = new Properties(); - Enumeration names = getServletConfig().getInitParameterNames(); - while (names.hasMoreElements()) { - String name = (String) names.nextElement(); - if (name.startsWith("java.naming.")) { - String initParam = getServletConfig().getInitParameter(name); - if (initParam.equals("")) { - log.info(" ignoring empty JNDI init param: " + name); - } else { - env.put(name, initParam); - log.info(" adding property to JNDI environment: " + name + "=" + initParam); + /** + * Returns the instance of this servlet + * @param ctx the servlet context + * @return this servlet + */ + private static RepositoryAccessServlet getInstance(ServletContext ctx) { + return (RepositoryAccessServlet) ctx.getAttribute(CTX_PARAM_THIS); + } + + /** + * Returns the bootstrap config + * @return the bootstrap config + * @throws ServletException if the config is not valid + */ + private BootstrapConfig getConfig() throws ServletException { + if (config == null) { + // check if there is a loadable bootstrap config + Properties bootstrapProps = new Properties(); + String 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 + File file = new File(bstrp); + if (file.canRead()) { + try { + in = new FileInputStream(file); + } catch (FileNotFoundException e) { + log.error("Error while opening bootstrap properties: {}", e.toString()); + throw new ServletException("Error while opening bootstrap properties: " + e.toString()); + } + } + } + if (in != null) { + try { + bootstrapProps.load(in); + } catch (IOException e) { + log.error("Error while loading bootstrap properties: {}", e.toString()); + throw new ServletException("Error while loading bootstrap properties: " + e.toString()); + } finally { + try { + in.close(); + } catch (IOException e) { + // ignore + } } } } - return new InitialContext(env); - } catch (NamingException e) { - log.error("Create initial context: " + e.toString()); - return null; + + // read bootstrap config + BootstrapConfig tmpConfig = new BootstrapConfig(); + tmpConfig.init(getServletConfig()); + tmpConfig.init(bootstrapProps); + tmpConfig.validate(); + if (!tmpConfig.isValid()) { + log.error("Repository acesss configuration is not valid."); + throw new ServletException("Repository access configuration is not valid."); + } + tmpConfig.logInfos(); + config = tmpConfig; } + return config; } - private String getRMIUri() { - // setup repository name - return getServletConfig().getInitParameter(INIT_PARAM_RMI_URI); + /** + * Returns the initial jndi context or null if the jndi access + * is not configured or erroous. + * @return the initial context or null + */ + private InitialContext getInitialContext() { + if (jndiContext == null && config.getJndiConfig().enabled()) { + // retrieve JNDI Context environment + try { + jndiContext = new InitialContext(config.getJndiConfig().getJndiEnv()); + } catch (NamingException e) { + log.error("Create initial context: " + e.toString()); + } + } + return jndiContext; } /** - * tries to retrieve the repository using RMI + * Checks if the repository is available via JNDI and returns it. + * @return the repository or null + * @throws ServletException if this servlet is not properly configured. */ - private static Repository getRepositoryByJNDI(ServletContext ctx) { + private Repository getRepositoryByJNDI() throws ServletException { + BootstrapConfig config = getConfig(); + if (!config.getJndiConfig().isValid() || !config.getJndiConfig().enabled()) { + return null; + } // acquire via JNDI - String repositoryName = (String) ctx.getAttribute(CTX_ATTR_REPOSITORY_NAME); - InitialContext jndiContext = (InitialContext) ctx.getAttribute(CTX_ATTR_REPOSITORY_JNDI_CONTEXT); - if (jndiContext == null) { + String repositoryName = config.getRepositoryName(); + InitialContext ctx = getInitialContext(); + if (ctx == null) { return null; } try { - Repository r = (Repository) jndiContext.lookup(repositoryName); + Repository r = (Repository) ctx.lookup(repositoryName); log.info("Acquired repository via JNDI."); return r; } catch (NamingException e) { @@ -139,18 +199,25 @@ } /** - * tries to retrieve the repository using RMI + * Checks if the repository is available via RMI and returns it. + * @return the repository or null + * @throws ServletException if this servlet is not properly configured. */ - private static Repository getRepositoryByRMI(ServletContext ctx) { + private Repository getRepositoryByRMI() throws ServletException { + BootstrapConfig config = getConfig(); + if (!config.getRmiConfig().isValid() || !config.getRmiConfig().enabled()) { + return null; + } + // acquire via RMI - String rmiURI = (String) ctx.getAttribute(CTX_ATTR_REPOSITORY_RMI_URI); + String rmiURI = config.getRmiConfig().getRmiUri(); if (rmiURI == null) { return null; } log.info(" trying to retrieve repository using rmi. uri=" + rmiURI); ClientFactoryDelegater cfd; try { - Class clazz = Class.forName("org.apache.jackrabbit.j2ee.RMIClientFactoryDelegater"); + Class clazz = Class.forName(getServerFactoryDelegaterClass()); cfd = (ClientFactoryDelegater) clazz.newInstance(); } catch (NoClassDefFoundError e) { log.error("Unable to locate RMI ClientRepositoryFactory. jcr-rmi.jar missing? " + e.toString()); @@ -171,54 +238,75 @@ } /** - * Returns the JSR170 repository + * Return the fully qualified name of the class providing the client + * repository. The class whose name is returned must implement the + * {@link ClientFactoryDelegater} interface. + * + * @return the qfn of the factory class. + */ + protected String getServerFactoryDelegaterClass() { + return getClass().getName() + "$RMIClientFactoryDelegater"; + } + + /** + * Returns the JCR repository * - * @return a jsr170 repository + * @return a JCR repository * @throws IllegalStateException if the repository is not available in the context. */ - public static Repository getRepository(ServletContext ctx) { - Repository repository = (Repository) ctx.getAttribute(CTX_ATTR_REPOSITORY); - if (repository != null) { + public Repository getRepository() { + try { + if (repository == null) { + // try to retrieve via jndi + repository = getRepositoryByJNDI(); + } + if (repository == null) { + // try to get via rmi + repository = getRepositoryByRMI(); + } + if (repository == null) { + throw new ServletException("N/A"); + } return repository; - } else { - repository = getRepositoryByRMI(ctx); - } - // try to retrieve via jndi - if (repository == null) { - repository = getRepositoryByJNDI(ctx); - } - // error - if (repository == null) { + } catch (ServletException e) { log.error("The repository is not available. Check config of 'RepositoryAccessServlet'."); throw new IllegalStateException("The repository is not available."); - } else { - ctx.setAttribute(CTX_ATTR_REPOSITORY, repository); - log.info(repository.getDescriptor(Repository.REP_NAME_DESC) + " v" + repository.getDescriptor(Repository.REP_VERSION_DESC)); - return repository; } } -} -/** - * optional class for RMI, will only be used, if RMI client is present - */ -abstract class ClientFactoryDelegater { + /** + * Returns the JCR repository + * + * @param ctx the servlet context + * @return a JCR repository + * @throws IllegalStateException if the repository is not available in the context. + */ + public static Repository getRepository(ServletContext ctx) { + return getInstance(ctx).getRepository(); + } - public abstract Repository getRepository(String uri) - throws RemoteException, MalformedURLException, NotBoundException; -} + /** + * optional class for RMI, will only be used, if RMI client is present + */ + protected static abstract class ClientFactoryDelegater { -/** - * optional class for RMI, will only be used, if RMI server is present - */ -class RMIClientFactoryDelegater extends ClientFactoryDelegater { + public abstract Repository getRepository(String uri) + throws RemoteException, MalformedURLException, NotBoundException; + } - // only used to enforce linking upon Class.forName() - static String FactoryClassName = ClientRepositoryFactory.class.getName(); + /** + * optional class for RMI, will only be used, if RMI server is present + */ + protected static class RMIClientFactoryDelegater extends ClientFactoryDelegater { - public Repository getRepository(String uri) - throws MalformedURLException, NotBoundException, RemoteException { - System.setProperty("java.rmi.server.useCodebaseOnly", "true"); - return new ClientRepositoryFactory().getRepository(uri); + // only used to enforce linking upon Class.forName() + static String FactoryClassName = ClientRepositoryFactory.class.getName(); + + public Repository getRepository(String uri) + throws MalformedURLException, NotBoundException, RemoteException { + System.setProperty("java.rmi.server.useCodebaseOnly", "true"); + return new ClientRepositoryFactory().getRepository(uri); + } } -} \ No newline at end of file +} + Modified: jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java?view=diff&rev=495531&r1=495530&r2=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java (original) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java Fri Jan 12 01:38:40 2007 @@ -16,6 +16,7 @@ */ package org.apache.jackrabbit.j2ee; +import org.apache.jackrabbit.api.JackrabbitRepository; import org.apache.jackrabbit.core.RepositoryImpl; import org.apache.jackrabbit.core.config.RepositoryConfig; import org.apache.jackrabbit.rmi.server.ServerAdapterFactory; @@ -30,8 +31,6 @@ import java.io.InputStream; import java.net.InetAddress; import java.net.ServerSocket; -import java.net.URI; -import java.net.URISyntaxException; import java.net.UnknownHostException; import java.rmi.AlreadyBoundException; import java.rmi.Naming; @@ -40,22 +39,24 @@ import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.RMIServerSocketFactory; -import java.util.Enumeration; 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.HttpServlet; +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. *

* Registration with RMI - *

+ *

* 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 @@ -66,36 +67,36 @@ * repository is to be bound in the registry, and rmi-uri * designating an RMI URI complete with host, optional port and name to which * the object is bound. - *

+ *

* If the rmi-uri parameter is configured with a non-empty value, * the rmi-port and rmi-host parameters are ignored. * The repository-name parameter is only considered if a non-empty * rmi-uri parameter is configured if the latter does not contain * a name to which to bind the repository. - *

+ *

* This is the algorithm used to find out the host, port and name for RMI * registration: *

    *
  1. If neither a rmi-uri nor a rmi-host nor a - * rmi-port parameter is configured, the repository is not - * registered with any RMI registry. + * rmi-port parameter is configured, the repository is not + * registered with any RMI registry. *
  2. If a non-empty rmi-uri 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 0.0.0.0 - * meaning all interfaces on the local host, port defaults to the RMI - * default port (1099) and the name defaults to the value - * of the repository-name parameter. + * host name (or IP address), port number and name to bind to from the + * URI. If the URI is not valid, host defaults to 0.0.0.0 + * meaning all interfaces on the local host, port defaults to the RMI + * default port (1099) and the name defaults to the value + * of the repository-name parameter. *
  3. If a non-empty rmi-uri is not configured, the host is taken - * from the rmi-host parameter, the port from the - * rmi-port parameter and the name to bind the repository to - * from the repository-name parameter. If the - * rmi-host parameter is empty or not configured, the host - * defaults to 0.0.0.0 meaning all interfaces on the local - * host. If the rmi-port parameter is empty, not configured, - * zero or a negative value, the default port for the RMI registry - * (1099) is used. + * from the rmi-host parameter, the port from the + * rmi-port parameter and the name to bind the repository to + * from the repository-name parameter. If the + * rmi-host parameter is empty or not configured, the host + * defaults to 0.0.0.0 meaning all interfaces on the local + * host. If the rmi-port parameter is empty, not configured, + * zero or a negative value, the default port for the RMI registry + * (1099) is used. *
- *

+ *

* 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 @@ -105,69 +106,67 @@ * method is called to get a remote instance of the registry. Note, that * getRegistry does not create an actual registry on the given * host/port nor does it check, whether an RMI registry is active. - *

+ *

* 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. - *

+ *

* Possible causes for registration failures include: *

    *
  • The web application is not configured to register with an RMI registry at - * all. + * all. *
  • The registry is expected to be running on a remote host but does not. *
  • 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. + * 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. *
  • An object may already be bound to the same name as is configured to be - * used for the repository. + * used for the repository. *
+ *

+ * Note: if a bootstrap-config 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 web.xml. + *

+ *

+ * Setup Wizard Functionality
+ * When using the first time, the configuraition can miss the relevant + * repository parameters in the web.xml. if so, it must contain a + * bootstrap-config parameter that referrs to a propertiy file. + * This file must exsit for proper working. If not, the repository is not + * started.
+ * 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 HttpServlet { - /** the default logger */ + /** + * the default logger + */ private static final Logger log = LoggerFactory.getLogger(RepositoryStartupServlet.class); - /** initial param name for the repository config location */ - public final static String INIT_PARAM_REPOSITORY_CONFIG = "repository-config"; - - /** initial param name for the repository home directory */ - public final static String INIT_PARAM_REPOSITORY_HOME = "repository-home"; - - /** initial param name for the repository name */ - public final static String INIT_PARAM_REPOSITORY_NAME = "repository-name"; - - /** initial param name for the rmi port */ - public final static String INIT_PARAM_RMI_PORT = "rmi-port"; - - /** initial param name for the rmi host */ - public final static String INIT_PARAM_RMI_HOST = "rmi-host"; - - /** initial param name for the rmi uri */ - public final static String INIT_PARAM_RMI_URI = "rmi-uri"; + /** + * the context attribute name foe 'this' instance. + */ + private final static String CTX_PARAM_THIS = "repository.startup.servet"; - /** initial param name for the log4j config properties */ - public final static String INIT_PARAM_LOG4J_CONFIG = "log4j-config"; + /** + * initial param name for the bootstrap config location + */ + public final static String INIT_PARAM_BOOTSTRAP_CONFIG = "bootstrap-config"; - /** the registered repository */ + /** + * the registered repository + */ private Repository repository; - /** the name of the repository as configured */ - private String repositoryName; - - /** the jndi context, created base on configuration */ - private InitialContext jndiContext; - /** - * The rmi uri, in the form of '//${rmi-host}:${rmi-port}/${repository-name}' - * This field is only set to a non-null value, if registration - * of the repository to an RMI registry succeeded in the - * {@link #registerRMI()} method. - * - * @see #registerRMI() - * @see #unregisterRMI() + * the jndi context; created based on configuration */ - private String rmiURI; + private InitialContext jndiContext; /** * Keeps a strong reference to the server side RMI repository instance to @@ -183,114 +182,235 @@ private Remote rmiRepository; /** - * Initializes the servlet - * @throws ServletException + * the file to the bootstrap config + */ + private File bootstrapConfigFile; + + /** + * The bootstrap configuration + */ + private BootstrapConfig config; + + /** + * Initializes the servlet.
+ * 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 { super.init(); + // 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..."); - initRepository(); try { - registerRMI(); + configure(); + initRepository(); + registerRMI(); registerJNDI(); + log.info("RepositoryStartupServlet initialized."); } catch (ServletException e) { // shutdown repository shutdownRepository(); - log.error("RepositoryStartupServlet initializing failed: "+ e, e); - throw e; + log.error("RepositoryStartupServlet initializing failed: " + e, e); } - log.info("RepositoryStartupServlet initialized."); } /** - * destroy the servlet + * 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 destroy() { - super.destroy(); - if (log == null) { - log("RepositoryStartupServlet shutting down..."); + public void shutdown() { + if (repository == null) { + log.info("Shutdown: Repository already stopped."); } else { log.info("RepositoryStartupServlet shutting down..."); - } - shutdownRepository(); - unregisterRMI(); - unregisterJNDI(); - if (log == null) { - log("RepositoryStartupServlet shut down."); - } else { + shutdownRepository(); + unregisterRMI(); + unregisterJNDI(); log.info("RepositoryStartupServlet shut down."); } } /** - * Creates a new Repository based on configuration - * @throws ServletException + * 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 */ - private void initRepository() throws ServletException { - // setup home directory - String repHome = getServletConfig().getInitParameter(INIT_PARAM_REPOSITORY_HOME); - if (repHome==null) { - log.error(INIT_PARAM_REPOSITORY_HOME + " missing."); - throw new ServletException(INIT_PARAM_REPOSITORY_HOME + " missing."); + public void destroy() { + super.destroy(); + shutdown(); + } + + /** + * Returns the started repository or null if not started + * yet. + * @return the JCR repository + */ + public Repository getRepository() { + return repository; + } + + /** + * Reads the configuration and initializes the {@link #config} field if + * successful. + * @throws ServletException if an error occurs. + */ + private void configure() throws ServletException { + // check if there is a loadable bootstrap config + Properties bootstrapProps = new Properties(); + String 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) { + log.error("Error while opening bootstrap properties: {}", e.toString()); + throw new ServletException("Error while opening bootstrap properties: " + e.toString()); + } + } + } + if (in != null) { + try { + bootstrapProps.load(in); + } catch (IOException e) { + log.error("Error while loading bootstrap properties: {}", e.toString()); + throw new ServletException("Error while loading bootstrap properties: " + e.toString()); + } finally { + try { + in.close(); + } catch (IOException e) { + // ignore + } + } + } } - File repositoryHome; - try { - repositoryHome = new File(repHome).getCanonicalFile(); - } catch (IOException e) { - log.error(INIT_PARAM_REPOSITORY_HOME + " invalid." + e.toString()); - throw new ServletException(INIT_PARAM_REPOSITORY_HOME + " invalid." + e.toString()); + + // read bootstrap config + config = new BootstrapConfig(); + config.init(getServletConfig()); + config.init(bootstrapProps); + config.validate(); + if (!config.isValid() + || config.getRepositoryHome() == null + || config.getRepositoryConfig() == null) { + if (bstrp == null) { + log.error("Repository startup configuration is not valid."); + throw new ServletException("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."); + throw new ServletException("Repository startup configuration is not valid."); + } } - log.info(" repository-home = " + repositoryHome.getPath()); + config.logInfos(); + } + /** + * 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 - String repConfig = getServletConfig().getInitParameter(INIT_PARAM_REPOSITORY_CONFIG); - if (repConfig==null) { - log.error(INIT_PARAM_REPOSITORY_CONFIG + " missing."); - throw new ServletException(INIT_PARAM_REPOSITORY_CONFIG + " missing."); + File repHome; + try { + repHome = new File(config.getRepositoryHome()).getCanonicalFile(); + } catch (IOException e) { + log.error("Repository startup configuration invalid: " + e.toString()); + throw new ServletException("Repository startup configuration invalid: " + e.toString()); } - log.info(" repository-config = " + repConfig); - + String repConfig = config.getRepositoryConfig(); InputStream in = getServletContext().getResourceAsStream(repConfig); - if (in==null) { + if (in == null) { try { - in = new FileInputStream(new File(repositoryHome, repConfig)); + in = new FileInputStream(new File(repConfig)); } catch (FileNotFoundException e) { - log.error(INIT_PARAM_REPOSITORY_CONFIG + " invalid." + e.toString()); - throw new ServletException(INIT_PARAM_REPOSITORY_CONFIG + " invalid." + e.toString()); + // fallback to old config + try { + in = new FileInputStream(new File(repHome, repConfig)); + } catch (FileNotFoundException e1) { + log.error("Repository startup configuration invalid: " + e1.toString()); + throw new ServletException("Repository startup configuration invalid: " + e.toString()); + } } } - // get repository name - repositoryName = getServletConfig().getInitParameter(INIT_PARAM_REPOSITORY_NAME); - if (repositoryName==null) { - repositoryName="default"; - } - log.info(" repository-name = " + repositoryName); - try { - repository = createRepository(new InputSource(in), repositoryHome); + repository = createRepository(new InputSource(in), repHome); } catch (RepositoryException e) { throw new ServletException("Error while creating repository", e); } } /** - * Shuts down the repository + * 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 + * nulled. */ private void shutdownRepository() { - if (repository instanceof RepositoryImpl) { - ((RepositoryImpl) repository).shutdown(); - repository = null; + if (repository instanceof JackrabbitRepository) { + ((JackrabbitRepository) repository).shutdown(); } + repository = null; } /** - * Creates the repository for the given config and homedir. + * Creates the repository instance for the given config and homedir. + * Subclasses may override this method of providing own implementations of + * a {@link Repository}. * - * @param is - * @param homedir - * @return - * @throws RepositoryException + * @param is 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(InputSource is, File homedir) throws RepositoryException { @@ -299,40 +419,29 @@ } /** - * Registers the repository in the JNDI context + * Binds the repository to the JNDI context + * @throws ServletException if an error occurs. */ private void registerJNDI() throws ServletException { - // registering via jndi - Properties env = new Properties(); - Enumeration names = getServletConfig().getInitParameterNames(); - while (names.hasMoreElements()) { - String name = (String) names.nextElement(); - if (name.startsWith("java.naming.")) { - String initParam = getServletConfig().getInitParameter(name); - if (initParam.equals("")) { - log.info(" ignoring empty JNDI init param: " + name); - } else { - env.put(name, initParam); - log.info(" adding property to JNDI environment: " + name + "=" + initParam); - } + 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 ServletException("Unable to bind repository using JNDI.", e); } } - try { - jndiContext = new InitialContext(env); - jndiContext.bind(repositoryName, repository); - log.info("Repository bound to JNDI with name: " + repositoryName); - } catch (NamingException e) { - throw new ServletException("Unable to bind repository using JNDI.", e); - } } /** - * Unregisters the repository from the JNDI context + * Unbinds the repository from the JNDI context. */ private void unregisterJNDI() { if (jndiContext != null) { try { - jndiContext.unbind(repositoryName); + jndiContext.unbind(config.getJndiConfig().getJndiName()); } catch (NamingException e) { log("Error while unbinding repository from JNDI: " + e); } @@ -344,77 +453,14 @@ * application. See Registration with RMI 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() throws ServletException { - // check registering via RMI - String rmiPortStr = getServletConfig().getInitParameter(INIT_PARAM_RMI_PORT); - String rmiHost = getServletConfig().getInitParameter(INIT_PARAM_RMI_HOST); - String rmiURI = getServletConfig().getInitParameter(INIT_PARAM_RMI_URI); - - // no registration if neither port nor host nor URI is configured - if (rmiPortStr == null && rmiHost == null && rmiURI == null) { + RMIConfig rc = config.getRmiConfig(); + if (!rc.isValid() || !rc.enabled()) { return; } - // URI takes precedences, so check whether the configuration has to - // be set from the URI - int rmiPort = -1; - String rmiName = null; - if (rmiURI != null && rmiURI.length() > 0) { - URI uri = null; - try { - uri = new URI(rmiURI); - - // extract values from the URI, check later - rmiHost = uri.getHost(); - rmiPort = uri.getPort(); - rmiName = uri.getPath(); - - } catch (URISyntaxException use) { - log.warn("Cannot parse RMI URI '" + rmiURI + "'.", use); - rmiURI = null; // clear RMI URI use another one - rmiHost = null; // use default host, ignore rmi-host param - } - - // cut of leading slash from name if defined at all - if (rmiName != null && rmiName.startsWith("/")) { - rmiName = rmiName.substring(1); - } - } else { - // convert RMI port configuration - if (rmiPortStr != null) { - try { - rmiPort = Integer.parseInt(rmiPortStr); - } catch (NumberFormatException e) { - log.warn("Invalid port in rmi-port param: " + e + ". using default port."); - rmiPort = Registry.REGISTRY_PORT; - } - } - } - - // check RMI port - if (rmiPort == -1 || rmiPort == 0) { - // accept -1 or 0 as a hint to use the default - rmiPort = Registry.REGISTRY_PORT; - } else if (rmiPort < -1 || rmiPort > 0xFFFF) { - // emit a warning if out of range, use defualt in this case - log.warn("Invalid port in rmi-port param " + rmiPort + ". using default port."); - rmiPort = Registry.REGISTRY_PORT; - } - - // check host - use an empty name if null (i.e. not configured) - if (rmiHost == null) { - rmiHost = ""; - } - - // check name - use repositoryName if empty or null - if (rmiName == null || rmiName.length() ==0) { - rmiName = repositoryName; - } - - // reconstruct the rmiURI now because values might have been changed - rmiURI = "//" + rmiHost + ":" + rmiPort + "/" + rmiName; - // try to create remote repository Remote remote; try { @@ -440,9 +486,9 @@ // find the server socket factory: use the default if the // rmiHost is not configured RMIServerSocketFactory sf; - if (rmiHost.length() > 0) { - log.debug("Creating RMIServerSocketFactory for host " + rmiHost); - InetAddress hostAddress = InetAddress.getByName(rmiHost); + 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 @@ -454,7 +500,7 @@ // 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(rmiPort, null, sf); + reg = LocateRegistry.createRegistry(rc.rmiPort(), null, sf); } catch (UnknownHostException uhe) { // thrown if the rmiHost cannot be resolved into an IP-Address @@ -471,27 +517,26 @@ // 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 " + rmiHost - + ":"+ rmiPort); + log.debug("Trying to access existing registry at " + rc.getRmiHost() + + ":" + rc.getRmiPort()); try { - reg = LocateRegistry.getRegistry(rmiHost, rmiPort); + reg = LocateRegistry.getRegistry(rc.getRmiHost(), rc.rmiPort()); } catch (RemoteException re) { log.error("Cannot create the reference to the registry at " - + rmiHost + ":" + rmiPort, re); - } + + 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 " + rmiName - + " to registry " + reg); - reg.bind(rmiName, remote); + log.debug("Registering repository as " + rc.getRmiName() + + " to registry " + reg); + reg.bind(rc.getRmiName(), remote); // when successfull, keep references - this.rmiURI = rmiURI; this.rmiRepository = remote; - log.info("Repository bound via RMI with name: " + rmiURI); + log.info("Repository bound via RMI with name: " + rc.getRmiUri()); } else { log.info("RMI registry missing, cannot bind repository via RMI"); } @@ -504,18 +549,41 @@ } /** + * Unregisters the repository from the RMI registry, if it has previously + * been registered. + */ + private void unregisterRMI() { + if (rmiRepository != null) { + // drop strong referenece to remote repository + rmiRepository = null; + + // unregister repository + try { + Naming.unbind(config.getRmiConfig().getRmiUri()); + } catch (Exception e) { + log("Error while unbinding repository from JNDI: " + e); + } + } + } + + /** * Return the fully qualified name of the class providing the remote * repository. The class whose name is returned must implement the * {@link RemoteFactoryDelegater} interface. + *

+ * Subclasses may override this method for providing a name of a own + * implementation. + * + * @return getClass().getName() + "$RMIRemoteFactoryDelegater" */ protected String getRemoteFactoryDelegaterClass() { - return "org.apache.jackrabbit.j2ee.RepositoryStartupServlet$RMIRemoteFactoryDelegater"; + return getClass().getName() + "$RMIRemoteFactoryDelegater"; } /** * Returns an RMIServerSocketFactory used to create the server * socket for a locally created RMI registry. - *

+ *

* This implementation returns a new instance of a simple * RMIServerSocketFactory which just creates instances of * the java.net.ServerSocket class bound to the given @@ -524,12 +592,11 @@ * creation, such as SSL server sockets. * * @param hostAddress The InetAddress instance representing the - * the interface on the local host to which the server sockets are - * bound. - * + * the interface on the local host to which the server sockets are + * bound. * @return A new instance of a simple RMIServerSocketFactory - * creating java.net.ServerSocket instances bound to - * the rmiHost. + * creating java.net.ServerSocket instances bound to + * the rmiHost. */ protected RMIServerSocketFactory getRMIServerSocketFactory( final InetAddress hostAddress) { @@ -541,27 +608,6 @@ } /** - * Unregisters the repository from the RMI registry, if it has previously - * been registered. - */ - private void unregisterRMI() { - // drop strong referenece to remote repository - rmiRepository = null; - - // unregister repository - if (rmiURI != null) { - try { - Naming.unbind(rmiURI); - } catch (Exception e) { - log("Error while unbinding repository from JNDI: " + e); - } finally { - // do not try again to unregister - rmiURI = null; - } - } - } - - /** * optional class for RMI, will only be used, if RMI server is present */ protected static abstract class RemoteFactoryDelegater { @@ -569,6 +615,7 @@ public abstract Remote createRemoteRepository(Repository repository) throws RemoteException; } + /** * optional class for RMI, will only be used, if RMI server is present */ @@ -583,5 +630,76 @@ } } + //-------------------------------------------------< Installer Routines >--- + + /** + * {@inheritDoc} + */ + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + if (repository == null) { + redirect(req, resp, "/bootstrap/missing.html"); + } else { + redirect(req, resp, "/bootstrap/running.html"); + } + } + + /** + * {@inheritDoc} + */ + protected void doPost(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + if (repository != null) { + redirect(req, resp, "/bootstrap/reconfigure.html"); + } 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.html"); + } else { + redirect(req, resp, "/bootstrap/success.html"); + } + break; + case Installer.C_INVALID_INPUT: + redirect(req, resp, "/bootstrap/missing.html"); + break; + case Installer.C_CONFIG_EXISTS: + case Installer.C_BOOTSTRAP_EXISTS: + case Installer.C_HOME_EXISTS: + redirect(req, resp, "/bootstrap/exists.html"); + break; + case Installer. C_HOME_MISSING: + case Installer.C_CONFIG_MISSING: + redirect(req, resp, "/bootstrap/notexists.html"); + break; + case Installer.C_INSTALL_ERROR: + redirect(req, resp, "/bootstrap/error.html"); + 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); + } } Added: jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties?view=auto&rev=495531 ============================================================================== --- jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties (added) +++ jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties Fri Jan 12 01:38:40 2007 @@ -0,0 +1,24 @@ +# This is the template file for the 'bootstrap.properties' that will +# be placed in the repository home directory (or whatever is specified +# in the "bootstrap-config" init parameter. + +# Repository configuration settings (will be adjusted by installer) +repository.config=/WEB-INF/repository/repository.xml +repository.home=jackrabbit/repository +repository.name=jackrabbit.repository + +# RMI Settings +rmi.enabled=true +rmi.port=0 +rmi.host=localhost +# If the URI is not specified, it's composed as follows: +#rmi.uri=//${rmi.host}:${rmi.port}/${repository.name} + +# JNDI Settings +# all properties starting with 'java.naming.' will go into the +# environment of the initial context +jndi.enabled=true +# if the name is not specified, it's initialized with the repository.name +#jndi.name=${repository.name} +java.naming.provider.url=http://www.apache.org/jackrabbit +java.naming.factory.initial=org.apache.jackrabbit.core.jndi.provider.DummyInitialContextFactory Propchange: jackrabbit/trunk/jackrabbit-webapp/src/main/webapp/WEB-INF/templates/bootstrap.properties ------------------------------------------------------------------------------ svn:eol-style = native