You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by fm...@apache.org on 2005/12/12 13:06:08 UTC
svn commit: r356251 -
/incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java
Author: fmeschbe
Date: Mon Dec 12 04:03:37 2005
New Revision: 356251
URL: http://svn.apache.org/viewcvs?rev=356251&view=rev
Log:
JCR-291 - implementing proposed patch
Modified:
incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java
Modified: incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java?rev=356251&r1=356250&r2=356251&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java (original)
+++ incubator/jackrabbit/trunk/contrib/jcr-server/webapp/src/java/org/apache/jackrabbit/j2ee/RepositoryStartupServlet.java Mon Dec 12 04:03:37 2005
@@ -32,19 +32,93 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
-import java.net.MalformedURLException;
+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;
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.util.Enumeration;
import java.util.Properties;
/**
* 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>
*/
public class RepositoryStartupServlet extends HttpServlet {
@@ -81,7 +155,15 @@
/** the jndi context, created base on configuration */
private InitialContext jndiContext;
- /** the rmi uri, in the form of '//${rmi-host}:${rmi-port}/${repository-name}' */
+ /**
+ * The rmi uri, in the form of '//${rmi-host}:${rmi-port}/${repository-name}'
+ * 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 String rmiURI;
/**
@@ -89,27 +171,27 @@
* @throws ServletException
*/
public void init() throws ServletException {
- super.init();
- log.info("RepositoryStartupServlet initializing...");
- initRepository();
- registerJNDI();
- registerRMI();
- log.info("RepositoryStartupServlet initialized.");
+ super.init();
+ log.info("RepositoryStartupServlet initializing...");
+ initRepository();
+ registerJNDI();
+ registerRMI();
+ log.info("RepositoryStartupServlet initialized.");
}
/**
* destroy the servlet
*/
public void destroy() {
- super.destroy();
- if (log == null) {
- log("RepositoryStartupServlet shutting down...");
- } else {
- log.info("RepositoryStartupServlet shutting down...");
- }
+ super.destroy();
+ if (log == null) {
+ log("RepositoryStartupServlet shutting down...");
+ } else {
+ log.info("RepositoryStartupServlet shutting down...");
+ }
shutdownRepository();
- unregisterRMI();
- unregisterJNDI();
+ unregisterRMI();
+ unregisterJNDI();
if (log == null) {
log("RepositoryStartupServlet shut down.");
} else {
@@ -122,51 +204,51 @@
* @throws ServletException
*/
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.");
- }
- 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());
- }
- log.info(" repository-home = " + repositoryHome.getPath());
-
- // 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.");
- }
- log.info(" repository-config = " + repConfig);
-
- InputStream in = getServletContext().getResourceAsStream(repConfig);
- if (in==null) {
- try {
- in = new FileInputStream(new File(repositoryHome, repConfig));
- } catch (FileNotFoundException e) {
- log.error(INIT_PARAM_REPOSITORY_CONFIG + " invalid." + e.toString());
- throw new ServletException(INIT_PARAM_REPOSITORY_CONFIG + " 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);
- } catch (RepositoryException e) {
- throw new ServletException("Error while creating repository", e);
- }
+ // 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.");
+ }
+ 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());
+ }
+ log.info(" repository-home = " + repositoryHome.getPath());
+
+ // 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.");
+ }
+ log.info(" repository-config = " + repConfig);
+
+ InputStream in = getServletContext().getResourceAsStream(repConfig);
+ if (in==null) {
+ try {
+ in = new FileInputStream(new File(repositoryHome, repConfig));
+ } catch (FileNotFoundException e) {
+ log.error(INIT_PARAM_REPOSITORY_CONFIG + " invalid." + e.toString());
+ throw new ServletException(INIT_PARAM_REPOSITORY_CONFIG + " 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);
+ } catch (RepositoryException e) {
+ throw new ServletException("Error while creating repository", e);
+ }
}
private void shutdownRepository() {
@@ -193,12 +275,12 @@
* Registers the repository in the JNDI context
*/
private void registerJNDI() {
- // registering via jndi
- Properties env = new Properties();
- Enumeration names = getServletConfig().getInitParameterNames();
- while (names.hasMoreElements()) {
- String name = (String) names.nextElement();
- if (name.startsWith("java.naming.")) {
+ // 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);
@@ -206,92 +288,193 @@
env.put(name, initParam);
log.info(" adding property to JNDI environment: " + name + "=" + initParam);
}
- }
- }
- try {
- jndiContext = new InitialContext(env);
- jndiContext.bind(repositoryName, repository);
+ }
+ }
+ try {
+ jndiContext = new InitialContext(env);
+ jndiContext.bind(repositoryName, repository);
log.info("Repository bound to JNDI with name: " + repositoryName);
- } catch (NamingException e) {
+ } catch (NamingException e) {
log.error("Unable to bind repository using JNDI: " + e, e);
- }
+ }
}
/**
* Unregisters the repository from the JNDI context
*/
private void unregisterJNDI() {
- if (jndiContext != null) {
- try {
- jndiContext.unbind(repositoryName);
- } catch (NamingException e) {
- log("Error while unbinding repository from JNDI: " + e);
- }
- }
+ if (jndiContext != null) {
+ try {
+ jndiContext.unbind(repositoryName);
+ } catch (NamingException e) {
+ log("Error while unbinding repository from JNDI: " + e);
+ }
+ }
}
/**
- * Registers the repositroy to the RMI registry
+ * 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.
*/
private void registerRMI() {
- // check registering via RMI
- String rmiPortStr = getServletConfig().getInitParameter(INIT_PARAM_RMI_PORT);
+ // check registering via RMI
+ String rmiPortStr = getServletConfig().getInitParameter(INIT_PARAM_RMI_PORT);
String rmiHost = getServletConfig().getInitParameter(INIT_PARAM_RMI_HOST);
- rmiURI = getServletConfig().getInitParameter(INIT_PARAM_RMI_URI);
+ String rmiURI = getServletConfig().getInitParameter(INIT_PARAM_RMI_URI);
+
+ // no registration if neither port nor host nore URI is configured
if (rmiPortStr == null && rmiHost == null && rmiURI == null) {
return;
}
- int rmiPort = Registry.REGISTRY_PORT;
- if (rmiPortStr != null) {
- try {
- rmiPort = Integer.parseInt(rmiPortStr);
- } catch (NumberFormatException e) {
- log.warn("Invalid port in rmi-port param: " + e + ". using default port.");
+
+ // 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 {
+ // 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) {
+ RemoteFactoryDelegater rmf = (RemoteFactoryDelegater) clazz.newInstance();
+ remote = rmf.createRemoteRepository(repository);
+ } catch (RemoteException e) {
log.error("Unable to create remote repository: " + e, e);
return;
- } catch (NoClassDefFoundError e) {
- log.warn("Unable to create RMI repository. jcr-rmi.jar might be missing.: " + e.toString());
- return;
- } catch (Exception e) {
- log.warn("Unable to create RMI repository. jcr-rmi.jar might be missing.: " + e.toString());
- return;
- }
-
- try {
- System.setProperty("java.rmi.server.useCodebaseOnly", "true");
- try {
- // start registry
- LocateRegistry.createRegistry(rmiPort);
- } catch (RemoteException e) {
- // ignore
- }
- if (rmiURI == null) {
- rmiURI = "//" + rmiHost + ":" + rmiPort + "/" + repositoryName;
+ } catch (NoClassDefFoundError e) {
+ log.warn("Unable to create RMI repository. jcr-rmi.jar might be missing.: " + e.toString());
+ return;
+ } catch (Exception e) {
+ log.warn("Unable to create RMI repository. jcr-rmi.jar might be missing.: " + e.toString());
+ 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 (rmiHost.length() > 0) {
+ log.debug("Creating RMIServerSocketFactory for host " + rmiHost);
+ InetAddress hostAddress = InetAddress.getByName(rmiHost);
+ 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(rmiPort, null, sf);
+
+ } 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);
}
- Naming.bind(rmiURI, remote);
- log.info("Repository bound via RMI with name: " + rmiURI);
- } catch (MalformedURLException e) {
- log.error("Unable to bind repository via RMI: " + e, e);
- } catch (RemoteException 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 " + rmiHost
+ + ":"+ rmiPort);
+ try {
+ reg = LocateRegistry.getRegistry(rmiHost, rmiPort);
+ } catch (RemoteException re) {
+ log.error("Cannot create the reference to the registry at "
+ + rmiHost + ":" + rmiPort, 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);
+ this.rmiURI = rmiURI;
+ log.info("Repository bound via RMI with name: " + rmiURI);
+ } else {
+ log.info("RMI registry missing, cannot bind repository via RMI");
+ }
+
+ } catch (RemoteException e) {
log.error("Unable to bind repository via RMI: " + e, e);
- } catch (AlreadyBoundException e) {
+ } catch (AlreadyBoundException e) {
log.error("Unable to bind repository via RMI: " + e, e);
- }
- }
+ }
+ }
/**
* Return the fully qualified name of the class providing the remote
@@ -303,16 +486,51 @@
}
/**
- * Unregisters the repository from the RMI registry
+ * 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>.
+ *
+ * @throws UnknownHostException If the <code>rmiHost</code> is a host name
+ * which cannot be mapped to an IP address.
+ */
+ protected RMIServerSocketFactory getRMIServerSocketFactory(
+ final InetAddress hostAddress) {
+ return new RMIServerSocketFactory() {
+ public ServerSocket createServerSocket(int port) throws IOException {
+ return new ServerSocket(port, -1, hostAddress);
+ }
+ };
+ }
+
+ /**
+ * Unregisters the repository from the RMI registry, if it has previously
+ * been registered.
*/
private void unregisterRMI() {
- if (rmiURI != null) {
- try {
- Naming.unbind(rmiURI);
- } catch (Exception e) {
- log("Error while unbinding repository from JNDI: " + e);
- }
- }
+ 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;
+ }
+ }
}
}
@@ -323,7 +541,7 @@
abstract class RemoteFactoryDelegater {
public abstract Remote createRemoteRepository(Repository repository)
- throws RemoteException;
+ throws RemoteException;
}
/**
* optional class for RMI, will only be used, if RMI server is present
@@ -334,7 +552,7 @@
static String FactoryClassName = ServerAdapterFactory.class.getName();
public Remote createRemoteRepository(Repository repository)
- throws RemoteException {
- return new ServerAdapterFactory().getRemoteRepository(repository);
+ throws RemoteException {
+ return new ServerAdapterFactory().getRemoteRepository(repository);
}
}