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