blob: 00a031fc7716ee270a321b6312605edede9d3111 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
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
19package org.eclipse.jetty.jmx;
20
21import java.io.IOException;
22import java.util.ArrayList;
23import java.util.HashMap;
24import java.util.HashSet;
25import java.util.Iterator;
26import java.util.List;
27import java.util.Locale;
28import java.util.Map;
29import java.util.Set;
30import java.util.WeakHashMap;
31
32import javax.management.MBeanServer;
33import javax.management.ObjectInstance;
34import javax.management.ObjectName;
35
36import org.eclipse.jetty.util.component.AbstractLifeCycle;
37import org.eclipse.jetty.util.component.AggregateLifeCycle;
38import org.eclipse.jetty.util.component.Container;
39import org.eclipse.jetty.util.component.Container.Relationship;
40import org.eclipse.jetty.util.component.Dumpable;
41import org.eclipse.jetty.util.log.Log;
42import org.eclipse.jetty.util.log.Logger;
43import org.eclipse.jetty.util.log.StdErrLog;
44import org.eclipse.jetty.util.thread.ShutdownThread;
45
46/**
47 * Container class for the MBean instances
48 */
49public 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
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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
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
362 public String dump()
363 {
364 return AggregateLifeCycle.dump(this);
365 }
366}