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 }