/* * 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.jackrabbit.rmi.client.ClientRepositoryFactory; 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; /** * This Class implements a servlet that is used as unified mechanism to retrieve * a jcr repository either through JNDI or RMI. */ public class RepositoryAccessServlet extends HttpServlet { /** * default logger */ private static final Logger log = LoggerFactory.getLogger(RepositoryAccessServlet.class); /** * initial param name for the bootstrap config location */ public final static String INIT_PARAM_BOOTSTRAP_CONFIG = "bootstrap-config"; /** * Context parameter name for 'this' instance. */ private final static String CTX_PARAM_THIS = "repository.access.servlet"; /** * Ugly hack to override the bootstrap file location in the test cases */ static String bootstrapOverride = null; /** * the bootstrap config */ private BootstrapConfig config; /** * the initialized initial context */ private InitialContext jndiContext; /** * if this is set we try to get a Repository from the ServletContext */ private String repositoryContextAttributeName; /** * the repository */ private Repository repository; /** * Initializes the servlet.<br> * Please note that only one repository startup servlet may exist per * webapp. it registers itself as context attribute and acts as singleton. * * @throws ServletException if a same servlet is already registered or of * another initialization error occurs. */ public void init() throws ServletException { // check if servlet is defined twice if (getServletContext().getAttribute(CTX_PARAM_THIS) != null) { throw new ServletException("Only one repository access servlet allowed per web-app."); } getServletContext().setAttribute(CTX_PARAM_THIS, this); repositoryContextAttributeName = getServletConfig().getInitParameter("repository.context.attribute.name"); log.info("RepositoryAccessServlet initialized."); } /** * Returns the instance of this servlet * @param ctx the servlet context * @return this servlet */ public static RepositoryAccessServlet getInstance(ServletContext ctx) { final RepositoryAccessServlet instance = (RepositoryAccessServlet) ctx.getAttribute(CTX_PARAM_THIS); if(instance==null) { throw new IllegalStateException( "No RepositoryAccessServlet instance in ServletContext, RepositoryAccessServlet servlet not initialized?" ); } return instance; } /** * 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 = bootstrapOverride; if (bstrp == null) { bstrp = getServletConfig().getInitParameter(INIT_PARAM_BOOTSTRAP_CONFIG); } if (bstrp != null) { // check if it's a web-resource InputStream in = getServletContext().getResourceAsStream(bstrp); if (in == null) { // check if it's a file File file = new File(bstrp); if (file.canRead()) { try { in = new FileInputStream(file); } catch (FileNotFoundException e) { throw new ServletExceptionWithCause( "Bootstrap configuration not found: " + bstrp, e); } } } if (in != null) { try { bootstrapProps.load(in); } catch (IOException e) { throw new ServletExceptionWithCause( "Bootstrap configuration failure: " + bstrp, e); } finally { try { in.close(); } catch (IOException e) { // ignore } } } } // read bootstrap config BootstrapConfig tmpConfig = new BootstrapConfig(); tmpConfig.init(getServletConfig()); tmpConfig.init(bootstrapProps); tmpConfig.validate(); if (!tmpConfig.isValid()) { throw new ServletException( "Repository access configuration is not valid."); } tmpConfig.logInfos(); config = tmpConfig; } return config; } /** * Returns the initial jndi context or <code>null</code> if the jndi access * is not configured or erroous. * @return the initial context or <code>null</code> */ 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; } /** * Checks if the repository is available via JNDI and returns it. * @return the repository or <code>null</code> * @throws ServletException if this servlet is not properly configured. */ private Repository getRepositoryByJNDI() throws ServletException { BootstrapConfig config = getConfig(); if (!config.getJndiConfig().isValid() || !config.getJndiConfig().enabled()) { return null; } // acquire via JNDI String repositoryName = config.getRepositoryName(); InitialContext ctx = getInitialContext(); if (ctx == null) { return null; } try { Repository r = (Repository) ctx.lookup(repositoryName); log.info("Acquired repository via JNDI."); return r; } catch (NamingException e) { log.error("Error while retrieving repository using JNDI (name={})", repositoryName, e); return null; } } /** * Checks if the repository is available via RMI and returns it. * @return the repository or <code>null</code> * @throws ServletException if this servlet is not properly configured. */ private Repository getRepositoryByRMI() throws ServletException { BootstrapConfig config = getConfig(); if (!config.getRmiConfig().isValid() || !config.getRmiConfig().enabled()) { return null; } // acquire via RMI 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(getServerFactoryDelegaterClass()); cfd = (ClientFactoryDelegater) clazz.newInstance(); } catch (Throwable e) { log.error("Unable to locate RMI ClientRepositoryFactory. Is jcr-rmi.jar missing?", e); return null; } try { Repository r = cfd.getRepository(rmiURI); log.info("Acquired repository via RMI."); return r; } catch (Exception e) { log.error("Error while retrieving repository using RMI: {}", rmiURI, e); return null; } } /** * If our config said so, try to retrieve a Repository from the ServletContext */ protected Repository getRepositoryByContextAttribute() { Repository result = null; if(repositoryContextAttributeName!=null) { result = (Repository)getServletContext().getAttribute(repositoryContextAttributeName); if(log.isDebugEnabled()) { if(result!=null) { log.debug("Got Repository from ServletContext attribute '{}'", repositoryContextAttributeName); } else { log.debug("ServletContext attribute '{}' does not provide a Repository", repositoryContextAttributeName); } } } return result; } /** * 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 JCR repository * @throws IllegalStateException if the repository is not available in the context. */ public Repository getRepository() { try { if (repository == null) { // try to get via context attribute repository = getRepositoryByContextAttribute(); } 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; } catch (ServletException e) { throw new IllegalStateException( "The repository is not available. Please check" + " RepositoryAccessServlet configuration in web.xml.", e); } } /** * 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(); } /** * Returns the config that was used to bootstrap this servlet. * @return the bootstrap config or <code>null</code>. */ public BootstrapConfig getBootstrapConfig() { return config; } /** * optional class for RMI, will only be used, if RMI client is present */ protected static abstract class ClientFactoryDelegater { public abstract Repository getRepository(String uri) throws RemoteException, MalformedURLException, NotBoundException; } /** * optional class for RMI, will only be used, if RMI server is present */ protected static class RMIClientFactoryDelegater extends ClientFactoryDelegater { // 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); } } }