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