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