JBossWeb SVN: r1041 - trunk/java/org/apache/naming/resources.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2009-05-04 20:36:22 -0400 (Mon, 04 May 2009)
New Revision: 1041
Added:
trunk/java/org/apache/naming/resources/JARDirContext.java
Log:
- Add the basic plan for the Jar based DirContext.
Added: trunk/java/org/apache/naming/resources/JARDirContext.java
===================================================================
--- trunk/java/org/apache/naming/resources/JARDirContext.java (rev 0)
+++ trunk/java/org/apache/naming/resources/JARDirContext.java 2009-05-05 00:36:22 UTC (rev 1041)
@@ -0,0 +1,1020 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, JBoss Inc., and individual contributors as indicated
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright 1999-2009 The Apache Software Foundation
+ *
+ * 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.naming.resources;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+
+import javax.naming.CompositeName;
+import javax.naming.Name;
+import javax.naming.NameAlreadyBoundException;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.NotContextException;
+import javax.naming.OperationNotSupportedException;
+import javax.naming.directory.AttributeModificationException;
+import javax.naming.directory.Attributes;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.InvalidAttributesException;
+import javax.naming.directory.InvalidSearchControlsException;
+import javax.naming.directory.InvalidSearchFilterException;
+import javax.naming.directory.ModificationItem;
+import javax.naming.directory.SearchControls;
+
+import org.apache.naming.NamingContextBindingsEnumeration;
+import org.apache.naming.NamingContextEnumeration;
+import org.apache.naming.NamingEntry;
+
+/**
+ * JAR Directory Context implementation, variant of WAR dir context. It uses
+ * a JarFile directly, as well as a path prefix.
+ *
+ * @author Remy Maucherat
+ * @version $Revision: 515 $ $Date: 2008-03-17 22:02:23 +0100 (Mon, 17 Mar 2008) $
+ */
+
+public class JARDirContext extends BaseDirContext {
+
+ private static org.jboss.logging.Logger log =
+ org.jboss.logging.Logger.getLogger(JARDirContext.class);
+
+ // ----------------------------------------------------------- Constructors
+
+
+ /**
+ * Builds a WAR directory context using the given environment.
+ */
+ public JARDirContext() {
+ super();
+ }
+
+
+ /**
+ * Builds a WAR directory context using the given environment.
+ */
+ public JARDirContext(Hashtable env) {
+ super(env);
+ }
+
+
+ /**
+ * Constructor used for returning fake subcontexts.
+ */
+ protected JARDirContext(JarFile base, Entry entries) {
+ this.base = base;
+ this.entries = entries;
+ }
+
+
+ // ----------------------------------------------------- Instance Variables
+
+
+ /**
+ * The Jar file.
+ */
+ protected JarFile base = null;
+
+
+ /**
+ * WAR entries.
+ */
+ protected Entry entries = null;
+
+
+ /**
+ * Path prefix.
+ */
+ protected String prefix = null;
+
+
+ // ------------------------------------------------------------- Properties
+
+
+ /**
+ * Set the JAR file from which data will be read.
+ *
+ * @param jarFile The JAR file
+ *
+ * @exception IllegalArgumentException if the specified value is not
+ * supported by this implementation
+ * @exception IllegalArgumentException if this would create a
+ * malformed URL
+ */
+ public void setJarFile(JarFile jarFile, String prefix) {
+
+ // Validate the format of the proposed document root
+ if (jarFile == null)
+ throw new IllegalArgumentException
+ (sm.getString("resources.null"));
+
+ this.prefix = prefix;
+ this.base = jarFile;
+
+ loadEntries();
+
+ }
+
+
+ // --------------------------------------------------------- Public Methods
+
+
+ /**
+ * Release any resources allocated for this directory context.
+ */
+ public void release() {
+
+ entries = null;
+ // Base is not closed as it is supposed to be managed through
+ // a repository
+ base = null;
+ super.release();
+
+ }
+
+
+ // -------------------------------------------------------- Context Methods
+
+
+ /**
+ * Retrieves the named object.
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookup(String name)
+ throws NamingException {
+ return lookup(new CompositeName(name));
+ }
+
+
+ /**
+ * Retrieves the named object. If name is empty, returns a new instance
+ * of this context (which represents the same naming context as this
+ * context, but its environment may be modified independently and it may
+ * be accessed concurrently).
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookup(Name name)
+ throws NamingException {
+ //if (prefixName != null)
+ // name.addAll(0, prefixName);
+ if (name.isEmpty())
+ return this;
+ Entry entry = treeLookup(name);
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+ ZipEntry zipEntry = entry.getEntry();
+ if (zipEntry.isDirectory())
+ return new JARDirContext(base, entry);
+ else
+ return new JARResource(entry.getEntry());
+ }
+
+
+ /**
+ * Unbinds the named object. Removes the terminal atomic name in name
+ * from the target context--that named by all but the terminal atomic
+ * part of name.
+ * <p>
+ * This method is idempotent. It succeeds even if the terminal atomic
+ * name is not bound in the target context, but throws
+ * NameNotFoundException if any of the intermediate contexts do not exist.
+ *
+ * @param name the name to bind; may not be empty
+ * @exception NameNotFoundException if an intermediate context does not
+ * exist
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void unbind(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a new name to the object bound to an old name, and unbinds the
+ * old name. Both names are relative to this context. Any attributes
+ * associated with the old name become associated with the new name.
+ * Intermediate contexts of the old name are not changed.
+ *
+ * @param oldName the name of the existing binding; may not be empty
+ * @param newName the name of the new binding; may not be empty
+ * @exception NameAlreadyBoundException if newName is already bound
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void rename(String oldName, String newName)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the class
+ * names of objects bound to them. The contents of any subcontexts are
+ * not included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the names and class names of the bindings in
+ * this context. Each element of the enumeration is of type NameClassPair.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration list(String name)
+ throws NamingException {
+ return list(new CompositeName(name));
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the class
+ * names of objects bound to them. The contents of any subcontexts are
+ * not included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the names and class names of the bindings in
+ * this context. Each element of the enumeration is of type NameClassPair.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration list(Name name)
+ throws NamingException {
+ //if (prefixName != null)
+ // name.addAll(0, prefixName);
+ if (name.isEmpty())
+ return new NamingContextEnumeration(list(entries).iterator());
+ Entry entry = treeLookup(name);
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+ return new NamingContextEnumeration(list(entry).iterator());
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the
+ * objects bound to them. The contents of any subcontexts are not
+ * included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the bindings in this context.
+ * Each element of the enumeration is of type Binding.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration listBindings(String name)
+ throws NamingException {
+ return listBindings(new CompositeName(name));
+ }
+
+
+ /**
+ * Enumerates the names bound in the named context, along with the
+ * objects bound to them. The contents of any subcontexts are not
+ * included.
+ * <p>
+ * If a binding is added to or removed from this context, its effect on
+ * an enumeration previously returned is undefined.
+ *
+ * @param name the name of the context to list
+ * @return an enumeration of the bindings in this context.
+ * Each element of the enumeration is of type Binding.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration listBindings(Name name)
+ throws NamingException {
+ //if (prefixName != null)
+ // name.addAll(0, prefixName);
+ if (name.isEmpty())
+ return new NamingContextBindingsEnumeration(list(entries).iterator(),
+ this);
+ Entry entry = treeLookup(name);
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+ return new NamingContextBindingsEnumeration(list(entry).iterator(),
+ this);
+ }
+
+
+ /**
+ * Destroys the named context and removes it from the namespace. Any
+ * attributes associated with the name are also removed. Intermediate
+ * contexts are not destroyed.
+ * <p>
+ * This method is idempotent. It succeeds even if the terminal atomic
+ * name is not bound in the target context, but throws
+ * NameNotFoundException if any of the intermediate contexts do not exist.
+ *
+ * In a federated naming system, a context from one naming system may be
+ * bound to a name in another. One can subsequently look up and perform
+ * operations on the foreign context using a composite name. However, an
+ * attempt destroy the context using this composite name will fail with
+ * NotContextException, because the foreign context is not a "subcontext"
+ * of the context in which it is bound. Instead, use unbind() to remove
+ * the binding of the foreign context. Destroying the foreign context
+ * requires that the destroySubcontext() be performed on a context from
+ * the foreign context's "native" naming system.
+ *
+ * @param name the name of the context to be destroyed; may not be empty
+ * @exception NameNotFoundException if an intermediate context does not
+ * exist
+ * @exception NotContextException if the name is bound but does not name
+ * a context, or does not name a context of the appropriate type
+ */
+ public void destroySubcontext(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves the named object, following links except for the terminal
+ * atomic component of the name. If the object bound to name is not a
+ * link, returns the object itself.
+ *
+ * @param name the name of the object to look up
+ * @return the object bound to name, not following the terminal link
+ * (if any).
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Object lookupLink(String name)
+ throws NamingException {
+ // Note : Links are not supported
+ return lookup(name);
+ }
+
+
+ /**
+ * Retrieves the full name of this context within its own namespace.
+ * <p>
+ * Many naming services have a notion of a "full name" for objects in
+ * their respective namespaces. For example, an LDAP entry has a
+ * distinguished name, and a DNS record has a fully qualified name. This
+ * method allows the client application to retrieve this name. The string
+ * returned by this method is not a JNDI composite name and should not be
+ * passed directly to context methods. In naming systems for which the
+ * notion of full name does not make sense,
+ * OperationNotSupportedException is thrown.
+ *
+ * @return this context's name in its own namespace; never null
+ * @exception OperationNotSupportedException if the naming system does
+ * not have the notion of a full name
+ * @exception NamingException if a naming exception is encountered
+ */
+ public String getNameInNamespace()
+ throws NamingException {
+ return docBase;
+ }
+
+
+ // ----------------------------------------------------- DirContext Methods
+
+
+ /**
+ * Retrieves selected attributes associated with a named object.
+ * See the class description regarding attribute models, attribute type
+ * names, and operational attributes.
+ *
+ * @return the requested attributes; never null
+ * @param name the name of the object from which to retrieve attributes
+ * @param attrIds the identifiers of the attributes to retrieve. null
+ * indicates that all attributes should be retrieved; an empty array
+ * indicates that none should be retrieved
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Attributes getAttributes(String name, String[] attrIds)
+ throws NamingException {
+ return getAttributes(new CompositeName(name), attrIds);
+ }
+
+
+ /**
+ * Retrieves all of the attributes associated with a named object.
+ *
+ * @return the set of attributes associated with name.
+ * Returns an empty attribute set if name has no attributes; never null.
+ * @param name the name of the object from which to retrieve attributes
+ * @exception NamingException if a naming exception is encountered
+ */
+ public Attributes getAttributes(Name name, String[] attrIds)
+ throws NamingException {
+
+ //if (prefixName != null)
+ // name.addAll(0, prefixName);
+
+ Entry entry = null;
+ if (name.isEmpty())
+ entry = entries;
+ else
+ entry = treeLookup(name);
+ if (entry == null)
+ throw new NamingException
+ (sm.getString("resources.notFound", name));
+
+ ZipEntry zipEntry = entry.getEntry();
+
+ ResourceAttributes attrs = new ResourceAttributes();
+ attrs.setCreationDate(new Date(zipEntry.getTime()));
+ attrs.setName(entry.getName());
+ if (!zipEntry.isDirectory())
+ attrs.setResourceType("");
+ attrs.setContentLength(zipEntry.getSize());
+ attrs.setLastModified(zipEntry.getTime());
+
+ return attrs;
+
+ }
+
+
+ /**
+ * Modifies the attributes associated with a named object. The order of
+ * the modifications is not specified. Where possible, the modifications
+ * are performed atomically.
+ *
+ * @param name the name of the object whose attributes will be updated
+ * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
+ * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
+ * @param attrs the attributes to be used for the modification; may not
+ * be null
+ * @exception AttributeModificationException if the modification cannot be
+ * completed successfully
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void modifyAttributes(String name, int mod_op, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Modifies the attributes associated with a named object using an an
+ * ordered list of modifications. The modifications are performed in the
+ * order specified. Each modification specifies a modification operation
+ * code and an attribute on which to operate. Where possible, the
+ * modifications are performed atomically.
+ *
+ * @param name the name of the object whose attributes will be updated
+ * @param mods an ordered sequence of modifications to be performed; may
+ * not be null
+ * @exception AttributeModificationException if the modification cannot be
+ * completed successfully
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void modifyAttributes(String name, ModificationItem[] mods)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a name to an object, along with associated attributes. If attrs
+ * is null, the resulting binding will have the attributes associated
+ * with obj if obj is a DirContext, and no attributes otherwise. If attrs
+ * is non-null, the resulting binding will have attrs as its attributes;
+ * any attributes associated with obj are ignored.
+ *
+ * @param name the name to bind; may not be empty
+ * @param obj the object to bind; possibly null
+ * @param attrs the attributes to associate with the binding
+ * @exception NameAlreadyBoundException if name is already bound
+ * @exception InvalidAttributesException if some "mandatory" attributes
+ * of the binding are not supplied
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void bind(String name, Object obj, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Binds a name to an object, along with associated attributes,
+ * overwriting any existing binding. If attrs is null and obj is a
+ * DirContext, the attributes from obj are used. If attrs is null and obj
+ * is not a DirContext, any existing attributes associated with the object
+ * already bound in the directory remain unchanged. If attrs is non-null,
+ * any existing attributes associated with the object already bound in
+ * the directory are removed and attrs is associated with the named
+ * object. If obj is a DirContext and attrs is non-null, the attributes
+ * of obj are ignored.
+ *
+ * @param name the name to bind; may not be empty
+ * @param obj the object to bind; possibly null
+ * @param attrs the attributes to associate with the binding
+ * @exception InvalidAttributesException if some "mandatory" attributes
+ * of the binding are not supplied
+ * @exception NamingException if a naming exception is encountered
+ */
+ public void rebind(String name, Object obj, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Creates and binds a new context, along with associated attributes.
+ * This method creates a new subcontext with the given name, binds it in
+ * the target context (that named by all but terminal atomic component of
+ * the name), and associates the supplied attributes with the newly
+ * created object. All intermediate and target contexts must already
+ * exist. If attrs is null, this method is equivalent to
+ * Context.createSubcontext().
+ *
+ * @param name the name of the context to create; may not be empty
+ * @param attrs the attributes to associate with the newly created context
+ * @return the newly created context
+ * @exception NameAlreadyBoundException if the name is already bound
+ * @exception InvalidAttributesException if attrs does not contain all
+ * the mandatory attributes required for creation
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext createSubcontext(String name, Attributes attrs)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves the schema associated with the named object. The schema
+ * describes rules regarding the structure of the namespace and the
+ * attributes stored within it. The schema specifies what types of
+ * objects can be added to the directory and where they can be added;
+ * what mandatory and optional attributes an object can have. The range
+ * of support for schemas is directory-specific.
+ *
+ * @param name the name of the object whose schema is to be retrieved
+ * @return the schema associated with the context; never null
+ * @exception OperationNotSupportedException if schema not supported
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext getSchema(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Retrieves a context containing the schema objects of the named
+ * object's class definitions.
+ *
+ * @param name the name of the object whose object class definition is to
+ * be retrieved
+ * @return the DirContext containing the named object's class
+ * definitions; never null
+ * @exception OperationNotSupportedException if schema not supported
+ * @exception NamingException if a naming exception is encountered
+ */
+ public DirContext getSchemaClassDefinition(String name)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in a single context for objects that contain a specified set
+ * of attributes, and retrieves selected attributes. The search is
+ * performed using the default SearchControls settings.
+ *
+ * @param name the name of the context to search
+ * @param matchingAttributes the attributes to search for. If empty or
+ * null, all objects in the target context are returned.
+ * @param attributesToReturn the attributes to return. null indicates
+ * that all attributes are to be returned; an empty array indicates that
+ * none are to be returned.
+ * @return a non-null enumeration of SearchResult objects. Each
+ * SearchResult contains the attributes identified by attributesToReturn
+ * and the name of the corresponding object, named relative to the
+ * context named by name.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, Attributes matchingAttributes,
+ String[] attributesToReturn)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in a single context for objects that contain a specified set
+ * of attributes. This method returns all the attributes of such objects.
+ * It is equivalent to supplying null as the atributesToReturn parameter
+ * to the method search(Name, Attributes, String[]).
+ *
+ * @param name the name of the context to search
+ * @param matchingAttributes the attributes to search for. If empty or
+ * null, all objects in the target context are returned.
+ * @return a non-null enumeration of SearchResult objects. Each
+ * SearchResult contains the attributes identified by attributesToReturn
+ * and the name of the corresponding object, named relative to the
+ * context named by name.
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, Attributes matchingAttributes)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in the named context or object for entries that satisfy the
+ * given search filter. Performs the search as specified by the search
+ * controls.
+ *
+ * @param name the name of the context or object to search
+ * @param filter the filter expression to use for the search; may not be
+ * null
+ * @param cons the search controls that control the search. If null,
+ * the default search controls are used (equivalent to
+ * (new SearchControls())).
+ * @return an enumeration of SearchResults of the objects that satisfy
+ * the filter; never null
+ * @exception InvalidSearchFilterException if the search filter specified
+ * is not supported or understood by the underlying directory
+ * @exception InvalidSearchControlsException if the search controls
+ * contain invalid settings
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, String filter,
+ SearchControls cons)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ /**
+ * Searches in the named context or object for entries that satisfy the
+ * given search filter. Performs the search as specified by the search
+ * controls.
+ *
+ * @param name the name of the context or object to search
+ * @param filterExpr the filter expression to use for the search.
+ * The expression may contain variables of the form "{i}" where i is a
+ * nonnegative integer. May not be null.
+ * @param filterArgs the array of arguments to substitute for the
+ * variables in filterExpr. The value of filterArgs[i] will replace each
+ * occurrence of "{i}". If null, equivalent to an empty array.
+ * @param cons the search controls that control the search. If null, the
+ * default search controls are used (equivalent to (new SearchControls())).
+ * @return an enumeration of SearchResults of the objects that satisy the
+ * filter; never null
+ * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i}
+ * expressions where i is outside the bounds of the array filterArgs
+ * @exception InvalidSearchControlsException if cons contains invalid
+ * settings
+ * @exception InvalidSearchFilterException if filterExpr with filterArgs
+ * represents an invalid search filter
+ * @exception NamingException if a naming exception is encountered
+ */
+ public NamingEnumeration search(String name, String filterExpr,
+ Object[] filterArgs, SearchControls cons)
+ throws NamingException {
+ throw new OperationNotSupportedException();
+ }
+
+
+ // ------------------------------------------------------ Protected Methods
+
+
+ /**
+ * Add a prefix.
+ */
+ protected String prefix(String path) {
+ if (prefix == null) {
+ return path;
+ } else if (path.startsWith("/")) {
+ return prefix + path;
+ } else {
+ return prefix + "/" + path;
+ }
+ }
+
+
+ /**
+ * Remove a prefix.
+ */
+ protected String unprefix(String path) {
+ if (prefix == null) {
+ return path;
+ } else if (path.startsWith("/")) {
+ if (path.startsWith(prefix)) {
+
+ }
+ return prefix + path;
+ } else {
+ return prefix + "/" + path;
+ }
+ }
+
+
+ /**
+ * Normalize the name of an entry read from the Zip.
+ */
+ protected String normalize(ZipEntry entry) {
+
+ String result = "/" + entry.getName();
+ if (entry.isDirectory()) {
+ result = result.substring(0, result.length() - 1);
+ }
+ return result;
+
+ }
+
+
+ /**
+ * Constructs a tree of the entries contained in a WAR file.
+ */
+ protected void loadEntries() {
+
+ try {
+
+ Enumeration<JarEntry> entryList = base.entries();
+ entries = new Entry("/", new JarEntry(prefix + "/"));
+
+ while (entryList.hasMoreElements()) {
+
+ JarEntry entry = entryList.nextElement();
+ String name = normalize(entry);
+ if (prefix != null && (!name.startsWith(prefix))) {
+ // Don't create entries for everything in the JAR if there is a prefix
+ continue;
+ }
+ name = name.substring(prefix.length());
+ int pos = name.lastIndexOf('/');
+ // Check that parent entries exist and, if not, create them.
+ // This fixes a bug for war files that don't record separate
+ // zip entries for the directories.
+ int currentPos = -1;
+ int lastPos = 0;
+ while ((currentPos = name.indexOf('/', lastPos)) != -1) {
+ Name parentName = new CompositeName(name.substring(0, lastPos));
+ Name childName = new CompositeName(name.substring(0, currentPos));
+ String entryName = name.substring(lastPos, currentPos);
+ // Parent should have been created in last cycle through
+ // this loop
+ Entry parent = treeLookup(parentName);
+ Entry child = treeLookup(childName);
+ if (child == null) {
+ // Create a new entry for missing entry and strip off
+ // the leading '/' character and appended on by the
+ // normalize method and add '/' character to end to
+ // signify that it is a directory entry
+ String zipName = name.substring(1, currentPos) + "/";
+ child = new Entry(entryName, new JarEntry(prefix + zipName));
+ if (parent != null)
+ parent.addChild(child);
+ }
+ // Increment lastPos
+ lastPos = currentPos + 1;
+ }
+ String entryName = name.substring(pos + 1, name.length());
+ Name compositeName = new CompositeName(name.substring(0, pos));
+ Entry parent = treeLookup(compositeName);
+ Entry child = new Entry(entryName, entry);
+ if (parent != null)
+ parent.addChild(child);
+
+ }
+
+ } catch (Exception e) {
+ }
+
+ }
+
+
+ /**
+ * Entry tree lookup.
+ */
+ protected Entry treeLookup(Name name) {
+ if (name.isEmpty())
+ return entries;
+ Entry currentEntry = entries;
+ for (int i = 0; i < name.size(); i++) {
+ if (name.get(i).length() == 0)
+ continue;
+ currentEntry = currentEntry.getChild(name.get(i));
+ if (currentEntry == null)
+ return null;
+ }
+ return currentEntry;
+ }
+
+
+ /**
+ * List children as objects.
+ */
+ protected ArrayList list(Entry entry) {
+
+ ArrayList entries = new ArrayList();
+ Entry[] children = entry.getChildren();
+ Arrays.sort(children);
+ NamingEntry namingEntry = null;
+
+ for (int i = 0; i < children.length; i++) {
+ JarEntry current = children[i].getEntry();
+ Object object = null;
+ if (current.isDirectory()) {
+ object = new JARDirContext(base, children[i]);
+ } else {
+ object = new JARResource(current);
+ }
+ namingEntry = new NamingEntry
+ (children[i].getName(), object, NamingEntry.ENTRY);
+ entries.add(namingEntry);
+ }
+
+ return entries;
+
+ }
+
+
+ // ---------------------------------------------------- Entries Inner Class
+
+
+ /**
+ * Entries structure.
+ */
+ protected class Entry implements Comparable {
+
+
+ // -------------------------------------------------------- Constructor
+
+
+ public Entry(String name, JarEntry entry) {
+ this.name = name;
+ this.entry = entry;
+ }
+
+
+ // --------------------------------------------------- Member Variables
+
+
+ protected String name = null;
+
+
+ protected JarEntry entry = null;
+
+
+ protected Entry children[] = new Entry[0];
+
+
+ // ----------------------------------------------------- Public Methods
+
+
+ public int compareTo(Object o) {
+ if (!(o instanceof Entry))
+ return (+1);
+ return (name.compareTo(((Entry) o).getName()));
+ }
+
+ public JarEntry getEntry() {
+ return entry;
+ }
+
+
+ public String getName() {
+ return name;
+ }
+
+
+ public void addChild(Entry entry) {
+ Entry[] newChildren = new Entry[children.length + 1];
+ for (int i = 0; i < children.length; i++)
+ newChildren[i] = children[i];
+ newChildren[children.length] = entry;
+ children = newChildren;
+ }
+
+
+ public Entry[] getChildren() {
+ return children;
+ }
+
+
+ public Entry getChild(String name) {
+ for (int i = 0; i < children.length; i++) {
+ if (children[i].name.equals(name)) {
+ return children[i];
+ }
+ }
+ return null;
+ }
+
+
+ }
+
+
+ // ------------------------------------------------ WARResource Inner Class
+
+
+ /**
+ * This specialized resource implementation avoids opening the IputStream
+ * to the JAR right away.
+ */
+ protected class JARResource extends Resource {
+
+
+ // -------------------------------------------------------- Constructor
+
+
+ public JARResource(JarEntry entry) {
+ this.entry = entry;
+ }
+
+
+ // --------------------------------------------------- Member Variables
+
+
+ protected JarEntry entry;
+
+
+ // ----------------------------------------------------- Public Methods
+
+
+ /**
+ * Content accessor.
+ *
+ * @return InputStream
+ */
+ public InputStream streamContent()
+ throws IOException {
+ try {
+ if (binaryContent == null) {
+ inputStream = base.getInputStream(entry);
+ }
+ } catch (ZipException e) {
+ throw new IOException(e.getMessage());
+ }
+ return super.streamContent();
+ }
+
+
+ }
+
+
+}
+
16 years, 4 months
JBossWeb SVN: r1040 - in trunk: java/org/apache/catalina/realm and 2 other directories.
by jbossweb-commits@lists.jboss.org
Author: remy.maucherat(a)jboss.com
Date: 2009-05-01 12:52:00 -0400 (Fri, 01 May 2009)
New Revision: 1040
Modified:
trunk/java/org/apache/catalina/core/StandardHostValve.java
trunk/java/org/apache/catalina/realm/JNDIRealm.java
trunk/java/org/apache/catalina/startup/Tomcat.java
trunk/webapps/docs/changelog.xml
Log:
- Port 3 Tomcat patches (mostly the JNDI realm rewrite).
Modified: trunk/java/org/apache/catalina/core/StandardHostValve.java
===================================================================
--- trunk/java/org/apache/catalina/core/StandardHostValve.java 2009-04-30 17:50:00 UTC (rev 1039)
+++ trunk/java/org/apache/catalina/core/StandardHostValve.java 2009-05-01 16:52:00 UTC (rev 1040)
@@ -33,7 +33,6 @@
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.deploy.ErrorPage;
-import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.util.StringManager;
import org.apache.catalina.valves.ValveBase;
import org.jboss.logging.Logger;
@@ -251,10 +250,10 @@
(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
errorPage.getLocation());
request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- new Integer(ApplicationFilterFactory.ERROR));
+ Integer.valueOf(ApplicationFilterFactory.ERROR));
request.setAttribute
(Globals.STATUS_CODE_ATTR,
- new Integer(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
+ Integer.valueOf(HttpServletResponse.SC_INTERNAL_SERVER_ERROR));
request.setAttribute(Globals.ERROR_MESSAGE_ATTR,
throwable.getMessage());
request.setAttribute(Globals.EXCEPTION_ATTR,
@@ -320,9 +319,9 @@
if (errorPage != null) {
response.setAppCommitted(false);
request.setAttribute(Globals.STATUS_CODE_ATTR,
- new Integer(statusCode));
+ Integer.valueOf(statusCode));
- String message = RequestUtil.filter(response.getMessage());
+ String message = response.getMessage();
if (message == null)
message = "";
request.setAttribute(Globals.ERROR_MESSAGE_ATTR, message);
@@ -330,7 +329,7 @@
(ApplicationFilterFactory.DISPATCHER_REQUEST_PATH_ATTR,
errorPage.getLocation());
request.setAttribute(ApplicationFilterFactory.DISPATCHER_TYPE_ATTR,
- new Integer(ApplicationFilterFactory.ERROR));
+ Integer.valueOf(ApplicationFilterFactory.ERROR));
Wrapper wrapper = request.getWrapper();
Modified: trunk/java/org/apache/catalina/realm/JNDIRealm.java
===================================================================
--- trunk/java/org/apache/catalina/realm/JNDIRealm.java 2009-04-30 17:50:00 UTC (rev 1039)
+++ trunk/java/org/apache/catalina/realm/JNDIRealm.java 2009-05-01 16:52:00 UTC (rev 1040)
@@ -29,7 +29,6 @@
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import javax.naming.AuthenticationException;
@@ -145,9 +144,10 @@
* authenticated by setting the <code>commonRole</code> property to the
* name of this role. The role doesn't have to exist in the directory.</li>
*
- * <li>If the directory server contains nested roles, you can search for roles
- * recursively by setting <code>roleRecursionLimit</code> to some positive value.
- * The default value is <code>0</code>, so role searches do not recurse.</li>
+ * <li>If the directory server contains nested roles, you can search for them
+ * by setting <code>roleNested</code> to <code>true</code>.
+ * The default value is <code>false</code>, so role searches will not find
+ * nested roles.</li>
*
* <li>Note that the standard <code><security-role-ref></code> element in
* the web application deployment descriptor allows applications to refer
@@ -320,15 +320,7 @@
*/
protected MessageFormat[] userPatternFormatArray = null;
-
/**
- * The maximum recursion depth when resolving roles recursively.
- * By default we don't resolve roles recursively.
- */
- protected int roleRecursionLimit = 0;
-
-
- /**
* The base element for role searches.
*/
protected String roleBase = "";
@@ -365,7 +357,13 @@
* Should we search the entire subtree for matching memberships?
*/
protected boolean roleSubtree = false;
+
+ /**
+ * Should we look for nested group in order to determine roles?
+ */
+ protected boolean roleNested = false;
+
/**
* An alternate URL, to which, we should connect if connectionURL fails.
*/
@@ -655,28 +653,6 @@
/**
- * Return the maximum recursion depth for role searches.
- */
- public int getRoleRecursionLimit() {
-
- return (this.roleRecursionLimit);
-
- }
-
-
- /**
- * Set the maximum recursion depth for role searches.
- *
- * @param roleRecursionLimit The new recursion limit
- */
- public void setRoleRecursionLimit(int roleRecursionLimit) {
-
- this.roleRecursionLimit = roleRecursionLimit;
-
- }
-
-
- /**
* Return the base element for role searches.
*/
public String getRoleBase() {
@@ -766,9 +742,31 @@
this.roleSubtree = roleSubtree;
}
+
+ /**
+ * Return the "The nested group search flag" flag.
+ */
+ public boolean getRoleNested() {
+ return (this.roleNested);
+ }
+
+
/**
+ * Set the "search subtree for roles" flag.
+ *
+ * @param roleNested The nested group search flag
+ */
+ public void setRoleNested(boolean roleNested) {
+
+ this.roleNested = roleNested;
+
+ }
+
+
+
+ /**
* Return the password attribute used to retrieve the user password.
*/
public String getUserPassword() {
@@ -1297,7 +1295,7 @@
attrIds = new String[0];
constraints.setReturningAttributes(attrIds);
- NamingEnumeration results =
+ NamingEnumeration<SearchResult> results =
context.search(userBase, filter, constraints);
@@ -1314,7 +1312,7 @@
}
// Get result for the first entry found
- SearchResult result = (SearchResult)results.next();
+ SearchResult result = results.next();
// Check no further entries were found
try {
@@ -1549,71 +1547,7 @@
return (validated);
}
-
/**
- * Add roles to a user and search for other roles containing them themselves.
- * We search recursively with a limited depth.
- * By default the depth is 0, and we only use direct roles.
- * The search needs to use the distinguished role names,
- * but to return the role names.
- *
- * @param depth Recursion depth, starting at zero
- * @param context The directory context we are searching
- * @param recursiveMap The cumulative result map of role names and DNs.
- * @param recursiveSet The cumulative result set of role names.
- * @param groupName The role name to add to the list.
- * @param groupDName The distinguished name of the role.
- *
- * @exception NamingException if a directory server error occurs
- */
- private void getRolesRecursive(int depth, DirContext context, Map<String, String> recursiveMap, Set<String> recursiveSet,
- String groupName, String groupDName) throws NamingException {
- if (containerLog.isTraceEnabled())
- containerLog.trace("Recursive search depth " + depth + " for group '" + groupDName + " (" + groupName + ")'");
- // Adding the given group to the result set if not already found
- if (!recursiveSet.contains(groupDName)) {
- recursiveSet.add(groupDName);
- recursiveMap.put(groupDName, groupName);
- if (depth >= roleRecursionLimit) {
- if (roleRecursionLimit > 0)
- containerLog.warn("Terminating recursive role search because of recursion limit " +
- roleRecursionLimit + ", results might be incomplete");
- return;
- }
- // Prepare the parameters for searching groups
- String filter = roleFormat.format(new String[] { groupDName });
- SearchControls controls = new SearchControls();
- controls.setSearchScope(roleSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE);
- controls.setReturningAttributes(new String[] { roleName });
- if (containerLog.isTraceEnabled()) {
- containerLog.trace("Recursive search in role base '" + roleBase + "' for attribute '" + roleName + "'" +
- " with filter expression '" + filter + "'");
- }
- // Searching groups that assign the given group
- NamingEnumeration results = context.search(roleBase, filter, controls);
- if (results != null) {
- // Iterate over the resulting groups
- try {
- while (results.hasMore()) {
- SearchResult result = (SearchResult) results.next();
- Attributes attrs = result.getAttributes();
- if (attrs == null)
- continue;
- String dname = getDistinguishedName(context, roleBase, result);
- String name = getAttributeValue(roleName, attrs);
- if (name != null && dname != null) {
- getRolesRecursive(depth+1, context, recursiveMap, recursiveSet, name, dname);
- }
- }
- } catch (PartialResultException ex) {
- if (!adCompat)
- throw ex;
- }
- }
- }
- }
-
- /**
* Return a List of roles associated with the given User. Any
* roles present in the user's directory entry are supplemented by
* a directory search. If no roles are associated with this user,
@@ -1648,19 +1582,15 @@
list.add(commonRole);
if (containerLog.isTraceEnabled()) {
- if (list != null) {
- containerLog.trace(" Found " + list.size() + " user internal roles");
- for (int i=0; i<list.size(); i++)
- containerLog.trace( " Found user internal role " + list.get(i));
- } else {
- containerLog.trace(" Found no user internal roles");
- }
+ containerLog.trace(" Found " + list.size() + " user internal roles");
+ for (int i=0; i<list.size(); i++)
+ containerLog.trace( " Found user internal role " + list.get(i));
}
// Are we configured to do role searches?
if ((roleFormat == null) || (roleName == null))
return (list);
-
+
// Set up parameters for an appropriate search
String filter = roleFormat.format(new String[] { doRFC2254Encoding(dn), username });
SearchControls controls = new SearchControls();
@@ -1671,7 +1601,7 @@
controls.setReturningAttributes(new String[] {roleName});
// Perform the configured search and process the results
- NamingEnumeration results =
+ NamingEnumeration<SearchResult> results =
context.search(roleBase, filter, controls);
if (results == null)
return (list); // Should never happen, but just in case ...
@@ -1679,7 +1609,7 @@
HashMap<String, String> groupMap = new HashMap<String, String>();
try {
while (results.hasMore()) {
- SearchResult result = (SearchResult) results.next();
+ SearchResult result = results.next();
Attributes attrs = result.getAttributes();
if (attrs == null)
continue;
@@ -1697,30 +1627,60 @@
Set<String> keys = groupMap.keySet();
if (containerLog.isTraceEnabled()) {
containerLog.trace(" Found " + keys.size() + " direct roles");
- for (Iterator<String> i = keys.iterator(); i.hasNext();) {
- Object k = i.next();
- containerLog.trace( " Found direct role " + k + " -> " + groupMap.get(k));
+ for (String key: keys) {
+ containerLog.trace( " Found direct role " + key + " -> " + groupMap.get(key));
}
}
- HashSet<String> recursiveSet = new HashSet<String>();
- HashMap<String, String> recursiveMap = new HashMap<String, String>();
+ // if nested group search is enabled, perform searches for nested groups until no new group is found
+ if (getRoleNested()) {
- for (Iterator<String> i = keys.iterator(); i.hasNext();) {
- String k = i.next();
- getRolesRecursive(0, context, recursiveMap, recursiveSet, groupMap.get(k), k);
- }
+ // The following efficient algorithm is known as memberOf Algorithm, as described in "Practices in
+ // Directory Groups". It avoids group slurping and handles cyclic group memberships as well.
+ // See http://middleware.internet2.edu/dir/ for details
- HashSet<String> resultSet = new HashSet<String>(list);
- resultSet.addAll(recursiveMap.values());
+ Set<String> newGroupDNs = new HashSet<String>(groupMap.keySet());
+ while (!newGroupDNs.isEmpty()) {
+ Set<String> newThisRound = new HashSet<String>(); // Stores the groups we find in this iteration
- if (containerLog.isTraceEnabled()) {
- containerLog.trace(" Returning " + resultSet.size() + " roles");
- for (Iterator<String> i = resultSet.iterator(); i.hasNext();)
- containerLog.trace( " Found role " + i.next());
+ for (String groupDN : newGroupDNs) {
+ filter = roleFormat.format(new String[] { groupDN });
+
+ if (containerLog.isTraceEnabled()) {
+ containerLog.trace("Perform a nested group search with base "+ roleBase + " and filter " + filter);
+ }
+
+ results = context.search(roleBase, filter, controls);
+
+ try {
+ while (results.hasMore()) {
+ SearchResult result = results.next();
+ Attributes attrs = result.getAttributes();
+ if (attrs == null)
+ continue;
+ String dname = getDistinguishedName(context, roleBase, result);
+ String name = getAttributeValue(roleName, attrs);
+ if (name != null && dname != null && !groupMap.keySet().contains(dname)) {
+ groupMap.put(dname, name);
+ newThisRound.add(dname);
+
+ if (containerLog.isTraceEnabled()) {
+ containerLog.trace(" Found nested role " + dname + " -> " + name);
+ }
+
+ }
+ }
+ } catch (PartialResultException ex) {
+ if (!adCompat)
+ throw ex;
+ }
+ }
+
+ newGroupDNs = newThisRound;
+ }
}
- return new ArrayList<String>(resultSet);
+ return new ArrayList<String>(groupMap.values());
}
@@ -1781,7 +1741,7 @@
Attribute attr = attrs.get(attrId);
if (attr == null)
return (values);
- NamingEnumeration e = attr.getAll();
+ NamingEnumeration<?> e = attr.getAll();
try {
while(e.hasMore()) {
String value = (String)e.next();
@@ -1972,7 +1932,7 @@
*
* @return java.util.Hashtable the configuration for the directory context.
*/
- protected Hashtable getDirectoryContextEnvironment() {
+ protected Hashtable<String,String> getDirectoryContextEnvironment() {
Hashtable<String,String> env = new Hashtable<String,String>();
@@ -2011,7 +1971,7 @@
*/
protected void release(DirContext context) {
- ; // NO-OP since we are not pooling anything
+ // NO-OP since we are not pooling anything
}
@@ -2095,7 +2055,7 @@
startingPoint = endParenLoc+1;
startParenLoc = userPatternString.indexOf('(', startingPoint);
}
- return (String[])pathList.toArray(new String[] {});
+ return pathList.toArray(new String[] {});
}
return null;
Modified: trunk/java/org/apache/catalina/startup/Tomcat.java
===================================================================
--- trunk/java/org/apache/catalina/startup/Tomcat.java 2009-04-30 17:50:00 UTC (rev 1039)
+++ trunk/java/org/apache/catalina/startup/Tomcat.java 2009-05-01 16:52:00 UTC (rev 1040)
@@ -359,7 +359,7 @@
if(engine == null ) {
getServer();
engine = new StandardEngine();
- engine.setName( "default" );
+ engine.setName( "Tomcat" );
engine.setDefaultHost(hostname);
service.setContainer(engine);
}
Modified: trunk/webapps/docs/changelog.xml
===================================================================
--- trunk/webapps/docs/changelog.xml 2009-04-30 17:50:00 UTC (rev 1039)
+++ trunk/webapps/docs/changelog.xml 2009-05-01 16:52:00 UTC (rev 1040)
@@ -77,6 +77,13 @@
<add>
New JarRepository component to handle better management of JarFiles following the Servlet 3.0 update. (remm)
</add>
+ <fix>
+ <bug>47050</bug>: Remove unnecessary filtering in StandardHostValve. (markt)
+ </fix>
+ <fix>
+ <bug>46925</bug>: Improve search for nested roles in JNDIRealm by replacing recursive search with
+ iterative search. Patch provided by Stefan Zoerner. (rjung)
+ </fix>
</changelog>
</subsection>
<subsection name="Coyote">
16 years, 4 months