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                        bindings.put(bindingName, binding.getObject());
119                    }
120                }
121            } catch (NotContextException e) {
122                //this context does not include the supplied name
123            }
124        }
125        return bindings;
126    }
127
128    protected boolean addBinding(String name, Object value, boolean rebind) throws NamingException {
129        for (Context context : getFederatedContexts()) {
130
131            try {
132                if (rebind) {
133                    context.rebind(name, value);
134                } else {
135                    context.bind(name, value);
136                }
137                return true;
138            } catch (OperationNotSupportedException ignored) {
139            }
140        }
141        return false;
142    }
143
144    protected boolean removeBinding(String name) throws NamingException {
145        for (Context context : getFederatedContexts()) {
146
147            try {
148                context.unbind(name);
149                return true;
150            } catch (OperationNotSupportedException ignored) {
151            }
152        }
153        return false;
154    }
155
156    public Object lookup(Name name) {
157        for (Context federatedContext : getFederatedContexts()) {
158            try {
159                Object value = federatedContext.lookup(name);
160                if (value instanceof Context) {
161                    return new VirtualSubcontext(name, actualContext);
162                } else {
163                    return value;
164                }
165            } catch (NamingException ignored) {
166            }
167        }
168        return null;
169    }
170
171    public ContextFederation createSubcontextFederation(String subcontextName, Context actualSubcontext) throws NamingException {
172        Name parsedSubcontextName = actualContext.getNameParser("").parse(subcontextName);
173
174        ContextFederation subcontextFederation = new ContextFederation(actualSubcontext);
175        for (Context federatedContext : getFederatedContexts()) {
176            VirtualSubcontext virtualSubcontext = new VirtualSubcontext(parsedSubcontextName, federatedContext);
177            subcontextFederation.addContext(virtualSubcontext);
178        }
179        return subcontextFederation;
180    }
181}