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 java.util.Collections; 020 import java.util.HashMap; 021 import java.util.HashSet; 022 import java.util.LinkedHashMap; 023 import java.util.Map; 024 import java.util.Set; 025 026 import javax.naming.Name; 027 import javax.naming.NamingException; 028 import javax.naming.ContextNotEmptyException; 029 030 import org.apache.commons.logging.Log; 031 import org.apache.commons.logging.LogFactory; 032 import org.apache.geronimo.gbean.AbstractName; 033 import org.apache.geronimo.gbean.AbstractNameQuery; 034 import org.apache.geronimo.gbean.GBeanInfo; 035 import org.apache.geronimo.gbean.GBeanInfoBuilder; 036 import org.apache.geronimo.gbean.GBeanLifecycle; 037 import org.apache.geronimo.kernel.GBeanNotFoundException; 038 import org.apache.geronimo.kernel.Kernel; 039 import org.apache.geronimo.kernel.lifecycle.LifecycleAdapter; 040 import org.apache.geronimo.kernel.lifecycle.LifecycleListener; 041 import org.apache.xbean.naming.context.ContextAccess; 042 import org.apache.xbean.naming.context.WritableContext; 043 044 /** 045 * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $ 046 */ 047 public class KernelContextGBean extends WritableContext implements GBeanLifecycle { 048 private static final Log log = LogFactory.getLog(KernelContextGBean.class); 049 050 private final Kernel kernel; 051 private final AbstractNameQuery abstractNameQuery; 052 private final LifecycleListener listener = new ContextLifecycleListener(); 053 private final Map<AbstractName, Set<Name>> bindingsByAbstractName = new HashMap<AbstractName, Set<Name>>(); 054 055 public KernelContextGBean(String nameInNamespace, AbstractNameQuery abstractNameQuery, Kernel kernel) throws NamingException { 056 super(nameInNamespace, Collections.EMPTY_MAP, ContextAccess.MODIFIABLE, false); 057 this.abstractNameQuery = abstractNameQuery; 058 this.kernel = kernel; 059 } 060 061 public synchronized void doStart() { 062 kernel.getLifecycleMonitor().addLifecycleListener(listener, abstractNameQuery); 063 Set<AbstractName> set = kernel.listGBeans(abstractNameQuery); 064 for (AbstractName abstractName : set) { 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<AbstractName> abstractNames = new HashSet<AbstractName>(bindingsByAbstractName.keySet()); 087 for (AbstractName abstractName : abstractNames) { 088 removeBinding(abstractName); 089 } 090 bindingsByAbstractName.clear(); 091 } 092 093 private class ContextLifecycleListener extends LifecycleAdapter { 094 public void running(AbstractName abstractName) { 095 try { 096 addBinding(abstractName); 097 } catch (NamingException e) { 098 log.error("Error adding binding for " + abstractName); 099 } 100 } 101 102 public void stopping(AbstractName abstractName) { 103 removeBinding(abstractName); 104 } 105 106 public void stopped(AbstractName abstractName) { 107 removeBinding(abstractName); 108 } 109 110 public void failed(AbstractName abstractName) { 111 removeBinding(abstractName); 112 } 113 114 public void unloaded(AbstractName abstractName) { 115 removeBinding(abstractName); 116 } 117 } 118 119 /** 120 * Binds the specified gbean. This method uses createBindingName and preprocessValue before binding the object. 121 * 122 * @param abstractName the abstract name of the gbean to bind 123 * @throws NamingException if an error occurs during binding 124 */ 125 protected synchronized void addBinding(AbstractName abstractName) throws NamingException { 126 if (bindingsByAbstractName.containsKey(abstractName)) { 127 // previously bound 128 return; 129 } 130 131 // get the gbean 132 Object instance; 133 try { 134 instance = kernel.getGBean(abstractName); 135 } catch (GBeanNotFoundException e) { 136 throw (NamingException)new NamingException("GBean not found: " + abstractName).initCause(e); 137 } 138 139 // create the bindings for this object 140 Map<Name, Object> bindings = createBindings(abstractName, instance); 141 if (bindings == null || bindings.isEmpty()) { 142 return; 143 } 144 145 // bind the value 146 for (Map.Entry<Name, Object> entry : bindings.entrySet()) { 147 Name name = entry.getKey(); 148 Object value = entry.getValue(); 149 addBinding(abstractName, name, value); 150 } 151 152 // remember where we bound this value 153 bindingsByAbstractName.put(abstractName, bindings.keySet()); 154 } 155 156 private Map<Name, LinkedHashMap<AbstractName, Object>> bindingsByName = new HashMap<Name, LinkedHashMap<AbstractName, Object>>(); 157 158 private synchronized void addBinding(AbstractName abstractName, Name name, Object value) throws NamingException { 159 LinkedHashMap<AbstractName, Object> bindings = bindingsByName.get(name); 160 if (bindings == null) { 161 addDeepBinding(name, value, true, true); 162 163 bindings = new LinkedHashMap<AbstractName, Object>(); 164 bindings.put(abstractName, value); 165 bindingsByName.put(name, bindings); 166 log.info("bound gbean " + abstractName + " at name " + name); 167 } else { 168 bindings.put(abstractName, value); 169 } 170 } 171 172 /** 173 * Unbinds the specified gbean. 174 * 175 * @param abstractName the abstract name of the gbean to unbind 176 */ 177 protected synchronized void removeBinding(AbstractName abstractName) { 178 Set<Name> bindingNames = bindingsByAbstractName.remove(abstractName); 179 if (bindingNames == null) return; 180 181 for (Name name : bindingNames) { 182 183 LinkedHashMap<AbstractName, Object> bindings = bindingsByName.get(name); 184 if (bindings == null) continue; 185 186 if (first(bindings).getKey().equals(abstractName)) { 187 bindings.remove(abstractName); 188 Map.Entry<AbstractName, Object> newEntry = first(bindings); 189 if (newEntry != null) { 190 Object newValue = newEntry.getValue(); 191 try { 192 addDeepBinding(name, newValue, true, true); 193 } catch (NamingException e) { 194 boolean logged = false; 195 try { 196 removeDeepBinding(name, true); 197 } catch (NamingException e1) { 198 logged = true; 199 log.error("Unable to remove binding " + name + " to " + abstractName, e); 200 } 201 if (!logged) log.error("Unable to rebind binding " + name + " to " + newEntry.getKey()); 202 } 203 } else { 204 bindingsByName.remove(name); 205 try { 206 removeDeepBinding(name, true, true); 207 } catch (ContextNotEmptyException e) { 208 //ignore 209 } catch (NamingException e) { 210 log.error("Unable to remove binding " + name + " to " + abstractName, e); 211 } 212 log.info("unbound gbean " + abstractName + " at name " + name); 213 } 214 } else { 215 bindings.remove(abstractName); 216 } 217 } 218 } 219 220 private static Map.Entry<AbstractName, Object> first(LinkedHashMap<AbstractName, Object> map) { 221 if (map.isEmpty()) return null; 222 return map.entrySet().iterator().next(); 223 } 224 225 protected Map<Name, Object> 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 return Collections.singletonMap(name, value); 235 } 236 237 /** 238 * Create a name under which we will bind the specified gbean with the specified value. 239 * By default, this method simply returns the "name" element of the abstract name 240 * 241 * @param abstractName the abstract name of the gbean to bind 242 * @param value the gbean instance 243 * @return the name under which the gbean should be bound 244 * @throws javax.naming.NamingException should something go wrong 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 * @throws javax.naming.NamingException should something go wrong 260 */ 261 protected Object preprocessVaue(AbstractName abstractName, Name name, Object value) throws NamingException { 262 return value; 263 } 264 265 public static final GBeanInfo GBEAN_INFO; 266 267 public static GBeanInfo getGBeanInfo() { 268 return GBEAN_INFO; 269 } 270 271 static { 272 GBeanInfoBuilder builder = GBeanInfoBuilder.createStatic(KernelContextGBean.class, "Context"); 273 builder.addAttribute("nameInNamespace", String.class, true); 274 builder.addAttribute("abstractNameQuery", AbstractNameQuery.class, true); 275 builder.setConstructor(new String[]{"nameInNamespace", "abstractNameQuery", "kernel"}); 276 GBEAN_INFO = builder.getBeanInfo(); 277 } 278 }