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.binding;
017:
018: import java.util.HashSet;
019: import java.util.LinkedHashMap;
020: import java.util.Map;
021: import java.util.Set;
022:
023: import javax.naming.Context;
024: import javax.naming.NamingException;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.apache.geronimo.gbean.AbstractName;
029: import org.apache.geronimo.gbean.AbstractNameQuery;
030: import org.apache.geronimo.gbean.GBeanInfo;
031: import org.apache.geronimo.gbean.GBeanInfoBuilder;
032: import org.apache.geronimo.gbean.GBeanLifecycle;
033: import org.apache.geronimo.kernel.GBeanNotFoundException;
034: import org.apache.geronimo.kernel.Kernel;
035: import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
036: import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
037:
038: /**
039: * @version $Rev$ $Date$
040: */
041: public class GBeanBinding implements GBeanLifecycle {
042: private static final Log log = LogFactory
043: .getLog(GBeanBinding.class);
044:
045: private final Context context;
046: private final String name;
047: private final AbstractNameQuery abstractNameQuery;
048: private final Kernel kernel;
049:
050: private final LifecycleListener listener = new GBeanLifecycleListener();
051: private final LinkedHashMap<AbstractName, Object> bindings = new LinkedHashMap<AbstractName, Object>();
052:
053: public GBeanBinding(Context context, String name,
054: AbstractNameQuery abstractNameQuery, Kernel kernel) {
055: this .context = context;
056: this .name = name;
057: this .abstractNameQuery = abstractNameQuery;
058: this .kernel = kernel;
059: }
060:
061: public synchronized void doStart() {
062: kernel.getLifecycleMonitor().addLifecycleListener(listener,
063: abstractNameQuery);
064: Set<AbstractName> set = kernel.listGBeans(abstractNameQuery);
065: for (AbstractName abstractName : set) {
066: try {
067: if (kernel.isRunning(abstractName)) {
068: addBinding(abstractName);
069: }
070: } catch (NamingException e) {
071: log
072: .error("Error adding binding for "
073: + abstractName, e);
074: }
075: }
076:
077: }
078:
079: public void doStop() {
080: destroy();
081: }
082:
083: public void doFail() {
084: destroy();
085: }
086:
087: private synchronized void destroy() {
088: kernel.getLifecycleMonitor().removeLifecycleListener(listener);
089: Set<AbstractName> abstractNames = new HashSet<AbstractName>(
090: bindings.keySet());
091: for (AbstractName abstractName : abstractNames) {
092: removeBinding(abstractName);
093: }
094: bindings.clear();
095: }
096:
097: private class GBeanLifecycleListener extends LifecycleAdapter {
098: public void running(AbstractName abstractName) {
099: try {
100: addBinding(abstractName);
101: } catch (NamingException e) {
102: log.error("Error adding binding for " + abstractName);
103: }
104: }
105:
106: public void stopping(AbstractName abstractName) {
107: removeBinding(abstractName);
108: }
109:
110: public void stopped(AbstractName abstractName) {
111: removeBinding(abstractName);
112: }
113:
114: public void failed(AbstractName abstractName) {
115: removeBinding(abstractName);
116: }
117:
118: public void unloaded(AbstractName abstractName) {
119: removeBinding(abstractName);
120: }
121: }
122:
123: /**
124: * Binds the specified gbean. This method uses createBindingName and preprocessValue before binding the object.
125: *
126: * @param abstractName the abstract name of the gbean to bind
127: * @throws NamingException if an error occurs during binding
128: */
129: protected synchronized void addBinding(AbstractName abstractName)
130: throws NamingException {
131: if (bindings.containsKey(abstractName)) {
132: // previously bound
133: return;
134: }
135:
136: // get the gbean
137: Object instance;
138: try {
139: instance = kernel.getGBean(abstractName);
140: } catch (GBeanNotFoundException e) {
141: throw (NamingException) new NamingException(
142: "GBean not found: " + abstractName).initCause(e);
143: }
144:
145: // preprocess the instance
146: instance = preprocessVaue(abstractName, instance);
147:
148: addBinding(abstractName, instance);
149: }
150:
151: private synchronized void addBinding(AbstractName abstractName,
152: Object value) throws NamingException {
153: if (bindings.isEmpty()) {
154: context.bind(name, value);
155: }
156: bindings.put(abstractName, value);
157: }
158:
159: /**
160: * Unbinds the specified gbean.
161: *
162: * @param abstractName the abstract name of the gbean to unbind
163: */
164: protected synchronized void removeBinding(AbstractName abstractName) {
165: Map.Entry entry = first(bindings);
166: if (entry != null && entry.getKey().equals(abstractName)) {
167: Object oldValue = bindings.remove(abstractName);
168: entry = first(bindings);
169: if (entry != null) {
170: Object newAbstractName = entry.getValue();
171: Object newValue = entry.getValue();
172: try {
173: context.rebind(name, newValue);
174: } catch (NamingException e) {
175: boolean unbound = unbind(abstractName, oldValue);
176: // avoid double logging
177: if (unbound)
178: log.error("Unable to rebind binding " + name
179: + " to " + newAbstractName);
180: }
181: } else {
182: unbind(abstractName, oldValue);
183: }
184: } else {
185: bindings.remove(abstractName);
186: }
187: }
188:
189: private boolean unbind(AbstractName abstractName, Object value) {
190: // first check if we are still bound
191: try {
192: if (context.lookup(name) != value) {
193: return true;
194: }
195: } catch (NamingException ignored) {
196: // binding doesn't exist
197: return true;
198: }
199:
200: try {
201: context.unbind(name);
202: return true;
203: } catch (NamingException e1) {
204: log.error("Unable to remove binding " + name + " to "
205: + abstractName, e1);
206: }
207: return false;
208: }
209:
210: private static Map.Entry first(LinkedHashMap map) {
211: if (map.isEmpty())
212: return null;
213: return (Map.Entry) map.entrySet().iterator().next();
214: }
215:
216: /**
217: * Preprocess the value before it is bound. This is usefult for wrapping values with reference objects.
218: * By default, this method simply return the value.
219: *
220: * @param abstractName the abstract name of the gbean to bind
221: * @param value the gbean instance
222: * @return the value to bind
223: */
224: protected Object preprocessVaue(AbstractName abstractName,
225: Object value) throws NamingException {
226: return value;
227: }
228:
229: public static final GBeanInfo GBEAN_INFO;
230:
231: public static GBeanInfo getGBeanInfo() {
232: return GBEAN_INFO;
233: }
234:
235: static {
236: GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(
237: GBeanBinding.class, "GBeanBinding");
238: builder.addReference("Context", Context.class);
239: builder.addAttribute("name", String.class, true);
240: builder.addAttribute("abstractNameQuery",
241: AbstractNameQuery.class, true);
242: builder.setConstructor(new String[] { "Context", "name",
243: "abstractNameQuery", "kernel" });
244: GBEAN_INFO = builder.getBeanInfo();
245: }
246: }
|