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.binding;
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  
32  import javax.naming.Context;
33  import javax.naming.NamingException;
34  import java.util.HashSet;
35  import java.util.Iterator;
36  import java.util.LinkedHashMap;
37  import java.util.Map;
38  import java.util.Set;
39  
40  /**
41   * @version $Rev$ $Date$
42   */
43  public class GBeanBinding implements GBeanLifecycle {
44      private static final Log log = LogFactory.getLog(GBeanBinding.class);
45  
46      private final Context context;
47      private final String name;
48      private final AbstractNameQuery abstractNameQuery;
49      private final Kernel kernel;
50  
51      private final LifecycleListener listener = new GBeanLifecycleListener();
52      private final LinkedHashMap bindings = new LinkedHashMap();
53  
54      public GBeanBinding(Context context, String name, AbstractNameQuery abstractNameQuery, Kernel kernel) {
55          this.context = context;
56          this.name = name;
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, e);
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(bindings.keySet());
88          for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
89              AbstractName abstractName = (AbstractName) iterator.next();
90              removeBinding(abstractName);
91          }
92          bindings.clear();
93      }
94  
95      private class GBeanLifecycleListener 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 (bindings.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         // preprocess the instance
142         instance = preprocessVaue(abstractName, instance);
143 
144         addBinding(abstractName, instance);
145     }
146 
147     private synchronized void addBinding(AbstractName abstractName, Object value) throws NamingException {
148         if (bindings.isEmpty()) {
149             context.bind(name, value);
150         }
151         bindings.put(abstractName, value);
152     }
153 
154     /**
155      * Unbinds the specified gbean.
156      *
157      * @param abstractName the abstract name of the gbean to unbind
158      */
159     protected synchronized void removeBinding(AbstractName abstractName) {
160         if (first(bindings).getKey().equals(abstractName)) {
161             Object oldValue = bindings.remove(abstractName);
162             Map.Entry newEntry = first(bindings);
163             if (newEntry != null) {
164                 Object newAbstractName = newEntry.getValue();
165                 Object newValue = newEntry.getValue();
166                 try {
167                     context.rebind(name, newValue);
168                 } catch (NamingException e) {
169                     boolean unbound = unbind(abstractName, oldValue);
170                     // avoid double logging
171                     if (unbound) log.error("Unable to rebind binding " + name + " to " + newAbstractName);
172                 }
173             } else {
174                 unbind(abstractName, oldValue);
175             }
176         } else {
177             bindings.remove(abstractName);
178         }
179     }
180 
181     private boolean unbind(AbstractName abstractName, Object value) {
182         // first check if we are still bound
183         try {
184             if (context.lookup(name) != value) {
185                 return true;
186             }
187         } catch (NamingException ignored) {
188             // binding doesn't exist
189             return true;
190         }
191 
192         try {
193             context.unbind(name);
194             return true;
195         } catch (NamingException e1) {
196             log.error("Unable to remove binding " + name + " to " + abstractName, e1);
197         }
198         return false;
199     }
200 
201     private static Map.Entry first(LinkedHashMap map) {
202         if (map.isEmpty()) return null;
203         return (Map.Entry) map.entrySet().iterator().next();
204     }
205 
206     /**
207      * Preprocess the value before it is bound.  This is usefult for wrapping values with reference objects.
208      * By default, this method simply return the value.
209      *
210      * @param abstractName the abstract name of the gbean to bind
211      * @param value        the gbean instance
212      * @return the value to bind
213      */
214     protected Object preprocessVaue(AbstractName abstractName, Object value) throws NamingException {
215         return value;
216     }
217 
218     public static final GBeanInfo GBEAN_INFO;
219 
220     public static GBeanInfo getGBeanInfo() {
221         return GBEAN_INFO;
222     }
223 
224     static {
225         GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(GBeanBinding.class, "GBeanBinding");
226         builder.addReference("Context", Context.class);
227         builder.addAttribute("name", String.class, true);
228         builder.addAttribute("abstractNameQuery", AbstractNameQuery.class, true);
229         builder.setConstructor(new String[]{"Context", "name", "abstractNameQuery", "kernel"});
230         GBEAN_INFO = builder.getBeanInfo();
231     }
232 }