org.apache.activemq.artemis.jndi.ReadOnlyContext Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* 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.activemq.artemis.jndi;
import javax.naming.Binding;
import javax.naming.CompositeName;
import javax.naming.Context;
import javax.naming.LinkRef;
import javax.naming.Name;
import javax.naming.NameClassPair;
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.Reference;
import javax.naming.spi.NamingManager;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import org.apache.activemq.artemis.core.client.ActiveMQClientLogger;
/**
* A read-only Context
*
* This version assumes it and all its subcontext are
* read-only and any attempt to modify (e.g. through bind) will result in an
* OperationNotSupportedException. Each Context in the tree builds a cache of
* the entries in all sub-contexts to optimise the performance of lookup.
*
* This implementation is intended to optimise the performance of lookup(String)
* to about the level of a HashMap get. It has been observed that the scheme
* resolution phase performed by the JVM takes considerably longer, so for
* optimum performance lookups should be coded like:
*
*
* Context componentContext = (Context)new InitialContext().lookup("java:comp");
* String envEntry = (String) componentContext.lookup("env/myEntry");
* String envEntry2 = (String) componentContext.lookup("env/myEntry2");
*
*/
@SuppressWarnings("unchecked")
public class ReadOnlyContext implements Context, Serializable {
public static final String SEPARATOR = "/";
protected static final NameParser NAME_PARSER = new NameParserImpl();
private static final long serialVersionUID = -5754338187296859149L;
protected final Hashtable environment; // environment for this context
protected final Map bindings; // bindings at my level
protected final Map treeBindings; // all bindings under me
private boolean frozen;
private String nameInNamespace = "";
public ReadOnlyContext() {
environment = new Hashtable<>();
bindings = new HashMap<>();
treeBindings = new HashMap<>();
}
public ReadOnlyContext(Hashtable env) {
if (env == null) {
this.environment = new Hashtable<>();
} else {
this.environment = new Hashtable<>(env);
}
this.bindings = Collections.EMPTY_MAP;
this.treeBindings = Collections.EMPTY_MAP;
}
public ReadOnlyContext(Hashtable environment, Map bindings) {
if (environment == null) {
this.environment = new Hashtable<>();
} else {
this.environment = new Hashtable<>(environment);
}
this.bindings = new HashMap<>();
treeBindings = new HashMap<>();
if (bindings != null) {
for (Map.Entry binding : bindings.entrySet()) {
try {
internalBind(binding.getKey(), binding.getValue());
} catch (Throwable e) {
ActiveMQClientLogger.LOGGER.failedToBind(binding.getKey(), binding.getValue().toString(), e);
}
}
}
frozen = true;
}
public ReadOnlyContext(Hashtable environment, Map bindings, String nameInNamespace) {
this(environment, bindings);
this.nameInNamespace = nameInNamespace;
}
protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env) {
this.bindings = clone.bindings;
this.treeBindings = clone.treeBindings;
this.environment = new Hashtable<>(env);
}
protected ReadOnlyContext(ReadOnlyContext clone, Hashtable env, String nameInNamespace) {
this(clone, env);
this.nameInNamespace = nameInNamespace;
}
public void freeze() {
frozen = true;
}
boolean isFrozen() {
return frozen;
}
/**
* internalBind is intended for use only during setup or possibly by
* suitably synchronized superclasses. It binds every possible lookup into a
* map in each context. To do this, each context strips off one name segment
* and if necessary creates a new context for it. Then it asks that context
* to bind the remaining name. It returns a map containing all the bindings
* from the next context, plus the context it just created (if it in fact
* created it). (the names are suitably extended by the segment originally
* lopped off).
*
* @param name
* @param value
* @return
* @throws javax.naming.NamingException
*/
protected Map internalBind(String name, Object value) throws NamingException {
assert name != null && name.length() > 0;
assert !frozen;
Map newBindings = new HashMap<>();
int pos = name.indexOf('/');
if (pos == -1) {
if (treeBindings.put(name, value) != null) {
throw new NamingException("Something already bound at " + name);
}
bindings.put(name, value);
newBindings.put(name, value);
} else {
String segment = name.substring(0, pos);
assert segment != null;
assert !segment.equals("");
Object o = treeBindings.get(segment);
if (o == null) {
o = newContext();
treeBindings.put(segment, o);
bindings.put(segment, o);
newBindings.put(segment, o);
} else if (!(o instanceof ReadOnlyContext)) {
throw new NamingException("Something already bound where a subcontext should go");
}
ReadOnlyContext readOnlyContext = (ReadOnlyContext) o;
String remainder = name.substring(pos + 1);
Map subBindings = readOnlyContext.internalBind(remainder, value);
for (Map.Entry entry : subBindings.entrySet()) {
String subName = segment + "/" + entry.getKey();
Object bound = entry.getValue();
treeBindings.put(subName, bound);
newBindings.put(subName, bound);
}
}
return newBindings;
}
protected ReadOnlyContext newContext() {
return new ReadOnlyContext();
}
@Override
public Object addToEnvironment(String propName, Object propVal) throws NamingException {
return environment.put(propName, propVal);
}
@Override
public Hashtable getEnvironment() throws NamingException {
return (Hashtable) environment.clone();
}
@Override
public Object removeFromEnvironment(String propName) throws NamingException {
return environment.remove(propName);
}
@Override
public Object lookup(String name) throws NamingException {
if (name.length() == 0) {
return this;
}
Object result = treeBindings.get(name);
if (result == null) {
result = bindings.get(name);
}
if (result == null) {
int pos = name.indexOf(':');
if (pos > 0 && !name.contains("::")) {
String scheme = name.substring(0, pos);
Context ctx = NamingManager.getURLContext(scheme, environment);
if (ctx == null) {
throw new NamingException("scheme " + scheme + " not recognized");
}
return ctx.lookup(name);
} else {
// Split out the first name of the path
// and look for it in the bindings map.
CompositeName path = new CompositeName(name);
if (path.size() == 0) {
return this;
} else {
String first = path.get(0);
Object obj = bindings.get(first);
if (obj == null) {
throw new NameNotFoundException(name);
} else if (obj instanceof Context && path.size() > 1) {
Context subContext = (Context) obj;
obj = subContext.lookup(path.getSuffix(1));
}
return obj;
}
}
}
if (result instanceof LinkRef) {
LinkRef ref = (LinkRef) result;
result = lookup(ref.getLinkName());
}
if (result instanceof Reference) {
try {
result = NamingManager.getObjectInstance(result, null, null, this.environment);
} catch (NamingException e) {
throw e;
} catch (Exception e) {
throw (NamingException) new NamingException("could not look up : " + name).initCause(e);
}
}
if (result instanceof ReadOnlyContext) {
String prefix = getNameInNamespace();
if (prefix.length() > 0) {
prefix = prefix + SEPARATOR;
}
result = new ReadOnlyContext((ReadOnlyContext) result, environment, prefix + name);
}
return result;
}
@Override
public Object lookup(Name name) throws NamingException {
return lookup(name.toString());
}
@Override
public Object lookupLink(String name) throws NamingException {
return lookup(name);
}
@Override
public Name composeName(Name name, Name prefix) throws NamingException {
Name result = (Name) prefix.clone();
result.addAll(name);
return result;
}
@Override
public String composeName(String name, String prefix) throws NamingException {
CompositeName result = new CompositeName(prefix);
result.addAll(new CompositeName(name));
return result.toString();
}
@Override
public NamingEnumeration list(String name) throws NamingException {
Object o = lookup(name);
if (o == this) {
return new ListEnumeration();
} else if (o instanceof Context) {
return ((Context) o).list("");
} else {
throw new NotContextException();
}
}
@Override
public NamingEnumeration listBindings(String name) throws NamingException {
Object o = lookup(name);
if (o == this) {
return new ListBindingEnumeration();
} else if (o instanceof Context) {
return ((Context) o).listBindings("");
} else {
throw new NotContextException();
}
}
@Override
public Object lookupLink(Name name) throws NamingException {
return lookupLink(name.toString());
}
@Override
public NamingEnumeration list(Name name) throws NamingException {
return list(name.toString());
}
@Override
public NamingEnumeration listBindings(Name name) throws NamingException {
return listBindings(name.toString());
}
@Override
public void bind(Name name, Object obj) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void bind(String name, Object obj) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void close() throws NamingException {
// ignore
}
@Override
public Context createSubcontext(Name name) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public Context createSubcontext(String name) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void destroySubcontext(Name name) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void destroySubcontext(String name) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public String getNameInNamespace() throws NamingException {
return nameInNamespace;
}
@Override
public NameParser getNameParser(Name name) throws NamingException {
return NAME_PARSER;
}
@Override
public NameParser getNameParser(String name) throws NamingException {
return NAME_PARSER;
}
@Override
public void rebind(Name name, Object obj) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void rebind(String name, Object obj) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void rename(Name oldName, Name newName) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void rename(String oldName, String newName) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void unbind(Name name) throws NamingException {
throw new OperationNotSupportedException();
}
@Override
public void unbind(String name) throws NamingException {
throw new OperationNotSupportedException();
}
private abstract class LocalNamingEnumeration implements NamingEnumeration {
private final Iterator> i = bindings.entrySet().iterator();
@Override
public boolean hasMore() throws NamingException {
return i.hasNext();
}
@Override
public boolean hasMoreElements() {
return i.hasNext();
}
protected Map.Entry getNext() {
return i.next();
}
@Override
public void close() throws NamingException {
}
}
private class ListEnumeration extends LocalNamingEnumeration {
ListEnumeration() {
}
@Override
public Object next() throws NamingException {
return nextElement();
}
@Override
public Object nextElement() {
Map.Entry entry = getNext();
return new NameClassPair(entry.getKey(), entry.getValue().getClass().getName());
}
}
private class ListBindingEnumeration extends LocalNamingEnumeration {
ListBindingEnumeration() {
}
@Override
public Object next() throws NamingException {
return nextElement();
}
@Override
public Object nextElement() {
Map.Entry entry = getNext();
return new Binding(entry.getKey(), entry.getValue());
}
}
}