http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/c0221bed/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentImpl.java
----------------------------------------------------------------------
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentImpl.java
b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentImpl.java
new file mode 100644
index 0000000..65f2901
--- /dev/null
+++
b/geode-core/src/main/java/org/apache/geode/internal/admin/api/jmx/impl/AgentImpl.java
@@ -0,0 +1,1618 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.geode.internal.admin.api.jmx.impl;
+
+import java.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.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 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.internal.admin.api.AdminDistributedSystem;
+import org.apache.geode.internal.admin.api.AdminException;
+import org.apache.geode.internal.admin.api.DistributedSystemConfig;
+import org.apache.geode.internal.admin.api.jmx.Agent;
+import org.apache.geode.internal.admin.api.jmx.AgentConfig;
+import org.apache.geode.internal.admin.api.jmx.AgentFactory;
+import org.apache.geode.distributed.internal.DistributionManager;
+import org.apache.geode.i18n.StringId;
+import org.apache.geode.internal.Banner;
+import org.apache.geode.internal.GemFireVersion;
+import org.apache.geode.internal.admin.remote.TailLogResponse;
+import org.apache.geode.internal.i18n.LocalizedStrings;
+import org.apache.geode.internal.logging.InternalLogWriter;
+import org.apache.geode.internal.logging.LogConfig;
+import org.apache.geode.internal.logging.LogService;
+import org.apache.geode.internal.logging.LogWriterFactory;
+import org.apache.geode.internal.logging.LoggingThreadGroup;
+import org.apache.geode.internal.logging.log4j.AlertAppender;
+import org.apache.geode.internal.logging.log4j.LocalizedMessage;
+import org.apache.geode.internal.logging.log4j.LogMarker;
+import org.apache.geode.internal.logging.log4j.LogWriterAppender;
+import org.apache.geode.internal.logging.log4j.LogWriterAppenders;
+
+/**
+ * The GemFire JMX Agent provides the ability to administrate one GemFire
+ * distributed system via JMX.
+ *
+ * @since GemFire 3.5
+ */
+public class AgentImpl
+implements Agent, ManagedResource {
+
+ 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 LogWriterAppender logWriterAppender;
+ 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 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 AdminException TODO-javadocs
+ * @throws IllegalArgumentException if agentConfig is null
+ */
+ public AgentImpl(AgentConfigImpl 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());
+
+ // LOG: create LogWriterAppender and LogWriterLogger
+ 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) {
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN),
e);
+ } catch (MBeanRegistrationException e) {
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_FAILED_TO_INITIALIZE_MEMBERINFOWITHSTATSMBEAN),
e);
+ } catch (AdminException e) {
+
logger.info(LocalizedMessage.create(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) {
+ AlertAppender.getInstance().shuttingDown();
+ LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
+ LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
+ throw e;
+ }
+
+ try {
+ startRMIConnectorServer();
+ } catch (StartupException e) {
+ stopHttpAdaptor();
+ AlertAppender.getInstance().shuttingDown();
+ LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
+ LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
+ throw e;
+ }
+
+ try {
+ startSnmpAdaptor();
+ } catch (StartupException e) {
+ stopRMIConnectorServer();
+ stopHttpAdaptor();
+ AlertAppender.getInstance().shuttingDown();
+ LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
+ LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
+ 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) {
+
logger.error(LocalizedMessage.create(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;
+ logger.error(LocalizedMessage.create(autoConnectFailed,
ex.getMessage()));
+ this.stop();
+ throw new StartupException(new
AdminException(autoConnectFailed.toLocalizedString(new Object[] {
ex.getMessage() }), ex));
+ }
+ } // getAutoConnect
+
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_GEMFIRE_JMX_AGENT_IS_RUNNING));
+ LogWriterAppenders.startupComplete(LogWriterAppenders.Identifier.MAIN);
+
+ if (memberInfoWithStatsMBean == null) {
+ initializeHelperMbean();
+ }
+ }
+
+ /**
+ * Deregisters everything this Agent registered and releases the MBeanServer.
+ */
+ public void stop() {
+ try {
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_STOPPING_JMX_AGENT));
+ AlertAppender.getInstance().shuttingDown();
+ LogWriterAppenders.stop(LogWriterAppenders.Identifier.MAIN);
+
+ // 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();
+
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_AGENT_HAS_STOPPED));
+ }
+ finally {
+ LogWriterAppenders.destroy(LogWriterAppenders.Identifier.MAIN);
+ LoggingThreadGroup.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 (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();
+ }
+ }
+
+ 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
+ */
+
@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) this.system).getObjectName();
+ }
+
+ DistributionManager.isDedicatedAdminVM = true;
+
+ AdminDistributedSystemJmxImpl systemJmx =
+ (AdminDistributedSystemJmxImpl) this.system;
+ if (systemJmx == null) {
+ systemJmx = (AdminDistributedSystemJmxImpl)
+ createDistributedSystem(this.agentConfig);
+ this.system = systemJmx;
+ }
+ systemJmx.connect(this.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).
+ */
+
@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 (this.system == null || !this.system.isConnected()) {
+ return;
+ }
+ ((AdminDistributedSystemJmxImpl)this.system).disconnect();
+// this.system = null;
+ } 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 {
+ 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.logWriterAppender.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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <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 this.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) {
+ 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);
+ LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
+ }
+
+ /**
+ * 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);
+ LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
+ }
+
+ /**
+ * 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);
+ LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
+ }
+
+ /**
+ * 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);
+ LogWriterAppenders.configChanged(LogWriterAppenders.Identifier.MAIN);
+ }
+
+ /** 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 <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 (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 <code>File</code>. */
+ 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
+ *
+ * @return the logWriter
+ */
+ public LogWriter getLogWriter() {
+ return this.logWriter;
+ }
+
+ private final Thread shutdownHook =
+ new Thread(LoggingThreadGroup.createThreadGroup("Shutdown"), "Shutdown") {
+ @Override
+ public void run() {
+ disconnectFromSystem();
+ }
+ };
+
+ /**
+ * 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 AdminException {
+ final LogConfig logConfig = this.agentConfig.createLogConfig();
+
+ // LOG: create logWriterAppender here
+ this.logWriterAppender =
LogWriterAppenders.getOrCreateAppender(LogWriterAppenders.Identifier.MAIN,
false, logConfig, false);
+
+ // LOG: look in AgentConfigImpl for existing LogWriter to use
+ InternalLogWriter existingLogWriter =
this.agentConfig.getInternalLogWriter();
+ if (existingLogWriter != null) {
+ this.logWriter = existingLogWriter;
+ } else {
+ // LOG: create LogWriterLogger
+ this.logWriter = LogWriterFactory.createLogWriterLogger(false, false,
logConfig, false);
+ // LOG: changed statement from config to info
+ this.logWriter.info(Banner.getString(null));
+ // Set this log writer in AgentConfigImpl
+ this.agentConfig.setInternalLogWriter(this.logWriter);
+ }
+
+ // LOG: create logWriter here
+ this.logWriter = LogWriterFactory.createLogWriterLogger(false, false,
logConfig, false);
+
+ // Set this log writer in AgentConfig
+ this.agentConfig.setInternalLogWriter(this.logWriter);
+
+ // Print Banner information
+ logger.info(Banner.getString(this.agentConfig.getOriginalArgs()));
+
+ // LOG:CONFIG: changed next three statements from config to info
+ logger.info(LogMarker.CONFIG,
LocalizedStrings.AgentImpl_AGENT_CONFIG_PROPERTY_FILE_NAME_0.toLocalizedString(AgentConfigImpl.retrievePropertyFile()));
+ logger.info(LogMarker.CONFIG,
this.agentConfig.getPropertyFileDescription());
+ logger.info(LogMarker.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) {
+ logger.warn(e.getMessage(), e);
+ }
+
+ try {
+ MBeanUtil.unregisterMBean(getHttpAdaptorName());
+ MBeanUtil.unregisterMBean(getXsltProcessorName());
+ } catch (MalformedObjectNameException e) {
+ logger.warn(e.getMessage(), 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) {
+ logger.warn(e.getMessage(), 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) {
+ 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)) {
+ MBeanUtil.unregisterMBean(rmiConnectorServerName);
+ }
+ } catch (MalformedObjectNameException e) {
+ logger.warn(e.getMessage(), 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) {
+ logger.warn(e.getMessage(), e);
+ }
+
+ try {
+ MBeanUtil.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>.
+ *
+ * @param config
+ */
+ private AdminDistributedSystem createDistributedSystem(AgentConfigImpl
config)
+ throws 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 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(LocalizedStrings.AgentImpl_FAILED_READING_CONFIGURATION_0.toLocalizedString(ex));
+ System.exit(1);
+ 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();
+ 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) {
+
logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_0__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 (!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);
+ logger.info(LocalizedMessage.create(LocalizedStrings.AgentImpl_SETTING_0,
+ new StringBuilder(rmiStubServerNameKey).append(" =
").append(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(LocalizedMessage.create(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://<rmi-binding-address>:<rmi-port><JNDI_NAME>
+ */
+ 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);
+
+ logger.debug("JMX Service URL string is : \"{}\"", urlString);
+
+ // The address of the connector
+ JMXServiceURL url = new JMXServiceURL(urlString);
+
+ Map<String, Object> env = new HashMap<String, Object>();
+// 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
+ 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 (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(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.
+ * <p>
+ * 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...
+
logger.info(LocalizedMessage.create(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 (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(LocalizedMessage.create(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.
+ * <p>
+ * 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...
+
logger.info(LocalizedMessage.create(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());
+ logger.info(LogMarker.CONFIG,
LocalizedMessage.create(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_PORT__0,
this.agentConfig.getHttpPort()));
+ }
+ else {
+
logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_INCORRECT_PORT_VALUE__0,
this.agentConfig.getHttpPort()));
+ }
+
+ if (this.agentConfig.getHttpBindAddress() != null) {
+ String host = this.agentConfig.getHttpBindAddress();
+ logger.info(LogMarker.CONFIG,
LocalizedMessage.create(LocalizedStrings.AgentImpl_HTTP_ADAPTOR_LISTENING_ON_ADDRESS__0,
host));
+ this.httpAdaptor.setHost(host);
+ }
+ else {
+
logger.error(LocalizedMessage.create(LocalizedStrings.AgentImpl_INCORRECT_NULL_HOSTNAME));
+ }
+
+ // SSL support...
+ MX4JServerSocketFactory socketFactory =
+ new MX4JServerSocketFactory(
+ this.agentConfig.isAgentSSLEnabled(),
+ this.agentConfig.isHttpSSLRequireAuth(),
+ this.agentConfig.getAgentSSLProtocols(),
+ this.agentConfig.getAgentSSLCiphers(),
+ 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 (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(LocalizedMessage.create(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...
+
logger.info(LocalizedMessage.create(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()]);
+// }
+
+ // -------------------------------------------------------------------------
+ // 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 GemFire 6.0
+ */
+ void updateRmiClientsCount() {
+ int noOfClientsConnected = 0;
+
+ String[] connectionIds = this.rmiConnector.getConnectionIds();
+
+ if (connectionIds != null) {
+ noOfClientsConnected = connectionIds.length;
+ }
+
+ logger.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
+ *
+ * @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)
+ */
+
@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.
+ */
+ 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;
+ }
+}
+