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.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         LOG.debug("beanAdded {}->{}",parent,obj);
132         
133         // Is their an object name for the parent
134         ObjectName pname=null;
135         if (parent!=null)
136         {
137             pname=_beans.get(parent);
138             if (pname==null)
139             {
140                 // create the parent bean
141                 beanAdded(null,parent);
142                 pname=_beans.get(parent);
143             }
144         }
145         
146         // Does an 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             
158             ObjectName oname = null;
159             if (mbean instanceof ObjectMBean)
160             {
161                 ((ObjectMBean)mbean).setMBeanContainer(this);
162                 oname = ((ObjectMBean)mbean).getObjectName();
163             }
164 
165             //no override mbean object name, so make a generic one
166             if (oname == null)
167             {      
168                 //if no explicit domain, create one
169                 String domain = _domain;
170                 if (domain == null)
171                     domain = obj.getClass().getPackage().getName();
172 
173 
174                 String type = obj.getClass().getName().toLowerCase(Locale.ENGLISH);
175                 int dot = type.lastIndexOf('.');
176                 if (dot >= 0)
177                     type = type.substring(dot + 1);
178 
179 
180                 StringBuffer buf = new StringBuffer();
181 
182                 String context = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectContextBasis()):null;
183                 if (context==null && pname!=null)
184                     context=pname.getKeyProperty("context");
185                                 
186                 if (context != null && context.length()>1)
187                     buf.append("context=").append(context).append(",");
188                 
189                 buf.append("type=").append(type);
190 
191                 String name = (mbean instanceof ObjectMBean)?makeName(((ObjectMBean)mbean).getObjectNameBasis()):context;
192                 if (name != null && name.length()>1)
193                     buf.append(",").append("name=").append(name);
194 
195                 String basis = buf.toString();
196                 
197                 AtomicInteger count = __unique.get(basis);
198                 if (count==null)
199                 {
200                     count=__unique.putIfAbsent(basis,new AtomicInteger());
201                     if (count==null)
202                         count=__unique.get(basis);
203                 }
204                 
205                 oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count.getAndIncrement());
206             }
207 
208             ObjectInstance oinstance = _mbeanServer.registerMBean(mbean, oname);
209             LOG.debug("Registered {}", oinstance.getObjectName());
210             _beans.put(obj, oinstance.getObjectName());
211 
212         }
213         catch (Exception e)
214         {
215             LOG.warn("bean: " + obj, e);
216         }
217     }
218 
219     @Override
220     public void beanRemoved(Container parent, Object obj)
221     {
222         LOG.debug("beanRemoved {}",obj);
223         ObjectName bean = _beans.remove(obj);
224 
225         if (bean != null)
226         {
227             try
228             {
229                 _mbeanServer.unregisterMBean(bean);
230                 LOG.debug("Unregistered {}", bean);
231             }
232             catch (javax.management.InstanceNotFoundException e)
233             {
234                 LOG.ignore(e);
235             }
236             catch (Exception e)
237             {
238                 LOG.warn(e);
239             }
240         }
241     }
242 
243     /**
244      * @param basis name to strip of special characters.
245      * @return normalized name
246      */
247     public String makeName(String basis)
248     {
249         if (basis==null)
250             return basis;
251         return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
252     }
253 
254     @Override
255     public void dump(Appendable out, String indent) throws IOException
256     {
257         ContainerLifeCycle.dumpObject(out,this);
258         ContainerLifeCycle.dump(out, indent, _beans.entrySet());
259     }
260 
261     @Override
262     public String dump()
263     {
264         return ContainerLifeCycle.dump(this);
265     }
266 
267     public void destroy()
268     {
269         for (ObjectName oname : _beans.values())
270             if (oname!=null)
271             {
272                 try
273                 {
274                     _mbeanServer.unregisterMBean(oname);
275                 }
276                 catch (MBeanRegistrationException | InstanceNotFoundException e)
277                 {
278                     LOG.warn(e);
279                 }
280             }
281     }
282 }