Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id E13E7200BBB for ; Thu, 27 Oct 2016 00:49:04 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id DFCC7160B07; Wed, 26 Oct 2016 22:49:04 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id DCFDD160AFD for ; Thu, 27 Oct 2016 00:49:02 +0200 (CEST) Received: (qmail 54849 invoked by uid 500); 26 Oct 2016 22:49:02 -0000 Mailing-List: contact commits-help@geode.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@geode.incubator.apache.org Delivered-To: mailing list commits@geode.incubator.apache.org Received: (qmail 54840 invoked by uid 99); 26 Oct 2016 22:49:02 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2016 22:49:02 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 7FB1BC76B7 for ; Wed, 26 Oct 2016 22:49:01 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -6.219 X-Spam-Level: X-Spam-Status: No, score=-6.219 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-2.999] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id 0OKhq81fFBRS for ; Wed, 26 Oct 2016 22:48:56 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with SMTP id CD3555FCA3 for ; Wed, 26 Oct 2016 22:48:31 +0000 (UTC) Received: (qmail 50614 invoked by uid 99); 26 Oct 2016 22:48:29 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 26 Oct 2016 22:48:29 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 05B93E00DB; Wed, 26 Oct 2016 22:48:29 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: klund@apache.org To: commits@geode.incubator.apache.org Date: Wed, 26 Oct 2016 22:49:26 -0000 Message-Id: <204810acdac84ec9a83a12853d9b0e25@git.apache.org> In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [60/93] incubator-geode git commit: GEODE-288: move admin package to internal archived-at: Wed, 26 Oct 2016 22:49:05 -0000 http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthConfigJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthConfigJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthConfigJmxImpl.java new file mode 100644 index 0000000..23e9038 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthConfigJmxImpl.java @@ -0,0 +1,211 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.jmx.impl; + +import javax.management.ObjectName; +import javax.management.modelmbean.ModelMBean; + +import org.apache.geode.internal.admin.api.AdminException; +import org.apache.geode.internal.admin.api.GemFireHealth; +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.internal.admin.api.impl.GemFireHealthConfigImpl; + +/** + * The JMX "managed resource" that represents the configuration for the health of GemFire. + * Basically, it provides the behavior of GemFireHealthConfigImpl, but does some JMX + * stuff like registering beans with the agent. + * + *

+ * + * Unlike other ManagedResources this class cannot simply subclass + * GemFireHealthImpl because it instances are serialized and sent to other VMs. This is + * problematic because the other VMs most likely do not have JMX classes like + * ModelMBean on their classpaths. So, instead we delegate all of the + * GemFireHealthConfig behavior to another object which IS serialized. + * + * @see GemFireHealthJmxImpl#createDistributedSystemHealthConfig + * + * + * @since GemFire 3.5 + */ +@edu.umd.cs.findbugs.annotations.SuppressWarnings( + justification = "This class is deprecated. Also, any further changes so close to the release is inadvisable.") +public class GemFireHealthConfigJmxImpl + implements GemFireHealthConfig, ManagedResource, java.io.Serializable { + + private static final long serialVersionUID = 1482719647163239953L; + + /** The GemFireHealth that we help configure */ + private GemFireHealth health; + + /** The name of the MBean that will manage this resource */ + private String mbeanName; + + /** The ModelMBean that is configured to manage this resource */ + private ModelMBean modelMBean; + + /** The delegate that contains the real config state */ + private GemFireHealthConfig delegate; + + /** The object name of this managed resource */ + private ObjectName objectName; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new GemFireHealthConfigJmxImpl that configures the health monitoring of + * components running on the given host. + */ + GemFireHealthConfigJmxImpl(GemFireHealthJmxImpl health, String hostName) throws AdminException { + + this.delegate = new GemFireHealthConfigImpl(hostName); + this.health = health; + this.mbeanName = new StringBuffer().append(MBEAN_NAME_PREFIX).append("GemFireHealthConfig,id=") + .append(MBeanUtil.makeCompliantMBeanNameProperty(health.getDistributedSystem().getId())) + .append(",host=") + .append((hostName == null ? "default" : MBeanUtil.makeCompliantMBeanNameProperty(hostName))) + .toString(); + this.objectName = MBeanUtil.createMBean(this); + } + + ////////////////////// Instance Methods ////////////////////// + + /** + * Applies the changes made to this config back to the health monitor. + * + * @see GemFireHealth#setDistributedSystemHealthConfig + */ + public void applyChanges() { + String hostName = this.getHostName(); + if (hostName == null) { + this.health.setDefaultGemFireHealthConfig(this); + + } else { + this.health.setGemFireHealthConfig(hostName, this); + } + } + + public String getMBeanName() { + return this.mbeanName; + } + + public ModelMBean getModelMBean() { + return this.modelMBean; + } + + public ObjectName getObjectName() { + return this.objectName; + } + + public void setModelMBean(ModelMBean modelMBean) { + this.modelMBean = modelMBean; + } + + public ManagedResourceType getManagedResourceType() { + return ManagedResourceType.GEMFIRE_HEALTH_CONFIG; + } + + /** + * Replace this object with the delegate that can be properly serialized. + */ + public Object writeReplace() { + return this.delegate; + } + + ////////////////////// MemberHealthConfig ////////////////////// + + public long getMaxVMProcessSize() { + return delegate.getMaxVMProcessSize(); + } + + public void setMaxVMProcessSize(long size) { + delegate.setMaxVMProcessSize(size); + } + + public long getMaxMessageQueueSize() { + return delegate.getMaxMessageQueueSize(); + } + + public void setMaxMessageQueueSize(long maxMessageQueueSize) { + delegate.setMaxMessageQueueSize(maxMessageQueueSize); + } + + public long getMaxReplyTimeouts() { + return delegate.getMaxReplyTimeouts(); + } + + public void setMaxReplyTimeouts(long maxReplyTimeouts) { + delegate.setMaxReplyTimeouts(maxReplyTimeouts); + } + + public double getMaxRetransmissionRatio() { + return delegate.getMaxRetransmissionRatio(); + } + + public void setMaxRetransmissionRatio(double ratio) { + delegate.setMaxRetransmissionRatio(ratio); + } + + ////////////////////// CacheHealthConfig ////////////////////// + + public long getMaxNetSearchTime() { + return delegate.getMaxNetSearchTime(); + } + + public void setMaxNetSearchTime(long maxNetSearchTime) { + delegate.setMaxNetSearchTime(maxNetSearchTime); + } + + public long getMaxLoadTime() { + return delegate.getMaxLoadTime(); + } + + public void setMaxLoadTime(long maxLoadTime) { + delegate.setMaxLoadTime(maxLoadTime); + } + + public double getMinHitRatio() { + return delegate.getMinHitRatio(); + } + + public void setMinHitRatio(double minHitRatio) { + delegate.setMinHitRatio(minHitRatio); + } + + public long getMaxEventQueueSize() { + return delegate.getMaxEventQueueSize(); + } + + public void setMaxEventQueueSize(long maxEventQueueSize) { + delegate.setMaxEventQueueSize(maxEventQueueSize); + } + + ////////////////////// GemFireHealthConfig ////////////////////// + + public String getHostName() { + return delegate.getHostName(); + } + + public void setHealthEvaluationInterval(int interval) { + delegate.setHealthEvaluationInterval(interval); + } + + public int getHealthEvaluationInterval() { + return delegate.getHealthEvaluationInterval(); + } + + public void cleanupResource() {} + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthJmxImpl.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthJmxImpl.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthJmxImpl.java new file mode 100644 index 0000000..fcff9be --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GemFireHealthJmxImpl.java @@ -0,0 +1,171 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.jmx.impl; + +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; +import javax.management.modelmbean.ModelMBean; + +import org.apache.logging.log4j.Logger; + +import org.apache.geode.SystemFailure; +import org.apache.geode.internal.admin.api.AdminException; +import org.apache.geode.internal.admin.api.DistributedSystemHealthConfig; +import org.apache.geode.internal.admin.api.GemFireHealthConfig; +import org.apache.geode.internal.admin.api.RuntimeAdminException; +import org.apache.geode.internal.admin.api.impl.GemFireHealthImpl; +import org.apache.geode.internal.admin.GfManagerAgent; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; +// import org.apache.commons.modeler.ManagedBean; + +/** + * The JMX "managed resource" that represents the health of GemFire. Basically, it provides the + * behavior of GemFireHealthImpl, but does some JMX stuff like registering beans with + * the agent. + * + * @see AdminDistributedSystemJmxImpl#createGemFireHealth + * + * + * @since GemFire 3.5 + */ +public class GemFireHealthJmxImpl extends GemFireHealthImpl implements ManagedResource { + + private static final Logger logger = LogService.getLogger(); + + /** The name of the MBean that will manage this resource */ + private String mbeanName; + + /** The ModelMBean that is configured to manage this resource */ + private ModelMBean modelMBean; + + /** The object name of the MBean created for this managed resource */ + private final ObjectName objectName; + + /////////////////////// Constructors /////////////////////// + + /** + * Creates a new GemFireHealthJmxImpl that monitors the health of the given + * distributed system and uses the given JMX agent. + */ + GemFireHealthJmxImpl(GfManagerAgent agent, AdminDistributedSystemJmxImpl system) + throws AdminException { + + super(agent, system); + this.mbeanName = new StringBuffer().append(MBEAN_NAME_PREFIX).append("GemFireHealth,id=") + .append(MBeanUtil.makeCompliantMBeanNameProperty(system.getId())).toString(); + this.objectName = MBeanUtil.createMBean(this); + } + + ////////////////////// Instance Methods ////////////////////// + + public String getHealthStatus() { + return getHealth().toString(); + } + + public ObjectName manageGemFireHealthConfig(String hostName) throws MalformedObjectNameException { + try { + GemFireHealthConfig config = getGemFireHealthConfig(hostName); + GemFireHealthConfigJmxImpl jmx = (GemFireHealthConfigJmxImpl) config; + return new ObjectName(jmx.getMBeanName()); + } // catch (AdminException e) { logWriter.warning(e); throw e; } + catch (RuntimeException e) { + logger.warn(e.getMessage(), e); + throw e; + } catch (VirtualMachineError err) { + SystemFailure.initiateFailure(err); + // If this ever returns, rethrow the error. We're poisoned + // now, so don't let this thread continue. + throw err; + } catch (Error e) { + // Whenever you catch Error or Throwable, you must also + // catch VirtualMachineError (see above). However, there is + // _still_ a possibility that you are dealing with a cascading + // error condition, so you also need to check to see if the JVM + // is still usable: + SystemFailure.checkFailure(); + logger.error(e.getMessage(), e); + throw e; + } + } + + /** + * Creates a new {@link DistributedSystemHealthConfigJmxImpl} + */ + @Override + protected DistributedSystemHealthConfig createDistributedSystemHealthConfig() { + + try { + return new DistributedSystemHealthConfigJmxImpl(this); + + } catch (AdminException ex) { + throw new RuntimeAdminException( + LocalizedStrings.GemFireHealthJmxImpl_WHILE_GETTING_THE_DISTRIBUTEDSYSTEMHEALTHCONFIG + .toLocalizedString(), + ex); + } + } + + /** + * Creates a new {@link GemFireHealthConfigJmxImpl} + */ + @Override + protected GemFireHealthConfig createGemFireHealthConfig(String hostName) { + + try { + return new GemFireHealthConfigJmxImpl(this, hostName); + + } catch (AdminException ex) { + throw new RuntimeAdminException( + LocalizedStrings.GemFireHealthJmxImpl_WHILE_GETTING_THE_GEMFIREHEALTHCONFIG + .toLocalizedString(), + ex); + } + } + + /** + * Ensures that the three primary Health MBeans are registered and returns their ObjectNames. + */ + protected void ensureMBeansAreRegistered() { + MBeanUtil.ensureMBeanIsRegistered(this); + MBeanUtil.ensureMBeanIsRegistered((ManagedResource) this.defaultConfig); + MBeanUtil.ensureMBeanIsRegistered((ManagedResource) this.dsHealthConfig); + } + + public String getMBeanName() { + return this.mbeanName; + } + + public ModelMBean getModelMBean() { + return this.modelMBean; + } + + public void setModelMBean(ModelMBean modelMBean) { + this.modelMBean = modelMBean; + } + + public ManagedResourceType getManagedResourceType() { + return ManagedResourceType.GEMFIRE_HEALTH; + } + + public ObjectName getObjectName() { + return this.objectName; + } + + public void cleanupResource() { + close(); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GenerateMBeanHTML.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GenerateMBeanHTML.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GenerateMBeanHTML.java new file mode 100644 index 0000000..bdf4d36 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/GenerateMBeanHTML.java @@ -0,0 +1,500 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.jmx.impl; + +import org.apache.geode.internal.ClassPathLoader; +import org.apache.geode.internal.i18n.LocalizedStrings; + +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.*; +import org.xml.sax.helpers.DefaultHandler; +import java.io.*; + +/** + * A tool that reads the XML description of MBeans used with the Jakarta Commons Modeler and + * generates an HTML file that documents each MBean. + * + * @since GemFire 3.5 + */ +public class GenerateMBeanHTML extends DefaultHandler { + + /** The location of the DTD for the MBean descriptions */ + private static final String DTD_LOCATION = "/org/apache/geode/internal/admin/api/jmx/impl/doc-files/mbeans-descriptors.dtd"; + + // /** The system id of MBean description's DTD */ + // private static final String SYSTEM_ID = + // "http://jakarta.apache.org/commons/dtds/mbeans-descriptors.dtd"; + + // /** The public id for the DTD */ + // private static final String PUBLIC_ID = + // "-//Apache Software Foundation//DTD Model MBeans Configuration File"; + + /** The name of the "mbean-descriptors" element */ + private static final String MBEANS_DESCRIPTORS = "mbeans-descriptors"; + + /** The name of the "mbean" element */ + private static final String MBEAN = "mbean"; + + /** The name of the "name" attribute */ + private static final String NAME = "name"; + + /** The name of the "description" attribute */ + private static final String DESCRIPTION = "description"; + + /** The name of the "type" attribute */ + private static final String TYPE = "type"; + + /** The name of the "attribute" element */ + private static final String ATTRIBUTE = "attribute"; + + /** The name of the "writeable" attribute */ + private static final String WRITEABLE = "writeable"; + + /** The name of the "operation" element */ + private static final String OPERATION = "operation"; + + /** The name of the "returnType" attribute */ + private static final String RETURN_TYPE = "returnType"; + + /** The name of the "paremeter" element */ + private static final String PARAMETER = "parameter"; + + /** The name of the "notification" element */ + private static final String NOTIFICATION = "notification"; + + // /** The name of the "description" element */ + // private static final String DESCRIPTOR = "descriptor"; + + /** The name of the "field" element */ + private static final String FIELD = "field"; + + /** The name of the "value" attribute */ + private static final String VALUE = "value"; + + ////////////////////// Instance Fields /////////////////////// + + /** Where the generated HTML data is written */ + private PrintWriter pw; + + /** Have we seen attributes for the current MBean? */ + private boolean seenAttribute = false; + + /** Have we seen operations for the current MBean? */ + private boolean seenOperation = false; + + /** Have we seen notifications for the current MBean? */ + private boolean seenNotifications = false; + + /////////////////////// Static Methods /////////////////////// + + /** + * Converts data from the given InputStream into HTML that is written to the given + * PrintWriter + */ + private static void convert(InputStream in, PrintWriter out) throws Exception { + + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(true); + SAXParser parser = factory.newSAXParser(); + DefaultHandler handler = new GenerateMBeanHTML(out); + parser.parse(in, handler); + } + + //////////////////////// Constructors //////////////////////// + + /** + * Creates a new GenerateMBeanHTML that writes to the given PrintWriter. + */ + private GenerateMBeanHTML(PrintWriter pw) { + this.pw = pw; + } + + ////////////////////// Instance Methods ////////////////////// + + /** + * Given a public id, attempt to resolve it to a DTD. Returns an InputSoure for the + * DTD. + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) throws SAXException { + + if (publicId == null || systemId == null) { + throw new SAXException(LocalizedStrings.GenerateMBeanHTML_PUBLIC_ID_0_SYSTEM_ID_1 + .toLocalizedString(new Object[] {publicId, systemId})); + } + + // Figure out the location for the publicId. + String location = DTD_LOCATION; + + InputSource result; + // if (location != null) (cannot be null) + { + InputStream stream = ClassPathLoader.getLatest().getResourceAsStream(getClass(), location); + if (stream != null) { + result = new InputSource(stream); + } else { + throw new SAXNotRecognizedException( + LocalizedStrings.GenerateMBeanHTML_DTD_NOT_FOUND_0.toLocalizedString(location)); + } + + // } else { + // throw new + // SAXNotRecognizedException(LocalizedStrings.GenerateMBeanHTML_COULD_NOT_FIND_DTD_FOR_0_1.toLocalizedString(new + // Object[] {publicId, systemId})); + } + + return result; + } + + /** + * Warnings are ignored + */ + @Override + public void warning(SAXParseException ex) throws SAXException { + + } + + /** + * Rethrow the SAXParseException + */ + @Override + public void error(SAXParseException ex) throws SAXException { + throw ex; + } + + /** + * Rethrow the SAXParseException + */ + @Override + public void fatalError(SAXParseException ex) throws SAXException { + throw ex; + } + + /** + * Starts the HTML document + */ + private void startMBeansDescriptors() { + pw.println(""); + pw.println(""); + pw.println("GemFire MBeans Interface"); + pw.println(""); + pw.println(""); + pw.println("

GemFire Management Beans

"); + pw.println(""); + pw.println("

This document describes the attributes, operations,"); + pw.println("and notifications of the GemFire Administration"); + pw.println("Management Beans (MBeans).

"); + pw.println(""); + } + + /** + * Ends the HTML document + */ + private void endMBeansDescriptors() { + pw.println(""); + } + + /** + * Generates a heading and a table declaration for an MBean + */ + private void startMBean(Attributes atts) { + String name = atts.getValue(NAME); + /* String description = */ atts.getValue(DESCRIPTION); + pw.println("

" + name + " MBean

"); + pw.println(""); + pw.println(""); + pw.println(" "); + pw.println(" "); + pw.println(""); + } + + /** + * Ends the MBean table + */ + private void endMBean() { + this.seenAttribute = false; + this.seenOperation = false; + this.seenNotifications = false; + + pw.println("
Description:GemFire distributed system
"); + pw.println(""); + + pw.println("

"); + pw.println(""); + } + + /** + * Generates a table row for an MBean attribute + */ + private void startAttribute(Attributes atts) { + if (!this.seenAttribute) { + // Print header row + pw.println(""); + pw.println(" Attributes"); + pw.println(" Name"); + pw.println(" Type"); + pw.println(" Description"); + pw.println(" Writable"); + pw.println(""); + + } + + this.seenAttribute = true; + + String name = atts.getValue(NAME); + String description = atts.getValue(DESCRIPTION); + String type = atts.getValue(TYPE); + String writeable = atts.getValue(WRITEABLE); + + pw.println(""); + pw.println(" "); + pw.println(" " + name + ""); + pw.println(" " + type + ""); + pw.println(" " + description + ""); + pw.println(" " + writeable + ""); + pw.println(""); + } + + /** + * Generates a table row for an MBean operation + */ + private void startOperation(Attributes atts) { + if (!this.seenOperation) { + if (!this.seenAttribute) { + pw.println(""); + pw.println(" Operations"); + pw.println(" Name"); + pw.println(" Type"); + pw.println(" Description"); + pw.println(" "); + pw.println(""); + + } else { + String title = "Operations and Parameters"; + pw.println(""); + pw.println(" " + title + ""); + pw.println(""); + } + } + + this.seenOperation = true; + + String name = atts.getValue(NAME); + String type = atts.getValue(RETURN_TYPE); + String description = atts.getValue(DESCRIPTION); + + pw.println(""); + pw.println(" "); + pw.println(" " + name + ""); + pw.println(" " + type + ""); + pw.println(" " + description + ""); + pw.println(""); + + } + + /** + * Generates a table row for the parameter of an MBean operation + */ + private void startParameter(Attributes atts) { + String name = atts.getValue(NAME); + String description = atts.getValue(DESCRIPTION); + String type = atts.getValue(TYPE); + + pw.println(""); + pw.println(" "); + pw.println(" "); + pw.println(" " + name + ""); + pw.println(" " + type + ""); + pw.println(" " + description + ""); + pw.println(""); + } + + /** + * Generates a row in a table for an MBean notification + */ + private void startNotification(Attributes atts) { + if (!this.seenNotifications) { + if (!this.seenAttribute && !this.seenOperation) { + pw.println(""); + pw.println(" Notifications"); + pw.println(" Name"); + pw.println(" Type"); + pw.println(" Description"); + pw.println(" "); + pw.println(""); + pw.println(""); + + } else { + pw.println(""); + pw.println(" Notifications and Fields"); + pw.println(""); + } + } + + this.seenNotifications = true; + + String name = atts.getValue(NAME); + String description = atts.getValue(DESCRIPTION); + + pw.println(""); + pw.println(" "); + pw.println(" " + name + ""); + pw.println(" " + description + ""); + pw.println(""); + + } + + /** + * Generates a table row for a descriptor field + */ + private void startField(Attributes atts) { + String name = atts.getValue(NAME); + String value = atts.getValue(VALUE); + + pw.println(""); + pw.println(" "); + pw.println(" "); + pw.println(" " + name + ""); + pw.println(" " + value + ""); + pw.println(""); + + } + + @Override + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) + throws SAXException { + + if (qName.equals(MBEANS_DESCRIPTORS)) { + startMBeansDescriptors(); + + } else if (qName.equals(MBEAN)) { + startMBean(atts); + + } else if (qName.equals(ATTRIBUTE)) { + startAttribute(atts); + + } else if (qName.equals(OPERATION)) { + startOperation(atts); + + } else if (qName.equals(PARAMETER)) { + startParameter(atts); + + } else if (qName.equals(NOTIFICATION)) { + startNotification(atts); + + } else if (qName.equals(FIELD)) { + startField(atts); + } + + } + + + @Override + public void endElement(String namespaceURI, String localName, String qName) throws SAXException { + + if (qName.equals(MBEANS_DESCRIPTORS)) { + endMBeansDescriptors(); + + } else if (qName.equals(MBEAN)) { + endMBean(); + } + + } + + ////////// Inherited methods that don't do anything ////////// + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + + } + + @Override + public void setDocumentLocator(Locator locator) {} + + @Override + public void startDocument() throws SAXException {} + + @Override + public void endDocument() throws SAXException {} + + @Override + public void startPrefixMapping(String prefix, String uri) throws SAXException {} + + @Override + public void endPrefixMapping(String prefix) throws SAXException {} + + @Override + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {} + + @Override + public void processingInstruction(String target, String data) throws SAXException {} + + @Override + public void skippedEntity(String name) throws SAXException {} + + //////////////////////// Main Program //////////////////////// + + // private static final PrintStream out = System.out; + private static final PrintStream err = System.err; + + /** + * Prints usage information about this program + */ + private static void usage(String s) { + err.println("\n** " + s + "\n"); + err.println("usage: java GenerateMBeanHTML xmlFile htmlFile"); + err.println(""); + err.println("Converts an MBeans description XML file into an HTML"); + err.println("file suitable for documentation"); + + err.println(""); + + System.exit(1); + } + + public static void main(String[] args) throws Exception { + String xmlFileName = null; + String htmlFileName = null; + + for (int i = 0; i < args.length; i++) { + if (xmlFileName == null) { + xmlFileName = args[i]; + + } else if (htmlFileName == null) { + htmlFileName = args[i]; + + } else { + usage("Extraneous command line argument: " + args[i]); + } + } + + if (xmlFileName == null) { + usage("Missing XML file name"); + + } else if (htmlFileName == null) { + usage("Missing HTML file name"); + } + + File xmlFile = new File(xmlFileName); + if (!xmlFile.exists()) { + usage("XML file \"" + xmlFile + "\" does not exist"); + } + + File htmlFile = new File(htmlFileName); + convert(new FileInputStream(xmlFile), new PrintWriter(new FileWriter(htmlFile), true)); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/895fd144/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MBeanUtil.java ---------------------------------------------------------------------- diff --git a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MBeanUtil.java b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MBeanUtil.java new file mode 100755 index 0000000..9e4ad36 --- /dev/null +++ b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/MBeanUtil.java @@ -0,0 +1,765 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package org.apache.geode.internal.admin.api.jmx.impl; + +import java.net.URL; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import javax.management.InstanceNotFoundException; +import javax.management.JMException; +import javax.management.JMRuntimeException; +import javax.management.ListenerNotFoundException; +import javax.management.MBeanRegistrationException; +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; +import javax.management.MBeanServerNotification; +import javax.management.MalformedObjectNameException; +import javax.management.Notification; +import javax.management.NotificationListener; +import javax.management.ObjectName; +import javax.management.timer.TimerMBean; + +import org.apache.commons.modeler.ManagedBean; +import org.apache.commons.modeler.Registry; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; + +import org.apache.geode.SystemFailure; +import org.apache.geode.internal.admin.api.RuntimeAdminException; +import org.apache.geode.internal.ClassPathLoader; +import org.apache.geode.internal.i18n.LocalizedStrings; +import org.apache.geode.internal.logging.LogService; + +/** + * Common support for MBeans and {@link ManagedResource}s. Static loading of this class creates the + * MBeanServer and Modeler Registry. + * + * @since GemFire 3.5 + * + */ +public class MBeanUtil { + + private static final Logger logger = LogService.getLogger(); + + /** The default MBeanServer domain name is "GemFire" */ + private static final String DEFAULT_DOMAIN = "GemFire"; + + /** MBean Name for refreshTimer */ + private static String REFRESH_TIMER_NAME = DEFAULT_DOMAIN + ":type=RefreshTimer"; + + /* indicates whether the mbeanServer, registry & refreshTimer are started */ + private static boolean isStarted; + + /** The Commons-Modeler configuration registry for our managed beans */ + private static Registry registry; + + /** The MBeanServer for this application */ + private static MBeanServer mbeanServer; + + /** MBean name of the Timer which handles refresh notifications */ + private static ObjectName refreshTimerObjectName; + + /** Actual TimerMBean responsible for refresh notifications */ + private static TimerMBean refreshTimer; + + /** + * Map of ObjectNames to current timerNotificationIds + *

+ * map: key=ObjectName, value=map: key=RefreshNotificationType, value=timerNotificationId + */ + private static Map> refreshClients = + new HashMap>(); + + /** key=ObjectName, value=ManagedResource */ + private final static Map managedResources = + new HashMap(); + + static { + try { + refreshTimerObjectName = ObjectName.getInstance(REFRESH_TIMER_NAME); + } catch (Exception e) { + logStackTrace(Level.ERROR, e); + } + } + + /** + * Initializes Mbean Server, Registry, Refresh Timer & registers Server Notification Listener. + * + * @return reference to the mbeanServer + */ + static MBeanServer start() { + if (!isStarted) { + mbeanServer = createMBeanServer(); + registry = createRegistry(); + + registerServerNotificationListener(); + createRefreshTimer(); + isStarted = true; + } + + return mbeanServer; + } + + /** + * Stops Registry, Refresh Timer. Releases Mbean Server after. + */ + static void stop() { + if (isStarted) { + stopRefreshTimer(); + + registry.stop(); + registry = null; + releaseMBeanServer();// makes mbeanServer null + isStarted = false; + } + } + + /** + * Create and configure (if necessary) and return the MBeanServer with which we will + * be registering our ModelMBean implementations. + * + * @see javax.management.MBeanServer + */ + static synchronized MBeanServer createMBeanServer() { + if (mbeanServer == null) { + mbeanServer = MBeanServerFactory.createMBeanServer(DEFAULT_DOMAIN); + } + return mbeanServer; + } + + /** + * Create and configure (if necessary) and return the Commons-Modeler registry of managed object + * descriptions. + * + * @see org.apache.commons.modeler.Registry + */ + static synchronized Registry createRegistry() { + if (registry == null) { + try { + registry = Registry.getRegistry(null, null); + if (mbeanServer == null) { + throw new IllegalStateException( + LocalizedStrings.MBeanUtil_MBEAN_SERVER_NOT_INITIALIZED_YET.toLocalizedString()); + } + registry.setMBeanServer(mbeanServer); + + String mbeansResource = getOSPath("/org/apache/geode/internal/admin/api/jmx/mbeans-descriptors.xml"); + //System.out.println(LocalizedStrings.MBeanUtil_LOADING_RESOURCE_0.toLocalizedString(mbeansResource)); + + URL url = ClassPathLoader.getLatest().getResource(MBeanUtil.class, mbeansResource); + raiseOnFailure(url != null, LocalizedStrings.MBeanUtil_FAILED_TO_FIND_0 + .toLocalizedString(new Object[] {mbeansResource})); + registry.loadMetadata(url); + + // simple test to make sure the xml was actually loaded and is valid... + String[] test = registry.findManagedBeans(); + raiseOnFailure(test != null && test.length > 0, LocalizedStrings.MBeanUtil_FAILED_TO_LOAD_0 + .toLocalizedString(new Object[] {mbeansResource})); + } catch (Exception e) { + logStackTrace(Level.WARN, e); + throw new RuntimeAdminException( + LocalizedStrings.MBeanUtil_FAILED_TO_GET_MBEAN_REGISTRY.toLocalizedString(), e); + } + } + return registry; + } + + /** + * Creates and registers a ModelMBean for the specified ManagedResource. + * State changing callbacks into the ManagedResource will also be made. + * + * @param resource the ManagedResource to create a managing MBean for + * + * @return The object name of the newly-created MBean + * + * @see ManagedResource#setModelMBean + */ + static ObjectName createMBean(ManagedResource resource) { + return createMBean(resource, lookupManagedBean(resource)); + } + + /** + * Creates and registers a ModelMBean for the specified ManagedResource. + * State changing callbacks into the ManagedResource will also be made. + * + * @param resource the ManagedResource to create a managing MBean for + * @param managed the ManagedBean definition to create the MBean with + * @see ManagedResource#setModelMBean + */ + static ObjectName createMBean(ManagedResource resource, ManagedBean managed) { + + try { + DynamicManagedBean mb = new DynamicManagedBean(managed); + resource.setModelMBean(mb.createMBean(resource)); + + // create the ObjectName and register the MBean... + final ObjectName objName; + try { + objName = ObjectName.getInstance(resource.getMBeanName()); + } catch (MalformedObjectNameException e) { + throw new MalformedObjectNameException(LocalizedStrings.MBeanUtil_0_IN_1 + .toLocalizedString(new Object[] {e.getMessage(), resource.getMBeanName()})); + } + + synchronized (MBeanUtil.class) { + // Only register a bean once. Otherwise, you risk race + // conditions with things like the RMI connector accessing it. + + if (mbeanServer != null && !mbeanServer.isRegistered(objName)) { + mbeanServer.registerMBean(resource.getModelMBean(), objName); + synchronized (managedResources) { + managedResources.put(objName, resource); + } + } + } + return objName; + } catch (java.lang.Exception e) { + throw new RuntimeAdminException(LocalizedStrings.MBeanUtil_FAILED_TO_CREATE_MBEAN_FOR_0 + .toLocalizedString(new Object[] {resource.getMBeanName()}), e); + } + } + + /** + * Ensures that an MBean is registered for the specified ManagedResource. If an MBean + * cannot be found in the MBeanServer, then this creates and registers a + * ModelMBean. State changing callbacks into the ManagedResource will + * also be made. + * + * @param resource the ManagedResource to create a managing MBean for + * + * @return The object name of the MBean that manages the ManagedResource + * + * @see ManagedResource#setModelMBean + */ + static ObjectName ensureMBeanIsRegistered(ManagedResource resource) { + try { + ObjectName objName = ObjectName.getInstance(resource.getMBeanName()); + synchronized (MBeanUtil.class) { + if (mbeanServer != null && !mbeanServer.isRegistered(objName)) { + return createMBean(resource); + } + } + raiseOnFailure(mbeanServer.isRegistered(objName), + LocalizedStrings.MBeanUtil_COULDNT_FIND_MBEAN_REGISTERED_WITH_OBJECTNAME_0 + .toLocalizedString(new Object[] {objName.toString()})); + return objName; + } catch (java.lang.Exception e) { + throw new RuntimeAdminException(e); + } + } + + /** + * Retrieves the ManagedBean configuration from the Registry for the specified + * ManagedResource + * + * @param resource the ManagedResource to find the configuration for + */ + static ManagedBean lookupManagedBean(ManagedResource resource) { + // find the registry defn for our MBean... + ManagedBean managed = null; + if (registry != null) { + managed = registry.findManagedBean(resource.getManagedResourceType().getClassTypeName()); + } else { + throw new IllegalArgumentException( + LocalizedStrings.MBeanUtil_MANAGEDBEAN_IS_NULL.toLocalizedString()); + } + + if (managed == null) { + throw new IllegalArgumentException( + LocalizedStrings.MBeanUtil_MANAGEDBEAN_IS_NULL.toLocalizedString()); + } + + // customize the defn... + managed.setClassName( + "MX4JModelMBean"); + + return managed; + } + + /** + * Registers a refresh notification for the specified client MBean. Specifying zero for the + * refreshInterval disables notification for the refresh client. Note: this does not currently + * support remote connections. + * + * @param client client to listen for refresh notifications + * @param userData userData to register with the Notification + * @param type refresh notification type the client will use + * @param refreshInterval the seconds between refreshes + */ + static void registerRefreshNotification(NotificationListener client, Object userData, + RefreshNotificationType type, int refreshInterval) { + if (client == null) { + throw new IllegalArgumentException( + LocalizedStrings.MBeanUtil_NOTIFICATIONLISTENER_IS_REQUIRED.toLocalizedString()); + } + if (type == null) { + throw new IllegalArgumentException( + LocalizedStrings.MBeanUtil_REFRESHNOTIFICATIONTYPE_IS_REQUIRED.toLocalizedString()); + } + if (refreshTimerObjectName == null || refreshTimer == null) { + throw new IllegalStateException( + LocalizedStrings.MBeanUtil_REFRESHTIMER_HAS_NOT_BEEN_PROPERLY_INITIALIZED + .toLocalizedString()); + } + + try { + // get the notifications for the specified client... + Map notifications = null; + synchronized (refreshClients) { + notifications = (Map) refreshClients.get(client); + } + + if (notifications == null) { + // If refreshInterval is being set to zero and notifications is removed return + if (refreshInterval <= 0) { + return; + } + + // never registered before, so add client... + notifications = new HashMap(); + synchronized (refreshClients) { + refreshClients.put(client, notifications); + } + validateRefreshTimer(); + try { + // register client as a listener with MBeanServer... + mbeanServer.addNotificationListener(refreshTimerObjectName, // timer to listen to + client, // the NotificationListener object + null, // optional NotificationFilter TODO: convert to using + new Object() // not used but null throws IllegalArgumentException + ); + } catch (InstanceNotFoundException e) { + // should not happen since we already checked refreshTimerObjectName + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_COULD_NOT_FIND_REGISTERED_REFRESHTIMER_INSTANCE + .toLocalizedString()); + } + } + + // TODO: change to manipulating timer indirectly thru mserver... + + // check for pre-existing refresh notification entry... + Integer timerNotificationId = (Integer) notifications.get(type); + if (timerNotificationId != null) { + try { + // found one, so let's remove it... + refreshTimer.removeNotification(timerNotificationId); + } catch (InstanceNotFoundException e) { + // that's ok cause we just wanted to remove it anyway + } finally { + // null out the map entry for that notification type... + notifications.put(type, null); + } + } + + if (refreshInterval > 0) { + // add notification to the refresh timer... + timerNotificationId = refreshTimer.addNotification(type.getType(), // type + type.getMessage(), // message = "refresh" + userData, // userData + new Date(System.currentTimeMillis() + refreshInterval * 1000), // first occurence + refreshInterval * 1000); // period to repeat + + // put an entry into the map for the listener... + notifications.put(type, timerNotificationId); + } else { + // do nothing! refreshInterval must be over 0 to do anything... + } + } catch (java.lang.RuntimeException e) { + logStackTrace(Level.WARN, e); + throw e; + } catch (VirtualMachineError err) { + SystemFailure.initiateFailure(err); + // If this ever returns, rethrow the error. We're poisoned + // now, so don't let this thread continue. + throw err; + } catch (java.lang.Error e) { + // Whenever you catch Error or Throwable, you must also + // catch VirtualMachineError (see above). However, there is + // _still_ a possibility that you are dealing with a cascading + // error condition, so you also need to check to see if the JVM + // is still usable: + SystemFailure.checkFailure(); + logStackTrace(Level.ERROR, e); + throw e; + } + } + + /** + * Verifies a refresh notification for the specified client MBean. If notification is not + * registered, then returns a false + * + * @param client client to listen for refresh notifications + * @param type refresh notification type the client will use + * + * @return isRegistered boolean indicating if a notification is registered + */ + static boolean isRefreshNotificationRegistered(NotificationListener client, + RefreshNotificationType type) { + boolean isRegistered = false; + + // get the notifications for the specified client... + Map notifications = null; + synchronized (refreshClients) { + notifications = (Map) refreshClients.get(client); + } + + // never registered before if null ... + if (notifications != null) { + // check for pre-existing refresh notification entry... + Integer timerNotificationId = notifications.get(type); + if (timerNotificationId != null) { + isRegistered = true; + } + } + + return isRegistered; + } + + /** + * Validates refreshTimer has been registered without problems and attempts to re-register if + * there is a problem. + */ + static void validateRefreshTimer() { + if (refreshTimerObjectName == null || refreshTimer == null) { + // if (refreshTimerObjectName == null) System.out.println("refreshTimerObjectName is null"); + // if (refreshTimer == null) System.out.println("refreshTimer is null"); + // System.out.println("[validateRefreshTimer] createRefreshTimer"); + createRefreshTimer(); + } + + raiseOnFailure(refreshTimer != null, "Failed to validate Refresh Timer"); + + if (mbeanServer != null && !mbeanServer.isRegistered(refreshTimerObjectName)) { + // System.out.println("[validateRefreshTimer] registerMBean"); + try { + mbeanServer.registerMBean(refreshTimer, refreshTimerObjectName); + } catch (JMException e) { + logStackTrace(Level.WARN, e); + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, e); + } + } + } + + /** + * Initializes the timer for sending refresh notifications. + */ + static void createRefreshTimer() { + try { + refreshTimer = new javax.management.timer.Timer(); + mbeanServer.registerMBean(refreshTimer, refreshTimerObjectName); + + refreshTimer.start(); + } catch (JMException e) { + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_FAILED_TO_CREATE_REFRESH_TIMER.toLocalizedString()); + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_FAILED_TO_CREATE_REFRESH_TIMER.toLocalizedString()); + } catch (Exception e) { + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_FAILED_TO_CREATE_REFRESH_TIMER.toLocalizedString()); + } + } + + /** + * Initializes the timer for sending refresh notifications. + */ + static void stopRefreshTimer() { + try { + if (refreshTimer != null && mbeanServer != null) { + mbeanServer.unregisterMBean(refreshTimerObjectName); + + refreshTimer.stop(); + } + } catch (JMException e) { + logStackTrace(Level.WARN, e); + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, e); + } catch (Exception e) { + logStackTrace(Level.DEBUG, e, "Failed to stop refresh timer for MBeanUtil"); + } + } + + /** + * Return a String that been modified to be compliant as a property of an ObjectName. + *

+ * The property name of an ObjectName may not contain any of the following characters: : , = + * * ? + *

+ * This method will replace the above non-compliant characters with a dash: - + *

+ * If value is empty, this method will return the string "nothing". + *

+ * Note: this is public because certain tests call this from outside of the package. + * TODO: clean this up + * + * @param value the potentially non-compliant ObjectName property + * @return the value modified to be compliant as an ObjectName property + */ + public static String makeCompliantMBeanNameProperty(String value) { + value = value.replace(':', '-'); + value = value.replace(',', '-'); + value = value.replace('=', '-'); + value = value.replace('*', '-'); + value = value.replace('?', '-'); + if (value.length() < 1) { + value = "nothing"; + } + return value; + } + + /** + * Unregisters all GemFire MBeans and then releases the MBeanServer for garbage collection. + */ + static void releaseMBeanServer() { + try { + // unregister all GemFire mbeans... + Iterator iter = mbeanServer.queryNames(null, null).iterator(); + while (iter.hasNext()) { + ObjectName name = (ObjectName) iter.next(); + if (name.getDomain().startsWith(DEFAULT_DOMAIN)) { + unregisterMBean(name); + } + } + + // last, release the mbean server... + MBeanServerFactory.releaseMBeanServer(mbeanServer); + mbeanServer = null; + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, e); + } + /* + * See #42391. Cleaning up the static maps which might be still holding references to + * ManagedResources + */ + synchronized (MBeanUtil.managedResources) { + MBeanUtil.managedResources.clear(); + } + synchronized (refreshClients) { + refreshClients.clear(); + } + /* + * See #42391. Cleaning up the static maps which might be still holding references to + * ManagedResources + */ + synchronized (MBeanUtil.managedResources) { + MBeanUtil.managedResources.clear(); + } + synchronized (refreshClients) { + refreshClients.clear(); + } + } + + /** + * Returns true if a MBean with given ObjectName is registered. + * + * @param objectName ObjectName to use for checking if MBean is registered + * @return true if MBeanServer is not null & MBean with given ObjectName is registered with the + * MBeanServer + */ + static boolean isRegistered(ObjectName objectName) { + return mbeanServer != null && mbeanServer.isRegistered(objectName); + } + + /** + * Unregisters the identified MBean if it's registered. + */ + static void unregisterMBean(ObjectName objectName) { + try { + if (mbeanServer != null && mbeanServer.isRegistered(objectName)) { + mbeanServer.unregisterMBean(objectName); + } + } catch (MBeanRegistrationException e) { + logStackTrace(Level.WARN, null, + LocalizedStrings.MBeanUtil_FAILED_WHILE_UNREGISTERING_MBEAN_WITH_OBJECTNAME_0 + .toLocalizedString(new Object[] {objectName})); + } catch (InstanceNotFoundException e) { + logStackTrace(Level.WARN, null, + LocalizedStrings.MBeanUtil_WHILE_UNREGISTERING_COULDNT_FIND_MBEAN_WITH_OBJECTNAME_0 + .toLocalizedString(new Object[] {objectName})); + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, null, + LocalizedStrings.MBeanUtil_COULD_NOT_UNREGISTER_MBEAN_WITH_OBJECTNAME_0 + .toLocalizedString(new Object[] {objectName})); + } + } + + static void unregisterMBean(ManagedResource resource) { + if (resource != null) { + unregisterMBean(resource.getObjectName()); + + // call cleanup on managedResource here and not rely on listener + // since it is possible that notification listener not deliver + // all notifications of un-registration. If resource is + // cleaned here, another call from the listener should be as good as a no-op + cleanupResource(resource); + } + } + + // cleanup resource + private static void cleanupResource(ManagedResource resource) { + synchronized (MBeanUtil.managedResources) { + MBeanUtil.managedResources.remove(resource.getObjectName()); + } + resource.cleanupResource(); + + // get the notifications for the specified client... + Map notifications = null; + synchronized (refreshClients) { + notifications = (Map) refreshClients.remove(resource); + } + + // never registered before if null ... + // Also as of current, there is ever only 1 Notification type per + // MBean, so we do need need a while loop here + if (notifications != null) { + + // Fix for findbugs reported inefficiency with keySet(). + Set> entries = notifications.entrySet(); + + for (Map.Entry e : entries) { + Integer timerNotificationId = e.getValue(); + if (null != timerNotificationId) { + try { + // found one, so let's remove it... + refreshTimer.removeNotification(timerNotificationId); + } catch (InstanceNotFoundException xptn) { + // that's ok cause we just wanted to remove it anyway + logStackTrace(Level.DEBUG, xptn); + } + } + } + + try { + if (mbeanServer != null && mbeanServer.isRegistered(refreshTimerObjectName)) { + // remove client as a listener with MBeanServer... + mbeanServer.removeNotificationListener(refreshTimerObjectName, // timer to listen to + (NotificationListener) resource // the NotificationListener object + ); + } + } catch (ListenerNotFoundException xptn) { + // should not happen since we already checked refreshTimerObjectName + logStackTrace(Level.WARN, null, xptn.getMessage()); + } catch (InstanceNotFoundException xptn) { + // should not happen since we already checked refreshTimerObjectName + logStackTrace(Level.WARN, null, + LocalizedStrings.MBeanUtil_WHILE_UNREGISTERING_COULDNT_FIND_MBEAN_WITH_OBJECTNAME_0 + .toLocalizedString(new Object[] {refreshTimerObjectName})); + } + } + } + + // ----- borrowed the following from RemoteCommand ----- + /** Translates the path between Windows and UNIX. */ + static String getOSPath(String path) { + if (pathIsWindows(path)) { + return path.replace('/', '\\'); + } else { + return path.replace('\\', '/'); + } + } + + /** Returns true if the path is on Windows. */ + static boolean pathIsWindows(String path) { + if (path != null && path.length() > 1) { + return (Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') + || (path.startsWith("//") || path.startsWith("\\\\")); + } + return false; + } + + static void registerServerNotificationListener() { + if (mbeanServer == null) { + return; + } + try { + // the MBeanServerDelegate name is spec'ed as the following... + ObjectName delegate = ObjectName.getInstance("JMImplementation:type=MBeanServerDelegate"); + mbeanServer.addNotificationListener(delegate, new NotificationListener() { + public void handleNotification(Notification notification, Object handback) { + MBeanServerNotification serverNotification = (MBeanServerNotification) notification; + if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION + .equals(serverNotification.getType())) { + ObjectName objectName = serverNotification.getMBeanName(); + synchronized (MBeanUtil.managedResources) { + Object entry = MBeanUtil.managedResources.get(objectName); + if (entry == null) + return; + if (!(entry instanceof ManagedResource)) { + throw new ClassCastException(LocalizedStrings.MBeanUtil_0_IS_NOT_A_MANAGEDRESOURCE + .toLocalizedString(new Object[] {entry.getClass().getName()})); + } + ManagedResource resource = (ManagedResource) entry; + { + // call cleanup on managedResource + cleanupResource(resource); + } + } + } + } + }, null, null); + } catch (JMException e) { + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_FAILED_TO_REGISTER_SERVERNOTIFICATIONLISTENER + .toLocalizedString()); + } catch (JMRuntimeException e) { + logStackTrace(Level.WARN, e, + LocalizedStrings.MBeanUtil_FAILED_TO_REGISTER_SERVERNOTIFICATIONLISTENER + .toLocalizedString()); + } + } + + /** + * Logs the stack trace for the given Throwable if logger is initialized else prints the stack + * trace using System.out. + * + * @param level severity level to log at + * @param throwable Throwable to log stack trace for + */ + public static void logStackTrace(Level level, Throwable throwable) { + logStackTrace(level, throwable, null); + } + + /** + * Logs the stack trace for the given Throwable if logger is initialized else prints the stack + * trace using System.out. + * + * @param level severity level to log at + * @param throwable Throwable to log stack trace for + * @param message user friendly error message to show + */ + public static void logStackTrace(Level level, Throwable throwable, String message) { + logger.log(level, message, throwable); + } + + /** + * Raises RuntimeAdminException with given 'message' if given 'condition' is false. + * + * @param condition condition to evaluate + * @param message failure message + */ + private static void raiseOnFailure(boolean condition, String message) { + if (!condition) { + throw new RuntimeAdminException(message); + } + } +} +