/*
* Copyright 2003-2006 Rick Knowles <winstone-devel at lists sourceforge net>
* Distributed under the terms of either:
* - the common development and distribution license (CDDL), v1.0; or
* - the GNU Lesser General Public License, v2.1 or later
*/
package winstone.jndi;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameNotFoundException;
import javax.naming.NameParser;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.NotContextException;
import javax.naming.OperationNotSupportedException;
import javax.naming.spi.NamingManager;
import winstone.Logger;
/**
* The main jndi context implementation class.
*
* @author <a href="mailto:rick_knowles@hotmail.com">Rick Knowles</a>
* @version $Id: WinstoneContext.java,v 1.3 2006/02/28 07:32:48 rickknowles Exp $
*/
public class WinstoneContext implements Context {
static final String PREFIX = "java:";
static final String FIRST_CHILD = "comp";
static final String BODGED_PREFIX = "java:comp";
private Hashtable environment;
private Hashtable bindings;
private final static NameParser nameParser = new WinstoneNameParser();
private WinstoneContext parent;
private String myAbsoluteName;
private Object contextLock;
/**
* Constructor - sets up environment
*/
public WinstoneContext(Map sourceEnvironment, WinstoneContext parent,
String absoluteName, Object contextLock) throws NamingException {
this.environment = new Hashtable();
List sourceKeys = new ArrayList(sourceEnvironment.keySet());
for (Iterator i = sourceKeys.iterator(); i.hasNext();) {
String key = (String) i.next();
addToEnvironment(key, sourceEnvironment.get(key));
}
this.parent = parent;
this.myAbsoluteName = absoluteName;
this.contextLock = contextLock;
this.bindings = new Hashtable();
Logger.log(Logger.FULL_DEBUG, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.Initialised", this.myAbsoluteName);
}
/**
* Constructor - sets up environment and copies the bindings across
*/
protected WinstoneContext(Map sourceEnvironment, WinstoneContext parent,
String absoluteName, Object contextLock, Hashtable bindings) throws NamingException {
this.environment = new Hashtable();
List sourceKeys = new ArrayList(sourceEnvironment.keySet());
for (Iterator i = sourceKeys.iterator(); i.hasNext();) {
String key = (String) i.next();
addToEnvironment(key, sourceEnvironment.get(key));
}
this.parent = parent;
this.myAbsoluteName = absoluteName;
this.contextLock = contextLock;
this.bindings = bindings;
Logger.log(Logger.FULL_DEBUG, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.Copied", this.myAbsoluteName);
}
public void close() throws NamingException {
}
public Hashtable getEnvironment() throws NamingException {
return new Hashtable(this.environment);
}
public Object removeFromEnvironment(String property) throws NamingException {
return this.environment.remove(property);
}
public Object addToEnvironment(String property, Object value)
throws NamingException {
return this.environment.put(property, value);
}
/**
* Handles the processing of relative and absolute names. If a relative name
* is detected, it is processed by the name parser. If an absolute name is
* detected, it determines first if the absolute name refers to this
* context. If not, it then determines whether the request can be passed
* back to the parent or not, and returns null if it can, and throws an
* exception otherwise.
*/
protected Name validateName(Name name) throws NamingException {
// Check for absolute urls and redirect or correct
if (name.isEmpty())
return name;
else if (name.get(0).equals(BODGED_PREFIX)) {
Name newName = name.getSuffix(1).add(0, FIRST_CHILD).add(0, PREFIX);
return validateName(newName);
} else if (name.get(0).equals(PREFIX)) {
String stringName = name.toString();
if (stringName.equals(this.myAbsoluteName))
return nameParser.parse("");
else if (stringName.startsWith(this.myAbsoluteName))
return nameParser.parse(stringName
.substring(this.myAbsoluteName.length() + 1));
else if (this.parent != null)
return null;
else
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString()));
} else if (name instanceof CompositeName)
return nameParser.parse(name.toString());
else
return name;
}
/**
* Lookup an object in the context. Returns a copy of this context if the
* name is empty, or the specified resource (if we have it). If the name is
* unknown, throws a NameNotFoundException.
*/
public Object lookup(Name name) throws NamingException {
Name searchName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (searchName == null)
return this.parent.lookup(name);
// If empty name, return a copy of this Context
else if (searchName.isEmpty())
return new WinstoneContext(this.environment, this.parent,
this.myAbsoluteName, this.contextLock, this.bindings);
String thisName = searchName.get(0);
synchronized (this.contextLock) {
Object thisValue = bindings.get(thisName);
// If the name points to something in this level, try to find it,
// and give
// an error if not available
if (searchName.size() == 1) {
if (thisValue == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString()));
try {
return NamingManager.getObjectInstance(thisValue,
new CompositeName().add(thisName), this,
this.environment);
} catch (Exception e) {
NamingException ne = new NamingException(ContainerJNDIManager.JNDI_RESOURCES
.getString("WinstoneContext.FailedToGetInstance"));
ne.setRootCause(e);
throw ne;
}
}
else if (thisValue == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", thisName.toString()));
// If it's not in this level and what we found is not a context,
// complain
else if (!(thisValue instanceof Context))
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
thisName.toString(),
thisValue.getClass().getName() }));
// Open the context, perform a lookup, then close the context we
// opened
else
try {
return ((Context) thisValue)
.lookup(searchName.getSuffix(1));
} finally {
((Context) thisValue).close();
}
}
}
public Object lookup(String name) throws NamingException {
return lookup(new CompositeName(name));
}
public Object lookupLink(Name name) throws NamingException {
Logger.log(Logger.WARNING, ContainerJNDIManager.JNDI_RESOURCES,
"WinstoneContext.LinkRefUnsupported");
return lookup(name);
}
public Object lookupLink(String name) throws NamingException {
return lookupLink(new CompositeName(name));
}
/**
* Returns a list of objects bound to the context
*/
public NamingEnumeration list(Name name) throws NamingException {
Name searchName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (searchName == null)
return this.parent.list(name);
// If empty name, return a copy of this Context
else if (searchName.isEmpty()) {
NamingEnumeration e = null;
synchronized (this.contextLock) {
e = new WinstoneNameEnumeration(this.bindings);
}
return e;
}
// Lookup the object - if it's not a context, throw an error
else {
Object ctx = this.lookup(searchName);
if (ctx instanceof Context)
try {
return ((Context) ctx).list(new CompositeName(""));
} finally {
((Context) ctx).close();
}
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", searchName.toString()));
else
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext",
new String[] { searchName.toString(),
ctx.getClass().getName() }));
}
}
public NamingEnumeration list(String name) throws NamingException {
return list(new CompositeName(name));
}
public NamingEnumeration listBindings(Name name) throws NamingException {
Name searchName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (searchName == null)
return this.parent.list(name);
// If empty name, return a copy of this Context
else if (searchName.isEmpty()) {
NamingEnumeration e = null;
synchronized (this.contextLock) {
e = new WinstoneBindingEnumeration(this.bindings,
this.environment, this);
}
return e;
}
// Lookup the object - if it's not a context, throw an error
else {
Object ctx = this.lookup(searchName);
if (ctx instanceof Context)
try {
return ((Context) ctx).listBindings(new CompositeName(""));
} finally {
((Context) ctx).close();
}
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", searchName.toString()));
else
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext",
new String[] { searchName.toString(),
ctx.getClass().getName() }));
}
}
public NamingEnumeration listBindings(String name) throws NamingException {
return listBindings(new CompositeName(name));
}
public NameParser getNameParser(Name name) throws NamingException {
Object obj = lookup(name);
if (obj instanceof Context) {
((Context) obj).close();
}
return nameParser;
}
public NameParser getNameParser(String name) throws NamingException {
return getNameParser(new CompositeName(name));
}
public String getNameInNamespace() throws NamingException {
return this.myAbsoluteName;
}
/***************************************************************************
* Below here is for read-write contexts ... *
**************************************************************************/
public void bind(String name, Object value) throws NamingException {
bind(new CompositeName(name), value);
}
public void bind(Name name, Object value) throws NamingException {
bind(name, value, false);
}
protected void bind(Name name, Object value, boolean allowOverwrites)
throws NamingException {
Name bindName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (bindName == null)
this.parent.bind(name, value, allowOverwrites);
// If empty name, complain - we should have a child name here
else if (bindName.isEmpty())
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString()));
else if (bindName.size() > 1) {
Object ctx = lookup(bindName.get(0));
if (!(ctx instanceof Context))
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
bindName.get(0), ctx.getClass().getName() }));
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", bindName.get(0)));
else
try {
if (allowOverwrites)
((Context) ctx).rebind(bindName.getSuffix(1), value);
else
((Context) ctx).bind(bindName.getSuffix(1), value);
} finally {
((Context) ctx).close();
}
} else if ((!allowOverwrites) && this.bindings.get(name.get(0)) != null)
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString()));
else {
value = NamingManager.getStateToBind(value, new CompositeName()
.add(bindName.get(0)), this, this.environment);
synchronized (this.contextLock) {
this.bindings.put(bindName.get(0), value);
}
}
}
public void rebind(String name, Object value) throws NamingException {
rebind(new CompositeName(name), value);
}
public void rebind(Name name, Object value) throws NamingException {
bind(name, value, true);
}
public void unbind(String name) throws NamingException {
unbind(new CompositeName(name));
}
public void unbind(Name name) throws NamingException {
Name unbindName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (unbindName == null)
this.parent.unbind(name);
// If empty name, complain - we should have a child name here
else if (unbindName.isEmpty())
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES
.getString("WinstoneContext.CantUnbindEmptyName"));
else if (unbindName.size() > 1) {
Object ctx = lookup(unbindName.get(0));
if (!(ctx instanceof Context))
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
unbindName.get(0), ctx.getClass().getName() }));
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", unbindName.get(0)));
else
try {
((Context) ctx).unbind(unbindName.getSuffix(1));
} finally {
((Context) ctx).close();
}
} else if (this.bindings.get(name.get(0)) == null)
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", name.toString()));
else {
synchronized (this.contextLock) {
// Object removing = this.bindings.get(unbindName.get(0));
this.bindings.remove(unbindName.get(0));
}
}
}
public void rename(Name oldName, Name newName) throws NamingException {
throw new OperationNotSupportedException(
"rename not supported in Winstone java:/ context");
}
public void rename(String oldName, String newName) throws NamingException {
rename(new CompositeName(oldName), new CompositeName(newName));
}
public Context createSubcontext(String name) throws NamingException {
return createSubcontext(new CompositeName(name));
}
public Context createSubcontext(Name name) throws NamingException {
Name childName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the
// parent
if (childName == null)
return this.parent.createSubcontext(name);
// If empty name, complain - we should have a child name here
else if (childName.isEmpty())
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", name.toString()));
else if (childName.size() > 1) {
Object ctx = lookup(childName.get(0));
if (!(ctx instanceof Context))
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
childName.get(0), ctx.getClass().getName() }));
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", childName.get(0)));
else
try {
((Context) ctx).createSubcontext(childName.getSuffix(1));
} finally {
((Context) ctx).close();
}
}
Context childContext = null;
synchronized (this.contextLock) {
if (this.bindings.get(childName.get(0)) != null)
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.AlreadyExists", childName.get(0)));
else {
childContext = new WinstoneContext(this.environment, this,
this.myAbsoluteName + "/" + childName.get(0),
new Boolean(true));
this.bindings.put(childName.get(0), childContext);
}
}
return childContext;
}
public void destroySubcontext(String name) throws NamingException {
destroySubcontext(new CompositeName(name));
}
public void destroySubcontext(Name name) throws NamingException {
Name childName = validateName(name);
// If null, it means we don't know how to handle this -> throw to the parent
if (childName == null)
this.parent.destroySubcontext(name);
// If absolutely referring to this context, tell the parent to delete this context
else if (childName.isEmpty()) {
if (!name.isEmpty())
this.parent.destroySubcontext(name.getSuffix(name.size() - 2));
else
throw new NamingException(ContainerJNDIManager.JNDI_RESOURCES
.getString("WinstoneContext.CantDestroyEmptyName"));
} else if (childName.size() > 1) {
Object ctx = lookup(childName.get(0));
if (!(ctx instanceof Context))
throw new NotContextException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NotContext", new String[] {
childName.get(0), ctx.getClass().getName() }));
else if (ctx == null)
throw new NameNotFoundException(ContainerJNDIManager.JNDI_RESOURCES.getString(
"WinstoneContext.NameNotFound", childName.get(0)));
else
try {
((Context) ctx).destroySubcontext(childName.getSuffix(1));
} finally {
((Context) ctx).close();
}
} else
synchronized (this.contextLock) {
Context childContext = (Context) lookup(childName.get(0));
childContext.close();
this.bindings.remove(childName.get(0));
}
}
public String composeName(String name1, String name2)
throws NamingException {
Name name = composeName(new CompositeName(name1), new CompositeName(
name2));
return name == null ? null : name.toString();
}
public Name composeName(Name name1, Name name2) throws NamingException {
throw new OperationNotSupportedException(
"composeName not supported in Winstone java:/ namespace");
}
}