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.binding;
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
032 import javax.naming.Context;
033 import javax.naming.NamingException;
034 import java.util.HashSet;
035 import java.util.Iterator;
036 import java.util.LinkedHashMap;
037 import java.util.Map;
038 import java.util.Set;
039
040 /**
041 * @version $Rev$ $Date$
042 */
043 public class GBeanBinding implements GBeanLifecycle {
044 private static final Log log = LogFactory.getLog(GBeanBinding.class);
045
046 private final Context context;
047 private final String name;
048 private final AbstractNameQuery abstractNameQuery;
049 private final Kernel kernel;
050
051 private final LifecycleListener listener = new GBeanLifecycleListener();
052 private final LinkedHashMap bindings = new LinkedHashMap();
053
054 public GBeanBinding(Context context, String name, 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, 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, e);
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(bindings.keySet());
088 for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
089 AbstractName abstractName = (AbstractName) iterator.next();
090 removeBinding(abstractName);
091 }
092 bindings.clear();
093 }
094
095 private class GBeanLifecycleListener 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 (bindings.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 // preprocess the instance
142 instance = preprocessVaue(abstractName, instance);
143
144 addBinding(abstractName, instance);
145 }
146
147 private synchronized void addBinding(AbstractName abstractName, Object value) throws NamingException {
148 if (bindings.isEmpty()) {
149 context.bind(name, value);
150 }
151 bindings.put(abstractName, value);
152 }
153
154 /**
155 * Unbinds the specified gbean.
156 *
157 * @param abstractName the abstract name of the gbean to unbind
158 */
159 protected synchronized void removeBinding(AbstractName abstractName) {
160 if (first(bindings).getKey().equals(abstractName)) {
161 Object oldValue = bindings.remove(abstractName);
162 Map.Entry newEntry = first(bindings);
163 if (newEntry != null) {
164 Object newAbstractName = newEntry.getValue();
165 Object newValue = newEntry.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 }