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