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 }