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 }