/* * 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.servlet; import java.io.IOException; import java.util.Properties; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.jackrabbit.commons.repository.ProxyRepository; /** * Abstract base class for servlets that make a repository available in * the servlet context. This class handles the initialization and cleanup * tasks of setting up and clearing the configured repository attribute, * while a subclass only needs to implement the abstract * {@link #getRepository()} method that returns the actual content repository. * <p> * The {@link Repository} instance bound to the servlet context is actually * a {@link ProxyRepository} for late binding of the underlying content repository. * <p> * The default name of the repository attribute is * "<code>javax.jcr.Repository</code>", but it can be changed by specifying * an init parameter with the same name: * <pre> * <servlet> * <init-param> * <param-name>javax.jcr.Repository</param-name> * <param-value>my.repository.attribute</param-value> * <description> * This init parameter causes the repository to be looked up from * the "my.repository.attribute" attribute instead of the default * "javax.jcr.Repository". * </description> * </init-param> * </servlet> * </pre> * <p> * A repository servlet can also be mapped to the URL space. See the * {@link #doGet(HttpServletRequest, HttpServletResponse)} method for * the details of the default behavior. * * @since 1.4 */ public abstract class AbstractRepositoryServlet extends HttpServlet { /** * Binds a {@link ProxyRepository} with the repository returned by * {@link #getRepository()} in the configured servlet context attribute. * * @throws ServletException */ public void init() throws ServletException { getServletContext().setAttribute( getAttributeName(), new ProxyRepository() { @Override protected Repository getRepository() throws RepositoryException { return AbstractRepositoryServlet.this.getRepository(); } }); } /** * Removes the repository attribute from the servlet context. */ public void destroy() { getServletContext().removeAttribute(getAttributeName()); } /** * Returns the repository that will be used by the * {@link ProxyRepository} bound to the servlet context. * * @return repository * @throws RepositoryException if the repository could not be created */ protected abstract Repository getRepository() throws RepositoryException; /** * Returns the name of the repository attribute. The default * implementation returns "<code>javax.jcr.Repository</code>" or * the value of the "<code>javax.jcr.Repository</code>" init parameter. * <p> * A subclass can override this method to customize the attribute name, * but for consistency it is generally better not to do that. * * @return name of the repository attribute */ protected String getAttributeName() { String name = Repository.class.getName(); return getInitParameter(name, name); } /** * Utility method that returns the named init parameter or the given * default value if the parameter does not exist. * * @param name name of the init parameter * @param def default value * @return value of the init parameter, or the default value */ protected String getInitParameter(String name, String def) { String value = getInitParameter(name); if (value == null) { value = def; } return value; } /** * Outputs the repository descriptors either as a collection of properties * (see {@link Properties#store(java.io.OutputStream, String)} or * individually addressable text/plain resources based on the request URI. * <p> * A typical mapping for a repository servlet would be: * <pre> * <servlet-mapping> * <servlet-name>Repository</servlet-name> * <url-pattern>/repository/*</url-pattern> * </servlet-mapping> * </pre> * <p> * This mapping would allow clients to retrieve all repository descriptors * from <code>http://server/context/repository/</code> and to address * individual descriptors by key with URIs like * <code>http://server/context/repository/<i>key</i></code>. * For example, the name of the repository vendor could be retrieved from * <code>http://server/context/repository/jcr.repository.vendor</code>. * Likewise, a 404 (not found) response from * <code>http://server/context/repository/level.2.supported</code> would * indicate that the repository does not support Level 2 features. * <p> * Note that mapping a repository servlet to the URL space is optional, * as the main purpose of the servlet is to make a repository available * in the servlet context, not to expose repository information to web * clients. * * @param request HTTP request * @param response HTTP response * @throws IOException on IO errors * @throws ServletException on servlet errors */ protected void doGet( HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { Repository repository = new ServletRepository(this); String info = request.getPathInfo(); if (info == null || info.equals("/")) { Properties descriptors = new Properties(); String[] keys = repository.getDescriptorKeys(); for (int i = 0; i < keys.length; i++) { descriptors.setProperty( keys[i], repository.getDescriptor(keys[i])); } // TODO: Using UTF-8 instead of ISO-8859-1 would be better, but // would require re-implementing the Properties.store() method response.setContentType("text/plain; charset=ISO-8859-1"); descriptors.store(response.getOutputStream(), getAttributeName()); } else { String key = info.substring(1); // skip the leading "/" String descriptor = repository.getDescriptor(key); if (descriptor != null) { response.setContentType("text/plain; charset=UTF-8"); response.getWriter().write(descriptor); } else { response.sendError( HttpServletResponse.SC_NOT_FOUND, "Repository descriptor " + key + " not found"); } } } }