001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.xbean.naming.context;
018
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.LinkedHashSet;
022import java.util.Map;
023import java.util.Set;
024import java.util.concurrent.atomic.AtomicReference;
025
026import javax.naming.Binding;
027import javax.naming.Context;
028import javax.naming.Name;
029import javax.naming.NamingEnumeration;
030import javax.naming.NamingException;
031import javax.naming.NotContextException;
032import javax.naming.OperationNotSupportedException;
033
034/**
035 * @version $Rev$ $Date$
036 */
037public class ContextFederation {
038    private final Context actualContext;
039    private final AtomicReference<Set<Context>> federatedContextRef = new AtomicReference<Set<Context>>(Collections.<Context>emptySet());
040    public static final int MAX_WRITE_ATTEMPTS = 10;
041
042    public ContextFederation(Context actualContext) {
043        this.actualContext = actualContext;
044    }
045
046    public ContextFederation(Context actualContext, Set<Context> federatedContexts) {
047        this.actualContext = actualContext;
048        Set<Context> copy = new LinkedHashSet<Context>(federatedContexts);
049        federatedContextRef.set(Collections.unmodifiableSet(copy));
050    }
051
052    public void addContext(Context context) {
053        Set<Context> federatedContext;
054        Set<Context> newFederatedContext;
055        for (int i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
056            federatedContext = getFederatedContexts();
057
058            newFederatedContext = new LinkedHashSet<Context>(federatedContext);
059            newFederatedContext.add(context);
060            newFederatedContext = Collections.unmodifiableSet(newFederatedContext);
061            if (federatedContextRef.compareAndSet(federatedContext, newFederatedContext)) {
062                return;
063            }
064        }
065        throw new RuntimeException("Unable to update federatedContextRef within " + MAX_WRITE_ATTEMPTS + " attempts");
066    }
067    
068    public void removeContext(Context context) {
069        Set<Context> federatedContext;
070        Set<Context> newFederatedContext;
071        for (int i = 0; i < MAX_WRITE_ATTEMPTS; i++) {
072            federatedContext = getFederatedContexts();
073
074            newFederatedContext = new LinkedHashSet<Context>(federatedContext);
075            newFederatedContext.remove(context);
076            newFederatedContext = Collections.unmodifiableSet(newFederatedContext);
077            if (federatedContextRef.compareAndSet(federatedContext, newFederatedContext)) {
078                return;
079            }
080        }
081        throw new RuntimeException("Unable to update federatedContextRef within " + MAX_WRITE_ATTEMPTS + " attempts");
082    }
083
084    public Set<Context> getFederatedContexts() {
085        return federatedContextRef.get();
086    }
087
088    public Object getFederatedBinding(String name) throws NamingException {
089        for (Context context : getFederatedContexts()) {
090
091            try {
092                Object value = context.lookup(name);
093                if (value != null) {
094                    return value;
095                }
096            } catch (NamingException e) {
097                // ignore
098            }
099        }
100        return null;
101    }
102
103    public Map<String, Object> getFederatedBindings(String name) throws NamingException {
104        Map<String, Object> bindings = new HashMap<String, Object>();
105        for (Context context : getFederatedContexts()) {
106
107            // list federated context
108            try {
109                NamingEnumeration namingEnumeration = context.listBindings(name);
110
111                // add to bindings
112                while (namingEnumeration.hasMoreElements()) {
113                    Binding binding = (Binding) namingEnumeration.nextElement();
114                    String bindingName = binding.getName();
115
116                    // don't overwrite existing bindings
117                    if (!bindings.containsKey(bindingName)) {
118                        try {
119                            bindings.put(bindingName, binding.getObject());
120                        } catch (RuntimeException e) {
121                            // if this is a wrapped NamingException, unwrap and throw the original 
122                            Throwable cause = e.getCause(); 
123                            if (cause != null && cause instanceof NamingException ) {
124                                throw (NamingException)cause; 
125                            }
126                            // Wrap this into a RuntimeException.  
127                            throw (NamingException)new NamingException("Could retrieve bound instance " + name).initCause(e);
128                        }
129                    }
130                }
131            } catch (NotContextException e) {
132                //this context does not include the supplied name
133            }
134        }
135        return bindings;
136    }
137
138    protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
139        for (Context context : getFederatedContexts()) {
140
141            try {
142                if (rebind) {
143                    context.rebind(name, value);
144                } else {
145                    context.bind(name, value);
146                }
147                return true;
148            } catch (OperationNotSupportedException ignored) {
149            }
150        }
151        return false;
152    }
153
154    protected boolean removeBinding(String name) throws NamingException {
155        for (Context context : getFederatedContexts()) {
156
157            try {
158                context.unbind(name);
159                return true;
160            } catch (OperationNotSupportedException ignored) {
161            }
162        }
163        return false;
164    }
165
166    public Object lookup(Name name) {
167        for (Context federatedContext : getFederatedContexts()) {
168            try {
169                Object value = federatedContext.lookup(name);
170                if (value instanceof Context) {
171                    return new VirtualSubcontext(name, actualContext);
172                } else {
173                    return value;
174                }
175            } catch (NamingException ignored) {
176            }
177        }
178        return null;
179    }
180
181    public ContextFederation createSubcontextFederation(String subcontextName, Context actualSubcontext) throws NamingException {
182        Name parsedSubcontextName = actualContext.getNameParser("").parse(subcontextName);
183
184        ContextFederation subcontextFederation = new ContextFederation(actualSubcontext);
185        for (Context federatedContext : getFederatedContexts()) {
186            VirtualSubcontext virtualSubcontext = new VirtualSubcontext(parsedSubcontextName, federatedContext);
187            subcontextFederation.addContext(virtualSubcontext);
188        }
189        return subcontextFederation;
190    }
191}