View Javadoc

1   /*
2    * EL4J, the Extension Library for the J2EE, adds incremental enhancements to
3    * the spring framework, http://el4j.sf.net
4    * Copyright (C) 2005 by ELCA Informatique SA, Av. de la Harpe 22-24,
5    * 1000 Lausanne, Switzerland, http://www.elca.ch
6    *
7    * EL4J is published under the GNU Lesser General Public License (LGPL)
8    * Version 2.1. See http://www.gnu.org/licenses/
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU Lesser General Public License for more details.
14   *
15   * For alternative licensing, please contact info@elca.ch
16   */
17  
18  package ch.elca.el4j.services.monitoring.jmx;
19  
20  import java.lang.management.ManagementFactory;
21  import java.util.ArrayList;
22  import java.util.HashMap;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.management.InstanceAlreadyExistsException;
28  import javax.management.MBeanRegistrationException;
29  import javax.management.MBeanServer;
30  import javax.management.MalformedObjectNameException;
31  import javax.management.NotCompliantMBeanException;
32  import javax.management.ObjectInstance;
33  import javax.management.ObjectName;
34  
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  import org.springframework.beans.factory.InitializingBean;
38  import org.springframework.context.ApplicationContext;
39  import org.springframework.context.ApplicationContextAware;
40  import org.springframework.context.ApplicationEvent;
41  import org.springframework.context.ApplicationListener;
42  import org.springframework.context.ConfigurableApplicationContext;
43  import org.springframework.context.event.ContextRefreshedEvent;
44  
45  import ch.elca.el4j.core.exceptions.BaseException;
46  import ch.elca.el4j.core.exceptions.BaseRTException;
47  import ch.elca.el4j.services.monitoring.notification.CoreNotificationHelper;
48  import ch.elca.el4j.util.codingsupport.Reject;
49  
50  /**
51   * Entry point for the JMX package. If this bean is defined in an Application
52   * Context, then it will set up the whole JMX world by creating the proxies and
53   * setting the corresponding references.
54   *
55   * @svnLink $Revision: 4010 $;$Date: 2009-12-01 10:59:54 +0100 (Di, 01. Dez 2009) $;$Author: jonasha $;$URL: https://el4j.svn.sourceforge.net/svnroot/el4j/branches/el4j_3_1/el4j/framework/modules/jmx/src/main/java/ch/elca/el4j/services/monitoring/jmx/Loader.java $
56   *
57   * @author Raphael Boog (RBO)
58   * @author Rashid Waraich (RWA)
59   */
60  public class Loader implements ApplicationContextAware, InitializingBean,
61  	ApplicationListener {
62  
63  	/**
64  	 * A map containing the MBean Server as key and the JvmMB as value.
65  	 */
66  	protected static final Map<MBeanServer, JvmMBMBean> s_jvmMBs
67  		= new HashMap<MBeanServer, JvmMBMBean>();
68  
69  	/**
70  	 * Private logger of this class.
71  	 */
72  	private static Logger s_logger = LoggerFactory.getLogger(Loader.class);
73  
74  	/**
75  	 * The Application Context proxy for the Application Context containing this
76  	 * loader.
77  	 */
78  	protected ApplicationContextMB m_acMB;
79  
80  	/**
81  	 * The JVM proxy containing this Application Context.
82  	 */
83  	protected JvmMB m_jvmmb;
84  
85  	/**
86  	 * The MBean Server.
87  	 */
88  	private MBeanServer m_server;
89  
90  	/**
91  	 * The Application Context having instantiated this loader.
92  	 */
93  	private ConfigurableApplicationContext m_applicationContext;
94  	
95  	/**
96  	 * Flag to tell if in method <code>afterPropertiesSet</code> this bean
97  	 * should be initialized. Default is set to <code>false</code>.
98  	 */
99  	private boolean m_initAfterPropertiesSet = false;
100 
101 	/**
102 	 * Returns the MBean Server of this ApplicationContext.
103 	 *
104 	 * @return Returns the MBean Server.
105 	 */
106 	public MBeanServer getServer() {
107 		return m_server;
108 	}
109 
110 	/**
111 	 * Sets the MBean Server of this ApplicationContext.
112 	 *
113 	 * @param server
114 	 *            The MBean Server to set.
115 	 */
116 	public void setServer(MBeanServer server) {
117 		this.m_server = server;
118 	}
119 
120 	/**
121 	 * Takes the JvmMB Bean from the MBean Server or creates a new JvmMB if no
122 	 * such bean is registered at the MBean Server. The JvmMBs have to be stored
123 	 * in a static map for later Application Contexts referring to the same
124 	 * MBeanServer.
125 	 *
126 	 * @throws BaseException
127 	 *             in case the registration at the MBean Server failed
128 	 */
129 	protected void setJvmMB() throws BaseException {
130 		String jreVersion = System.getProperty("java.version");
131 
132 		// Since we access the static member s_jvmMBs, this block is
133 		// synchronized.
134 		synchronized (Loader.class) {
135 
136 			// Get all the object names of this MBean Server in the JVM_DOMAIN.
137 			ObjectName[] jvms = getObjectNames(this.m_server, JvmMB.JVM_DOMAIN,
138 				null, null);
139 
140 			// Remove names of Log4jConfig beans from the jvms-array.
141 			// Because the JVM_DOMAIN was made under the assumption, that
142 			// the JVM_DOMAIN will only contain JVM's. But this assuption
143 			// is not true anymore, therefor the following tests would fail
144 			// if non-JVM beans are not removed from the jvms-array.
145 
146 			int numberOfMBeans = 0;
147 
148 			for (int i = 0; i < jvms.length; i++) {
149 				if (!jvms[i].getCanonicalName().contains("log4jConfig")) {
150 					numberOfMBeans++;
151 				}
152 			}
153 
154 			// In case there is no MBean in the JVM_DOMAIN, we create one and
155 			// register it at the MBean Server.
156 			if (numberOfMBeans == 0) {
157 
158 				// execute the following code only on a
159 				// JRE, with version >= 1.5
160 				if (jreVersion.startsWith("1.5") || jreVersion.startsWith("1.6")
161 					|| jreVersion.startsWith("1.7")) {
162 					s_logger.info("Registering Jdk 1.5 Mbean");
163 					registerJdk15Mbean(m_server);
164 				}
165 
166 				m_jvmmb = new JvmMB();
167 				s_jvmMBs.put(m_server, m_jvmmb);
168 				try {
169 					m_server.registerMBean(m_jvmmb, m_jvmmb.getObjectName());
170 				} catch (InstanceAlreadyExistsException e) {
171 					String message = "The MBean is already under the "
172 						+ "control of the MBean server.";
173 					s_logger.error(message);
174 					throw new BaseException(message, e);
175 				} catch (MBeanRegistrationException e) {
176 					String message = "The MBean will not be registered.";
177 					s_logger.error(message);
178 					throw new BaseException(message, e);
179 				} catch (NotCompliantMBeanException e) {
180 					String message = "This object is not a JMX compliant"
181 						+ " MBean.";
182 					s_logger.error(message);
183 					throw new BaseException(message, e);
184 				}
185 			} else if (numberOfMBeans == 1) {
186 				// In case there is one MBean in the JVM_DOMAIN, we make
187 				// reference to it in this loader.
188 				m_jvmmb = (JvmMB) s_jvmMBs.get(m_server);
189 				if (m_jvmmb == null) {
190 					CoreNotificationHelper
191 						.notifyMisconfiguration("Error in Mapping: "
192 							+ "No JVM defined on this MBean Server.");
193 				}
194 			} else {
195 				// In case there is more than one JVM proxy registered at this
196 				// MBean Server, we throw a BaseRTException.
197 				String message = "There is more than 1 JVM defined on this"
198 					+ " MBean Server.";
199 				s_logger.error(message);
200 				throw new BaseRTException(message, (Object[]) null);
201 			}
202 		}
203 	}
204 
205 	/**
206 	 * Returns all object names of the beans in the domain "domain" and where
207 	 * "keyProperty"="name" which are registered on the MBeanServer server.
208 	 *
209 	 * @param server
210 	 *            the MBean Server
211 	 * @param domain
212 	 *            the domain of the wanted beans
213 	 * @param keyProperty
214 	 *            the key property (e.g. name) as registered on the MBean Server
215 	 * @param value
216 	 *            the value of the key property
217 	 * @return an array of object names satisfying the constraints
218 	 */
219 	public ObjectName[] getObjectNames(MBeanServer server, String domain,
220 		String keyProperty, String value) {
221 
222 		// Get all MBeans at this MBean Server.
223 		Set mBeansSet = server.queryMBeans(null, null);
224 
225 		Reject.ifNull(mBeansSet, "The 'queryMBeans(ObjectName, QueryExp)' "
226 			+ "method on the MBeanServer returned null.");
227 
228 		List<ObjectName> relatedBeans = new ArrayList<ObjectName>();
229 
230 		for (Object oi : mBeansSet) {
231 			ObjectInstance objectInstance = (ObjectInstance) oi;
232 			ObjectName objectName = objectInstance.getObjectName();
233 
234 			// Check whether this object name contains the requested domain and
235 			// the requested value of the requested key property.
236 			if (objectName.getDomain().equals(domain)) {
237 				if (keyProperty != null) {
238 					if (objectName.getKeyProperty(keyProperty).equals(value)) {
239 						relatedBeans.add(objectName);
240 					}
241 				} else {
242 					relatedBeans.add(objectName);
243 				}
244 			}
245 		}
246 			
247 		return relatedBeans.toArray(new ObjectName[relatedBeans.size()]);
248 	}
249 
250 	/**
251 	 * The ApplicationContext is set via ApplicationContextAware.
252 	 *
253 	 * @param applicationContext
254 	 *            The Application Context delivered by the
255 	 *            ApplicationContextAware
256 	 */
257 	public void setApplicationContext(ApplicationContext applicationContext) {
258 		Reject.ifNotAssignableTo(applicationContext,
259 			ConfigurableApplicationContext.class);
260 		m_applicationContext
261 			= (ConfigurableApplicationContext) applicationContext;
262 
263 	}
264 
265 	/**
266 	 * Getter method for the Application Context.
267 	 *
268 	 * @return The Application Context containing this loader object
269 	 */
270 	public ConfigurableApplicationContext getApplicationContext() {
271 		return m_applicationContext;
272 	}
273 
274 	/**
275 	 * @return Returns the initAfterPropertiesSet.
276 	 */
277 	public final boolean isInitAfterPropertiesSet() {
278 		return m_initAfterPropertiesSet;
279 	}
280 
281 	/**
282 	 * @param initAfterPropertiesSet Is the initAfterPropertiesSet to set.
283 	 */
284 	public final void setInitAfterPropertiesSet(
285 		boolean initAfterPropertiesSet) {
286 		m_initAfterPropertiesSet = initAfterPropertiesSet;
287 	}
288 
289 	/**
290 	 * {@inheritDoc}
291 	 *
292 	 * @see #init()
293 	 */
294 	public void afterPropertiesSet() throws BaseException {
295 		if (isInitAfterPropertiesSet()) {
296 			init();
297 		}
298 	}
299 
300 	/**
301 	 * {@inheritDoc}
302 	 *
303 	 * @see #init()
304 	 */
305 	public void onApplicationEvent(ApplicationEvent event) {
306 		if (event instanceof ContextRefreshedEvent
307 			&& ((ContextRefreshedEvent) event).getSource()
308 				== getApplicationContext()) {
309 			try {
310 				init();
311 			} catch (BaseException e) {
312 				throw new RuntimeException(e);
313 			}
314 		}
315 	}
316 
317 	/**
318 	 * Creates the "Real World" image of the given Application Context by
319 	 * setting up the corresponding JMX objects.
320 	 *
321 	 * @throws BaseException On failure.
322 	 */
323 	protected void init() throws BaseException {
324 		// Set the JVM MBean to this Application Context.
325 		setJvmMB();
326 
327 		// Create the Application Context proxy.
328 		m_acMB = new ApplicationContextMB(m_applicationContext,
329 			m_applicationContext.getBeanFactory(),
330 			m_server, m_jvmmb);
331 		
332 		// Initialize the Application Context proxy.
333 		m_acMB.init();
334 	}
335 	/**
336 	 * Registers JDK 1.5 MBeans.
337 	 * @param ms The MBeanServer.
338 	 */
339 	protected static void registerJdk15Mbean(MBeanServer ms) {
340 		try {
341 			ms.registerMBean(ManagementFactory.getClassLoadingMXBean(),
342 				new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME));
343 
344 			ms.registerMBean(ManagementFactory.getThreadMXBean(),
345 				new ObjectName(ManagementFactory.THREAD_MXBEAN_NAME));
346 
347 			ms.registerMBean(ManagementFactory.getRuntimeMXBean(),
348 				new ObjectName(ManagementFactory.RUNTIME_MXBEAN_NAME));
349 			ms.registerMBean(ManagementFactory.getOperatingSystemMXBean(),
350 				new ObjectName(ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME));
351 			ms.registerMBean(ManagementFactory.getMemoryMXBean(),
352 				new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME));
353 			ms.registerMBean(ManagementFactory.getCompilationMXBean(),
354 				new ObjectName(ManagementFactory.COMPILATION_MXBEAN_NAME));
355 
356 			int i = 0;
357 			for (Object o : ManagementFactory.getGarbageCollectorMXBeans()) {
358 				ms.registerMBean(o, new ObjectName(
359 					ManagementFactory.GARBAGE_COLLECTOR_MXBEAN_DOMAIN_TYPE
360 						+ ",num=" + (i++)));
361 			}
362 
363 			i = 0;
364 			for (Object o : ManagementFactory.getMemoryManagerMXBeans()) {
365 				ms.registerMBean(o, new ObjectName(
366 					ManagementFactory.MEMORY_MANAGER_MXBEAN_DOMAIN_TYPE
367 						+ ",num=" + (i++)));
368 			}
369 
370 			i = 0;
371 			for (Object o : ManagementFactory.getMemoryPoolMXBeans()) {
372 				ms.registerMBean(o, new ObjectName(
373 					ManagementFactory.MEMORY_POOL_MXBEAN_DOMAIN_TYPE + ",num="
374 						+ (i++)));
375 			}
376 
377 		} catch (InstanceAlreadyExistsException e1) {
378 			// If we have the system's jmx already running,
379 			// we can expect some system beans already to exist.
380 			// Therefore, we can ignore these.
381 			s_logger.debug("Bean " + e1.getMessage() + "already exists.");
382 			// e1.printStackTrace();
383 		} catch (MBeanRegistrationException e1) {
384 			e1.printStackTrace();
385 		} catch (NotCompliantMBeanException e1) {
386 			e1.printStackTrace();
387 		} catch (MalformedObjectNameException e1) {
388 			e1.printStackTrace();
389 		} catch (NullPointerException e1) {
390 			e1.printStackTrace();
391 		}
392 	}
393 }