/* * JBoss, Home of Professional Open Source. * Copyright 2012, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file 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. */ package org.jboss.naming.remote.client; import java.util.Collections; import java.util.Hashtable; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import javax.naming.Binding; import javax.naming.CompositeName; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.LinkRef; import javax.naming.Name; import javax.naming.NameClassPair; import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.spi.ObjectFactory; import org.jboss.logging.Logger; import static org.jboss.naming.remote.client.ClientUtil.isEmpty; import static org.jboss.naming.remote.client.ClientUtil.namingEnumeration; import static org.jboss.naming.remote.client.ClientUtil.namingException; /** * * @author John Bailey */ public class RemoteContext implements Context, NameParser { private static final Logger log = Logger.getLogger(RemoteContext.class); // Work around JVM's broken finalizer. All code touching these values has a purpose. Do not remove! public static int STATIC_KEEP_ALIVE; private static AtomicIntegerFieldUpdater<RemoteContext> keepAliveUpdater = AtomicIntegerFieldUpdater.newUpdater(RemoteContext.class, "keepAlive"); private volatile int keepAlive = STATIC_KEEP_ALIVE; private final Name prefix; private final Hashtable<String, Object> environment; // All usage of namingStore must be surrounded with an update to keepAlive, to keep the context alive private final RemoteNamingStore namingStore; private final List<CloseTask> closeTasks; private final AtomicBoolean closed = new AtomicBoolean(); public RemoteContext(final RemoteNamingStore namingStore, final Hashtable<String, Object> environment) { this(namingStore, environment, Collections.<CloseTask>emptyList()); } public RemoteContext(final RemoteNamingStore namingStore, final Hashtable<String, Object> environment, final List<CloseTask> closeTasks) { this(new CompositeName(), namingStore, environment, closeTasks); } public RemoteContext(final Name prefix, final RemoteNamingStore namingStore, final Hashtable<String, Object> environment) { this(prefix, namingStore, environment, Collections.<CloseTask>emptyList()); } public RemoteContext(final Name prefix, final RemoteNamingStore namingStore, final Hashtable<String, Object> environment, final List<CloseTask> closeTasks) { this.prefix = prefix; this.namingStore = namingStore; this.environment = environment; this.closeTasks = closeTasks; } public Object lookup(final Name name) throws NamingException { try { return lookupInternal(name); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } private Object lookupInternal(Name name) throws NamingException { if (isEmpty(name)) { return new RemoteContext(prefix, namingStore, environment); } final Name absoluteName = getAbsoluteName(name); Object result = namingStore.lookup(absoluteName); if (result instanceof LinkRef) { result = resolveLink((LinkRef)result); } else if (result instanceof Reference) { result = getObjectInstance((Reference)result, name, environment); if (result instanceof LinkRef) { result = resolveLink((LinkRef)result); } } return result; } private Object getObjectInstance(final Reference reference, final Name name, final Hashtable<?, ?> environment) throws NamingException { try { final Class<?> factoryClass = Thread.currentThread().getContextClassLoader().loadClass(reference.getFactoryClassName()); ObjectFactory factory = ObjectFactory.class.cast(factoryClass.newInstance()); return factory.getObjectInstance(reference, name, this, environment); } catch(NamingException e) { throw e; } catch(Throwable t) { throw namingException("failed to get object instance from reference", t); } } private Object resolveLink(LinkRef result) throws NamingException { final Object linkResult; try { final LinkRef linkRef = (LinkRef) result; final String referenceName = linkRef.getLinkName(); if (referenceName.startsWith("./")) { linkResult = lookup(referenceName.substring(2)); } else { linkResult = new InitialContext().lookup(referenceName); } } catch (Throwable t) { throw namingException("failed to deref link",t); } return linkResult; } public Object lookup(final String name) throws NamingException { return lookup(parse(name)); } public void bind(final Name name, final Object object) throws NamingException { try { namingStore.bind(getAbsoluteName(name), object); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public void bind(final String name, final Object object) throws NamingException { bind(parse(name), object); } public void rebind(final Name name, final Object object) throws NamingException { try { namingStore.rebind(name, object); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public void rebind(final String name, final Object object) throws NamingException { rebind(parse(name), object); } public void unbind(final Name name) throws NamingException { try { namingStore.unbind(name); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public void unbind(final String name) throws NamingException { unbind(parse(name)); } public void rename(final Name name, final Name newName) throws NamingException { try { namingStore.rename(name, newName); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public void rename(final String name, final String newName) throws NamingException { rename(parse(name), parse(newName)); } public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException { try { return namingEnumeration(namingStore.list(name)); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public NamingEnumeration<NameClassPair> list(final String name) throws NamingException { return list(parse(name)); } public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException { try { return namingEnumeration(namingStore.listBindings(name)); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public NamingEnumeration<Binding> listBindings(final String name) throws NamingException { return listBindings(parse(name)); } public void destroySubcontext(final Name name) throws NamingException { try { namingStore.destroySubcontext(name); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public void destroySubcontext(final String name) throws NamingException { destroySubcontext(parse(name)); } public Context createSubcontext(final Name name) throws NamingException { try { return namingStore.createSubcontext(name); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public Context createSubcontext(final String name) throws NamingException { return createSubcontext(parse(name)); } public Object lookupLink(final Name name) throws NamingException { try { return namingStore.lookupLink(name); } finally { keepAliveUpdater.lazySet(this, keepAlive + 1); } } public Object lookupLink(final String name) throws NamingException { return lookupLink(parse(name)); } public NameParser getNameParser(Name name) throws NamingException { return this; } public NameParser getNameParser(String s) throws NamingException { return this; } public Name composeName(Name name, Name prefix) throws NamingException { final Name result = (Name) prefix.clone(); result.addAll(name); return result; } public String composeName(String name, String prefix) throws NamingException { return composeName(parse(name), parse(prefix)).toString(); } public Object addToEnvironment(String s, Object o) throws NamingException { return environment.put(s, o); } public Object removeFromEnvironment(String s) throws NamingException { return environment.remove(s); } public Hashtable<?, ?> getEnvironment() throws NamingException { return environment; } public void close() throws NamingException { if(closed.compareAndSet(false, true)) { for (CloseTask closeTask : closeTasks) { closeTask.close(false); } } } public void finalize() { if(closed.compareAndSet(false, true)) { STATIC_KEEP_ALIVE = keepAlive; for (CloseTask closeTask : closeTasks) { closeTask.close(true); } } } public String getNameInNamespace() throws NamingException { return prefix.toString(); } public Name parse(final String name) throws NamingException { return new CompositeName(name); } private Name getAbsoluteName(final Name name) throws NamingException { if (name.isEmpty()) { return composeName(name, prefix); } return composeName(name, prefix); } public static interface CloseTask { void close(boolean isFinalize); } }