1 // 2 // ======================================================================== 3 // Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4 // ------------------------------------------------------------------------ 5 // All rights reserved. This program and the accompanying materials 6 // are made available under the terms of the Eclipse Public License v1.0 7 // and Apache License v2.0 which accompanies this distribution. 8 // 9 // The Eclipse Public License is available at 10 // http://www.eclipse.org/legal/epl-v10.html 11 // 12 // The Apache License v2.0 is available at 13 // http://www.opensource.org/licenses/apache2.0.php 14 // 15 // You may elect to redistribute this code under either of these licenses. 16 // ======================================================================== 17 // 18 19 package org.eclipse.jetty.jmx; 20 21 import java.io.IOException; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.HashSet; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.Locale; 28 import java.util.Map; 29 import java.util.Set; 30 import java.util.WeakHashMap; 31 32 import javax.management.MBeanServer; 33 import javax.management.ObjectInstance; 34 import javax.management.ObjectName; 35 36 import org.eclipse.jetty.util.component.AbstractLifeCycle; 37 import org.eclipse.jetty.util.component.AggregateLifeCycle; 38 import org.eclipse.jetty.util.component.Container; 39 import org.eclipse.jetty.util.component.Container.Relationship; 40 import org.eclipse.jetty.util.component.Dumpable; 41 import org.eclipse.jetty.util.log.Log; 42 import org.eclipse.jetty.util.log.Logger; 43 import org.eclipse.jetty.util.log.StdErrLog; 44 import org.eclipse.jetty.util.thread.ShutdownThread; 45 46 /** 47 * Container class for the MBean instances 48 */ 49 public class MBeanContainer extends AbstractLifeCycle implements Container.Listener, Dumpable 50 { 51 private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName()); 52 private final static HashMap<String, Integer> __unique = new HashMap<String, Integer>(); 53 resetUnique()54 public final static void resetUnique() 55 { 56 synchronized (__unique) 57 { 58 __unique.clear(); 59 } 60 } 61 62 private final MBeanServer _server; 63 private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>(); 64 private final WeakHashMap<ObjectName,List<Container.Relationship>> _relations = new WeakHashMap<ObjectName,List<Container.Relationship>>(); 65 private String _domain = null; 66 67 /** 68 * Lookup an object name by instance 69 * 70 * @param object instance for which object name is looked up 71 * @return object name associated with specified instance, or null if not found 72 */ findMBean(Object object)73 public synchronized ObjectName findMBean(Object object) 74 { 75 ObjectName bean = _beans.get(object); 76 return bean == null ? null : bean; 77 } 78 79 /** 80 * Lookup an instance by object name 81 * 82 * @param oname object name of instance 83 * @return instance associated with specified object name, or null if not found 84 */ findBean(ObjectName oname)85 public synchronized Object findBean(ObjectName oname) 86 { 87 for (Map.Entry<Object, ObjectName> entry : _beans.entrySet()) 88 { 89 ObjectName bean = entry.getValue(); 90 if (bean.equals(oname)) 91 return entry.getKey(); 92 } 93 return null; 94 } 95 96 /** 97 * Constructs MBeanContainer 98 * 99 * @param server instance of MBeanServer for use by container 100 */ MBeanContainer(MBeanServer server)101 public MBeanContainer(MBeanServer server) 102 { 103 _server = server; 104 } 105 106 /** 107 * Retrieve instance of MBeanServer used by container 108 * 109 * @return instance of MBeanServer 110 */ getMBeanServer()111 public MBeanServer getMBeanServer() 112 { 113 return _server; 114 } 115 116 /** 117 * Set domain to be used to add MBeans 118 * 119 * @param domain domain name 120 */ setDomain(String domain)121 public void setDomain(String domain) 122 { 123 _domain = domain; 124 } 125 126 /** 127 * Retrieve domain name used to add MBeans 128 * 129 * @return domain name 130 */ getDomain()131 public String getDomain() 132 { 133 return _domain; 134 } 135 136 /** 137 * Implementation of Container.Listener interface 138 * 139 * @see org.eclipse.jetty.util.component.Container.Listener#add(org.eclipse.jetty.util.component.Container.Relationship) 140 */ add(Relationship relationship)141 public synchronized void add(Relationship relationship) 142 { 143 LOG.debug("add {}",relationship); 144 ObjectName parent = _beans.get(relationship.getParent()); 145 if (parent == null) 146 { 147 addBean(relationship.getParent()); 148 parent = _beans.get(relationship.getParent()); 149 } 150 151 ObjectName child = _beans.get(relationship.getChild()); 152 if (child == null) 153 { 154 addBean(relationship.getChild()); 155 child = _beans.get(relationship.getChild()); 156 } 157 158 if (parent != null && child != null) 159 { 160 List<Container.Relationship> rels = _relations.get(parent); 161 if (rels==null) 162 { 163 rels=new ArrayList<Container.Relationship>(); 164 _relations.put(parent,rels); 165 } 166 rels.add(relationship); 167 } 168 } 169 170 /** 171 * Implementation of Container.Listener interface 172 * 173 * @see org.eclipse.jetty.util.component.Container.Listener#remove(org.eclipse.jetty.util.component.Container.Relationship) 174 */ remove(Relationship relationship)175 public synchronized void remove(Relationship relationship) 176 { 177 LOG.debug("remove {}",relationship); 178 ObjectName parent = _beans.get(relationship.getParent()); 179 ObjectName child = _beans.get(relationship.getChild()); 180 181 if (parent != null && child != null) 182 { 183 List<Container.Relationship> rels = _relations.get(parent); 184 if (rels!=null) 185 { 186 for (Iterator<Container.Relationship> i=rels.iterator();i.hasNext();) 187 { 188 Container.Relationship r = i.next(); 189 if (relationship.equals(r) || r.getChild()==null) 190 i.remove(); 191 } 192 } 193 } 194 } 195 196 /** 197 * Implementation of Container.Listener interface 198 * 199 * @see org.eclipse.jetty.util.component.Container.Listener#removeBean(java.lang.Object) 200 */ removeBean(Object obj)201 public synchronized void removeBean(Object obj) 202 { 203 LOG.debug("removeBean {}",obj); 204 ObjectName bean = _beans.remove(obj); 205 206 if (bean != null) 207 { 208 List<Container.Relationship> beanRelations= _relations.remove(bean); 209 if (beanRelations != null) 210 { 211 LOG.debug("Unregister {}", beanRelations); 212 List<?> removeList = new ArrayList<Object>(beanRelations); 213 for (Object r : removeList) 214 { 215 Container.Relationship relation = (Relationship)r; 216 relation.getContainer().update(relation.getParent(), relation.getChild(), null, relation.getRelationship(), true); 217 } 218 } 219 220 try 221 { 222 _server.unregisterMBean(bean); 223 LOG.debug("Unregistered {}", bean); 224 } 225 catch (javax.management.InstanceNotFoundException e) 226 { 227 LOG.ignore(e); 228 } 229 catch (Exception e) 230 { 231 LOG.warn(e); 232 } 233 } 234 } 235 236 /** 237 * Implementation of Container.Listener interface 238 * 239 * @see org.eclipse.jetty.util.component.Container.Listener#addBean(java.lang.Object) 240 */ addBean(Object obj)241 public synchronized void addBean(Object obj) 242 { 243 LOG.debug("addBean {}",obj); 244 try 245 { 246 if (obj == null || _beans.containsKey(obj)) 247 return; 248 249 Object mbean = ObjectMBean.mbeanFor(obj); 250 if (mbean == null) 251 return; 252 253 ObjectName oname = null; 254 if (mbean instanceof ObjectMBean) 255 { 256 ((ObjectMBean)mbean).setMBeanContainer(this); 257 oname = ((ObjectMBean)mbean).getObjectName(); 258 } 259 260 //no override mbean object name, so make a generic one 261 if (oname == null) 262 { 263 String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH); 264 int dot = type.lastIndexOf('.'); 265 if (dot >= 0) 266 type = type.substring(dot + 1); 267 268 String context = null; 269 if (mbean instanceof ObjectMBean) 270 { 271 context = makeName(((ObjectMBean)mbean).getObjectContextBasis()); 272 } 273 274 String name = null; 275 if (mbean instanceof ObjectMBean) 276 { 277 name = makeName(((ObjectMBean)mbean).getObjectNameBasis()); 278 } 279 280 StringBuffer buf = new StringBuffer(); 281 buf.append("type=").append(type); 282 if (context != null && context.length()>1) 283 { 284 buf.append(buf.length()>0 ? ",":""); 285 buf.append("context=").append(context); 286 } 287 if (name != null && name.length()>1) 288 { 289 buf.append(buf.length()>0 ? ",":""); 290 buf.append("name=").append(name); 291 } 292 293 String basis = buf.toString(); 294 Integer count; 295 synchronized (__unique) 296 { 297 count = __unique.get(basis); 298 count = count == null ? 0 : 1 + count; 299 __unique.put(basis, count); 300 } 301 302 //if no explicit domain, create one 303 String domain = _domain; 304 if (domain == null) 305 domain = obj.getClass().getPackage().getName(); 306 307 oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count); 308 } 309 310 ObjectInstance oinstance = _server.registerMBean(mbean, oname); 311 LOG.debug("Registered {}", oinstance.getObjectName()); 312 _beans.put(obj, oinstance.getObjectName()); 313 314 } 315 catch (Exception e) 316 { 317 LOG.warn("bean: " + obj, e); 318 } 319 } 320 321 /** 322 * @param basis name to strip of special characters. 323 * @return normalized name 324 */ makeName(String basis)325 public String makeName(String basis) 326 { 327 if (basis==null) 328 return basis; 329 return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_'); 330 } 331 332 /** 333 * Perform actions needed to start lifecycle 334 * 335 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart() 336 */ doStart()337 public void doStart() 338 { 339 ShutdownThread.register(this); 340 } 341 342 /** 343 * Perform actions needed to stop lifecycle 344 * 345 * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop() 346 */ doStop()347 public void doStop() 348 { 349 Set<Object> removeSet = new HashSet<Object>(_beans.keySet()); 350 for (Object removeObj : removeSet) 351 { 352 removeBean(removeObj); 353 } 354 } 355 dump(Appendable out, String indent)356 public void dump(Appendable out, String indent) throws IOException 357 { 358 AggregateLifeCycle.dumpObject(out,this); 359 AggregateLifeCycle.dump(out, indent, _beans.entrySet()); 360 } 361 dump()362 public String dump() 363 { 364 return AggregateLifeCycle.dump(this); 365 } 366 } 367