/*
* 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.
*/
/**
* @author Vasily Zakharov
*/
package org.apache.harmony.jndi.provider;
import java.util.Hashtable;
import javax.naming.Binding;
import javax.naming.CannotProceedException;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.OperationNotSupportedException;
import javax.naming.spi.NamingManager;
import javax.naming.spi.ResolveResult;
import org.apache.harmony.jndi.internal.nls.Messages;
/**
* Base class for URL naming context implementations.
*
* In many cases, subclasses should only override
* {@link #getRootURLContext(String, Hashtable)} method and provide a public
* constructor calling
* {@link GenericURLContext#GenericURLContext(Hashtable) super(environment)}.
*/
public abstract class GenericURLContext implements Context {
/**
* Local environment.
*/
protected Hashtable<Object, Object> environment;
/**
* Creates instance of this context with empty environment.
*/
protected GenericURLContext() {
this(null);
}
/**
* Creates instance of this context with specified environment.
*
* @param environment
* Environment to copy.
*/
@SuppressWarnings("unchecked")
protected GenericURLContext(Hashtable<?, ?> environment) {
super();
if (environment == null) {
this.environment = new Hashtable<Object, Object>();
} else {
this.environment = (Hashtable<Object, Object>) environment.clone();
}
}
/**
* {@inheritDoc}
*/
public Object lookup(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return lookup(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.lookup(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Object lookup(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.lookup(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Object lookupLink(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return lookupLink(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.lookupLink(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Object lookupLink(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.lookupLink(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public void bind(Name name, Object obj) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
bind(name.get(0), obj);
} else {
Context context = getContinuationContext(name);
try {
context.bind(name.getSuffix(1), obj);
} finally {
context.close();
}
}
}
/**
* {@inheritDoc}
*/
public void bind(String name, Object obj) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
context.bind(result.getRemainingName(), obj);
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public void rebind(Name name, Object obj) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
rebind(name.get(0), obj);
} else {
Context context = getContinuationContext(name);
try {
context.rebind(name.getSuffix(1), obj);
} finally {
context.close();
}
}
}
/**
* {@inheritDoc}
*/
public void rebind(String name, Object obj) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
context.rebind(result.getRemainingName(), obj);
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public void unbind(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
unbind(name.get(0));
} else {
Context context = getContinuationContext(name);
try {
context.unbind(name.getSuffix(1));
} finally {
context.close();
}
}
}
/**
* {@inheritDoc}
*/
public void unbind(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
context.unbind(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Context createSubcontext(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return createSubcontext(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.createSubcontext(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Context createSubcontext(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.createSubcontext(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public void destroySubcontext(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
destroySubcontext(name.get(0));
} else {
Context context = getContinuationContext(name);
try {
context.destroySubcontext(name.getSuffix(1));
} finally {
context.close();
}
}
}
/**
* {@inheritDoc}
*/
public void destroySubcontext(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
context.destroySubcontext(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*
* This method uses {@link #urlEquals(String, String)} to compare URL
* prefixes of the names.
*/
public void rename(Name oldName, Name newName) throws NamingException {
if (!(oldName instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString(
"jndi.26", oldName)); //$NON-NLS-1$
}
if (!(newName instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString(
"jndi.26", newName)); //$NON-NLS-1$
}
if ((oldName.size() == 1) ^ (newName.size() != 1)) {
// jndi.27=Renaming of names of which one has only one component, +
// and another has more than one component is not supported: {0} ->
// {1}
throw new OperationNotSupportedException(Messages.getString(
"jndi.27", oldName, newName)); //$NON-NLS-1$
}
if (oldName.size() == 1) {
rename(oldName.get(0), newName.get(0));
} else {
if (!urlEquals(oldName.get(0), oldName.get(0))) {
// jndi.28=Renaming of names using different URLs as their first
// components is not supported: {0} -> {1}
throw new OperationNotSupportedException(Messages.getString(
"jndi.28", oldName, newName)); //$NON-NLS-1$
}
Context context = getContinuationContext(oldName);
try {
context.rename(oldName.getSuffix(1), newName.getSuffix(1));
} finally {
context.close();
}
}
}
/**
* {@inheritDoc}
*
* This method uses {@link #getURLPrefix(String)} and
* {@link #getURLSuffix(String, String)} methods to parse string names, and
* also uses {@link #urlEquals(String, String)} to compare URL prefixes of
* the names.
*/
public void rename(String oldName, String newName) throws NamingException {
String oldPrefix = getURLPrefix(oldName);
String newPrefix = getURLPrefix(newName);
if (!urlEquals(oldPrefix, newPrefix)) {
// jndi.29=Renaming of names using different URL prefixes is not
// supported: {0} -> {1}
throw new OperationNotSupportedException(Messages.getString(
"jndi.29", oldName, newName)); //$NON-NLS-1$
}
ResolveResult result = getRootURLContext(oldName, environment);
Context context = (Context) result.getResolvedObj();
try {
context.rename(result.getRemainingName(), getURLSuffix(newPrefix,
newName));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<NameClassPair> list(Name name)
throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return list(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.list(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<NameClassPair> list(String name)
throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.list(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<Binding> listBindings(Name name)
throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return listBindings(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.listBindings(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NamingEnumeration<Binding> listBindings(String name)
throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.listBindings(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NameParser getNameParser(Name name) throws NamingException {
if (!(name instanceof CompositeName)) {
// jndi.26=URL context can't accept non-composite name: {0}
throw new InvalidNameException(Messages.getString("jndi.26", name)); //$NON-NLS-1$
}
if (name.size() == 1) {
return getNameParser(name.get(0));
}
Context context = getContinuationContext(name);
try {
return context.getNameParser(name.getSuffix(1));
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public NameParser getNameParser(String name) throws NamingException {
ResolveResult result = getRootURLContext(name, environment);
Context context = (Context) result.getResolvedObj();
try {
return context.getNameParser(result.getRemainingName());
} finally {
context.close();
}
}
/**
* {@inheritDoc}
*/
public Name composeName(Name name, Name prefix) throws NamingException {
return ((Name) prefix.clone()).addAll(name);
}
/**
* {@inheritDoc}
*/
public String composeName(String name, String prefix) {
return ((prefix.length() < 1) ? name : (name.length() < 1) ? prefix
: (prefix + '/' + name));
}
/**
* {@inheritDoc}
*/
public String getNameInNamespace() {
return ""; //$NON-NLS-1$
}
/**
* {@inheritDoc}
*/
public Hashtable<?, ?> getEnvironment() {
return (Hashtable<?, ?>) environment.clone();
}
/**
* {@inheritDoc}
*/
public Object addToEnvironment(String propName, Object propVal) {
return environment.put(propName, propVal);
}
/**
* {@inheritDoc}
*/
public Object removeFromEnvironment(String propName) {
return environment.remove(propName);
}
/**
* {@inheritDoc}
*/
public void close() {
environment = null;
}
/**
* Lookups the first component (considered a URL) of the specified name
* using {@link #lookup(String)}, wraps it into
* {@link CannotProceedException}, passes it to
* {@link NamingManager#getContinuationContext(CannotProceedException)}
* method and returns the result.
*
* This method is used by {@link #lookup(Name)} and other public methods
* taking {@link Name} as a parameter.
*
* This method uses {@link #createCannotProceedException(Name)} method.
*
* @param name
* Name to parse.
*
* @return Continuation context.
*
* @throws NamingException
* If some naming error occurs.
*/
protected Context getContinuationContext(Name name) throws NamingException {
return NamingManager
.getContinuationContext(createCannotProceedException(name));
}
/**
* Lookups the first component (considered a URL) of the specified name
* using {@link #lookup(String)} and wraps it into
* {@link CannotProceedException}.
*
* @param name
* Name to parse.
*
* @return Created {@link CannotProceedException}.
*
* @throws NamingException
* If some naming error occurs.
*/
protected final CannotProceedException createCannotProceedException(
Name name) throws NamingException {
CannotProceedException cpe = new CannotProceedException();
cpe.setResolvedObj(lookup(name.get(0)));
cpe.setEnvironment(environment);
return cpe;
}
/**
* Determines the proper context from the specified URL and returns the
* {@link ResolveResult} object with that context as resolved object and the
* rest of the URL as remaining name.
*
* This method is used by {@link #lookup(String)} and other public methods
* taking {@link String} name as a parameter.
*
* This method must be overridden by particular URL context implementations.
*
* When overriding make sure that {@link #getURLPrefix(String)},
* {@link #getURLSuffix(String, String)} and
* {@link #getRootURLContext(String, Hashtable)} methods are in sync in how
* they parse URLs.
*
* @param url
* URL.
*
* @param environment
* Environment.
*
* @return {@link ResolveResult} object with resolved context as resolved
* object the rest of the URL as remaining name.
*
* @throws NamingException
* If some naming error occurs.
*/
protected abstract ResolveResult getRootURLContext(String url,
Hashtable<?, ?> environment) throws NamingException;
/**
* Compares two URLs for equality.
*
* Implemented here as <code>url1.equals(url2)</code>. Subclasses may
* provide different implementation.
*
* This method is only used by {@link #rename(Name, Name)} and
* {@link #rename(String, String)} methods.
*
* @param url1
* First URL to compare.
*
* @param url2
* Second URL to compare.
*
* @return <code>true</code> if specified URLs are equal,
* <code>false</code> otherwise.
*/
protected boolean urlEquals(String url1, String url2) {
return url1.equals(url2);
}
/**
* Returns URL prefix, containing scheme name, hostname and port.
*
* This method is only used by {@link #rename(String, String)} method and
* may be overridden by subclasses.
*
* When overriding make sure that {@link #getURLPrefix(String)},
* {@link #getURLSuffix(String, String)} and
* {@link #getRootURLContext(String, Hashtable)} methods are in sync in how
* they parse URLs.
*
* @param url
* URL to parse.
*
* @return URL prefix.
*
* @throws NamingException
* If some naming error occurs.
*/
protected String getURLPrefix(String url) throws NamingException {
int index = url.indexOf(':');
if (index < 0) {
// jndi.2A=Invalid URL: {0}
throw new OperationNotSupportedException(Messages.getString(
"jndi.2A", url)); //$NON-NLS-1$
}
index++;
if (url.startsWith("//", index)) { //$NON-NLS-1$
int slashPos = url.indexOf('/', index + 2);
index = ((slashPos >= 0) ? slashPos : url.length());
}
return url.substring(0, index);
}
/**
* Returns URL suffix, containing everything but the
* {@linkplain #getURLPrefix(String) prefix} and separating slash, as a
* single-component {@link CompositeName}.
*
* This method is only used by {@link #rename(String, String)} method and
* may be overridden by subclasses.
*
* This method uses {@link #decodeURLString(String)} to decode the suffix
* string.
*
* When overriding make sure that {@link #getURLPrefix(String)},
* {@link #getURLSuffix(String, String)} and
* {@link #getRootURLContext(String, Hashtable)} methods are in sync in how
* they parse URLs.
*
* @param prefix
* URL prefix, returned by {@link #getURLPrefix(String)}
* previously called on the same URL.
*
* @param url
* URL to parse.
*
* @return URL suffix as a single-component {@link CompositeName}.
*
* @throws NamingException
* If some naming error occurs.
*/
protected Name getURLSuffix(String prefix, String url)
throws NamingException {
int length = prefix.length();
if (length >= (url.length() - 1)) {
// If prefix is only 1 character shorter than URL,
// that character is slash and can be ignored.
return new CompositeName();
}
String suffix = url
.substring((url.charAt(length) == '/') ? (length + 1) : length);
try {
return new CompositeName().add(decodeURLString(suffix));
} catch (IllegalArgumentException e) {
throw (InvalidNameException) new InvalidNameException()
.initCause(e);
}
}
/**
* Decodes URL string by transforming URL-encoded characters into their
* Unicode character representations.
*
* This method is used by {@link #getURLSuffix(String, String)}.
*
* @param str
* URL or part of URL string.
*
* @return Decoded string.
*
* @throws IllegalArgumentException
* If URL format is incorrect.
*/
protected static final String decodeURLString(String str)
throws IllegalArgumentException {
int length = str.length();
byte bytes[] = new byte[length];
int index = 0;
for (int i = 0; i < length;) {
char c = str.charAt(i++);
if (c == '%') {
int next = i + 2;
if (next > length) {
// jndi.2B=Invalid URL format: {0}
throw new IllegalArgumentException(Messages.getString(
"jndi.2B", str)); //$NON-NLS-1$
}
try {
bytes[index++] = (byte) Integer.parseInt(str.substring(i,
next), 16);
} catch (NumberFormatException e) {
// jndi.2B=Invalid URL format: {0}
throw (IllegalArgumentException) new IllegalArgumentException(
Messages.getString("jndi.2B", str)).initCause(e); //$NON-NLS-1$
}
i = next;
} else {
bytes[index++] = (byte) c;
}
}
return new String(bytes, 0, index);
}
}