View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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.HashMap;
23  import java.util.Locale;
24  import java.util.Map;
25  import java.util.WeakHashMap;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.concurrent.ConcurrentMap;
28  import java.util.concurrent.atomic.AtomicInteger;
29  
30  import javax.management.InstanceNotFoundException;
31  import javax.management.MBeanRegistrationException;
32  import javax.management.MBeanServer;
33  import javax.management.ObjectInstance;
34  import javax.management.ObjectName;
35  
36  import org.eclipse.jetty.util.component.Container;
37  import org.eclipse.jetty.util.component.ContainerLifeCycle;
38  import org.eclipse.jetty.util.component.Dumpable;
39  import org.eclipse.jetty.util.log.Log;
40  import org.eclipse.jetty.util.log.Logger;
41  
42  /**
43   * Container class for the MBean instances
44   */
45  public class MBeanContainer implements Container.InheritedListener, Dumpable
46  {
47      private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
48      private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<String, AtomicInteger>();
49  
50      public static void resetUnique()
51      {
52          __unique.clear();
53      }
54      
55      private final MBeanServer _mbeanServer;
56      private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
57      private String _domain = null;
58  
59      /**
60       * Lookup an object name by instance
61       *
62       * @param object instance for which object name is looked up
63       * @return object name associated with specified instance, or null if not found
64       */
65      public synchronized ObjectName findMBean(Object object)
66      {
67          ObjectName bean = _beans.get(object);
68          return bean == null ? null : bean;
69      }
70  
71      /**
72       * Lookup an instance by object name
73       *
74       * @param oname object name of instance
75       * @return instance associated with specified object name, or null if not found
76       */
77      public synchronized Object findBean(ObjectName oname)
78      {
79          for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
80          {
81              ObjectName bean = entry.getValue();
82              if (bean.equals(oname))
83                  return entry.getKey();
84          }
85          return null;
86      }
87  
88      /**
89       * Constructs MBeanContainer
90       *
91       * @param server instance of MBeanServer for use by container
92       */
93      public MBeanContainer(MBeanServer server)
94      {
95          _mbeanServer = server;
96      }
97  
98      /**
99       * Retrieve instance of MBeanServer used by container
100      *
101      * @return instance of MBeanServer
102      */
103     public MBeanServer getMBeanServer()
104     {
105         return _mbeanServer;
106     }
107 
108     /**
109      * Set domain to be used to add MBeans
110      *
111      * @param domain domain name
112      */
113     public void setDomain(String domain)
114     {
115         _domain = domain;
116     }
117 
118     /**
119      * Retrieve domain name used to add MBeans
120      *
121      * @return domain name
122      */
123     public String getDomain()
124     {
125         return _domain;
126     }
127 
128 
129     @Override
130     public void beanAdded(Container parent, Object obj)
131     {
132         LOG.debug("beanAdded {}->{}",parent,obj);
133         
134         // Is their an object name for the parent
135         ObjectName pname=null;
136         if (parent!=null)
137         {
138             pname=_beans.get(parent);
139             if (pname==null)
140             {
141                 // create the parent bean
142                 beanAdded(null,parent);
143                 pname=_beans.get(parent);
144             }
145         }
146         
147         // Does an mbean already exist?
148         if (obj == null || _beans.containsKey(obj))
149             return;
150         
151         try
152         {
153             // Create an MBean for the object
154             Object mbean = ObjectMBean.mbeanFor(obj);
155             if (mbean == null)
156                 return;
157 
158             
159             ObjectName oname = null;
160             if (mbean instanceof ObjectMBean)
161             {
162                 ((ObjectMBean)mbean).setMBeanContainer(this);
163                 oname = ((ObjectMBean)mbean).getObjectName();
164             }
165 
166             //no override mbean object name, so make a generic one
167             if (oname == null)
168             {      
169                 //if no explicit domain, create one
170                 String domain = _domain;
171                 if (domain == null)
172                     domain = obj.getClass().getPackage().getName();
173 
174 
175                 String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
176                 int dot = type.lastIndexOf('.');
177                 if (dot >= 0)
178                     type = type.substring(dot + 1);
179 
180 
181                 StringBuffer buf = new StringBuffer();
182 
183                 String context = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectContextBasis()):null;
184                 if (context==null && pname!=null)
185                     context=pname.getKeyProperty("context");
186                                 
187                 if (context != null && context.length()>1)
188                     buf.append("context=").append(context).append(",");
189                 
190                 buf.append("type=").append(type);
191 
192                 String name = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectNameBasis()):context;
193                 if (name != null && name.length()>1)
194                     buf.append(",").append("name=").append(name);
195 
196                 String basis = buf.toString();
197                 
198                 AtomicInteger count = __unique.get(basis);
199                 if (count==null)
200                 {
201                     count=__unique.putIfAbsent(basis,new AtomicInteger());
202                     if (count==null)
203                         count=__unique.get(basis);
204                 }
205                 
206                 oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
207             }
208 
209             ObjectInstance oinstance = _mbeanServer.registerMBean(mbean, oname);
210             LOG.debug("Registered {}", oinstance.getObjectName());
211             _beans.put(obj, oinstance.getObjectName());
212 
213         }
214         catch (Exception e)
215         {
216             LOG.warn("bean: " + obj, e);
217         }
218     }
219 
220     @Override
221     public void beanRemoved(Container parent, Object obj)
222     {
223         LOG.debug("beanRemoved {}",obj);
224         ObjectName bean = _beans.remove(obj);
225 
226         if (bean != null)
227         {
228             try
229             {
230                 _mbeanServer.unregisterMBean(bean);
231                 LOG.debug("Unregistered {}", bean);
232             }
233             catch (javax.management.InstanceNotFoundException e)
234             {
235                 LOG.ignore(e);
236             }
237             catch (Exception e)
238             {
239                 LOG.warn(e);
240             }
241         }
242     }
243 
244     /**
245      * @param basis name to strip of special characters.
246      * @return normalized name
247      */
248     public String makeName(String basis)
249     {
250         if (basis==null)
251             return basis;
252         return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
253     }
254 
255     @Override
256     public void dump(Appendable out, String indent) throws IOException
257     {
258         ContainerLifeCycle.dumpObject(out,this);
259         ContainerLifeCycle.dump(out, indent, _beans.entrySet());
260     }
261 
262     @Override
263     public String dump()
264     {
265         return ContainerLifeCycle.dump(this);
266     }
267 
268     public void destroy()
269     {
270         for (ObjectName oname : _beans.values())
271             if (oname!=null)
272             {
273                 try
274                 {
275                     _mbeanServer.unregisterMBean(oname);
276                 }
277                 catch (MBeanRegistrationException | InstanceNotFoundException e)
278                 {
279                     LOG.warn(e);
280                 }
281             }
282     }
283 }