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.binding;
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
031 import javax.naming.Context;
032 import javax.naming.NamingException;
033 import java.util.HashSet;
034 import java.util.Iterator;
035 import java.util.LinkedHashMap;
036 import java.util.Map;
037 import java.util.Set;
038
039 /**
040 * @version $Rev$ $Date$
041 */
042 public class GBeanBinding implements GBeanLifecycle {
043 private static final Log log = LogFactory.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 bindings = new LinkedHashMap();
052
053 public GBeanBinding(Context context, String name, AbstractNameQuery abstractNameQuery, Kernel kernel) {
054 this.context = context;
055 this.name = name;
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, 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 abstractNames = new HashSet(bindings.keySet());
087 for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
088 AbstractName abstractName = (AbstractName) iterator.next();
089 removeBinding(abstractName);
090 }
091 bindings.clear();
092 }
093
094 private class GBeanLifecycleListener 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 (bindings.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 // preprocess the instance
141 instance = preprocessVaue(abstractName, instance);
142
143 addBinding(abstractName, instance);
144 }
145
146 private synchronized void addBinding(AbstractName abstractName, Object value) throws NamingException {
147 if (bindings.isEmpty()) {
148 context.bind(name, value);
149 }
150 bindings.put(abstractName, value);
151 }
152
153 /**
154 * Unbinds the specified gbean.
155 *
156 * @param abstractName the abstract name of the gbean to unbind
157 */
158 protected synchronized void removeBinding(AbstractName abstractName) {
159 Map.Entry entry = first(bindings);
160 if (entry != null && entry.getKey().equals(abstractName)) {
161 Object oldValue = bindings.remove(abstractName);
162 entry = first(bindings);
163 if (entry != null) {
164 Object newAbstractName = entry.getValue();
165 Object newValue = entry.getValue();
166 try {
167 context.rebind(name, newValue);
168 } catch (NamingException e) {
169 boolean unbound = unbind(abstractName, oldValue);
170 // avoid double logging
171 if (unbound) log.error("Unable to rebind binding " + name + " to " + newAbstractName);
172 }
173 } else {
174 unbind(abstractName, oldValue);
175 }
176 } else {
177 bindings.remove(abstractName);
178 }
179 }
180
181 private boolean unbind(AbstractName abstractName, Object value) {
182 // first check if we are still bound
183 try {
184 if (context.lookup(name) != value) {
185 return true;
186 }
187 } catch (NamingException ignored) {
188 // binding doesn't exist
189 return true;
190 }
191
192 try {
193 context.unbind(name);
194 return true;
195 } catch (NamingException e1) {
196 log.error("Unable to remove binding " + name + " to " + abstractName, e1);
197 }
198 return false;
199 }
200
201 private static Map.Entry first(LinkedHashMap map) {
202 if (map.isEmpty()) return null;
203 return (Map.Entry) map.entrySet().iterator().next();
204 }
205
206 /**
207 * Preprocess the value before it is bound. This is usefult for wrapping values with reference objects.
208 * By default, this method simply return the value.
209 *
210 * @param abstractName the abstract name of the gbean to bind
211 * @param value the gbean instance
212 * @return the value to bind
213 */
214 protected Object preprocessVaue(AbstractName abstractName, Object value) throws NamingException {
215 return value;
216 }
217
218 public static final GBeanInfo GBEAN_INFO;
219
220 public static GBeanInfo getGBeanInfo() {
221 return GBEAN_INFO;
222 }
223
224 static {
225 GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(GBeanBinding.class, "GBeanBinding");
226 builder.addReference("Context", Context.class);
227 builder.addAttribute("name", String.class, true);
228 builder.addAttribute("abstractNameQuery", AbstractNameQuery.class, true);
229 builder.setConstructor(new String[]{"Context", "name", "abstractNameQuery", "kernel"});
230 GBEAN_INFO = builder.getBeanInfo();
231 }
232 }