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