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