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