1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.geronimo.gjndi.binding;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.apache.geronimo.gbean.AbstractName;
23 import org.apache.geronimo.gbean.AbstractNameQuery;
24 import org.apache.geronimo.gbean.GBeanInfo;
25 import org.apache.geronimo.gbean.GBeanInfoBuilder;
26 import org.apache.geronimo.gbean.GBeanLifecycle;
27 import org.apache.geronimo.kernel.GBeanNotFoundException;
28 import org.apache.geronimo.kernel.Kernel;
29 import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter;
30 import org.apache.geronimo.kernel.lifecycle.LifecycleListener;
31
32 import javax.naming.Context;
33 import javax.naming.NamingException;
34 import java.util.HashSet;
35 import java.util.Iterator;
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 import java.util.Set;
39
40 /**
41 * @version $Rev$ $Date$
42 */
43 public class GBeanBinding implements GBeanLifecycle {
44 private static final Log log = LogFactory.getLog(GBeanBinding.class);
45
46 private final Context context;
47 private final String name;
48 private final AbstractNameQuery abstractNameQuery;
49 private final Kernel kernel;
50
51 private final LifecycleListener listener = new GBeanLifecycleListener();
52 private final LinkedHashMap bindings = new LinkedHashMap();
53
54 public GBeanBinding(Context context, String name, AbstractNameQuery abstractNameQuery, Kernel kernel) {
55 this.context = context;
56 this.name = name;
57 this.abstractNameQuery = abstractNameQuery;
58 this.kernel = kernel;
59 }
60
61 public synchronized void doStart() {
62 kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery);
63 Set set = kernel.listGBeans(abstractNameQuery);
64 for (Iterator iterator = set.iterator(); iterator.hasNext();) {
65 AbstractName abstractName = (AbstractName) iterator.next();
66 try {
67 if (kernel.isRunning(abstractName)) {
68 addBinding(abstractName);
69 }
70 } catch (NamingException e) {
71 log.error("Error adding binding for " + abstractName, e);
72 }
73 }
74
75 }
76
77 public void doStop() {
78 destroy();
79 }
80
81 public void doFail() {
82 destroy();
83 }
84
85 private synchronized void destroy() {
86 kernel.getLifecycleMonitor().removeLifecycleListener(listener);
87 Set abstractNames = new HashSet(bindings.keySet());
88 for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) {
89 AbstractName abstractName = (AbstractName) iterator.next();
90 removeBinding(abstractName);
91 }
92 bindings.clear();
93 }
94
95 private class GBeanLifecycleListener extends LifecycleAdapter {
96 public void running(AbstractName abstractName) {
97 try {
98 addBinding(abstractName);
99 } 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
130 return;
131 }
132
133
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
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
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
183 try {
184 if (context.lookup(name) != value) {
185 return true;
186 }
187 } catch (NamingException ignored) {
188
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 }