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; 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 import org.apache.xbean.naming.context.ContextAccess; 031 import org.apache.xbean.naming.context.WritableContext; 032 033 import javax.naming.Name; 034 import javax.naming.NamingException; 035 import java.util.Collections; 036 import java.util.HashMap; 037 import java.util.HashSet; 038 import java.util.Iterator; 039 import java.util.LinkedHashMap; 040 import java.util.Map; 041 import java.util.Set; 042 043 /** 044 * @version $Rev$ $Date$ 045 */ 046 public class KernelContextGBean extends WritableContext implements GBeanLifecycle { 047 private static final Log log = LogFactory.getLog(KernelContextGBean.class); 048 049 private final Kernel kernel; 050 private final AbstractNameQuery abstractNameQuery; 051 private final LifecycleListener listener = new ContextLifecycleListener(); 052 private final Map bindingsByAbstractName = new HashMap(); 053 054 public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException { 055 super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false); 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); 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(bindingsByAbstractName.keySet()); 087 for (Iterator iterator = abstractNames.iterator(); iterator.hasNext();) { 088 AbstractName abstractName = (AbstractName) iterator.next(); 089 removeBinding(abstractName); 090 } 091 bindingsByAbstractName.clear(); 092 } 093 094 private class ContextLifecycleListener 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 (bindingsByAbstractName.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 // create the bindings for this object 141 Map bindings = createBindings(abstractName, instance); 142 if (bindings == null || bindings.isEmpty()) { 143 return; 144 } 145 146 // bind the value 147 for (Iterator iterator = bindings.entrySet().iterator(); iterator.hasNext();) { 148 Map.Entry entry = (Map.Entry) iterator.next(); 149 Name name = (Name) entry.getKey(); 150 Object value = entry.getValue(); 151 addBinding(abstractName, name, value); 152 } 153 154 // remember where we bound this value 155 bindingsByAbstractName.put(abstractName, bindings.keySet()); 156 } 157 158 private Map bindingsByName = new HashMap(); 159 160 private synchronized void addBinding(AbstractName abstractName, Name name, Object value) throws NamingException { 161 LinkedHashMap bindings = (LinkedHashMap) bindingsByName.get(name); 162 if (bindings == null) { 163 addDeepBinding(name, value, true, true); 164 165 bindings = new LinkedHashMap(); 166 bindings.put(abstractName, value); 167 bindingsByName.put(name, bindings); 168 } else { 169 bindings.put(abstractName, value); 170 } 171 } 172 173 /** 174 * Unbinds the specified gbean. 175 * 176 * @param abstractName the abstract name of the gbean to unbind 177 */ 178 protected synchronized void removeBinding(AbstractName abstractName) { 179 Set bindingNames = (Set) bindingsByAbstractName.remove(abstractName); 180 if (bindingNames == null) return; 181 182 for (Iterator iterator = bindingNames.iterator(); iterator.hasNext();) { 183 Name name = (Name) iterator.next(); 184 185 LinkedHashMap bindings = (LinkedHashMap) bindingsByName.get(name); 186 if (bindings == null) continue; 187 188 if (first(bindings).getKey().equals(abstractName)) { 189 bindings.remove(abstractName); 190 Map.Entry newEntry = first(bindings); 191 if (newEntry != null) { 192 Object newAbstractName = newEntry.getValue(); 193 Object newValue = newEntry.getValue(); 194 try { 195 addDeepBinding(name, newValue, true, true); 196 } catch (NamingException e) { 197 boolean logged = false; 198 try { 199 removeDeepBinding(name, true); 200 } catch (NamingException e1) { 201 logged = true; 202 log.error("Unable to remove binding " + name + " to " + abstractName, e); 203 } 204 if (!logged) log.error("Unable to rebind binding " + name + " to " + newAbstractName); 205 } 206 } else { 207 bindingsByName.remove(name); 208 try { 209 removeDeepBinding(name, true, true); 210 } catch (NamingException e) { 211 log.error("Unable to remove binding " + name + " to " + abstractName, e); 212 } 213 } 214 } else { 215 bindings.remove(abstractName); 216 } 217 } 218 } 219 220 private static Map.Entry first(LinkedHashMap map) { 221 if (map.isEmpty()) return null; 222 return (Map.Entry) map.entrySet().iterator().next(); 223 } 224 225 protected Map createBindings(AbstractName abstractName, Object value) throws NamingException { 226 // generate a name for this binding 227 Name name = createBindingName(abstractName, value); 228 if (name == null) return null; 229 230 // give sub classes a chance to preprocess the value 231 value = preprocessVaue(abstractName, name, value); 232 if (value == null) return null; 233 234 Map bindings = Collections.singletonMap(name, value); 235 return bindings; 236 } 237 238 /** 239 * Create a name under which we will bind the specified gbean with the specified value. 240 * By default, this method simply returns the "name" element of the abstract name 241 * 242 * @param abstractName the abstract name of the gbean to bind 243 * @param value the gbean instance 244 * @return the name under which the gbean should be bound 245 */ 246 protected Name createBindingName(AbstractName abstractName, Object value) throws NamingException { 247 String shortName = (String) abstractName.getName().get("name"); 248 return getNameParser().parse(shortName); 249 } 250 251 /** 252 * Preprocess the value before it is bound. This is usefult for wrapping values with reference objects. 253 * By default, this method simply return the value. 254 * 255 * @param abstractName the abstract name of the gbean to bind 256 * @param name the name under which the gbean will be bound 257 * @param value the gbean instance 258 * @return the value to bind 259 */ 260 protected Object preprocessVaue(AbstractName abstractName, Name name, Object value) throws NamingException { 261 return value; 262 } 263 264 public static final GBeanInfo GBEAN_INFO; 265 266 public static GBeanInfo getGBeanInfo() { 267 return GBEAN_INFO; 268 } 269 270 static { 271 GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(KernelContextGBean.class, "Context"); 272 builder.addAttribute("nameInNamespace", String.class, true); 273 builder.addAttribute("abstractNameQuery", AbstractNameQuery.class, true); 274 builder.setConstructor(new String[]{"nameInNamespace", "abstractNameQuery", "kernel"}); 275 GBEAN_INFO = builder.getBeanInfo(); 276 } 277 }