com.gemstone.gemfire.admin.jmx.internal.AgentImpl Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.admin.jmx.internal;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.rmi.server.RMIClientSocketFactory;
import java.rmi.server.RMIServerSocketFactory;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
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 com.gemstone.gemfire.GemFireException;
import com.gemstone.gemfire.GemFireIOException;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.SystemFailure;
import com.gemstone.gemfire.admin.AdminDistributedSystem;
import com.gemstone.gemfire.admin.AdminException;
import com.gemstone.gemfire.admin.jmx.Agent;
import com.gemstone.gemfire.admin.jmx.AgentConfig;
import com.gemstone.gemfire.admin.jmx.AgentFactory;
import com.gemstone.gemfire.distributed.internal.DistributionManager;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.Banner;
import com.gemstone.gemfire.internal.ClassPathLoader;
import com.gemstone.gemfire.internal.GemFireVersion;
import com.gemstone.gemfire.internal.LogWriterImpl;
import com.gemstone.gemfire.internal.ManagerLogWriter;
import com.gemstone.gemfire.internal.admin.remote.TailLogResponse;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
import com.gemstone.gemfire.internal.util.IOUtils;
import com.gemstone.org.jgroups.util.StringId;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import mx4j.tools.adaptor.http.HttpAdaptor;
/**
* The GemFire JMX Agent provides the ability to administrate one GemFire
* distributed system via JMX.
*
* @author Kirk Lund
* @author David Whitlock
* @since 3.5
*/
public class AgentImpl
implements com.gemstone.gemfire.admin.jmx.Agent,
com.gemstone.gemfire.admin.jmx.internal.ManagedResource {
/**
* 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}";
private static boolean isGemFireXD = false;
/**
* 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");
}
try {
ClassPathLoader.getLatest().forName(
"com.pivotal.gemfirexd.internal.GemFireXDVersion");
isGemFireXD = true;
} catch (ClassNotFoundException e) {
isGemFireXD = false;
} finally {
System.setProperty("isSqlFire", String.valueOf(isGemFireXD));
}
}
/** 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 (Throwable t) {
Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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 ManagerLogWriter logWriter;
private FileOutputStream logFileOutputStream;
/** 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 AdminDistributedSystem
this Agent is currently
* connected to or null
*/
private AdminDistributedSystem system;
/** The agent's configuration file */
private String propertyFile;
/** Set to non-null if a local log file is opened on startup */
private LogConfigImpl logConfig;
/** 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;
// -------------------------------------------------------------------------
// Constructor(s)
// -------------------------------------------------------------------------
/**
* Constructs a new Agent using the specified configuration.
*
* @param agentConfig instance of configuration for Agent
* @throws com.gemstone.gemfire.admin.AdminException TODO-javadocs
* @throws IllegalArgumentException if agentConfig is null
*/
public AgentImpl(AgentConfig agentConfig)
throws AdminException, IllegalArgumentException {
addShutdownHook();
if (agentConfig == null) {
throw new IllegalArgumentException(LocalizedStrings.AgentImpl_AGENTCONFIG_MUST_NOT_BE_NULL.toLocalizedString());
}
this.agentConfig = (AgentConfigImpl)agentConfig;
this.mbeanName = MBEAN_NAME_PREFIX + MBeanUtil.makeCompliantMBeanNameProperty("Agent");
try {
this.objectName = new ObjectName(this.mbeanName);
} catch (MalformedObjectNameException ex) {
String s = LocalizedStrings.AgentImpl_WHILE_CREATING_OBJECTNAME_0.toLocalizedString(new Object[] { this.mbeanName });
throw new AdminException(s, ex);
}
this.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());
// init the logger
initLogWriter();
mBeanServer = MBeanUtil.start();
MBeanUtil.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) {
getLogWriterI18n().info(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN, e);
} catch (MBeanRegistrationException e) {
getLogWriterI18n().info(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN, e);
} catch (AdminException e) {
getLogWriterI18n().info(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN, e);
}
}
// -------------------------------------------------------------------------
// Public operations
// -------------------------------------------------------------------------
public AgentConfig getConfig() {
return this.agentConfig;
}
public AdminDistributedSystem getDistributedSystem() {
return this.system;
}
/**
* Persists the current Agent configuration to its property file.
*
* @throws GemFireIOException if unable to persist the configuration to props
* @see #getPropertyFile
*/
public void saveProperties() {
throw new GemFireIOException("saveProperties is no longer supported for security reasons");
}
/**
* Starts the jmx agent
*/
public void start() {
checkDebug();
this.agentConfig.validate();
if (mBeanServer == null) {
mBeanServer = MBeanUtil.start();
}
try {
startHttpAdaptor();
} catch (StartupException e) {
logWriter.shuttingDown();
throw e;
}
try {
startRMIConnectorServer();
} catch (StartupException e) {
stopHttpAdaptor();
logWriter.shuttingDown();
throw e;
}
try {
startSnmpAdaptor();
} catch (StartupException e) {
stopRMIConnectorServer();
stopHttpAdaptor();
logWriter.shuttingDown();
throw e;
}
if (this.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) {
getLogWriterI18n().error(LocalizedStrings.AgentImpl_AUTO_CONNECT_FAILED__0, ex.getMessage());
this.stop();
throw new StartupException(ex);
} catch (MalformedObjectNameException ex) {
StringId autoConnectFailed = LocalizedStrings.AgentImpl_AUTO_CONNECT_FAILED__0;
getLogWriterI18n().error(autoConnectFailed, ex.getMessage());
this.stop();
throw new StartupException(new AdminException(autoConnectFailed.toLocalizedString(new Object[] { ex.getMessage() }), ex));
}
} // getAutoConnect
this.logWriter.info(LocalizedStrings.AgentImpl_GEMFIRE_JMX_AGENT_IS_RUNNING);
this.logWriter.startupComplete();
if (memberInfoWithStatsMBean == null) {
initializeHelperMbean();
}
}
/**
* Deregisters everything this Agent registered and releases the MBeanServer.
*/
public void stop() {
try {
this.logWriter.info(LocalizedStrings.AgentImpl_STOPPING_JMX_AGENT);
this.logWriter.shuttingDown();
// stop the GemFire Distributed System
stopDistributedSystem();
// stop all JMX Adaptors and Connectors...
stopHttpAdaptor();
stopRMIConnectorServer();
memberInfoWithStatsMBean = null;
stopSnmpAdaptor();
// release the MBeanServer for cleanup...
MBeanUtil.stop();
mBeanServer = null;
// remove the register shutdown hook which disconnects the Agent from the Distributed System upon JVM shutdown
removeShutdownHook();
this.logWriter.info(LocalizedStrings.AgentImpl_AGENT_HAS_STOPPED);
}
finally {
if (this.logFileOutputStream != null) {
this.logWriter.closingLogFile();
IOUtils.close(this.logFileOutputStream);
}
LogWriterImpl.cleanUpThreadGroups(); // bug35388 - logwriters accumulate, causing mem leak
}
}
private void stopDistributedSystem() {
// disconnect from the distributed system...
try {
disconnectFromSystem();
}
catch (Exception e) {
// disconnectFromSystem already prints any Exceptions
}
catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
}
}
public ObjectName manageDistributedSystem()
throws MalformedObjectNameException {
synchronized (CONN_SYNC) {
if (isConnected()) {
return ((AdminDistributedSystemJmxImpl) this.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
*/
@SuppressFBWarnings(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) this.system).getObjectName();
}
DistributionManager.isDedicatedAdminVM = isGemFireXD ? false : true;
AdminDistributedSystemJmxImpl systemJmx =
(AdminDistributedSystemJmxImpl) this.system;
if (systemJmx == null) {
systemJmx = (AdminDistributedSystemJmxImpl)
createDistributedSystem(this.agentConfig);
this.system = systemJmx;
}
systemJmx.connect(getLogWriterI18n());
return new ObjectName(systemJmx.getMBeanName());
} catch (AdminException e) {
logWriter.warning(e);
throw e;
} catch (RuntimeException e) {
logWriter.warning(e);
throw e;
} catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
logWriter.error(e);
throw e;
}
}
}
/**
* Disconnects from the current DistributedSystem (if connected to one).
*/
@SuppressFBWarnings(value="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", justification="This is only a style warning.")
public void disconnectFromSystem() {
synchronized(CONN_SYNC) {
try {
if (this.system == null || !this.system.isConnected()) {
return;
}
((AdminDistributedSystemJmxImpl)this.system).disconnect();
// this.system = null;
} catch (RuntimeException e) {
logWriter.warning(e);
throw e;
} catch (Error e) {
if (SystemFailure.isJVMFailureError(e)) {
SystemFailure.initiateFailure(e);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw e;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
logWriter.warning(e);
throw e;
} finally {
DistributionManager.isDedicatedAdminVM = false;
}
}
}
/**
* Retrieves a displayable snapshot of this Agent's log.
*
* @return snapshot of the current log
*/
public String getLog() {
String childTail = tailFile(this.logWriter.getChildLogFile());
String mainTail = tailFile(new File(this.agentConfig.getLogFile()));
if (childTail == null && mainTail == null) {
return LocalizedStrings.AgentImpl_NO_LOG_FILE_CONFIGURED_LOG_MESSAGES_WILL_BE_DIRECTED_TO_STDOUT.toLocalizedString();
} else {
StringBuffer result = new StringBuffer();
if (mainTail != null) {
result.append(mainTail);
}
if (childTail != null) {
result.append("\n" + LocalizedStrings.AgentImpl_TAIL_OF_CHILD_LOG.toLocalizedString() + "\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. */
public boolean isConnected() {
boolean result = false;
synchronized (CONN_SYNC) {
result = ((this.system != null) && this.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 this.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(LocalizedStrings.AgentImpl_THE_FILE_0_IS_A_DIRECTORY.toLocalizedString(f));
}
File parent = f.getParentFile();
if (parent != null) {
if (!parent.isDirectory()) {
throw new IllegalArgumentException(LocalizedStrings.AgentImpl_THE_DIRECTORY_0_DOES_NOT_EXIST.toLocalizedString(parent));
}
}
this.propertyFile = f.getPath();
}
/**
* Gets the mcastAddress of the distributed system that this Agent is
* managing.
*
* @return The mcastAddress value
*/
public String getMcastAddress() {
return this.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) {
this.agentConfig.setMcastAddress(mcastAddress);
}
/**
* Gets the mcastPort of the distributed system that this Agent is managing.
*
* @return The mcastPort value
*/
public int getMcastPort() {
return this.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) {
this.agentConfig.setMcastPort(mcastPort);
}
/**
* Gets the locators of the distributed system that this Agent is managing.
*
* Format is a comma-delimited list of "host[port]" entries.
*
* @return The locators value
*/
public String getLocators() {
return this.agentConfig.getLocators();
}
/**
* Sets the locators of the distributed system that this Agent is managing.
*
* Format is a comma-delimited list of "host[port]" entries.
*
* @param locators The new locators value
*/
public void setLocators(String locators) {
this.agentConfig.setLocators(locators);
}
/**
* Gets the membership UDP port range in the distributed system that this
* Agent is monitoring.
*
* This range is given as two numbers separated by a minus sign like "min-max"
*
* @return membership UDP port range
*/
public String getMembershipPortRange() {
return this.agentConfig.getMembershipPortRange();
}
/**
* Sets the membership UDP port range in the distributed system that this
* Agent is monitoring.
*
* 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) {
this.agentConfig.setMembershipPortRange(membershipPortRange);
}
/**
* Gets the bindAddress of the distributed system that this Agent is managing.
*
* @return The bindAddress value
*/
public String getBindAddress() {
return this.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) {
this.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 this.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) {
this.agentConfig.setRemoteCommand(remoteCommand);
}
/** Returns the system identity for the DistributedSystem */
public String getSystemId() {
return this.agentConfig.getSystemId();
}
/** Sets the system identity for the DistributedSystem */
public void setSystemId(String systemId) {
this.agentConfig.setSystemId(systemId);
}
/**
* Gets the logFileSizeLimit in megabytes of this Agent. Zero indicates no
* limit.
*
* @return The logFileSizeLimit value
*/
public int getLogFileSizeLimit() {
return this.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) {
this.agentConfig.setLogFileSizeLimit(logFileSizeLimit);
this.logWriter.configChanged();
}
/**
* Gets the logDiskSpaceLimit in megabytes of this Agent. Zero indicates no
* limit.
*
* @return The logDiskSpaceLimit value
*/
public int getLogDiskSpaceLimit() {
return this.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) {
this.agentConfig.setLogDiskSpaceLimit(logDiskSpaceLimit);
this.logWriter.configChanged();
}
/**
* Gets the logFile name for this Agent to log to.
*
* @return The logFile value
*/
public String getLogFile() {
return this.agentConfig.getLogFile();
}
/**
* Sets the logFile name for this Agent to log to.
*
* @param logFile The new logFile value
*/
public void setLogFile(String logFile) {
this.agentConfig.setLogFile(logFile);
this.logWriter.configChanged();
}
/**
* Gets the logLevel of this Agent.
*
* @return The logLevel value
*/
public String getLogLevel() {
return this.agentConfig.getLogLevel();
}
/**
* Sets the logLevel of this Agent.
*
* @param logLevel The new logLevel value
*/
public void setLogLevel(String logLevel) {
this.agentConfig.setLogLevel(logLevel);
this.logWriter.configChanged();
}
/** Returns true if the Agent is set to auto connect to a system. */
public boolean getAutoConnect() {
return this.agentConfig.getAutoConnect();
}
/** Returns true if the Agent is set to auto connect to a system. */
public boolean isAutoConnect() {
return this.agentConfig.getAutoConnect();
}
/** Sets or unsets the option to auto connect to a system. */
public void setAutoConnect(boolean v) {
this.agentConfig.setAutoConnect(v);
}
/**
* Returns the address (URL) on which the RMI connector server runs
* or null
if the RMI connector server has not been
* started. This method is used primarily for testing purposes.
*
* @see JMXConnectorServer#getAddress()
*/
public JMXServiceURL getRMIAddress() {
if (this.rmiConnector != null) {
return this.rmiConnector.getAddress();
} else {
return null;
}
}
/**
* Gets the configuration for this Agent.
*
* @return the configuration for this Agent
*/
protected AgentConfig getAgentConfig() {
return this.agentConfig;
}
// -------------------------------------------------------------------------
// Internal implementation methods
// -------------------------------------------------------------------------
/** Returns the tail of the system log specified by File
. */
private String tailFile(File f) {
try {
return TailLogResponse.tailSystemLog(f);
}
catch (IOException ex) {
return LocalizedStrings.AgentImpl_COULD_NOT_TAIL_0_BECAUSE_1.toLocalizedString(new Object[] {f, ex});
}
}
/**
* Returns the active MBeanServer which has any GemFire MBeans registered.
*
* @return the GemFire mbeanServer
*/
public MBeanServer getMBeanServer() {
return mBeanServer;
}
// /**
// * Returns the active modeler Registry which has been initialized with all
// * the ModelMBean descriptors needed for GemFire MBeans.
// *
// * @return the modeler registry
// */
// private Registry getRegistry() {
// return MBeanUtil.getRegistry();
// }
/**
* Gets the current instance of LogWriter for logging
* NOTE: Since 6.0 use {@link AgentImpl#getLogWriterI18n()} instead.
*
* @return the logWriter
*/
public LogWriter getLogWriter() {
return this.logWriter;
}
private final Thread shutdownHook =
new Thread(LogWriterImpl.createThreadGroup(
"Shutdown", (LogWriterI18n) null), "Shutdown") {
@Override
public void run() {
disconnectFromSystem();
}
};
/**
* Gets the current instance of LogWriterI18n for logging
*
* @return the logWriter
* @since 6.0
*/
LogWriterI18n getLogWriterI18n() {
return this.logWriter;
}
/**
* Adds a ShutdownHook to the Agent for cleaning up any resources
*/
private void addShutdownHook() {
if( ! Boolean.getBoolean( com.gemstone.gemfire.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) {
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
}
private void removeShutdownHook() {
if( ! Boolean.getBoolean( com.gemstone.gemfire.distributed.internal.InternalDistributedSystem.DISABLE_SHUTDOWN_HOOK_PROPERTY)) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
}
}
/**
* Creates a LogWriterI18n for this Agent to use in logging.
*/
@SuppressFBWarnings(value="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", justification="Return value for file delete is not important here.")
private void initLogWriter() throws com.gemstone.gemfire.admin.AdminException {
LogWriterI18n acLogger = this.agentConfig.getLogWriter();
// If a logger exists use it
if ((acLogger != null) && acLogger instanceof ManagerLogWriter) {
this.logConfig = new LogConfigImpl();
this.logWriter = (ManagerLogWriter) acLogger;
this.logWriter.setConfig(this.logConfig);
// write all config props to the log...
// check if config enabled, for all 3 calls prior to logging
if (this.logWriter.configEnabled()) {
this.logWriter.config(LocalizedStrings.AgentImpl_AGENT_CONFIG_PROPERTY_FILE_NAME_0.toLocalizedString(AgentConfigImpl.retrievePropertyFile()));
this.logWriter.config(this.agentConfig.getPropertyFileDescription());
this.logWriter.config(this.agentConfig.toPropertiesAsString());
}
return;
}
final PrintStream out;
if (this.agentConfig.getLogFile() == null
|| this.agentConfig.getLogFile().length() == 0) {
out = System.out;
} else {
try {
File f = new File(this.agentConfig.getLogFile());
// attempt to delete it and then append if the delete failed
f.delete();
this.logFileOutputStream = new FileOutputStream(f, true);
out = new PrintStream(this.logFileOutputStream);
} catch (IOException ex) {
try {
this.logFileOutputStream.close();
} catch (Exception ignore) {
// ignore... just trying to clean up if possible
}
throw new AdminException(LocalizedStrings.AgentImpl_COULD_NOT_OPEN_LOG_FILE_0.toLocalizedString(this.agentConfig.getLogFile()), ex);
}
}
this.logConfig = new LogConfigImpl();
this.logWriter = new ManagerLogWriter(this.logConfig.getLogLevel(), out);
this.logWriter.setConfig(this.logConfig);
// Set this log writer in AgentConfig
this.agentConfig.setLogWriter(this.logWriter);
// Print Banner information
this.logWriter.info(Banner.getString(this.agentConfig.getOriginalArgs()));
// write all config props to the log...
// check if config enabled, for all 3 calls prior to logging
if (this.logWriter.configEnabled()) {
this.logWriter.config(LocalizedStrings.AgentImpl_AGENT_CONFIG_PROPERTY_FILE_NAME_0.toLocalizedString(AgentConfigImpl.retrievePropertyFile()));
this.logWriter.config(this.agentConfig.getPropertyFileDescription());
this.logWriter.config(this.agentConfig.toPropertiesAsString());
}
}
/**
* Stops the HttpAdaptor and its XsltProcessor. Unregisters the associated
* MBeans.
*/
private void stopHttpAdaptor() {
if (!this.agentConfig.isHttpEnabled()) return;
// stop the adaptor...
try {
this.httpAdaptor.stop();
} catch (Exception e) {
logWriter.warning(e);
}
try {
MBeanUtil.unregisterMBean(getHttpAdaptorName());
MBeanUtil.unregisterMBean(getXsltProcessorName());
} catch (MalformedObjectNameException e) {
logWriter.warning(e);
}
}
/** Stops the RMIConnectorServer and unregisters its MBean. */
private void stopRMIConnectorServer() {
if (!this.agentConfig.isRmiEnabled()) return;
// stop the RMI Connector server...
try {
this.rmiConnector.stop();
} catch (Exception e) {
logWriter.warning(e);
}
try {
ObjectName rmiRegistryNamingName = getRMIRegistryNamingName();
if (this.agentConfig.isRmiRegistryEnabled() &&
mBeanServer.isRegistered(rmiRegistryNamingName)) {
String[] empty = new String[0];
mBeanServer.invoke(rmiRegistryNamingName, "stop", empty, empty);
MBeanUtil.unregisterMBean(rmiRegistryNamingName);
}
} catch (MalformedObjectNameException e) {
logWriter.warning(e);
} catch (InstanceNotFoundException e) {
logWriter.warning(e);
} catch (ReflectionException e) {
logWriter.warning(e);
} catch (MBeanException e) {
logWriter.warning(e);
}
try {
ObjectName rmiConnectorServerName = getRMIConnectorServerName();
if (mBeanServer.isRegistered(rmiConnectorServerName)) {
MBeanUtil.unregisterMBean(rmiConnectorServerName);
}
} catch (MalformedObjectNameException e) {
logWriter.warning(e);
}
}
/** Stops the SnmpAdaptor and unregisters its MBean. */
private void stopSnmpAdaptor() {
if (!this.agentConfig.isSnmpEnabled()) return;
// stop the SnmpAdaptor...
try {
getMBeanServer().invoke(getSnmpAdaptorName(), "unbind",
new Object[0],
new String[0]);
} catch (Exception e) {
logWriter.warning(e);
}
try {
MBeanUtil.unregisterMBean(getSnmpAdaptorName());
} catch (MalformedObjectNameException e) {
logWriter.warning(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 DistributedSystem
.
*
* @param config
*/
protected AdminDistributedSystem createDistributedSystem(AgentConfig config)
throws com.gemstone.gemfire.admin.AdminException {
return new AdminDistributedSystemJmxImpl(config);
}
// -------------------------------------------------------------------------
// Agent main
// -------------------------------------------------------------------------
/**
* Command-line main for running the GemFire Management Agent.
*
* Accepts command-line arguments matching the options in {@link AgentConfig}
* and {@link com.gemstone.gemfire.admin.DistributedSystemConfig}.
*
* AgentConfig
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(LocalizedStrings.AgentImpl_FAILED_READING_CONFIGURATION_0.toLocalizedString(ex));
System.exit(1);
return;
}
try {
Agent agent = AgentFactory.getAgent(ac);
agent.start();
}
catch (Throwable t) {
Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
System.exit(1);
}
}
// -------------------------------------------------------------------------
// MX4J Connectors/Adaptors
// -------------------------------------------------------------------------
private void createRMIRegistry() throws Exception {
if (!this.agentConfig.isRmiRegistryEnabled()) {
return;
}
MBeanServer mbs = getMBeanServer();
String host = this.agentConfig.getRmiBindAddress();
int port = this.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) {
this.logWriter.info(LocalizedStrings.AgentImpl_0__IS_ALREADY_REGISTERED,
registryName);
}
mbs.invoke(registryName, "start", null, null);
}
/**
* Defines and starts the JMX RMIConnector and service.
*
* If {@link AgentConfig#isRmiEnabled} returns false, then this adaptor will
* not be started.
*/
private void startRMIConnectorServer() {
if (!this.agentConfig.isRmiEnabled()) return;
String rmiBindAddress = this.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);
this.logWriter.info(LocalizedStrings.AgentImpl_SETTING_0,
rmiStubServerNameKey + " = " + rmiBindAddress);
}
try {
createRMIRegistry();
ObjectName objName = getRMIConnectorServerName();
// make sure this adaptor is not already registered...
if (getMBeanServer().isRegistered(objName)) {
// dunno how we got here...
this.logWriter.info(LocalizedStrings.AgentImpl_RMICONNECTORSERVER_ALREADY_REGISTERED_AS__0, 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://:
*/
String urlString = null;
String connectorServerHost = "";
int connectorServerPort = this.agentConfig.getRmiServerPort();
String rmiRegistryHost = "";
int rmiRegistryPort = this.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);
this.logWriter.fine("JMX Service URL string is : \""+urlString+"\"");
// The address of the connector
JMXServiceURL url = new JMXServiceURL(urlString);
Map env = new HashMap();
// env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
// env.put(Context.PROVIDER_URL, "rmi://localhost:1099");
RMIServerSocketFactory ssf = new MX4JServerSocketFactory(
this.agentConfig.isAgentSSLEnabled(), // true,
this.agentConfig.isAgentSSLRequireAuth(), // true,
this.agentConfig.getAgentSSLProtocols(), // "any",
this.agentConfig.getAgentSSLCiphers(), // "any",
this.agentConfig.getRmiBindAddress(),
10, // backlog
getLogWriterI18n(),
this.agentConfig.getGfSecurityProperties());
env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE, ssf);
if (this.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
this.rmiConnector =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
// for cleanup
this.rmiConnector.addNotificationListener(
new ConnectionNotificationAdapter(),
new ConnectionNotificationFilterImpl(),
this);
// Register the JMXConnectorServer in the MBeanServer
getMBeanServer().registerMBean(this.rmiConnector, objName);
// Start the JMXConnectorServer
this.rmiConnector.start();
} catch (Throwable t) {
Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
this.logWriter.error(LocalizedStrings.AgentImpl_FAILED_TO_START_RMICONNECTORSERVER, t);
throw new StartupException(LocalizedStrings.AgentImpl_FAILED_TO_START_RMI_SERVICE.toLocalizedString(), t);
}
}
/**
* Starts the optional third-party AdventNet SNMP Adaptor.
*
* If {@link AgentConfig#isSnmpEnabled} returns false, then this adaptor will
* not be started.
*/
private void startSnmpAdaptor() {
if (!this.agentConfig.isSnmpEnabled()) return;
try {
ObjectName objName = getSnmpAdaptorName();
// make sure this adaptor is not already registered...
if (getMBeanServer().isRegistered(objName)) {
// dunno how we got here...
getLogWriterI18n().info(LocalizedStrings.AgentImpl_SNMPADAPTOR_ALREADY_REGISTERED_AS__0, objName);
return;
}
String className = "com.adventnet.adaptors.snmp.snmpsupport.SmartSnmpAdaptor";
String snmpDir = this.agentConfig.getSnmpDirectory();
// ex:/merry2/users/klund/agent
// validate the directory...
if (snmpDir == null || snmpDir.length() == 0) {
throw new IllegalArgumentException(LocalizedStrings.AgentImpl_SNMPDIRECTORY_MUST_BE_SPECIFIED_BECAUSE_SNMP_IS_ENABLED.toLocalizedString());
}
File root = new File(snmpDir);
if (!root.exists()) {
throw new IllegalArgumentException(LocalizedStrings.AgentImpl_SNMPDIRECTORY_DOES_NOT_EXIST.toLocalizedString());
}
// create the adaptor...
String[] sigs = new String[] { "java.lang.String" };
Object[] args = new Object[] { snmpDir };
String bindAddress = this.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(Throwable t) {
Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
this.logWriter.error(LocalizedStrings.AgentImpl_FAILED_TO_START_SNMPADAPTOR__0, t.getMessage());
throw new StartupException(LocalizedStrings.AgentImpl_FAILED_TO_START_SNMPADAPTOR__0.toLocalizedString(t.getMessage()), t);
}
}
/**
* Defines and starts the JMX Http Adaptor service from MX4J.
*
* If {@link AgentConfig#isHttpEnabled} returns false, then this adaptor will
* not be started.
*/
private void startHttpAdaptor() {
if (!this.agentConfig.isHttpEnabled()) return;
try {
ObjectName objName = getHttpAdaptorName();
// make sure this adaptor is not already registered...
if (getMBeanServer().isRegistered(objName)) {
// dunno how we got here...
getLogWriterI18n().info(LocalizedStrings.AgentImpl_HTTPADAPTOR_ALREADY_REGISTERED_AS__0, objName);
return;
}
this.httpAdaptor = new HttpAdaptor();
// validate and set host and port values...
if (this.agentConfig.getHttpPort() > 0) {
this.httpAdaptor.setPort(this.agentConfig.getHttpPort());
getLogWriterI18n().config(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_PORT__0, this.agentConfig.getHttpPort());
}
else {
getLogWriterI18n().error(LocalizedStrings.AgentImpl_INCORRECT_PORT_VALUE__0, this.agentConfig.getHttpPort());
}
if (this.agentConfig.getHttpBindAddress() != null) {
String host = this.agentConfig.getHttpBindAddress();
getLogWriterI18n().config(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_ADDRESS__0, host);
this.httpAdaptor.setHost(host);
}
else {
getLogWriterI18n().error(LocalizedStrings.AgentImpl_INCORRECT_NULL_HOSTNAME);
}
// SSL support...
MX4JServerSocketFactory socketFactory =
new MX4JServerSocketFactory(
this.agentConfig.isAgentSSLEnabled(),
this.agentConfig.isHttpSSLRequireAuth(),
this.agentConfig.getAgentSSLProtocols(),
this.agentConfig.getAgentSSLCiphers(),
getLogWriterI18n(),
this.agentConfig.getGfSecurityProperties());
this.httpAdaptor.setSocketFactory(socketFactory);
// authentication (user login) support...
if (this.agentConfig.isHttpAuthEnabled()) {
// this pops up a login dialog from the browser...
this.httpAdaptor.setAuthenticationMethod(
MX4J_HTTPADAPTOR_BASIC_AUTHENTICATION); // only basic works
this.httpAdaptor.addAuthorization(
this.agentConfig.getHttpAuthUser(),
this.agentConfig.getHttpAuthPassword());
}
// add the XsltProcessor...
this.httpAdaptor.setProcessorName(createXsltProcessor());
// register the HttpAdaptor and snap on the XsltProcessor...
getMBeanServer().registerMBean(this.httpAdaptor, objName);
this.httpAdaptor.start();
} catch (Throwable t) {
Error err;
if (t instanceof Error && SystemFailure.isJVMFailureError(
err = (Error)t)) {
SystemFailure.initiateFailure(err);
// If this ever returns, rethrow the error. We're poisoned
// now, so don't let this thread continue.
throw err;
}
// Whenever you catch Error or Throwable, you must also
// check for fatal JVM error (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();
this.logWriter.error(LocalizedStrings.AgentImpl_FAILED_TO_START_HTTPADAPTOR__0, t.getMessage());
throw new StartupException(LocalizedStrings.AgentImpl_FAILED_TO_START_HTTPADAPTOR__0.toLocalizedString(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...
getLogWriterI18n().info(LocalizedStrings.AgentImpl_XSLTPROCESSOR_ALREADY_REGISTERED_AS__0, objName);
return objName;
}
getMBeanServer().registerMBean(
new mx4j.tools.adaptor.http.XSLTProcessor(), objName);
return objName;
}
// -------------------------------------------------------------------------
// Private support methods...
// -------------------------------------------------------------------------
// /** Not used anymore but seems moderately useful... */
// private String[] parseSSLCiphers(String ciphers) {
// List list = new ArrayList();
// StringTokenizer st = new StringTokenizer(ciphers);
// while (st.hasMoreTokens()) {
// list.add(st.nextToken());
// }
// return (String[]) list.toArray(new String[list.size()]);
// }
// -------------------------------------------------------------------------
// Inner class for configuration of logging
// -------------------------------------------------------------------------
/**
* Used by our {@link ManagerLogWriter} instance to access the Agent's config.
*/
protected class LogConfigImpl implements ManagerLogWriter.LogConfig {
public int getLogLevel() {
return LogWriterImpl.levelNameToCode(AgentImpl.this.getAgentConfig().getLogLevel());
}
public File getLogFile() {
return new File(AgentImpl.this.getAgentConfig().getLogFile());
}
public int getLogFileSizeLimit() {
return AgentImpl.this.getAgentConfig().getLogFileSizeLimit();
}
public int getLogDiskSpaceLimit() {
return AgentImpl.this.getAgentConfig().getLogDiskSpaceLimit();
}
}
// -------------------------------------------------------------------------
// SSL configuration for GemFire
// -------------------------------------------------------------------------
public boolean isSSLEnabled() {
return this.agentConfig.isSSLEnabled();
}
public void setSSLEnabled(boolean enabled) {
this.agentConfig.setSSLEnabled(enabled);
}
public String getSSLProtocols() {
return this.agentConfig.getSSLProtocols();
}
public void setSSLProtocols(String protocols) {
this.agentConfig.setSSLProtocols(protocols);
}
public String getSSLCiphers() {
return this.agentConfig.getSSLCiphers();
}
public void setSSLCiphers(String ciphers) {
this.agentConfig.setSSLCiphers(ciphers);
}
public boolean isSSLAuthenticationRequired() {
return this.agentConfig.isSSLAuthenticationRequired();
}
public void setSSLAuthenticationRequired(boolean authRequired) {
this.agentConfig.setSSLAuthenticationRequired(authRequired);
}
public Properties getSSLProperties() {
return this.agentConfig.getSSLProperties();
}
public void setSSLProperties(Properties sslProperties) {
this.agentConfig.setSSLProperties(sslProperties);
}
public void addSSLProperty(String key, String value) {
this.agentConfig.addSSLProperty(key, value);
}
public void removeSSLProperty(String key) {
this.agentConfig.removeSSLProperty(key);
}
// -------------------------------------------------------------------------
// ManagedResource implementation
// -------------------------------------------------------------------------
public String getMBeanName() {
return this.mbeanName;
}
public ModelMBean getModelMBean() {
return this.modelMBean;
}
public void setModelMBean(ModelMBean modelMBean) {
this.modelMBean = modelMBean;
}
public ObjectName getObjectName() {
return this.objectName;
}
public ManagedResourceType getManagedResourceType() {
return ManagedResourceType.AGENT;
}
public void cleanupResource() {}
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 6.0
*/
void updateRmiClientsCount() {
int noOfClientsConnected = 0;
String[] connectionIds = this.rmiConnector.getConnectionIds();
if (connectionIds != null) {
noOfClientsConnected = connectionIds.length;
}
logWriter.info("No. of RMI clients connected :: "+noOfClientsConnected);
AdminDistributedSystemJmxImpl adminDSJmx =
(AdminDistributedSystemJmxImpl) this.system;
adminDSJmx.setRmiClientCountZero(noOfClientsConnected == 0);
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("AgentImpl[");
sb.append("config=" + agentConfig.toProperties().toString());
// sb.append("; adaptor=" + httpAdaptor.toString());
sb.append("; mbeanName=" + mbeanName);
sb.append("; modelMBean=" + modelMBean);
sb.append("; objectName=" + objectName);
sb.append("; propertyFile=" + propertyFile);
sb.append(": rmiConnector=" + rmiConnector);
// sb.append("; system=" + system);)
sb.append("]");
return sb.toString();
}
/** 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
*
* @author abhishek
* @since 6.0
*/
class ConnectionNotificationAdapter implements NotificationListener {
/**
* 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)
*/
@SuppressFBWarnings(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;
LogWriterI18n logWriter = agent.getLogWriterI18n();
logWriter.fine("Connection notification for connection id : '" +
jmxNotifn.getConnectionId() + "'");
agent.updateRmiClientsCount();
}
}
}
/**
* Filters out the notifications of the type JMXConnectionNotification.OPENED,
* JMXConnectionNotification.CLOSED and JMXConnectionNotification.FAILED.
*
* @author abhishek
* @since 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.
*/
public boolean isNotificationEnabled(Notification notification) {
boolean isThisNotificationEnabled = false;
if (notification.getType().equals(JMXConnectionNotification.OPENED) ||
notification.getType().equals(JMXConnectionNotification.CLOSED) ||
notification.getType().equals(JMXConnectionNotification.FAILED) ) {
isThisNotificationEnabled = true;
}
return isThisNotificationEnabled;
}
}