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: */package org.apache.geronimo.gjndi;
017:
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.HashSet;
021: import java.util.LinkedHashMap;
022: import java.util.Map;
023: import java.util.Set;
024:
025: import javax.naming.Name;
026: import javax.naming.NamingException;
027: import javax.naming.ContextNotEmptyException;
028:
029: import org.apache.commons.logging.Log;
030: import org.apache.commons.logging.LogFactory;
031: import org.apache.geronimo.gbean.AbstractName;
032: import org.apache.geronimo.gbean.AbstractNameQuery;
033: import org.apache.geronimo.gbean.GBeanInfo;
034: import org.apache.geronimo.gbean.GBeanInfoBuilder;
035: import org.apache.geronimo.gbean.GBeanLifecycle;
036: import org.apache.geronimo.kernel.GBeanNotFoundException;
037: import org.apache.geronimo.kernel.Kernel;
038: import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
039: import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
040: import org.apache.xbean.naming.context.ContextAccess;
041: import org.apache.xbean.naming.context.WritableContext;
042:
043: /**
044: * @version $Rev$ $Date$
045: */
046: public class KernelContextGBean extends WritableContext implements
047: GBeanLifecycle {
048: private static final Log log = LogFactory
049: .getLog(KernelContextGBean.class);
050:
051: private final Kernel kernel;
052: private final AbstractNameQuery abstractNameQuery;
053: private final LifecycleListener listener = new ContextLifecycleListener();
054: private final Map<AbstractName, Set<Name>> bindingsByAbstractName = new HashMap<AbstractName, Set<Name>>();
055:
056: public KernelContextGBean(String nameInNamespace,
057: AbstractNameQuery abstractNameQuery, Kernel kernel)
058: throws NamingException {
059: super (nameInNamespace, Collections.EMPTY_MAP,
060: ContextAccess.MODIFIABLE, false);
061: this .abstractNameQuery = abstractNameQuery;
062: this .kernel = kernel;
063: }
064:
065: public synchronized void doStart() {
066: kernel.getLifecycleMonitor().addLifecycleListener(listener,
067: abstractNameQuery);
068: Set<AbstractName> set = kernel.listGBeans(abstractNameQuery);
069: for (AbstractName abstractName : set) {
070: try {
071: if (kernel.isRunning(abstractName)) {
072: addBinding(abstractName);
073: }
074: } catch (NamingException e) {
075: log
076: .error("Error adding binding for "
077: + abstractName, e);
078: }
079: }
080:
081: }
082:
083: public void doStop() {
084: destroy();
085: }
086:
087: public void doFail() {
088: destroy();
089: }
090:
091: private synchronized void destroy() {
092: kernel.getLifecycleMonitor().removeLifecycleListener(listener);
093: Set<AbstractName> abstractNames = new HashSet<AbstractName>(
094: bindingsByAbstractName.keySet());
095: for (AbstractName abstractName : abstractNames) {
096: removeBinding(abstractName);
097: }
098: bindingsByAbstractName.clear();
099: }
100:
101: private class ContextLifecycleListener extends LifecycleAdapter {
102: public void running(AbstractName abstractName) {
103: try {
104: addBinding(abstractName);
105: } catch (NamingException e) {
106: log.error("Error adding binding for " + abstractName);
107: }
108: }
109:
110: public void stopping(AbstractName abstractName) {
111: removeBinding(abstractName);
112: }
113:
114: public void stopped(AbstractName abstractName) {
115: removeBinding(abstractName);
116: }
117:
118: public void failed(AbstractName abstractName) {
119: removeBinding(abstractName);
120: }
121:
122: public void unloaded(AbstractName abstractName) {
123: removeBinding(abstractName);
124: }
125: }
126:
127: /**
128: * Binds the specified gbean. This method uses createBindingName and preprocessValue before binding the object.
129: *
130: * @param abstractName the abstract name of the gbean to bind
131: * @throws NamingException if an error occurs during binding
132: */
133: protected synchronized void addBinding(AbstractName abstractName)
134: throws NamingException {
135: if (bindingsByAbstractName.containsKey(abstractName)) {
136: // previously bound
137: return;
138: }
139:
140: // get the gbean
141: Object instance;
142: try {
143: instance = kernel.getGBean(abstractName);
144: } catch (GBeanNotFoundException e) {
145: throw (NamingException) new NamingException(
146: "GBean not found: " + abstractName).initCause(e);
147: }
148:
149: // create the bindings for this object
150: Map<Name, Object> bindings = createBindings(abstractName,
151: instance);
152: if (bindings == null || bindings.isEmpty()) {
153: return;
154: }
155:
156: // bind the value
157: for (Map.Entry<Name, Object> entry : bindings.entrySet()) {
158: Name name = entry.getKey();
159: Object value = entry.getValue();
160: addBinding(abstractName, name, value);
161: }
162:
163: // remember where we bound this value
164: bindingsByAbstractName.put(abstractName, bindings.keySet());
165: }
166:
167: private Map<Name, LinkedHashMap<AbstractName, Object>> bindingsByName = new HashMap<Name, LinkedHashMap<AbstractName, Object>>();
168:
169: private synchronized void addBinding(AbstractName abstractName,
170: Name name, Object value) throws NamingException {
171: LinkedHashMap<AbstractName, Object> bindings = bindingsByName
172: .get(name);
173: if (bindings == null) {
174: addDeepBinding(name, value, true, true);
175:
176: bindings = new LinkedHashMap<AbstractName, Object>();
177: bindings.put(abstractName, value);
178: bindingsByName.put(name, bindings);
179: log
180: .info("bound gbean " + abstractName + " at name "
181: + name);
182: } else {
183: bindings.put(abstractName, value);
184: }
185: }
186:
187: /**
188: * Unbinds the specified gbean.
189: *
190: * @param abstractName the abstract name of the gbean to unbind
191: */
192: protected synchronized void removeBinding(AbstractName abstractName) {
193: Set<Name> bindingNames = bindingsByAbstractName
194: .remove(abstractName);
195: if (bindingNames == null)
196: return;
197:
198: for (Name name : bindingNames) {
199:
200: LinkedHashMap<AbstractName, Object> bindings = bindingsByName
201: .get(name);
202: if (bindings == null)
203: continue;
204:
205: if (first(bindings).getKey().equals(abstractName)) {
206: bindings.remove(abstractName);
207: Map.Entry<AbstractName, Object> newEntry = first(bindings);
208: if (newEntry != null) {
209: Object newValue = newEntry.getValue();
210: try {
211: addDeepBinding(name, newValue, true, true);
212: } catch (NamingException e) {
213: boolean logged = false;
214: try {
215: removeDeepBinding(name, true);
216: } catch (NamingException e1) {
217: logged = true;
218: log.error("Unable to remove binding "
219: + name + " to " + abstractName, e);
220: }
221: if (!logged)
222: log
223: .error("Unable to rebind binding "
224: + name + " to "
225: + newEntry.getKey());
226: }
227: } else {
228: bindingsByName.remove(name);
229: try {
230: removeDeepBinding(name, true, true);
231: } catch (ContextNotEmptyException e) {
232: //ignore
233: } catch (NamingException e) {
234: log.error("Unable to remove binding " + name
235: + " to " + abstractName, e);
236: }
237: log.info("unbound gbean " + abstractName
238: + " at name " + name);
239: }
240: } else {
241: bindings.remove(abstractName);
242: }
243: }
244: }
245:
246: private static Map.Entry<AbstractName, Object> first(
247: LinkedHashMap<AbstractName, Object> map) {
248: if (map.isEmpty())
249: return null;
250: return map.entrySet().iterator().next();
251: }
252:
253: protected Map<Name, Object> createBindings(
254: AbstractName abstractName, Object value)
255: throws NamingException {
256: // generate a name for this binding
257: Name name = createBindingName(abstractName, value);
258: if (name == null)
259: return null;
260:
261: // give sub classes a chance to preprocess the value
262: value = preprocessVaue(abstractName, name, value);
263: if (value == null)
264: return null;
265:
266: return Collections.singletonMap(name, value);
267: }
268:
269: /**
270: * Create a name under which we will bind the specified gbean with the specified value.
271: * By default, this method simply returns the "name" element of the abstract name
272: *
273: * @param abstractName the abstract name of the gbean to bind
274: * @param value the gbean instance
275: * @return the name under which the gbean should be bound
276: * @throws javax.naming.NamingException should something go wrong
277: */
278: protected Name createBindingName(AbstractName abstractName,
279: Object value) throws NamingException {
280: String shortName = (String) abstractName.getName().get("name");
281: return getNameParser().parse(shortName);
282: }
283:
284: /**
285: * Preprocess the value before it is bound. This is usefult for wrapping values with reference objects.
286: * By default, this method simply return the value.
287: *
288: * @param abstractName the abstract name of the gbean to bind
289: * @param name the name under which the gbean will be bound
290: * @param value the gbean instance
291: * @return the value to bind
292: * @throws javax.naming.NamingException should something go wrong
293: */
294: protected Object preprocessVaue(AbstractName abstractName,
295: Name name, Object value) throws NamingException {
296: return value;
297: }
298:
299: public static final GBeanInfo GBEAN_INFO;
300:
301: public static GBeanInfo getGBeanInfo() {
302: return GBEAN_INFO;
303: }
304:
305: static {
306: GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(
307: KernelContextGBean.class, "Context");
308: builder.addAttribute("nameInNamespace", String.class, true);
309: builder.addAttribute("abstractNameQuery",
310: AbstractNameQuery.class, true);
311: builder.setConstructor(new String[] { "nameInNamespace",
312: "abstractNameQuery", "kernel" });
313: GBEAN_INFO = builder.getBeanInfo();
314: }
315: }
|