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 java.util.Collections;
020 import java.util.HashMap;
021 import java.util.HashSet;
022 import java.util.LinkedHashMap;
023 import java.util.Map;
024 import java.util.Set;
025
026 import javax.naming.Name;
027 import javax.naming.NamingException;
028 import javax.naming.ContextNotEmptyException;
029
030 import org.apache.commons.logging.Log;
031 import org.apache.commons.logging.LogFactory;
032 import org.apache.geronimo.gbean.AbstractName;
033 import org.apache.geronimo.gbean.AbstractNameQuery;
034 import org.apache.geronimo.gbean.GBeanInfo;
035 import org.apache.geronimo.gbean.GBeanInfoBuilder;
036 import org.apache.geronimo.gbean.GBeanLifecycle;
037 import org.apache.geronimo.kernel.GBeanNotFoundException;
038 import org.apache.geronimo.kernel.Kernel;
039 import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
040 import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
041 import org.apache.xbean.naming.context.ContextAccess;
042 import org.apache.xbean.naming.context.WritableContext;
043
044 /**
045 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
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<AbstractName, Set<Name>> bindingsByAbstractName = new HashMap<AbstractName, Set<Name>>();
054
055 public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException {
056 super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false);
057 this.abstractNameQuery = abstractNameQuery;
058 this.kernel = kernel;
059 }
060
061 public synchronized void doStart() {
062 kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery);
063 Set<AbstractName> set = kernel.listGBeans(abstractNameQuery);
064 for (AbstractName abstractName : set) {
065 try {
066 if (kernel.isRunning(abstractName)) {
067 addBinding(abstractName);
068 }
069 } catch (NamingException e) {
070 log.error("Error adding binding for " + abstractName, e);
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<AbstractName> abstractNames = new HashSet<AbstractName>(bindingsByAbstractName.keySet());
087 for (AbstractName abstractName : abstractNames) {
088 removeBinding(abstractName);
089 }
090 bindingsByAbstractName.clear();
091 }
092
093 private class ContextLifecycleListener extends LifecycleAdapter {
094 public void running(AbstractName abstractName) {
095 try {
096 addBinding(abstractName);
097 } catch (NamingException e) {
098 log.error("Error adding binding for " + abstractName);
099 }
100 }
101
102 public void stopping(AbstractName abstractName) {
103 removeBinding(abstractName);
104 }
105
106 public void stopped(AbstractName abstractName) {
107 removeBinding(abstractName);
108 }
109
110 public void failed(AbstractName abstractName) {
111 removeBinding(abstractName);
112 }
113
114 public void unloaded(AbstractName abstractName) {
115 removeBinding(abstractName);
116 }
117 }
118
119 /**
120 * Binds the specified gbean. This method uses createBindingName and preprocessValue before binding the object.
121 *
122 * @param abstractName the abstract name of the gbean to bind
123 * @throws NamingException if an error occurs during binding
124 */
125 protected synchronized void addBinding(AbstractName abstractName) throws NamingException {
126 if (bindingsByAbstractName.containsKey(abstractName)) {
127 // previously bound
128 return;
129 }
130
131 // get the gbean
132 Object instance;
133 try {
134 instance = kernel.getGBean(abstractName);
135 } catch (GBeanNotFoundException e) {
136 throw (NamingException)new NamingException("GBean not found: " + abstractName).initCause(e);
137 }
138
139 // create the bindings for this object
140 Map<Name, Object> bindings = createBindings(abstractName, instance);
141 if (bindings == null || bindings.isEmpty()) {
142 return;
143 }
144
145 // bind the value
146 for (Map.Entry<Name, Object> entry : bindings.entrySet()) {
147 Name name = entry.getKey();
148 Object value = entry.getValue();
149 addBinding(abstractName, name, value);
150 }
151
152 // remember where we bound this value
153 bindingsByAbstractName.put(abstractName, bindings.keySet());
154 }
155
156 private Map<Name, LinkedHashMap<AbstractName, Object>> bindingsByName = new HashMap<Name, LinkedHashMap<AbstractName, Object>>();
157
158 private synchronized void addBinding(AbstractName abstractName, Name name, Object value) throws NamingException {
159 LinkedHashMap<AbstractName, Object> bindings = bindingsByName.get(name);
160 if (bindings == null) {
161 addDeepBinding(name, value, true, true);
162
163 bindings = new LinkedHashMap<AbstractName, Object>();
164 bindings.put(abstractName, value);
165 bindingsByName.put(name, bindings);
166 log.info("bound gbean " + abstractName + " at name " + name);
167 } else {
168 bindings.put(abstractName, value);
169 }
170 }
171
172 /**
173 * Unbinds the specified gbean.
174 *
175 * @param abstractName the abstract name of the gbean to unbind
176 */
177 protected synchronized void removeBinding(AbstractName abstractName) {
178 Set<Name> bindingNames = bindingsByAbstractName.remove(abstractName);
179 if (bindingNames == null) return;
180
181 for (Name name : bindingNames) {
182
183 LinkedHashMap<AbstractName, Object> bindings = bindingsByName.get(name);
184 if (bindings == null) continue;
185
186 if (first(bindings).getKey().equals(abstractName)) {
187 bindings.remove(abstractName);
188 Map.Entry<AbstractName, Object> newEntry = first(bindings);
189 if (newEntry != null) {
190 Object newValue = newEntry.getValue();
191 try {
192 addDeepBinding(name, newValue, true, true);
193 } catch (NamingException e) {
194 boolean logged = false;
195 try {
196 removeDeepBinding(name, true);
197 } catch (NamingException e1) {
198 logged = true;
199 log.error("Unable to remove binding " + name + " to " + abstractName, e);
200 }
201 if (!logged) log.error("Unable to rebind binding " + name + " to " + newEntry.getKey());
202 }
203 } else {
204 bindingsByName.remove(name);
205 try {
206 removeDeepBinding(name, true, true);
207 } catch (ContextNotEmptyException e) {
208 //ignore
209 } catch (NamingException e) {
210 log.error("Unable to remove binding " + name + " to " + abstractName, e);
211 }
212 log.info("unbound gbean " + abstractName + " at name " + name);
213 }
214 } else {
215 bindings.remove(abstractName);
216 }
217 }
218 }
219
220 private static Map.Entry<AbstractName, Object> first(LinkedHashMap<AbstractName, Object> map) {
221 if (map.isEmpty()) return null;
222 return map.entrySet().iterator().next();
223 }
224
225 protected Map<Name, Object> 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 return Collections.singletonMap(name, value);
235 }
236
237 /**
238 * Create a name under which we will bind the specified gbean with the specified value.
239 * By default, this method simply returns the "name" element of the abstract name
240 *
241 * @param abstractName the abstract name of the gbean to bind
242 * @param value the gbean instance
243 * @return the name under which the gbean should be bound
244 * @throws javax.naming.NamingException should something go wrong
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 * @throws javax.naming.NamingException should something go wrong
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 }