View Javadoc

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