001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 package org.apache.geronimo.gjndi;
019
020 import org.apache.commons.logging.Log;
021 import org.apache.commons.logging.LogFactory;
022 import org.apache.geronimo.gbean.AbstractName;
023 import org.apache.geronimo.gbean.AbstractNameQuery;
024 import org.apache.geronimo.gbean.GBeanInfo;
025 import org.apache.geronimo.gbean.GBeanInfoBuilder;
026 import org.apache.geronimo.gbean.GBeanLifecycle;
027 import org.apache.geronimo.kernel.GBeanNotFoundException;
028 import org.apache.geronimo.kernel.Kernel;
029 import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
030 import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
031 import org.apache.xbean.naming.context.ContextAccess;
032 import org.apache.xbean.naming.context.WritableContext;
033
034 import javax.naming.Name;
035 import javax.naming.NamingException;
036 import java.util.Collections;
037 import java.util.HashMap;
038 import java.util.HashSet;
039 import java.util.Iterator;
040 import java.util.LinkedHashMap;
041 import java.util.Map;
042 import java.util.Set;
043
044 /**
045 * @version $Rev$ $Date$
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 bindingsByAbstractName = new HashMap();
054
055 public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException {
056 super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.UNMODIFIABLE, false);
057 this.abstractNameQuery = abstractNameQuery;
058 this.kernel = kernel;
059 }
060
061 public synchronized void doStart() {
062 kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery);
063 Set set = kernel.listGBeans(abstractNameQuery);
064 for (Iterator iterator = set.iterator(); iterator.hasNext();) {
065 AbstractName abstractName = (AbstractName) iterator.next();
066 try {
067 if (kernel.isRunning(abstractName)) {
068 addBinding(abstractName);
069 }
070 } catch (NamingException e) {
071 log.error("Error adding binding for " + abstractName);
072 }
073 }
074
075 }
076
077 public void doStop() {
078 destroy();
079 }
080
081 public void doFail() {
082 destroy();
083 }
084
085 private synchronized void destroy() {
086 kernel.getLifecycleMonitor().removeLifecycleListener(listener);
087 Set abstractNames = new HashSet(bindingsByAbstractName.keySet());
088 for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
089 AbstractName abstractName = (AbstractName) iterator.next();
090 removeBinding(abstractName);
091 }
092 bindingsByAbstractName.clear();
093 }
094
095 private class ContextLifecycleListener extends LifecycleAdapter {
096 public void running(AbstractName abstractName) {
097 try {
098 addBinding(abstractName);
099 } 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 }