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     */
017    package org.apache.geronimo.gjndi;
018    
019    import org.apache.commons.logging.Log;
020    import org.apache.commons.logging.LogFactory;
021    import org.apache.geronimo.gbean.AbstractName;
022    import org.apache.geronimo.gbean.AbstractNameQuery;
023    import org.apache.geronimo.gbean.GBeanInfo;
024    import org.apache.geronimo.gbean.GBeanInfoBuilder;
025    import org.apache.geronimo.gbean.GBeanLifecycle;
026    import org.apache.geronimo.kernel.GBeanNotFoundException;
027    import org.apache.geronimo.kernel.Kernel;
028    import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
029    import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
030    import org.apache.xbean.naming.context.ContextAccess;
031    import org.apache.xbean.naming.context.WritableContext;
032    
033    import javax.naming.Name;
034    import javax.naming.NamingException;
035    import java.util.Collections;
036    import java.util.HashMap;
037    import java.util.HashSet;
038    import java.util.Iterator;
039    import java.util.LinkedHashMap;
040    import java.util.Map;
041    import java.util.Set;
042    
043    /**
044     * @version $Rev$ $Date$
045     */
046    public class KernelContextGBean extends WritableContext implements GBeanLifecycle {
047        private static final Log log = LogFactory.getLog(KernelContextGBean.class);
048    
049        private final Kernel kernel;
050        private final AbstractNameQuery abstractNameQuery;
051        private final LifecycleListener listener = new ContextLifecycleListener();
052        private final Map bindingsByAbstractName = new HashMap();
053    
054        public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException {
055            super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false);
056            this.abstractNameQuery = abstractNameQuery;
057            this.kernel = kernel;
058        }
059    
060        public synchronized void doStart() {
061            kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery);
062            Set set = kernel.listGBeans(abstractNameQuery);
063            for (Iterator iterator = set.iterator(); iterator.hasNext();) {
064                AbstractName abstractName = (AbstractName) iterator.next();
065                try {
066                    if (kernel.isRunning(abstractName)) {
067                        addBinding(abstractName);
068                    }
069                } catch (NamingException e) {
070                    log.error("Error adding binding for " + abstractName);
071                }
072            }
073    
074        }
075    
076        public void doStop() {
077            destroy();
078        }
079    
080        public void doFail() {
081            destroy();
082        }
083    
084        private synchronized void destroy() {
085            kernel.getLifecycleMonitor().removeLifecycleListener(listener);
086            Set abstractNames = new HashSet(bindingsByAbstractName.keySet());
087            for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
088                AbstractName abstractName = (AbstractName) iterator.next();
089                removeBinding(abstractName);
090            }
091            bindingsByAbstractName.clear();
092        }
093    
094        private class ContextLifecycleListener extends LifecycleAdapter {
095            public void running(AbstractName abstractName) {
096                try {
097                    addBinding(abstractName);
098                } catch (NamingException e) {
099                    log.error("Error adding binding for " + abstractName);
100                }
101            }
102    
103            public void stopping(AbstractName abstractName) {
104                removeBinding(abstractName);
105            }
106    
107            public void stopped(AbstractName abstractName) {
108                removeBinding(abstractName);
109            }
110    
111            public void failed(AbstractName abstractName) {
112                removeBinding(abstractName);
113            }
114    
115            public void unloaded(AbstractName abstractName) {
116                removeBinding(abstractName);
117            }
118        }
119    
120        /**
121         * Binds the specified gbean.  This method uses createBindingName and preprocessValue before binding the object.
122         *
123         * @param abstractName the abstract name of the gbean to bind
124         * @throws NamingException if an error occurs during binding
125         */
126        protected synchronized void addBinding(AbstractName abstractName) throws NamingException {
127            if (bindingsByAbstractName.containsKey(abstractName)) {
128                // previously bound
129                return;
130            }
131    
132            // get the gbean
133            Object instance = null;
134            try {
135                instance = kernel.getGBean(abstractName);
136            } catch (GBeanNotFoundException e) {
137                throw (NamingException)new NamingException("GBean not found: " + abstractName).initCause(e);
138            }
139    
140            // create the bindings for this object
141            Map bindings = createBindings(abstractName, instance);
142            if (bindings == null || bindings.isEmpty()) {
143                return;
144            }
145    
146            // bind the value
147            for (Iterator iterator = bindings.entrySet().iterator(); iterator.hasNext();) {
148                Map.Entry entry = (Map.Entry) iterator.next();
149                Name name = (Name) entry.getKey();
150                Object value = entry.getValue();
151                addBinding(abstractName, name, value);
152            }
153    
154            // remember where we bound this value
155            bindingsByAbstractName.put(abstractName, bindings.keySet());
156        }
157    
158        private Map bindingsByName = new HashMap();
159    
160        private synchronized void addBinding(AbstractName abstractName, Name name, Object value) throws NamingException {
161            LinkedHashMap bindings = (LinkedHashMap) bindingsByName.get(name);
162            if (bindings == null) {
163                addDeepBinding(name, value, true, true);
164    
165                bindings = new LinkedHashMap();
166                bindings.put(abstractName, value);
167                bindingsByName.put(name, bindings);
168            } else {
169                bindings.put(abstractName, value);
170            }
171        }
172    
173        /**
174         * Unbinds the specified gbean.
175         *
176         * @param abstractName the abstract name of the gbean to unbind
177         */
178        protected synchronized void removeBinding(AbstractName abstractName) {
179            Set bindingNames = (Set) bindingsByAbstractName.remove(abstractName);
180            if (bindingNames == null) return;
181    
182            for (Iterator iterator = bindingNames.iterator(); iterator.hasNext();) {
183                Name name = (Name) iterator.next();
184    
185                LinkedHashMap bindings = (LinkedHashMap) bindingsByName.get(name);
186                if (bindings == null) continue;
187    
188                if (first(bindings).getKey().equals(abstractName)) {
189                    bindings.remove(abstractName);
190                    Map.Entry newEntry = first(bindings);
191                    if (newEntry != null) {
192                        Object newAbstractName = newEntry.getValue();
193                        Object newValue = newEntry.getValue();
194                        try {
195                            addDeepBinding(name, newValue, true, true);
196                        } catch (NamingException e) {
197                            boolean logged = false;
198                            try {
199                                removeDeepBinding(name, true);
200                            } catch (NamingException e1) {
201                                logged = true;
202                                log.error("Unable to remove binding " + name + " to " + abstractName, e);
203                            }
204                            if (!logged) log.error("Unable to rebind binding " + name + " to " + newAbstractName);
205                        }
206                    } else {
207                        bindingsByName.remove(name);
208                        try {
209                            removeDeepBinding(name, true, true);
210                        } catch (NamingException e) {
211                            log.error("Unable to remove binding " + name + " to " + abstractName, e);
212                        }
213                    }
214                } else {
215                    bindings.remove(abstractName);
216                }
217            }
218        }
219    
220        private static Map.Entry first(LinkedHashMap map) {
221            if (map.isEmpty()) return null;
222            return (Map.Entry) map.entrySet().iterator().next();
223        }
224    
225        protected Map createBindings(AbstractName abstractName, Object value) throws NamingException {
226            // generate a name for this binding
227            Name name = createBindingName(abstractName, value);
228            if (name == null) return null;
229    
230            // give sub classes a chance to preprocess the value
231            value = preprocessVaue(abstractName, name, value);
232            if (value == null) return null;
233    
234            Map bindings = Collections.singletonMap(name, value);
235            return bindings;
236        }
237    
238        /**
239         * Create a name under which we will bind the specified gbean with the specified value.
240         * By default, this method simply returns the "name" element of the abstract name
241         *
242         * @param abstractName the abstract name of the gbean to bind
243         * @param value        the gbean instance
244         * @return the name under which the gbean should be bound
245         */
246        protected Name createBindingName(AbstractName abstractName, Object value) throws NamingException {
247            String shortName = (String) abstractName.getName().get("name");
248            return getNameParser().parse(shortName);
249        }
250    
251        /**
252         * Preprocess the value before it is bound.  This is usefult for wrapping values with reference objects.
253         * By default, this method simply return the value.
254         *
255         * @param abstractName the abstract name of the gbean to bind
256         * @param name         the name under which the gbean will be bound
257         * @param value        the gbean instance
258         * @return the value to bind
259         */
260        protected Object preprocessVaue(AbstractName abstractName, Name name, Object value) throws NamingException {
261            return value;
262        }
263    
264        public static final GBeanInfo GBEAN_INFO;
265    
266        public static GBeanInfo getGBeanInfo() {
267            return GBEAN_INFO;
268        }
269    
270        static {
271            GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(KernelContextGBean.class, "Context");
272            builder.addAttribute("nameInNamespace", String.class, true);
273            builder.addAttribute("abstractNameQuery", AbstractNameQuery.class, true);
274            builder.setConstructor(new String[]{"nameInNamespace", "abstractNameQuery", "kernel"});
275            GBEAN_INFO = builder.getBeanInfo();
276        }
277    }