blob: c572c2eb1b9bb8f93137c5ec374c02ef9e4c7593 [file] [log] [blame]
/*
* 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.management.internal;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Type;
import java.text.MessageFormat;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.management.AttributeNotFoundException;
import javax.management.InstanceAlreadyExistsException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationEmitter;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import org.apache.logging.log4j.Logger;
import org.apache.geode.annotations.internal.MutableForTesting;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.InternalDistributedSystem;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.ClassLoadUtils;
import org.apache.geode.logging.internal.log4j.api.LogService;
import org.apache.geode.management.AsyncEventQueueMXBean;
import org.apache.geode.management.CacheServerMXBean;
import org.apache.geode.management.DiskStoreMXBean;
import org.apache.geode.management.DistributedLockServiceMXBean;
import org.apache.geode.management.DistributedRegionMXBean;
import org.apache.geode.management.DistributedSystemMXBean;
import org.apache.geode.management.GatewayReceiverMXBean;
import org.apache.geode.management.GatewaySenderMXBean;
import org.apache.geode.management.LocatorMXBean;
import org.apache.geode.management.LockServiceMXBean;
import org.apache.geode.management.ManagementException;
import org.apache.geode.management.ManagerMXBean;
import org.apache.geode.management.MemberMXBean;
import org.apache.geode.management.RegionMXBean;
/**
* Utility class to interact with the JMX server
*
*
*/
public class MBeanJMXAdapter implements ManagementConstants {
/** The <code>MBeanServer</code> for this application */
@MutableForTesting
public static MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
private final Map<ObjectName, Object> localGemFireMBean;
private final DistributedMember distMember;
private static final Logger logger = LogService.getLogger();
/**
* public constructor
*/
public MBeanJMXAdapter(DistributedMember distMember) {
localGemFireMBean = new ConcurrentHashMap<>();
this.distMember = distMember;
}
/**
* This method will register an MBean in GemFire domain. Even if the client provides a domain name
* it will be ignored and GemFire domain name will be used.
*
* This method checks the local Filter for registering the MBean. If filtered the MBean wont be
* registered. Although the filter will remember the filtered MBean and register it once the
* filter is removed.
*
* @return modified ObjectName
*/
public ObjectName registerMBean(Object object, ObjectName objectName, boolean isGemFireMBean) {
ObjectName newObjectName = objectName;
try {
if (!isGemFireMBean) {
String member = getMemberNameOrUniqueId(distMember);
String objectKeyProperty = objectName.getKeyPropertyListString();
newObjectName = ObjectName.getInstance(
OBJECTNAME__PREFIX + objectKeyProperty + KEYVAL_SEPARATOR + "member=" + member);
}
if (isRegistered(newObjectName)) {
return newObjectName;
}
mbeanServer.registerMBean(object, newObjectName);
localGemFireMBean.put(newObjectName, object);
} catch (InstanceAlreadyExistsException | NullPointerException | MalformedObjectNameException
| NotCompliantMBeanException | MBeanRegistrationException e) {
throw new ManagementException(e);
}
return newObjectName;
}
/**
* Checks whether an MBean implements notification support classes or not
*
* @return if this MBean can be a notification broadcaster
*/
public boolean hasNotificationSupport(ObjectName objectName) {
try {
if (!isRegistered(objectName)) {
return false;
}
ObjectInstance instance = mbeanServer.getObjectInstance(objectName);
String className = instance.getClassName();
Class cls = ClassLoadUtils.classFromName(className);
Type[] intfTyps = cls.getGenericInterfaces();
for (Type intfTyp1 : intfTyps) {
Class intfTyp = (Class) intfTyp1;
if (intfTyp.equals(NotificationEmitter.class)) {
return true;
}
}
Class superClassType = (Class) cls.getGenericSuperclass();
if (superClassType != null && superClassType.equals(NotificationBroadcasterSupport.class)) {
return true;
}
} catch (InstanceNotFoundException | ClassNotFoundException e) {
throw new ManagementException(e);
}
return false;
}
/**
* This method will register an MBean in GemFire domain. Even if the client provides a domain name
* it will be ignored and GemFire domain name will be used.
*
* This method checks the local Filter for registering the MBean. If filtered the MBean wont be
* registered. Although the filter will remember the filtered MBean and register it once the
* filter is removed.
*
*/
public void registerMBeanProxy(Object object, ObjectName objectName) {
try {
if (isRegistered(objectName)) {
return;
}
mbeanServer.registerMBean(object, objectName);
} catch (InstanceAlreadyExistsException instanceAlreadyExistsException) {
// An InstanceAlreadyExistsException in this context means that the MBean
// has already been registered, so just log a warning message.
logRegistrationWarning(objectName, true);
} catch (NullPointerException | NotCompliantMBeanException
| MBeanRegistrationException e) {
throw new ManagementException(e);
}
}
/**
*
* This method will unregister the MBean from GemFire Domain
*
*/
public void unregisterMBean(ObjectName objectName) {
try {
if (!isRegistered(objectName)) {
return;
}
mbeanServer.unregisterMBean(objectName);
// For Local GemFire MBeans
if (localGemFireMBean.get(objectName) != null) {
localGemFireMBean.remove(objectName);
}
} catch (InstanceNotFoundException instanceNotFoundException) {
// An InstanceNotFoundException in this context means that the MBean
// has already been unregistered, so just log a debug message as it is
// essentially a no-op.
// has already been unregistered, so just log a warning message.
logRegistrationWarning(objectName, false);
} catch (NullPointerException | MBeanRegistrationException e) {
throw new ManagementException(e);
}
}
public Object getMBeanObject(ObjectName objectName) {
return localGemFireMBean.get(objectName);
}
/**
* Finds the MBean instance by {@link javax.management.ObjectName}
*
* @return instance of MBean
*/
public <T> T findMBeanByName(ObjectName objectName, Class<T> interfaceClass) {
Object mbeanInstance = localGemFireMBean.get(objectName);
if (mbeanInstance != null) {
return interfaceClass.cast(mbeanInstance);
} else {
return null;
}
}
public boolean isLocalMBean(ObjectName objectName) {
return localGemFireMBean.containsKey(objectName);
}
/**
* Method to unregister all the local GemFire MBeans
*
*/
public void unregisterAll() {
try {
ObjectName name = new ObjectName(OBJECTNAME__PREFIX + "*");
Set<ObjectName> gemFireObjects = mbeanServer.queryNames(name, null);
for (ObjectName objectName : gemFireObjects) {
unregisterMBean(objectName);
}
} catch (MalformedObjectNameException | NullPointerException e) {
throw new ManagementException(e);
}
}
public void cleanJMXResource() {
localGemFireMBean.clear();
unregisterAll();
}
public boolean isRegistered(ObjectName objectName) {
return mbeanServer.isRegistered(objectName);
}
/**
* This method returns the name that will be used for a DistributedMember when it is registered as
* a JMX bean.
*
* @param member Member to find the name for
* @return The name used to register this member as a JMX bean.
*/
public static String getMemberNameOrUniqueId(DistributedMember member) {
if (member.getName() != null && !member.getName().equals("")) {
return makeCompliantName(member.getName());
}
return makeCompliantName(member.getUniqueId());
}
/**
* Return a String that been modified to be compliant as a property of an ObjectName.
* <p>
* The property name of an ObjectName may not contain any of the following characters: <b><i>: , =
* * ?</i></b>
* <p>
* This method will replace the above non-compliant characters with a dash: <b><i>-</i></b>
* <p>
* If value is empty, this method will return the string "nothing".
* <p>
* Note: this is <code>public</code> because certain tests call this from outside of the package.
*
* @param value the potentially non-compliant ObjectName property
* @return the value modified to be compliant as an ObjectName property
*/
public static String makeCompliantName(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;
}
public static String makeCompliantRegionPath(String value) {
if (isQuoted(value)) {
value = ObjectName.unquote(value);
} else {
if (containsSpecialChar(value)) {
value = ObjectName.quote(value);
}
}
return value;
}
private static boolean containsSpecialChar(String value) {
return value.contains(":") || value.contains("@") || value.contains("-") || value.contains("#")
|| value.contains("+") || value.contains("?");
}
private static boolean isQuoted(String value) {
final int len = value.length();
return len >= 2 && value.charAt(0) == '"' && value.charAt(len - 1) == '"';
}
public static String makeCompliantRegionNameAppender(String value) {
return value.replace("/", "-").replace(":", "");
}
/**
* returns Member MBean if any
*
*/
public MemberMXBean getMemberMXBean() {
ObjectName objName = getMemberMBeanName(distMember);
return (MemberMXBean) localGemFireMBean.get(objName);
}
public RegionMXBean getLocalRegionMXBean(String regionPath) {
ObjectName objName = getRegionMBeanName(distMember, regionPath);
return (RegionMXBean) localGemFireMBean.get(objName);
}
public LockServiceMXBean getLocalLockServiceMXBean(String lockServiceName) {
ObjectName objName = getLockServiceMBeanName(distMember, lockServiceName);
return (LockServiceMXBean) localGemFireMBean.get(objName);
}
public DiskStoreMXBean getLocalDiskStoreMXBean(String disStoreName) {
ObjectName objName = getDiskStoreMBeanName(distMember, disStoreName);
return (DiskStoreMXBean) localGemFireMBean.get(objName);
}
public CacheServerMXBean getClientServiceMXBean(int serverPort) {
ObjectName objName = getClientServiceMBeanName(serverPort, distMember);
return (CacheServerMXBean) localGemFireMBean.get(objName);
}
public DistributedLockServiceMXBean getDistributedLockServiceMXBean(String lockServiceName) {
ObjectName objName = getDistributedLockServiceName(lockServiceName);
return (DistributedLockServiceMXBean) localGemFireMBean.get(objName);
}
public DistributedRegionMXBean getDistributedRegionMXBean(String regionPath) {
ObjectName objName = getDistributedRegionMbeanName(regionPath);
return (DistributedRegionMXBean) localGemFireMBean.get(objName);
}
public ManagerMXBean getManagerMXBean() {
ObjectName objName = getManagerName();
return (ManagerMXBean) localGemFireMBean.get(objName);
}
public DistributedSystemMXBean getDistributedSystemMXBean() {
ObjectName objName = getDistributedSystemName();
return (DistributedSystemMXBean) localGemFireMBean.get(objName);
}
public GatewayReceiverMXBean getGatewayReceiverMXBean() {
ObjectName objName = getGatewayReceiverMBeanName(distMember);
return (GatewayReceiverMXBean) localGemFireMBean.get(objName);
}
public GatewaySenderMXBean getGatewaySenderMXBean(String senderId) {
ObjectName objName = getGatewaySenderMBeanName(distMember, senderId);
return (GatewaySenderMXBean) localGemFireMBean.get(objName);
}
public AsyncEventQueueMXBean getAsyncEventQueueMXBean(String queueId) {
ObjectName objName = getAsyncEventQueueMBeanName(distMember, queueId);
return (AsyncEventQueueMXBean) localGemFireMBean.get(objName);
}
public LocatorMXBean getLocatorMXBean() {
ObjectName objName = getLocatorMBeanName(distMember);
return (LocatorMXBean) localGemFireMBean.get(objName);
}
public static ObjectName getObjectName(String name) {
try {
return ObjectName.getInstance(name);
} catch (MalformedObjectNameException | NullPointerException e) {
throw new ManagementException(e);
}
}
public static ObjectName getMemberMBeanName(DistributedMember member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__MEMBER_MXBEAN, getMemberNameOrUniqueId(member))));
}
public static ObjectName getMemberMBeanName(String member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__MEMBER_MXBEAN, makeCompliantName(member))));
}
public static ObjectName getRegionMBeanName(DistributedMember member, String regionPath) {
return getObjectName((MessageFormat.format(OBJECTNAME__REGION_MXBEAN,
makeCompliantRegionPath(regionPath), getMemberNameOrUniqueId(member))));
}
public static ObjectName getRegionMBeanName(String member, String regionPath) {
return getObjectName((MessageFormat.format(OBJECTNAME__REGION_MXBEAN,
makeCompliantRegionPath(regionPath), makeCompliantName(member))));
}
public static ObjectName getRegionMBeanName(ObjectName memberMBeanName, String regionPath) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__REGION_MXBEAN, makeCompliantRegionPath(regionPath),
memberMBeanName.getKeyProperty(ManagementConstants.OBJECTNAME_MEMBER_APPENDER))));
}
public static ObjectName getDiskStoreMBeanName(DistributedMember member, String diskName) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__DISKSTORE_MXBEAN, diskName,
getMemberNameOrUniqueId(member))));
}
public static ObjectName getDiskStoreMBeanName(String member, String diskName) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__DISKSTORE_MXBEAN, diskName, makeCompliantName(member))));
}
public static ObjectName getClientServiceMBeanName(int serverPort, DistributedMember member) {
return getObjectName((MessageFormat.format(OBJECTNAME__CLIENTSERVICE_MXBEAN,
String.valueOf(serverPort), getMemberNameOrUniqueId(member))));
}
public static ObjectName getClientServiceMBeanName(int serverPort, String member) {
return getObjectName((MessageFormat.format(OBJECTNAME__CLIENTSERVICE_MXBEAN,
String.valueOf(serverPort), makeCompliantName(member))));
}
public static ObjectName getLockServiceMBeanName(DistributedMember member,
String lockServiceName) {
return getObjectName((MessageFormat.format(OBJECTNAME__LOCKSERVICE_MXBEAN, lockServiceName,
getMemberNameOrUniqueId(member))));
}
public static ObjectName getLockServiceMBeanName(String member, String lockServiceName) {
return getObjectName((MessageFormat.format(OBJECTNAME__LOCKSERVICE_MXBEAN, lockServiceName,
makeCompliantName(member))));
}
public static ObjectName getGatewayReceiverMBeanName(DistributedMember member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__GATEWAYRECEIVER_MXBEAN,
getMemberNameOrUniqueId(member))));
}
public static ObjectName getGatewayReceiverMBeanName(String member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__GATEWAYRECEIVER_MXBEAN, makeCompliantName(member))));
}
public static ObjectName getGatewaySenderMBeanName(DistributedMember member, String id) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__GATEWAYSENDER_MXBEAN, id,
getMemberNameOrUniqueId(member))));
}
public static ObjectName getGatewaySenderMBeanName(String member, String id) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__GATEWAYSENDER_MXBEAN, id, makeCompliantName(member))));
}
public static ObjectName getAsyncEventQueueMBeanName(DistributedMember member, String queueId) {
return getObjectName((MessageFormat.format(OBJECTNAME__ASYNCEVENTQUEUE_MXBEAN, queueId,
getMemberNameOrUniqueId(member))));
}
public static ObjectName getDistributedRegionMbeanName(String regionPath) {
return getObjectName((MessageFormat.format(OBJECTNAME__DISTRIBUTEDREGION_MXBEAN,
makeCompliantRegionPath(regionPath))));
}
/**
* Without special character transformation
*
* @param regionPath region path
* @return ObjectName MBean name
*/
public static ObjectName getDistributedRegionMbeanNameInternal(String regionPath) {
return getObjectName((MessageFormat.format(OBJECTNAME__DISTRIBUTEDREGION_MXBEAN, regionPath)));
}
public static ObjectName getDistributedLockServiceName(String lockService) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__DISTRIBUTEDLOCKSERVICE_MXBEAN, lockService)));
}
public static ObjectName getDistributedSystemName() {
return getObjectName(OBJECTNAME__DISTRIBUTEDSYSTEM_MXBEAN);
}
public static ObjectName getManagerName() {
String member =
getMemberNameOrUniqueId(
InternalDistributedSystem.getConnectedInstance().getDistributedMember());
return getObjectName((MessageFormat.format(OBJECTNAME__MANAGER_MXBEAN, member)));
}
public static ObjectName getLocatorMBeanName(DistributedMember member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__LOCATOR_MXBEAN, getMemberNameOrUniqueId(member))));
}
public static ObjectName getLocatorMBeanName(String member) {
return getObjectName(
(MessageFormat.format(OBJECTNAME__LOCATOR_MXBEAN, makeCompliantName(member))));
}
public static ObjectName getCacheServiceMBeanName(DistributedMember member,
String cacheServiceId) {
return getObjectName((MessageFormat.format(OBJECTNAME__CACHESERVICE_MXBEAN, cacheServiceId,
getMemberNameOrUniqueId(member))));
}
public Map<ObjectName, Object> getLocalGemFireMBean() {
return localGemFireMBean;
}
public static String getUniqueIDForMember(InternalDistributedMember member) {
final String sb = member.getInetAddress().getHostAddress()
// View ID will be 0 for Loner, but in that case no federation as well
+ "<v" + member.getVmViewId() + ">"
+ member.getMembershipPort();
// Lower case to handle IPv6
return makeCompliantName(sb.toLowerCase());
}
public static boolean isAttributeAvailable(String attributeName, String objectName) {
try {
ObjectName objName = new ObjectName(objectName);
mbeanServer.getAttribute(objName, attributeName);
} catch (MalformedObjectNameException | ReflectionException | MBeanException
| InstanceNotFoundException | AttributeNotFoundException | NullPointerException e) {
return false;
}
return true;
}
void logRegistrationWarning(ObjectName objectName, boolean registering) {
logger.warn("MBean with ObjectName " + objectName + " has already been "
+ (registering ? "registered." : "unregistered."));
}
}