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
130 return;
131 }
132
133
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
142 Map bindings = createBindings(abstractName, instance);
143 if (bindings == null || bindings.isEmpty()) {
144 return;
145 }
146
147
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
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
228 Name name = createBindingName(abstractName, value);
229 if (name == null) return null;
230
231
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 }