View Javadoc

1   /**
2    *
3    *  Licensed to the Apache Software Foundation (ASF) under one or more
4    *  contributor license agreements.  See the NOTICE file distributed with
5    *  this work for additional information regarding copyright ownership.
6    *  The ASF licenses this file to You under the Apache License, Version 2.0
7    *  (the "License"); you may not use this file except in compliance with
8    *  the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software
13   *  distributed under the License is distributed on an "AS IS" BASIS,
14   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   *  See the License for the specific language governing permissions and
16   *  limitations under the License.
17   */
18  package org.apache.geronimo.gjndi;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.apache.geronimo.gbean.AbstractName;
23  import org.apache.geronimo.gbean.AbstractNameQuery;
24  import org.apache.geronimo.gbean.GBeanInfo;
25  import org.apache.geronimo.gbean.GBeanInfoBuilder;
26  import org.apache.geronimo.gbean.GBeanLifecycle;
27  import org.apache.geronimo.kernel.GBeanNotFoundException;
28  import org.apache.geronimo.kernel.Kernel;
29  import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
30  import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
31  import org.apache.xbean.naming.context.ContextAccess;
32  import org.apache.xbean.naming.context.WritableContext;
33  
34  import javax.naming.Name;
35  import javax.naming.NamingException;
36  import java.util.Collections;
37  import java.util.HashMap;
38  import java.util.HashSet;
39  import java.util.Iterator;
40  import java.util.LinkedHashMap;
41  import java.util.Map;
42  import java.util.Set;
43  
44  /**
45   * @version $Rev$ $Date$
46   */
47  public class KernelContextGBean extends WritableContext implements GBeanLifecycle {
48      private static final Log log = LogFactory.getLog(KernelContextGBean.class);
49  
50      private final Kernel kernel;
51      private final AbstractNameQuery abstractNameQuery;
52      private final LifecycleListener listener = new ContextLifecycleListener();
53      private final Map bindingsByAbstractName = new HashMap();
54  
55      public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException {
56          super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.UNMODIFIABLE, false);
57          this.abstractNameQuery = abstractNameQuery;
58          this.kernel = kernel;
59      }
60  
61      public synchronized void doStart() {
62          kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery);
63          Set set = kernel.listGBeans(abstractNameQuery);
64          for (Iterator iterator = set.iterator(); iterator.hasNext();) {
65              AbstractName abstractName = (AbstractName) iterator.next();
66              try {
67                  if (kernel.isRunning(abstractName)) {
68                      addBinding(abstractName);
69                  }
70              } catch (NamingException e) {
71                  log.error("Error adding binding for " + abstractName);
72              }
73          }
74  
75      }
76  
77      public void doStop() {
78          destroy();
79      }
80  
81      public void doFail() {
82          destroy();
83      }
84  
85      private synchronized void destroy() {
86          kernel.getLifecycleMonitor().removeLifecycleListener(listener);
87          Set abstractNames = new HashSet(bindingsByAbstractName.keySet());
88          for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
89              AbstractName abstractName = (AbstractName) iterator.next();
90              removeBinding(abstractName);
91          }
92          bindingsByAbstractName.clear();
93      }
94  
95      private class ContextLifecycleListener extends LifecycleAdapter {
96          public void running(AbstractName abstractName) {
97              try {
98                  addBinding(abstractName);
99              } 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 }