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 }