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.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.Set;
24  import java.util.WeakHashMap;
25  import javax.management.MBeanServer;
26  import javax.management.ObjectInstance;
27  import javax.management.ObjectName;
28  
29  import org.eclipse.jetty.util.MultiMap;
30  import org.eclipse.jetty.util.component.AbstractLifeCycle;
31  import org.eclipse.jetty.util.component.AggregateLifeCycle;
32  import org.eclipse.jetty.util.component.Container;
33  import org.eclipse.jetty.util.component.Container.Relationship;
34  import org.eclipse.jetty.util.component.Dumpable;
35  import org.eclipse.jetty.util.log.Log;
36  import org.eclipse.jetty.util.log.Logger;
37  import org.eclipse.jetty.util.thread.ShutdownThread;
38  
39  /**
40   * Container class for the MBean instances
41   */
42  public class MBeanContainer extends AbstractLifeCycle implements Container.Listener, Dumpable
43  {
44      private final static Logger LOG = Log.getLogger(MBeanContainer.class.getName());
45      
46      private final MBeanServer _server;
47      private final WeakHashMap<Object, ObjectName> _beans = new WeakHashMap<Object, ObjectName>();
48      private final HashMap<String, Integer> _unique = new HashMap<String, Integer>();
49      private final WeakHashMap<ObjectName,List<Container.Relationship>> _relations = new WeakHashMap<ObjectName,List<Container.Relationship>>();
50      private String _domain = null;
51  
52      /**
53       * Lookup an object name by instance
54       *
55       * @param object instance for which object name is looked up
56       * @return object name associated with specified instance, or null if not found
57       */
58      public synchronized ObjectName findMBean(Object object)
59      {
60          ObjectName bean = _beans.get(object);
61          return bean == null ? null : bean;
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 = entry.getValue();
75              if (bean.equals(oname))
76                  return entry.getKey();
77          }
78          return null;
79      }
80  
81      /**
82       * Constructs MBeanContainer
83       *
84       * @param server instance of MBeanServer for use by container
85       */
86      public MBeanContainer(MBeanServer server)
87      {
88          _server = server;
89  
90          try
91          {
92              start();
93          }
94          catch (Exception e)
95          {
96              LOG.ignore(e);
97          }
98      }
99  
100     /**
101      * Retrieve instance of MBeanServer used by container
102      *
103      * @return instance of MBeanServer
104      */
105     public MBeanServer getMBeanServer()
106     {
107         return _server;
108     }
109 
110     /**
111      * Set domain to be used to add MBeans
112      *
113      * @param domain domain name
114      */
115     public void setDomain(String domain)
116     {
117         _domain = domain;
118     }
119 
120     /**
121      * Retrieve domain name used to add MBeans
122      *
123      * @return domain name
124      */
125     public String getDomain()
126     {
127         return _domain;
128     }
129 
130     /**
131      * Implementation of Container.Listener interface
132      *
133      * @see org.eclipse.jetty.util.component.Container.Listener#add(org.eclipse.jetty.util.component.Container.Relationship)
134      */
135     public synchronized void add(Relationship relationship)
136     {
137         LOG.debug("add {}",relationship);
138         ObjectName parent = _beans.get(relationship.getParent());
139         if (parent == null)
140         {
141             addBean(relationship.getParent());
142             parent = _beans.get(relationship.getParent());
143         }
144 
145         ObjectName child = _beans.get(relationship.getChild());
146         if (child == null)
147         {
148             addBean(relationship.getChild());
149             child = _beans.get(relationship.getChild());
150         }
151 
152         if (parent != null && child != null)
153         {
154             List<Container.Relationship> rels = _relations.get(parent);
155             if (rels==null)
156             {
157                 rels=new ArrayList<Container.Relationship>();
158                 _relations.put(parent,rels);
159             }
160             rels.add(relationship);
161         }
162     }
163 
164     /**
165      * Implementation of Container.Listener interface
166      *
167      * @see org.eclipse.jetty.util.component.Container.Listener#remove(org.eclipse.jetty.util.component.Container.Relationship)
168      */
169     public synchronized void remove(Relationship relationship)
170     {
171         LOG.debug("remove {}",relationship);
172         ObjectName parent = _beans.get(relationship.getParent());
173         ObjectName child = _beans.get(relationship.getChild());
174 
175         if (parent != null && child != null)
176         {
177             List<Container.Relationship> rels = _relations.get(parent);
178             if (rels!=null)
179             {
180                 for (Iterator<Container.Relationship> i=rels.iterator();i.hasNext();)
181                 {
182                     Container.Relationship r = i.next();
183                     if (relationship.equals(r) || r.getChild()==null)
184                         i.remove();
185                 }
186             }
187         }
188     }
189 
190     /**
191      * Implementation of Container.Listener interface
192      *
193      * @see org.eclipse.jetty.util.component.Container.Listener#removeBean(java.lang.Object)
194      */
195     public synchronized void removeBean(Object obj)
196     {
197         LOG.debug("removeBean {}",obj);
198         ObjectName bean = _beans.remove(obj);
199 
200         if (bean != null)
201         {
202             List<Container.Relationship> beanRelations= _relations.remove(bean);
203             if (beanRelations != null)
204             {
205                 LOG.debug("Unregister {}", beanRelations);
206                 List<?> removeList = new ArrayList<Object>(beanRelations);
207                 for (Object r : removeList)
208                 {
209                     Container.Relationship relation = (Relationship)r;
210                     relation.getContainer().update(relation.getParent(), relation.getChild(), null, relation.getRelationship(), true);
211                 }
212             }
213 
214             try
215             {
216                 _server.unregisterMBean(bean);
217                 LOG.debug("Unregistered {}", bean);
218             }
219             catch (javax.management.InstanceNotFoundException e)
220             {
221                 LOG.ignore(e);
222             }
223             catch (Exception e)
224             {
225                 LOG.warn(e);
226             }
227         }
228     }
229 
230     /**
231      * Implementation of Container.Listener interface
232      *
233      * @see org.eclipse.jetty.util.component.Container.Listener#addBean(java.lang.Object)
234      */
235     public synchronized void addBean(Object obj)
236     {
237         LOG.debug("addBean {}",obj);
238         try
239         {
240             if (obj == null || _beans.containsKey(obj))
241                 return;
242 
243             Object mbean = ObjectMBean.mbeanFor(obj);
244             if (mbean == null)
245                 return;
246 
247             ObjectName oname = null;
248             if (mbean instanceof ObjectMBean)
249             {
250                 ((ObjectMBean)mbean).setMBeanContainer(this);
251                 oname = ((ObjectMBean)mbean).getObjectName();
252             }
253 
254             //no override mbean object name, so make a generic one
255             if (oname == null)
256             {
257                 String type = obj.getClass().getName().toLowerCase();
258                 int dot = type.lastIndexOf('.');
259                 if (dot >= 0)
260                     type = type.substring(dot + 1);
261 
262                 String context = null;
263                 if (mbean instanceof ObjectMBean)
264                 {
265                     context = makeName(((ObjectMBean)mbean).getObjectContextBasis());
266                 }
267 
268                 String name = null;
269                 if (mbean instanceof ObjectMBean)
270                 {
271                     name = makeName(((ObjectMBean)mbean).getObjectNameBasis());
272                 }
273 
274                 StringBuffer buf = new StringBuffer();
275                 buf.append("type=").append(type);
276                 if (context != null && context.length()>1)
277                 {
278                     buf.append(buf.length()>0 ? ",":"");
279                     buf.append("context=").append(context);
280                 }
281                 if (name != null && name.length()>1)
282                 {
283                     buf.append(buf.length()>0 ? ",":"");
284                     buf.append("name=").append(name);
285                 }
286                     
287                 String basis = buf.toString();
288                 Integer count = _unique.get(basis);
289                 count = count == null ? 0 : 1 + count;
290                 _unique.put(basis, count);
291 
292                 //if no explicit domain, create one
293                 String domain = _domain;
294                 if (domain == null)
295                     domain = obj.getClass().getPackage().getName();
296 
297                 oname = ObjectName.getInstance(domain + ":" + basis + ",id=" + count);
298             }
299 
300             ObjectInstance oinstance = _server.registerMBean(mbean, oname);
301             LOG.debug("Registered {}", oinstance.getObjectName());
302             _beans.put(obj, oinstance.getObjectName());
303 
304         }
305         catch (Exception e)
306         {
307             LOG.warn("bean: " + obj, e);
308         }
309     }
310 
311     /**
312      * @param basis name to strip of special characters.
313      * @return normalized name
314      */
315     public String makeName(String basis)
316     {
317         if (basis==null)
318             return basis;
319         return basis.replace(':', '_').replace('*', '_').replace('?', '_').replace('=', '_').replace(',', '_').replace(' ', '_');
320     }
321 
322     /**
323      * Perform actions needed to start lifecycle
324      *
325      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStart()
326      */
327     public void doStart()
328     {
329         ShutdownThread.register(this);
330     }
331 
332     /**
333      * Perform actions needed to stop lifecycle
334      *
335      * @see org.eclipse.jetty.util.component.AbstractLifeCycle#doStop()
336      */
337     public void doStop()
338     {
339         Set<Object> removeSet = new HashSet<Object>(_beans.keySet());
340         for (Object removeObj : removeSet)
341         {
342             removeBean(removeObj);
343         }
344     }
345 
346     public void dump(Appendable out, String indent) throws IOException
347     {
348         out.append(toString()).append("\n");
349         AggregateLifeCycle.dump(out, indent, _beans.entrySet());
350     }
351 
352     public String dump()
353     {
354         return AggregateLifeCycle.dump(this);
355     }
356 }