View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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.Locale;
23  import java.util.Map;
24  import java.util.WeakHashMap;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.ConcurrentMap;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
29  import javax.management.InstanceNotFoundException;
30  import javax.management.MBeanRegistrationException;
31  import javax.management.MBeanServer;
32  import javax.management.ObjectInstance;
33  import javax.management.ObjectName;
34  
35  import org.eclipse.jetty.util.component.Container;
36  import org.eclipse.jetty.util.component.ContainerLifeCycle;
37  import org.eclipse.jetty.util.component.Dumpable;
38  import org.eclipse.jetty.util.log.Log;
39  import org.eclipse.jetty.util.log.Logger;
40  
41  /**
42   * Container class for the MBean instances
43   */
44  public class MBeanContainer implements Container.InheritedListener, Dumpable
45  {
46      private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
47      private final static ConcurrentMap<String, AtomicInteger> __unique = new ConcurrentHashMap<String, AtomicInteger>();
48  
49      public static void resetUnique()
50      {
51          __unique.clear();
52      }
53      
54      private final MBeanServer _mbeanServer;
55      private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
56      private String _domain = null;
57  
58      /**
59       * Lookup an object name by instance
60       *
61       * @param object instance for which object name is looked up
62       * @return object name associated with specified instance, or null if not found
63       */
64      public synchronized ObjectName findMBean(Object object)
65      {
66          ObjectName bean = _beans.get(object);
67          return bean == null ? null : bean;
68      }
69  
70      /**
71       * Lookup an instance by object name
72       *
73       * @param oname object name of instance
74       * @return instance associated with specified object name, or null if not found
75       */
76      public synchronized Object findBean(ObjectName oname)
77      {
78          for (Map.Entry<Object, ObjectName> entry : _beans.entrySet())
79          {
80              ObjectName bean = entry.getValue();
81              if (bean.equals(oname))
82                  return entry.getKey();
83          }
84          return null;
85      }
86  
87      /**
88       * Constructs MBeanContainer
89       *
90       * @param server instance of MBeanServer for use by container
91       */
92      public MBeanContainer(MBeanServer server)
93      {
94          _mbeanServer = server;
95      }
96  
97      /**
98       * Retrieve instance of MBeanServer used by container
99       *
100      * @return instance of MBeanServer
101      */
102     public MBeanServer getMBeanServer()
103     {
104         return _mbeanServer;
105     }
106 
107     /**
108      * Set domain to be used to add MBeans
109      *
110      * @param domain domain name
111      */
112     public void setDomain(String domain)
113     {
114         _domain = domain;
115     }
116 
117     /**
118      * Retrieve domain name used to add MBeans
119      *
120      * @return domain name
121      */
122     public String getDomain()
123     {
124         return _domain;
125     }
126 
127 
128     @Override
129     public void beanAdded(Container parent, Object obj)
130     {
131         if (LOG.isDebugEnabled())
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             if (LOG.isDebugEnabled())
211                 LOG.debug("Registered {}", oinstance.getObjectName());
212             _beans.put(obj, oinstance.getObjectName());
213 
214         }
215         catch (Exception e)
216         {
217             LOG.warn("bean: " + obj, e);
218         }
219     }
220 
221     @Override
222     public void beanRemoved(Container parent, Object obj)
223     {
224         if (LOG.isDebugEnabled())
225             LOG.debug("beanRemoved {}",obj);
226         ObjectName bean = _beans.remove(obj);
227 
228         if (bean != null)
229         {
230             try
231             {
232                 _mbeanServer.unregisterMBean(bean);
233                 if (LOG.isDebugEnabled())
234                     LOG.debug("Unregistered {}", bean);
235             }
236             catch (javax.management.InstanceNotFoundException e)
237             {
238                 LOG.ignore(e);
239             }
240             catch (Exception e)
241             {
242                 LOG.warn(e);
243             }
244         }
245     }
246 
247     /**
248      * @param basis name to strip of special characters.
249      * @return normalized name
250      */
251     public String makeName(String basis)
252     {
253         if (basis==null)
254             return basis;
255         return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
256     }
257 
258     @Override
259     public void dump(Appendable out, String indent) throws IOException
260     {
261         ContainerLifeCycle.dumpObject(out,this);
262         ContainerLifeCycle.dump(out, indent, _beans.entrySet());
263     }
264 
265     @Override
266     public String dump()
267     {
268         return ContainerLifeCycle.dump(this);
269     }
270 
271     public void destroy()
272     {
273         for (ObjectName oname : _beans.values())
274             if (oname!=null)
275             {
276                 try
277                 {
278                     _mbeanServer.unregisterMBean(oname);
279                 }
280                 catch (MBeanRegistrationException | InstanceNotFoundException e)
281                 {
282                     LOG.warn(e);
283                 }
284             }
285     }
286 }