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