| /* |
| * 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.admin.jmx.internal; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.rmi.server.RMIClientSocketFactory; |
| import java.rmi.server.RMIServerSocketFactory; |
| import java.text.MessageFormat; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import javax.management.InstanceNotFoundException; |
| import javax.management.MBeanException; |
| import javax.management.MBeanRegistrationException; |
| import javax.management.MBeanServer; |
| import javax.management.MalformedObjectNameException; |
| import javax.management.Notification; |
| import javax.management.NotificationFilter; |
| import javax.management.NotificationListener; |
| import javax.management.ObjectName; |
| import javax.management.OperationsException; |
| import javax.management.ReflectionException; |
| import javax.management.modelmbean.ModelMBean; |
| import javax.management.remote.JMXConnectionNotification; |
| import javax.management.remote.JMXConnectorServer; |
| import javax.management.remote.JMXConnectorServerFactory; |
| import javax.management.remote.JMXServiceURL; |
| import javax.management.remote.rmi.RMIConnectorServer; |
| import javax.rmi.ssl.SslRMIClientSocketFactory; |
| |
| import mx4j.tools.adaptor.http.HttpAdaptor; |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.GemFireException; |
| import org.apache.geode.GemFireIOException; |
| import org.apache.geode.LogWriter; |
| import org.apache.geode.SystemFailure; |
| import org.apache.geode.admin.AdminDistributedSystem; |
| import org.apache.geode.admin.AdminException; |
| import org.apache.geode.admin.jmx.Agent; |
| import org.apache.geode.admin.jmx.AgentConfig; |
| import org.apache.geode.admin.jmx.AgentFactory; |
| import org.apache.geode.distributed.internal.ClusterDistributionManager; |
| import org.apache.geode.internal.ExitCode; |
| import org.apache.geode.internal.GemFireVersion; |
| import org.apache.geode.internal.admin.remote.TailLogResponse; |
| import org.apache.geode.internal.logging.InternalLogWriter; |
| import org.apache.geode.internal.logging.LogWriterFactory; |
| import org.apache.geode.internal.logging.log4j.LogMarker; |
| import org.apache.geode.internal.statistics.StatisticsConfig; |
| import org.apache.geode.logging.internal.LoggingSession; |
| import org.apache.geode.logging.internal.executors.LoggingThread; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| import org.apache.geode.logging.internal.spi.LogConfig; |
| import org.apache.geode.logging.internal.spi.LogConfigListener; |
| import org.apache.geode.logging.internal.spi.LogConfigSupplier; |
| |
| /** |
| * The GemFire JMX Agent provides the ability to administrate one GemFire distributed system via |
| * JMX. |
| * |
| * @since GemFire 3.5 |
| */ |
| @Deprecated |
| public class AgentImpl implements org.apache.geode.admin.jmx.Agent, |
| org.apache.geode.admin.jmx.internal.ManagedResource, LogConfigSupplier { |
| |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * MX4J HttpAdaptor only supports "basic" as an authentication method. Enabling HttpAdaptor |
| * authentication ({@link AgentConfig#HTTP_AUTHENTICATION_ENABLED_NAME}) causes the browser to |
| * require a login with username ({@link AgentConfig#HTTP_AUTHENTICATION_USER_NAME}) and password |
| * ({@link AgentConfig#HTTP_AUTHENTICATION_PASSWORD_NAME}). |
| */ |
| private static final String MX4J_HTTPADAPTOR_BASIC_AUTHENTICATION = "basic"; |
| |
| /** JMX Service URL template for JMX/RMI Connector Server */ |
| private static final String JMX_SERVICE_URL = "service:jmx:rmi://{0}:{1}/jndi/rmi://{2}:{3}{4}"; |
| |
| /* |
| * Set third-party logging configration: MX4J, Jakarta Commons-Logging. |
| */ |
| static { |
| checkDebug(); |
| String commonsLog = System.getProperty("org.apache.commons.logging.log"); |
| if (commonsLog == null || commonsLog.length() == 0) { |
| System.setProperty("org.apache.commons.logging.log", |
| "org.apache.commons.logging.impl.SimpleLog"); |
| } |
| } |
| |
| /** Enables mx4j tracing if Agent debugging is enabled. */ |
| private static void checkDebug() { |
| try { |
| if (Boolean.getBoolean("gfAgentDebug")) { |
| mx4j.log.Log.setDefaultPriority(mx4j.log.Logger.TRACE); // .DEBUG |
| } |
| } 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 (Throwable t) { |
| // 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(); |
| /* ignore */ |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Member variables |
| // ------------------------------------------------------------------------- |
| |
| /** This Agent's log writer */ |
| private InternalLogWriter logWriter; |
| |
| /** This Agent's JMX http adaptor from MX4J */ |
| private HttpAdaptor httpAdaptor; |
| |
| /** This Agent's RMI Connector Server from MX4J */ |
| private JMXConnectorServer rmiConnector; |
| |
| /** The name of the MBean manages this resource */ |
| private final String mbeanName; |
| |
| /** The ObjectName of the MBean that manages this resource */ |
| private final ObjectName objectName; |
| |
| /** The actual ModelMBean that manages this resource */ |
| private ModelMBean modelMBean; |
| |
| /** The configuration for this Agent */ |
| private final AgentConfigImpl agentConfig; |
| |
| /** |
| * The <code>AdminDistributedSystem</code> this Agent is currently connected to or |
| * <code>null</code> |
| */ |
| private AdminDistributedSystem system; |
| |
| /** The agent's configuration file */ |
| private String propertyFile; |
| |
| /** |
| * A lock object to guard the Connect and Disconnect calls being made on the agent for connections |
| * to the DS |
| **/ |
| private final Object CONN_SYNC = new Object(); |
| |
| protected MemberInfoWithStatsMBean memberInfoWithStatsMBean; |
| |
| private MBeanServer mBeanServer; |
| |
| private final LoggingSession loggingSession; |
| private final Set<LogConfigListener> logConfigListeners = new HashSet<>(); |
| |
| // ------------------------------------------------------------------------- |
| // Constructor(s) |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Constructs a new Agent using the specified configuration. |
| * |
| * @param agentConfig instance of configuration for Agent |
| * @throws org.apache.geode.admin.AdminException if the generated mbeanName does not have the |
| * correct format |
| * @throws IllegalArgumentException if agentConfig is null |
| */ |
| public AgentImpl(AgentConfigImpl agentConfig) throws AdminException, IllegalArgumentException { |
| loggingSession = LoggingSession.create(); |
| |
| shutdownHook = new LoggingThread("Shutdown", false, this::disconnectFromSystem); |
| addShutdownHook(); |
| if (agentConfig == null) { |
| throw new IllegalArgumentException( |
| "AgentConfig must not be null"); |
| } |
| this.agentConfig = agentConfig; |
| mbeanName = MBEAN_NAME_PREFIX + MBeanUtils.makeCompliantMBeanNameProperty("Agent"); |
| |
| try { |
| objectName = new ObjectName(mbeanName); |
| } catch (MalformedObjectNameException ex) { |
| String s = String.format("While creating ObjectName: %s", |
| mbeanName); |
| throw new AdminException(s, ex); |
| } |
| |
| propertyFile = this.agentConfig.getPropertyFile().getAbsolutePath(); |
| |
| // bind address only affects how the Agent VM connects to the system... |
| // It should be set only once in the agent lifecycle |
| this.agentConfig.setBindAddress(getBindAddress()); |
| |
| // LOG: create LogWriterAppender and LogWriterLogger |
| initLogWriter(); |
| |
| mBeanServer = MBeanUtils.start(); |
| |
| MBeanUtils.createMBean(this); |
| |
| initializeHelperMbean(); |
| } |
| |
| private void initializeHelperMbean() { |
| try { |
| memberInfoWithStatsMBean = new MemberInfoWithStatsMBean(this); |
| |
| MBeanServer mbs = getMBeanServer(); |
| mbs.registerMBean(memberInfoWithStatsMBean, memberInfoWithStatsMBean.getObjectName()); |
| /* |
| * We are not re-throwing these exceptions as failure create/register the GemFireTypesWrapper |
| * will not stop the Agent from working. But we are logging it as it could be an indication of |
| * some problem. Also not creating Localized String for the exception. |
| */ |
| } catch (OperationsException e) { |
| logger.info("Failed to initialize MemberInfoWithStatsMBean.", e); |
| } catch (MBeanRegistrationException e) { |
| logger.info("Failed to initialize MemberInfoWithStatsMBean.", e); |
| } catch (AdminException e) { |
| logger.info("Failed to initialize MemberInfoWithStatsMBean.", e); |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Public operations |
| // ------------------------------------------------------------------------- |
| |
| @Override |
| public AgentConfig getConfig() { |
| return agentConfig; |
| } |
| |
| @Override |
| public AdminDistributedSystem getDistributedSystem() { |
| return system; |
| } |
| |
| /** |
| * Persists the current Agent configuration to its property file. |
| * |
| * @throws GemFireIOException if unable to persist the configuration to props |
| * @see #getPropertyFile |
| */ |
| @Override |
| public void saveProperties() { |
| throw new GemFireIOException("saveProperties is no longer supported for security reasons"); |
| } |
| |
| /** |
| * Starts the jmx agent |
| */ |
| @Override |
| public void start() { |
| checkDebug(); |
| |
| agentConfig.validate(); |
| |
| if (mBeanServer == null) { |
| mBeanServer = MBeanUtils.start(); |
| } |
| |
| try { |
| startHttpAdaptor(); |
| } catch (StartupException e) { |
| loggingSession.stopSession(); |
| loggingSession.shutdown(); |
| throw e; |
| } |
| |
| try { |
| startRMIConnectorServer(); |
| } catch (StartupException e) { |
| stopHttpAdaptor(); |
| loggingSession.stopSession(); |
| loggingSession.shutdown(); |
| throw e; |
| } |
| |
| try { |
| startSnmpAdaptor(); |
| } catch (StartupException e) { |
| stopRMIConnectorServer(); |
| stopHttpAdaptor(); |
| loggingSession.stopSession(); |
| loggingSession.shutdown(); |
| throw e; |
| } |
| |
| if (agentConfig.getAutoConnect()) { |
| try { |
| connectToSystem(); |
| /* |
| * Call Agent.stop() if connectToSystem() fails. This should clean up agent-DS connection & |
| * stop all the HTTP/RMI/SNMP adapters started earlier. |
| */ |
| } catch (AdminException ex) { |
| logger.error("auto connect failed: {}", |
| ex.getMessage()); |
| stop(); |
| throw new StartupException(ex); |
| } catch (MalformedObjectNameException ex) { |
| String autoConnectFailed = "auto connect failed: {}"; |
| logger.error(autoConnectFailed, ex.getMessage()); |
| stop(); |
| throw new StartupException(new AdminException( |
| String.format("auto connect failed: %s", ex.getMessage()), ex)); |
| } |
| } // getAutoConnect |
| |
| logger.info("GemFire JMX Agent is running..."); |
| |
| if (memberInfoWithStatsMBean == null) { |
| initializeHelperMbean(); |
| } |
| } |
| |
| /** |
| * Deregisters everything this Agent registered and releases the MBeanServer. |
| */ |
| @Override |
| public void stop() { |
| try { |
| logger.info("Stopping JMX agent"); |
| |
| loggingSession.stopSession(); |
| |
| // stop the GemFire Distributed System |
| stopDistributedSystem(); |
| |
| // stop all JMX Adaptors and Connectors... |
| stopHttpAdaptor(); |
| stopRMIConnectorServer(); |
| memberInfoWithStatsMBean = null; |
| stopSnmpAdaptor(); |
| |
| // release the MBeanServer for cleanup... |
| MBeanUtils.stop(); |
| mBeanServer = null; |
| |
| // remove the register shutdown hook which disconnects the Agent from the Distributed System |
| // upon JVM shutdown |
| removeShutdownHook(); |
| |
| logger.info("Agent has stopped"); |
| } finally { |
| loggingSession.shutdown(); |
| } |
| |
| } |
| |
| private void stopDistributedSystem() { |
| // disconnect from the distributed system... |
| try { |
| disconnectFromSystem(); |
| } catch (Exception e) { |
| // disconnectFromSystem already prints any Exceptions |
| } catch (VirtualMachineError err) { |
| SystemFailure.initiateFailure(err); |
| 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(); |
| } |
| } |
| |
| @Override |
| public ObjectName manageDistributedSystem() throws MalformedObjectNameException { |
| synchronized (CONN_SYNC) { |
| if (isConnected()) { |
| return ((AdminDistributedSystemJmxImpl) system).getObjectName(); |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * Connects to the DistributedSystem currently described by this Agent's attributes for |
| * administration and monitoring. |
| * |
| * @return the object name of the system that the Agent is now connected to |
| */ |
| @Override |
| @edu.umd.cs.findbugs.annotations.SuppressWarnings( |
| value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", |
| justification = "This is only a style warning.") |
| public ObjectName connectToSystem() throws AdminException, MalformedObjectNameException { |
| synchronized (CONN_SYNC) { |
| try { |
| if (isConnected()) { |
| return ((AdminDistributedSystemJmxImpl) system).getObjectName(); |
| } |
| |
| ClusterDistributionManager.setIsDedicatedAdminVM(true); |
| |
| AdminDistributedSystemJmxImpl systemJmx = (AdminDistributedSystemJmxImpl) system; |
| if (systemJmx == null) { |
| systemJmx = (AdminDistributedSystemJmxImpl) createDistributedSystem(agentConfig); |
| system = systemJmx; |
| } |
| systemJmx.connect(logWriter); |
| |
| return new ObjectName(systemJmx.getMBeanName()); |
| } catch (AdminException e) { |
| logger.warn(e.getMessage(), 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; |
| } |
| } |
| } |
| |
| /** |
| * Disconnects from the current DistributedSystem (if connected to one). |
| */ |
| @Override |
| @edu.umd.cs.findbugs.annotations.SuppressWarnings( |
| value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", |
| justification = "This is only a style warning.") |
| public void disconnectFromSystem() { |
| synchronized (CONN_SYNC) { |
| try { |
| if (system == null || !system.isConnected()) { |
| return; |
| } |
| system.disconnect(); |
| } 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.warn(e.getMessage(), e); |
| throw e; |
| } finally { |
| ClusterDistributionManager.setIsDedicatedAdminVM(false); |
| } |
| } |
| } |
| |
| /** |
| * Retrieves a displayable snapshot of this Agent's log. |
| * |
| * @return snapshot of the current log |
| */ |
| public String getLog() { |
| File childLogFile = loggingSession.getLogFile().isPresent() |
| ? loggingSession.getLogFile().get().getChildLogFile() : null; |
| String childTail = tailFile(childLogFile); |
| String mainTail = tailFile(new File(agentConfig.getLogFile())); |
| if (childTail == null && mainTail == null) { |
| return "No log file configured, log messages will be directed to stdout."; |
| } else { |
| StringBuilder result = new StringBuilder(); |
| if (mainTail != null) { |
| result.append(mainTail); |
| } |
| if (childTail != null) { |
| result |
| .append("\n" + "-------------------- tail of child log --------------------" + "\n"); |
| result.append(childTail); |
| } |
| return result.toString(); |
| } |
| } |
| |
| /** |
| * Retrieves display-friendly GemFire version information. |
| */ |
| public String getVersion() { |
| return GemFireVersion.asString(); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Public attribute accessors/mutators |
| // ------------------------------------------------------------------------- |
| |
| /** Returns true if this Agent is currently connected to a system. */ |
| @Override |
| public boolean isConnected() { |
| boolean result = false; |
| synchronized (CONN_SYNC) { |
| result = ((system != null) && system.isConnected()); |
| } |
| return result; |
| } |
| |
| /** |
| * Gets the agent's property file. This is the file it will use when saving its configuration. It |
| * was also used when the agent started to initialize its configuration. |
| * |
| * @return the agent's property file |
| */ |
| public String getPropertyFile() { |
| return propertyFile; |
| } |
| |
| /** |
| * Sets the agent's property file. |
| * |
| * @param value the name of the file to save the agent properties in. |
| * @throws IllegalArgumentException if the specified file is a directory. |
| * @throws IllegalArgumentException if the specified file's parent is not an existing directory. |
| */ |
| public void setPropertyFile(String value) { |
| File f = (new File(value)).getAbsoluteFile(); |
| if (f.isDirectory()) { |
| throw new IllegalArgumentException( |
| String.format("The file %s is a directory.", f)); |
| } |
| File parent = f.getParentFile(); |
| if (parent != null) { |
| if (!parent.isDirectory()) { |
| throw new IllegalArgumentException( |
| String.format("The directory %s does not exist.", parent)); |
| } |
| } |
| propertyFile = f.getPath(); |
| } |
| |
| /** |
| * Gets the mcastAddress of the distributed system that this Agent is managing. |
| * |
| * @return The mcastAddress value |
| */ |
| public String getMcastAddress() { |
| return agentConfig.getMcastAddress(); |
| } |
| |
| /** |
| * Sets the mcastAddress of the distributed system that this Agent is managing. |
| * |
| * @param mcastAddress The new mcastAddress value |
| */ |
| public void setMcastAddress(String mcastAddress) { |
| agentConfig.setMcastAddress(mcastAddress); |
| } |
| |
| /** |
| * Gets the mcastPort of the distributed system that this Agent is managing. |
| * |
| * @return The mcastPort value |
| */ |
| public int getMcastPort() { |
| return agentConfig.getMcastPort(); |
| } |
| |
| /** |
| * Sets the mcastPort of the distributed system that this Agent is managing. |
| * |
| * @param mcastPort The new mcastPort value |
| */ |
| public void setMcastPort(int mcastPort) { |
| agentConfig.setMcastPort(mcastPort); |
| } |
| |
| /** |
| * Gets the locators of the distributed system that this Agent is managing. |
| * <p> |
| * Format is a comma-delimited list of "host[port]" entries. |
| * |
| * @return The locators value |
| */ |
| public String getLocators() { |
| return agentConfig.getLocators(); |
| } |
| |
| /** |
| * Sets the locators of the distributed system that this Agent is managing. |
| * <p> |
| * Format is a comma-delimited list of "host[port]" entries. |
| * |
| * @param locators The new locators value |
| */ |
| public void setLocators(String locators) { |
| agentConfig.setLocators(locators); |
| } |
| |
| /** |
| * Gets the membership UDP port range in the distributed system that this Agent is monitoring. |
| * <p> |
| * This range is given as two numbers separated by a minus sign like "min-max" |
| * |
| * @return membership UDP port range |
| */ |
| public String getMembershipPortRange() { |
| return agentConfig.getMembershipPortRange(); |
| } |
| |
| /** |
| * Sets the membership UDP port range in the distributed system that this Agent is monitoring. |
| * <p> |
| * This range is given as two numbers separated by a minus sign like "min-max" |
| * |
| * @param membershipPortRange membership UDP port range |
| */ |
| public void setMembershipPortRange(String membershipPortRange) { |
| agentConfig.setMembershipPortRange(membershipPortRange); |
| } |
| |
| /** |
| * Gets the bindAddress of the distributed system that this Agent is managing. |
| * |
| * @return The bindAddress value |
| */ |
| public String getBindAddress() { |
| return agentConfig.getBindAddress(); |
| } |
| |
| /** |
| * Sets the bindAddress of the distributed system that this Agent is managing. |
| * |
| * @param bindAddress The new bindAddress value |
| */ |
| public void setBindAddress(String bindAddress) { |
| agentConfig.setBindAddress(bindAddress); |
| } |
| |
| /** |
| * Retrieves the command that the DistributedSystem will use to perform remote manipulation of |
| * config files and log files. |
| * |
| * @return the remote command for DistributedSystem |
| */ |
| public String getRemoteCommand() { |
| return agentConfig.getRemoteCommand(); |
| } |
| |
| /** |
| * Sets the command that the DistributedSystem will use to perform remote manipulation of config |
| * files and log files. |
| * |
| * @param remoteCommand the remote command for DistributedSystem |
| */ |
| public void setRemoteCommand(String remoteCommand) { |
| agentConfig.setRemoteCommand(remoteCommand); |
| } |
| |
| /** Returns the system identity for the DistributedSystem */ |
| public String getSystemId() { |
| return agentConfig.getSystemId(); |
| } |
| |
| /** Sets the system identity for the DistributedSystem */ |
| public void setSystemId(String systemId) { |
| agentConfig.setSystemId(systemId); |
| } |
| |
| /** |
| * Gets the logFileSizeLimit in megabytes of this Agent. Zero indicates no limit. |
| * |
| * @return The logFileSizeLimit value |
| */ |
| public int getLogFileSizeLimit() { |
| return agentConfig.getLogFileSizeLimit(); |
| } |
| |
| /** |
| * Sets the logFileSizeLimit in megabytes of this Agent. Zero indicates no limit. |
| * |
| * @param logFileSizeLimit The new logFileSizeLimit value |
| */ |
| public void setLogFileSizeLimit(int logFileSizeLimit) { |
| agentConfig.setLogFileSizeLimit(logFileSizeLimit); |
| logConfigChanged(); |
| } |
| |
| /** |
| * Gets the logDiskSpaceLimit in megabytes of this Agent. Zero indicates no limit. |
| * |
| * @return The logDiskSpaceLimit value |
| */ |
| public int getLogDiskSpaceLimit() { |
| return agentConfig.getLogDiskSpaceLimit(); |
| } |
| |
| /** |
| * Sets the logDiskSpaceLimit in megabytes of this Agent. Zero indicates no limit. |
| * |
| * @param logDiskSpaceLimit The new logDiskSpaceLimit value |
| */ |
| public void setLogDiskSpaceLimit(int logDiskSpaceLimit) { |
| agentConfig.setLogDiskSpaceLimit(logDiskSpaceLimit); |
| logConfigChanged(); |
| } |
| |
| /** |
| * Gets the logFile name for this Agent to log to. |
| * |
| * @return The logFile value |
| */ |
| public String getLogFile() { |
| return agentConfig.getLogFile(); |
| } |
| |
| /** |
| * Sets the logFile name for this Agent to log to. |
| * |
| * @param logFile The new logFile value |
| */ |
| public void setLogFile(String logFile) { |
| agentConfig.setLogFile(logFile); |
| logConfigChanged(); |
| } |
| |
| /** |
| * Gets the logLevel of this Agent. |
| * |
| * @return The logLevel value |
| */ |
| public String getLogLevel() { |
| return agentConfig.getLogLevel(); |
| } |
| |
| /** |
| * Sets the logLevel of this Agent. |
| * |
| * @param logLevel The new logLevel value |
| */ |
| public void setLogLevel(String logLevel) { |
| agentConfig.setLogLevel(logLevel); |
| logConfigChanged(); |
| } |
| |
| /** Returns true if the Agent is set to auto connect to a system. */ |
| public boolean getAutoConnect() { |
| return agentConfig.getAutoConnect(); |
| } |
| |
| /** Returns true if the Agent is set to auto connect to a system. */ |
| public boolean isAutoConnect() { |
| return agentConfig.getAutoConnect(); |
| } |
| |
| /** Sets or unsets the option to auto connect to a system. */ |
| public void setAutoConnect(boolean v) { |
| agentConfig.setAutoConnect(v); |
| } |
| |
| /** |
| * Returns the address (URL) on which the RMI connector server runs or <code>null</code> if the |
| * RMI connector server has not been started. This method is used primarily for testing purposes. |
| * |
| * @see JMXConnectorServer#getAddress() |
| */ |
| public JMXServiceURL getRMIAddress() { |
| if (rmiConnector != null) { |
| return rmiConnector.getAddress(); |
| |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Gets the configuration for this Agent. |
| * |
| * @return the configuration for this Agent |
| */ |
| protected AgentConfig getAgentConfig() { |
| return agentConfig; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Internal implementation methods |
| // ------------------------------------------------------------------------- |
| |
| /** Returns the tail of the system log specified by <code>File</code>. */ |
| private String tailFile(File f) { |
| try { |
| return TailLogResponse.tailSystemLog(f); |
| } catch (IOException ex) { |
| return String.format("Could not tail %s because: %s", |
| f, ex); |
| } |
| } |
| |
| /** |
| * Returns the active MBeanServer which has any GemFire MBeans registered. |
| * |
| * @return the GemFire mbeanServer |
| */ |
| @Override |
| public MBeanServer getMBeanServer() { |
| return mBeanServer; |
| } |
| |
| /** |
| * Gets the current instance of LogWriter for logging |
| * |
| * @return the logWriter |
| */ |
| @Override |
| public LogWriter getLogWriter() { |
| return logWriter; |
| } |
| |
| private final Thread shutdownHook; |
| |
| /** |
| * Adds a ShutdownHook to the Agent for cleaning up any resources |
| */ |
| private void addShutdownHook() { |
| if (!Boolean.getBoolean( |
| org.apache.geode.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) { |
| Runtime.getRuntime().addShutdownHook(shutdownHook); |
| } |
| } |
| |
| private void removeShutdownHook() { |
| if (!Boolean.getBoolean( |
| org.apache.geode.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) { |
| Runtime.getRuntime().removeShutdownHook(shutdownHook); |
| } |
| } |
| |
| /** |
| * Creates a LogWriterI18n for this Agent to use in logging. |
| */ |
| @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", |
| justification = "Return value for file delete is not important here.") |
| private void initLogWriter() throws org.apache.geode.admin.AdminException { |
| loggingSession.createSession(this); |
| |
| final LogConfig logConfig = agentConfig.createLogConfig(); |
| |
| // LOG: create logWriterAppender here |
| loggingSession.startSession(); |
| |
| // LOG: look in AgentConfigImpl for existing LogWriter to use |
| InternalLogWriter existingLogWriter = agentConfig.getInternalLogWriter(); |
| if (existingLogWriter != null) { |
| logWriter = existingLogWriter; |
| } else { |
| // LOG: create LogWriterLogger |
| logWriter = LogWriterFactory.createLogWriterLogger(logConfig, false); |
| // Set this log writer in AgentConfigImpl |
| agentConfig.setInternalLogWriter(logWriter); |
| } |
| |
| // LOG: create logWriter here |
| logWriter = LogWriterFactory.createLogWriterLogger(logConfig, false); |
| |
| // Set this log writer in AgentConfig |
| agentConfig.setInternalLogWriter(logWriter); |
| |
| // LOG:CONFIG: changed next three statements from config to info |
| logger.info(LogMarker.CONFIG_MARKER, |
| String.format("Agent config property file name: %s", |
| AgentConfigImpl.retrievePropertyFile())); |
| logger.info(LogMarker.CONFIG_MARKER, agentConfig.getPropertyFileDescription()); |
| logger.info(LogMarker.CONFIG_MARKER, agentConfig.toPropertiesAsString()); |
| } |
| |
| /** |
| * Stops the HttpAdaptor and its XsltProcessor. Unregisters the associated MBeans. |
| */ |
| private void stopHttpAdaptor() { |
| if (!agentConfig.isHttpEnabled()) { |
| return; |
| } |
| |
| // stop the adaptor... |
| try { |
| httpAdaptor.stop(); |
| } catch (Exception e) { |
| logger.warn(e.getMessage(), e); |
| } |
| |
| try { |
| MBeanUtils.unregisterMBean(getHttpAdaptorName()); |
| MBeanUtils.unregisterMBean(getXsltProcessorName()); |
| } catch (MalformedObjectNameException e) { |
| logger.warn(e.getMessage(), e); |
| } |
| } |
| |
| /** Stops the RMIConnectorServer and unregisters its MBean. */ |
| private void stopRMIConnectorServer() { |
| if (!agentConfig.isRmiEnabled()) { |
| return; |
| } |
| |
| // stop the RMI Connector server... |
| try { |
| rmiConnector.stop(); |
| } catch (Exception e) { |
| logger.warn(e.getMessage(), e); |
| } |
| |
| try { |
| ObjectName rmiRegistryNamingName = getRMIRegistryNamingName(); |
| if (agentConfig.isRmiRegistryEnabled() |
| && mBeanServer.isRegistered(rmiRegistryNamingName)) { |
| String[] empty = new String[0]; |
| mBeanServer.invoke(rmiRegistryNamingName, "stop", empty, empty); |
| MBeanUtils.unregisterMBean(rmiRegistryNamingName); |
| } |
| } catch (MalformedObjectNameException e) { |
| logger.warn(e.getMessage(), e); |
| } catch (InstanceNotFoundException e) { |
| logger.warn(e.getMessage(), e); |
| } catch (ReflectionException e) { |
| logger.warn(e.getMessage(), e); |
| } catch (MBeanException e) { |
| logger.warn(e.getMessage(), e); |
| } |
| |
| try { |
| ObjectName rmiConnectorServerName = getRMIConnectorServerName(); |
| if (mBeanServer.isRegistered(rmiConnectorServerName)) { |
| MBeanUtils.unregisterMBean(rmiConnectorServerName); |
| } |
| } catch (MalformedObjectNameException e) { |
| logger.warn(e.getMessage(), e); |
| } |
| } |
| |
| /** Stops the SnmpAdaptor and unregisters its MBean. */ |
| private void stopSnmpAdaptor() { |
| if (!agentConfig.isSnmpEnabled()) { |
| return; |
| } |
| |
| // stop the SnmpAdaptor... |
| try { |
| getMBeanServer().invoke(getSnmpAdaptorName(), "unbind", new Object[0], new String[0]); |
| } catch (Exception e) { |
| logger.warn(e.getMessage(), e); |
| } |
| |
| try { |
| MBeanUtils.unregisterMBean(getSnmpAdaptorName()); |
| } catch (MalformedObjectNameException e) { |
| logger.warn(e.getMessage(), e); |
| } |
| } |
| |
| /** Returns the JMX ObjectName for the RMI registry Naming MBean. */ |
| private ObjectName getRMIRegistryNamingName() |
| throws javax.management.MalformedObjectNameException { |
| return ObjectName.getInstance("naming:type=rmiregistry"); |
| } |
| |
| /** Returns the JMX ObjectName for the HttpAdaptor. */ |
| private ObjectName getHttpAdaptorName() throws javax.management.MalformedObjectNameException { |
| return new ObjectName("Server:name=HttpAdaptor"); |
| } |
| |
| /** Returns the JMX ObjectName for the RMIConnectorServer. */ |
| private ObjectName getRMIConnectorServerName() |
| throws javax.management.MalformedObjectNameException { |
| return new ObjectName("connectors:protocol=rmi"); |
| } |
| |
| /** Returns the JMX ObjectName for the SnmpAdaptor. */ |
| private ObjectName getSnmpAdaptorName() throws javax.management.MalformedObjectNameException { |
| return new ObjectName("Adaptors:protocol=SNMP"); |
| } |
| |
| /** Returns the JMX ObjectName for the HttpAdaptor's XsltProcessor. */ |
| private ObjectName getXsltProcessorName() throws javax.management.MalformedObjectNameException { |
| return new ObjectName("Server:name=XSLTProcessor"); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Factory method for creating DistributedSystem |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Creates and connects to a <code>DistributedSystem</code>. |
| * |
| */ |
| private AdminDistributedSystem createDistributedSystem(AgentConfigImpl config) |
| throws org.apache.geode.admin.AdminException { |
| return new AdminDistributedSystemJmxImpl(config); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Agent main |
| // ------------------------------------------------------------------------- |
| |
| /** |
| * Command-line main for running the GemFire Management Agent. |
| * <p> |
| * Accepts command-line arguments matching the options in {@link AgentConfig} and |
| * {@link org.apache.geode.admin.DistributedSystemConfig}. |
| * <p> |
| * <code>AgentConfig</code> will convert -Jarguments to System properties. |
| */ |
| public static void main(String[] args) { |
| SystemFailure.loadEmergencyClasses(); |
| |
| AgentConfigImpl ac; |
| try { |
| ac = new AgentConfigImpl(args); |
| } catch (RuntimeException ex) { |
| System.err |
| .println(String.format("Failed reading configuration: %s", ex)); |
| ExitCode.FATAL.doSystemExit(); |
| return; |
| } |
| |
| try { |
| Agent agent = AgentFactory.getAgent(ac); |
| agent.start(); |
| |
| } 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 (Throwable t) { |
| // 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(); |
| t.printStackTrace(); |
| ExitCode.FATAL.doSystemExit(); |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| // MX4J Connectors/Adaptors |
| // ------------------------------------------------------------------------- |
| |
| private void createRMIRegistry() throws Exception { |
| if (!agentConfig.isRmiRegistryEnabled()) { |
| return; |
| } |
| MBeanServer mbs = getMBeanServer(); |
| String host = agentConfig.getRmiBindAddress(); |
| int port = agentConfig.getRmiPort(); |
| |
| /* |
| * Register and start the rmi-registry naming MBean, which is needed by JSR 160 |
| * RMIConnectorServer |
| */ |
| ObjectName registryName = getRMIRegistryNamingName(); |
| try { |
| RMIRegistryService registryNamingService = null; |
| if (host != null && !("".equals(host.trim()))) { |
| registryNamingService = new RMIRegistryService(host, port); |
| } else { |
| registryNamingService = new RMIRegistryService(port); |
| } |
| mbs.registerMBean(registryNamingService, registryName); |
| } catch (javax.management.InstanceAlreadyExistsException e) { |
| logger.info("{} is already registered.", |
| registryName); |
| } |
| mbs.invoke(registryName, "start", null, null); |
| } |
| |
| /** |
| * Defines and starts the JMX RMIConnector and service. |
| * <p> |
| * If {@link AgentConfig#isRmiEnabled} returns false, then this adaptor will not be started. |
| */ |
| private void startRMIConnectorServer() { |
| if (!agentConfig.isRmiEnabled()) { |
| return; |
| } |
| |
| String rmiBindAddress = agentConfig.getRmiBindAddress(); |
| |
| // Set RMI Stubs to use the given RMI Bind Address |
| // Default bindAddress is "", if none is set - ignore if not set |
| // If java.rmi.server.hostname property is specified then |
| // that override is not changed |
| String rmiStubServerNameKey = "java.rmi.server.hostname"; |
| String overrideHostName = System.getProperty(rmiStubServerNameKey); |
| if ((overrideHostName == null || overrideHostName.trim().length() == 0) |
| && (rmiBindAddress != null && rmiBindAddress.trim().length() != 0)) { |
| System.setProperty(rmiStubServerNameKey, rmiBindAddress); |
| logger.info(("Setting " + rmiStubServerNameKey + " = " |
| + rmiBindAddress)); |
| } |
| |
| try { |
| createRMIRegistry(); |
| ObjectName objName = getRMIConnectorServerName(); |
| |
| // make sure this adaptor is not already registered... |
| if (getMBeanServer().isRegistered(objName)) { |
| // dunno how we got here... |
| logger.info("RMIConnectorServer already registered as {}", objName); |
| return; |
| } |
| |
| /* |
| * url defined as: service:jmx:protocol:sap where 1. protocol: rmi 2. sap is: |
| * [host[:port]][url-path] where host: rmi-binding-address port: rmi-server-port url-path: |
| * /jndi/rmi://<rmi-binding-address>:<rmi-port><JNDI_NAME> |
| */ |
| String urlString = null; |
| String connectorServerHost = ""; |
| int connectorServerPort = agentConfig.getRmiServerPort(); |
| String rmiRegistryHost = ""; |
| int rmiRegistryPort = agentConfig.getRmiPort(); |
| |
| // Set registryHost to localhost if not specified |
| // RMI stubs would use a default IP if namingHost is left empty |
| if (rmiBindAddress == null || rmiBindAddress.trim().length() == 0) { |
| connectorServerHost = "localhost"; |
| rmiRegistryHost = ""; |
| } else { |
| connectorServerHost = applyRFC2732(rmiBindAddress); |
| rmiRegistryHost = connectorServerHost; |
| } |
| |
| urlString = MessageFormat.format(AgentImpl.JMX_SERVICE_URL, connectorServerHost, |
| String.valueOf(connectorServerPort), rmiRegistryHost, String.valueOf(rmiRegistryPort), |
| JNDI_NAME); |
| |
| logger.debug("JMX Service URL string is : \"{}\"", urlString); |
| |
| // The address of the connector |
| JMXServiceURL url = new JMXServiceURL(urlString); |
| |
| Map<String, Object> env = new HashMap<>(); |
| |
| RMIServerSocketFactory ssf = new MX4JServerSocketFactory(agentConfig.isAgentSSLEnabled(), // true, |
| agentConfig.isAgentSSLRequireAuth(), // true, |
| agentConfig.getAgentSSLProtocols(), // "any", |
| agentConfig.getAgentSSLCiphers(), // "any", |
| agentConfig.getRmiBindAddress(), 10, // backlog |
| agentConfig.getGfSecurityProperties()); |
| env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf); |
| |
| if (agentConfig.isAgentSSLEnabled()) { |
| RMIClientSocketFactory csf = new SslRMIClientSocketFactory(); |
| env.put(RMIConnectorServer.RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE, csf); |
| } |
| |
| MBeanServer mbs = null; // will be set by registering w/ mbeanServer |
| rmiConnector = JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs); |
| |
| // for cleanup |
| rmiConnector.addNotificationListener(new ConnectionNotificationAdapter(), |
| new ConnectionNotificationFilterImpl(), this); |
| |
| // Register the JMXConnectorServer in the MBeanServer |
| getMBeanServer().registerMBean(rmiConnector, objName); |
| |
| // Start the JMXConnectorServer |
| rmiConnector.start(); |
| } 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 (Throwable t) { |
| // 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("Failed to start RMIConnectorServer:", t); |
| throw new StartupException( |
| "Failed to start RMI service, verify RMI configuration properties", t); |
| } |
| } |
| |
| /** |
| * Starts the optional third-party AdventNet SNMP Adaptor. |
| * <p> |
| * If {@link AgentConfig#isSnmpEnabled} returns false, then this adaptor will not be started. |
| */ |
| private void startSnmpAdaptor() { |
| if (!agentConfig.isSnmpEnabled()) { |
| return; |
| } |
| try { |
| ObjectName objName = getSnmpAdaptorName(); |
| |
| // make sure this adaptor is not already registered... |
| if (getMBeanServer().isRegistered(objName)) { |
| // dunno how we got here... |
| logger.info("SnmpAdaptor already registered as {}", objName); |
| return; |
| } |
| |
| String className = "com.adventnet.adaptors.snmp.snmpsupport.SmartSnmpAdaptor"; |
| String snmpDir = agentConfig.getSnmpDirectory(); |
| // ex:/merry2/users/klund/agent |
| |
| // validate the directory... |
| if (snmpDir == null || snmpDir.length() == 0) { |
| throw new IllegalArgumentException( |
| "snmp-directory must be specified because SNMP is enabled"); |
| } |
| File root = new File(snmpDir); |
| if (!root.exists()) { |
| throw new IllegalArgumentException( |
| "snmp-directory does not exist"); |
| } |
| |
| // create the adaptor... |
| String[] sigs = new String[] {"java.lang.String"}; |
| Object[] args = new Object[] {snmpDir}; |
| |
| String bindAddress = agentConfig.getSnmpBindAddress(); |
| if (bindAddress != null && bindAddress.length() > 0) { |
| sigs = new String[] {"java.lang.String", sigs[0]}; |
| args = new Object[] {bindAddress, args[0]}; |
| } |
| |
| // go... |
| getMBeanServer().createMBean(className, objName, args, sigs); |
| } 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 (Throwable t) { |
| // 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("Failed to start SnmpAdaptor: {}", t.getMessage()); |
| throw new StartupException(String.format("Failed to start SnmpAdaptor: %s", |
| t.getMessage()), t); |
| } |
| } |
| |
| /** |
| * Defines and starts the JMX Http Adaptor service from MX4J. |
| * <p> |
| * If {@link AgentConfig#isHttpEnabled} returns false, then this adaptor will not be started. |
| */ |
| private void startHttpAdaptor() { |
| if (!agentConfig.isHttpEnabled()) { |
| return; |
| } |
| try { |
| ObjectName objName = getHttpAdaptorName(); |
| |
| // make sure this adaptor is not already registered... |
| if (getMBeanServer().isRegistered(objName)) { |
| // dunno how we got here... |
| logger.info("HttpAdaptor already registered as {}", objName); |
| return; |
| } |
| |
| httpAdaptor = new HttpAdaptor(); |
| |
| // validate and set host and port values... |
| if (agentConfig.getHttpPort() > 0) { |
| httpAdaptor.setPort(agentConfig.getHttpPort()); |
| logger.info(LogMarker.CONFIG_MARKER, |
| "HTTP adaptor listening on port: {}", |
| agentConfig.getHttpPort()); |
| } else { |
| logger.error("Incorrect port value {}", |
| agentConfig.getHttpPort()); |
| } |
| |
| if (agentConfig.getHttpBindAddress() != null) { |
| String host = agentConfig.getHttpBindAddress(); |
| logger.info(LogMarker.CONFIG_MARKER, "HTTP adaptor listening on address: {}", host); |
| httpAdaptor.setHost(host); |
| } else { |
| logger.error("Incorrect null hostname"); |
| } |
| |
| // SSL support... |
| MX4JServerSocketFactory socketFactory = |
| new MX4JServerSocketFactory(agentConfig.isAgentSSLEnabled(), |
| agentConfig.isHttpSSLRequireAuth(), agentConfig.getAgentSSLProtocols(), |
| agentConfig.getAgentSSLCiphers(), agentConfig.getGfSecurityProperties()); |
| httpAdaptor.setSocketFactory(socketFactory); |
| |
| // authentication (user login) support... |
| if (agentConfig.isHttpAuthEnabled()) { |
| // this pops up a login dialog from the browser... |
| httpAdaptor.setAuthenticationMethod(MX4J_HTTPADAPTOR_BASIC_AUTHENTICATION); // only |
| // basic |
| // works |
| |
| httpAdaptor.addAuthorization(agentConfig.getHttpAuthUser(), |
| agentConfig.getHttpAuthPassword()); |
| } |
| |
| // add the XsltProcessor... |
| httpAdaptor.setProcessorName(createXsltProcessor()); |
| |
| // register the HttpAdaptor and snap on the XsltProcessor... |
| getMBeanServer().registerMBean(httpAdaptor, objName); |
| httpAdaptor.start(); |
| } 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 (Throwable t) { |
| // 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("Failed to start HttpAdaptor: {}", t.getMessage()); |
| throw new StartupException(String.format("Failed to start HttpAdaptor: %s", |
| t.getMessage()), t); |
| } |
| } |
| |
| /** |
| * Defines and starts the Xslt Processor helper service for the Http Adaptor. |
| */ |
| private ObjectName createXsltProcessor() throws javax.management.JMException { |
| ObjectName objName = getXsltProcessorName(); |
| |
| // make sure this mbean is not already registered... |
| if (getMBeanServer().isRegistered(objName)) { |
| // dunno how we got here... |
| logger.info("XsltProcessor already registered as {}", objName); |
| return objName; |
| } |
| |
| getMBeanServer().registerMBean(new mx4j.tools.adaptor.http.XSLTProcessor(), objName); |
| return objName; |
| } |
| |
| // ------------------------------------------------------------------------- |
| // SSL configuration for GemFire |
| // ------------------------------------------------------------------------- |
| public boolean isSSLEnabled() { |
| return agentConfig.isSSLEnabled(); |
| } |
| |
| public void setSSLEnabled(boolean enabled) { |
| agentConfig.setSSLEnabled(enabled); |
| } |
| |
| public String getSSLProtocols() { |
| return agentConfig.getSSLProtocols(); |
| } |
| |
| public void setSSLProtocols(String protocols) { |
| agentConfig.setSSLProtocols(protocols); |
| } |
| |
| public String getSSLCiphers() { |
| return agentConfig.getSSLCiphers(); |
| } |
| |
| public void setSSLCiphers(String ciphers) { |
| agentConfig.setSSLCiphers(ciphers); |
| } |
| |
| public boolean isSSLAuthenticationRequired() { |
| return agentConfig.isSSLAuthenticationRequired(); |
| } |
| |
| public void setSSLAuthenticationRequired(boolean authRequired) { |
| agentConfig.setSSLAuthenticationRequired(authRequired); |
| } |
| |
| public Properties getSSLProperties() { |
| return agentConfig.getSSLProperties(); |
| } |
| |
| public void setSSLProperties(Properties sslProperties) { |
| agentConfig.setSSLProperties(sslProperties); |
| } |
| |
| public void addSSLProperty(String key, String value) { |
| agentConfig.addSSLProperty(key, value); |
| } |
| |
| public void removeSSLProperty(String key) { |
| agentConfig.removeSSLProperty(key); |
| } |
| |
| // ------------------------------------------------------------------------- |
| // ManagedResource implementation |
| // ------------------------------------------------------------------------- |
| |
| @Override |
| public String getMBeanName() { |
| return mbeanName; |
| } |
| |
| @Override |
| public ModelMBean getModelMBean() { |
| return modelMBean; |
| } |
| |
| @Override |
| public void setModelMBean(ModelMBean modelMBean) { |
| this.modelMBean = modelMBean; |
| } |
| |
| @Override |
| public ObjectName getObjectName() { |
| return objectName; |
| } |
| |
| @Override |
| public ManagedResourceType getManagedResourceType() { |
| return ManagedResourceType.AGENT; |
| } |
| |
| @Override |
| public void cleanupResource() {} |
| |
| @Override |
| public LogConfig getLogConfig() { |
| return agentConfig.createLogConfig(); |
| } |
| |
| @Override |
| public StatisticsConfig getStatisticsConfig() { |
| return agentConfig.createStatisticsConfig(); |
| } |
| |
| @Override |
| public void addLogConfigListener(LogConfigListener logConfigListener) { |
| logConfigListeners.add(logConfigListener); |
| } |
| |
| @Override |
| public void removeLogConfigListener(LogConfigListener logConfigListener) { |
| logConfigListeners.remove(logConfigListener); |
| } |
| |
| void logConfigChanged() { |
| for (LogConfigListener listener : logConfigListeners) { |
| listener.configChanged(); |
| } |
| } |
| |
| static class StartupException extends GemFireException { |
| private static final long serialVersionUID = 6614145962199330348L; |
| |
| StartupException(Throwable cause) { |
| super(cause); |
| } |
| |
| StartupException(String reason, Throwable cause) { |
| super(reason, cause); |
| } |
| } |
| |
| // ------------------------------------------------------------------------- |
| // Other Support methods |
| // ------------------------------------------------------------------------- |
| /** |
| * Checks the no. of active RMI clients and updates a flag in the Admin Distributed System. |
| * |
| * @see AdminDistributedSystemJmxImpl#setRmiClientCountZero(boolean) |
| * @since GemFire 6.0 |
| */ |
| void updateRmiClientsCount() { |
| int noOfClientsConnected = 0; |
| |
| String[] connectionIds = rmiConnector.getConnectionIds(); |
| |
| if (connectionIds != null) { |
| noOfClientsConnected = connectionIds.length; |
| } |
| |
| logger.info("No. of RMI clients connected :: {}", noOfClientsConnected); |
| |
| AdminDistributedSystemJmxImpl adminDSJmx = (AdminDistributedSystemJmxImpl) system; |
| |
| adminDSJmx.setRmiClientCountZero(noOfClientsConnected == 0); |
| } |
| |
| @Override |
| public String toString() { |
| return "AgentImpl[" |
| + "config=" + agentConfig.toProperties().toString() |
| + "; mbeanName=" + mbeanName |
| + "; modelMBean=" + modelMBean |
| + "; objectName=" + objectName |
| + "; propertyFile=" + propertyFile |
| + ": rmiConnector=" + rmiConnector |
| + "]"; |
| } |
| |
| /** |
| * Process the String form of a hostname to make it comply with Jmx URL restrictions. Namely wrap |
| * IPv6 literal address with "[", "]" |
| * |
| * @param hostname the name to safeguard. |
| * @return a string representation suitable for use in a Jmx connection URL |
| */ |
| private static String applyRFC2732(String hostname) { |
| if (hostname.indexOf(":") != -1) { |
| // Assuming an IPv6 literal because of the ':' |
| return "[" + hostname + "]"; |
| } |
| return hostname; |
| } |
| } |
| |
| |
| /** |
| * Adapter class for NotificationListener that listens to notifications of type |
| * javax.management.remote.JMXConnectionNotification |
| * |
| * @since GemFire 6.0 |
| */ |
| class ConnectionNotificationAdapter implements NotificationListener { |
| private static final Logger logger = LogService.getLogger(); |
| |
| /** |
| * If the handback object passed is an AgentImpl, updates the JMX client count |
| * |
| * @param notification JMXConnectionNotification for change in client connection status |
| * @param handback An opaque object which helps the listener to associate information regarding |
| * the MBean emitter. This object is passed to the MBean during the addListener call and |
| * resent, without modification, to the listener. The MBean object should not use or modify |
| * the object. (NOTE: copied from javax.management.NotificationListener) |
| */ |
| @Override |
| @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "BC_UNCONFIRMED_CAST", |
| justification = "Only JMXConnectionNotification instances are used.") |
| public void handleNotification(Notification notification, Object handback) { |
| if (handback instanceof AgentImpl) { |
| AgentImpl agent = (AgentImpl) handback; |
| |
| JMXConnectionNotification jmxNotifn = (JMXConnectionNotification) notification; |
| |
| if (logger.isDebugEnabled()) { |
| logger.debug("Connection notification for connection id : '{}'", |
| jmxNotifn.getConnectionId()); |
| } |
| |
| agent.updateRmiClientsCount(); |
| } |
| } |
| } |
| |
| |
| /** |
| * Filters out the notifications of the type JMXConnectionNotification.OPENED, |
| * JMXConnectionNotification.CLOSED and JMXConnectionNotification.FAILED. |
| * |
| * @since GemFire 6.0 |
| */ |
| class ConnectionNotificationFilterImpl implements NotificationFilter { |
| |
| /** |
| * Default serialVersionUID |
| */ |
| private static final long serialVersionUID = 1L; |
| |
| /** |
| * Invoked before sending the specified notification to the listener. Returns whether the given |
| * notification is to be sent to the listener. |
| * |
| * @param notification The notification to be sent. |
| * @return true if the notification has to be sent to the listener, false otherwise. |
| */ |
| @Override |
| public boolean isNotificationEnabled(Notification notification) { |
| return notification.getType().equals(JMXConnectionNotification.OPENED) |
| || notification.getType().equals(JMXConnectionNotification.CLOSED) |
| || notification.getType().equals(JMXConnectionNotification.FAILED); |
| } |
| } |