/*
* RHQ Management Platform
* Copyright (C) 2005-2008 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.enterprise.server.core.comm;
import java.io.File;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import mazz.i18n.Logger;
import org.rhq.core.util.obfuscation.ObfuscatedPreferences;
import org.rhq.enterprise.communications.ServiceContainerConfiguration;
import org.rhq.enterprise.communications.command.client.ClientCommandSenderConfiguration;
import org.rhq.enterprise.communications.command.client.PersistentFifo;
import org.rhq.enterprise.communications.command.client.RemoteInputStream;
/**
* Just provides some convienence methods to extract server configuration properties. The bulk of the server
* configuration really is just the {@link ServiceContainerConfiguration}, with some additional client-side type
* preferences to support {@link RemoteInputStream remote streaming}.
*
* @author John Mazzitelli
*/
public class ServerConfiguration {
/**
* Logger
*/
private static final Logger LOG = ServerI18NFactory.getLogger(ServerConfiguration.class);
/**
* The server configuration properties this object wraps. This should be the server preferences node.
*/
private final Preferences m_preferences;
/**
* Wraps a preferences object in this instance.
*
* @param prefs the configuration preferences
*
* @throws IllegalArgumentException if props is <code>null</code>
*/
public ServerConfiguration(Preferences prefs) {
if (prefs == null) {
throw new IllegalArgumentException("prefs=null");
}
m_preferences = new ObfuscatedPreferences(prefs, ServerConfigurationConstants.class);
}
/**
* Returns the raw preferences containing the server configuration.
*
* @return the server configuration preferences
*/
public Preferences getPreferences() {
return m_preferences;
}
/**
* Returns the service container configuration object that provides strongly typed methods to retrieve the
* server-side communications preferences.
*
* @return server-side communications preferences
*/
public ServiceContainerConfiguration getServiceContainerPreferences() {
return new ServiceContainerConfiguration(m_preferences);
}
/**
* Returns the version of the configuration schema.
*
* @return configuration version; if the configuration isn't versioned, 0 is returned
*/
public int getServerConfigurationVersion() {
int value = m_preferences.getInt(ServerConfigurationConstants.CONFIG_SCHEMA_VERSION, 0);
return value;
}
/**
* This tags the existing preferences by setting the configuration schema version preference appropriately.
*/
public void tagWithServerConfigurationVersion() {
m_preferences.putInt(ServerConfigurationConstants.CONFIG_SCHEMA_VERSION,
ServerConfigurationConstants.CURRENT_CONFIG_SCHEMA_VERSION);
}
/**
* Returns the data directory where all internally persisted data can be written to. If the data directory does not
* exist, it will be created. The data directory is the one that is defined in the
* {@link #getServiceContainerPreferences() service container configuration}. See
* {@link ServiceContainerConfiguration#getDataDirectory()}.
*
* <p>Because this does alittle extra work, it may not be suitable to call if you just want to get the value of the
* data directory or if you just want to know if its defined or not. In those instances, use
* {@link #getDataDirectoryIfDefined()} instead.</p>
*
* @return the data directory
*/
public File getDataDirectory() {
return getServiceContainerPreferences().getDataDirectory();
}
/**
* This will return the data directory string as found in the preferences. If the data directory is not defined in
* the preferences, <code>null</code> is returned. The data directory is the one that is defined in the
* {@link #getServiceContainerPreferences() service container configuration}. See
* {@link ServiceContainerConfiguration#getDataDirectoryIfDefined()}.
*
* @return the data directory string as defined in the preferences or <code>null</code> if it is not defined
*
* @see #getDataDirectory()
*/
public String getDataDirectoryIfDefined() {
return getServiceContainerPreferences().getDataDirectoryIfDefined();
}
/**
* This will return the directory name where the server stores all the files it can distribute to its agents. If
* this directory is not defined, <code>null</code> is returned. If <code>null</code> is returned, the server is not
* configured to distribute any files remotely.
*
* @return the directory string as defined in the preferences or <code>null</code> if it is not defined
*/
public String getAgentFilesDirectory() {
String dir_str = m_preferences.get(ServerConfigurationConstants.AGENT_FILES_DIRECTORY, null);
return dir_str;
}
/**
* Returns the client sender queue size which determines how many commands can be queued up for sending. If this is
* 0 or less, it means the queue is unbounded.
*
* @return queue size
*
* @see #getClientCommandSenderConfiguration()
*/
public int getClientSenderQueueSize() {
int value = m_preferences.getInt(ServerConfigurationConstants.CLIENT_SENDER_QUEUE_SIZE,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_QUEUE_SIZE);
return value;
}
/**
* Returns the maximum number of concurrent commands that the client sender will send at any one time.
*
* @return max concurrent value
*
* @see #getClientCommandSenderConfiguration()
*/
public int getClientSenderMaxConcurrent() {
int value = m_preferences.getInt(ServerConfigurationConstants.CLIENT_SENDER_MAX_CONCURRENT,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_MAX_CONCURRENT);
if (value < 1) {
LOG.warn(ServerI18NResourceKeys.PREF_MUST_BE_GREATER_THAN_0,
ServerConfigurationConstants.CLIENT_SENDER_MAX_CONCURRENT, value,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_MAX_CONCURRENT);
value = ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_MAX_CONCURRENT;
}
return value;
}
/**
* Returns the default timeout that the client sender will wait for a command to be processed by the remote
* endpoint. The timeout may be less than or equal to zero in which case the default will be to never timeout
* commands.
*
* @return timeout in milliseconds
*
* @see #getClientCommandSenderConfiguration()
*/
public long getClientSenderCommandTimeout() {
long value = m_preferences.getLong(ServerConfigurationConstants.CLIENT_SENDER_COMMAND_TIMEOUT,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_COMMAND_TIMEOUT);
return value;
}
/**
* Returns the time in milliseconds the client sender should wait in between retries of commands that have failed.
* This is a minimum but by no means is the limit before the command must be retried. The command may, in fact, be
* retried any amount of time after this retry interval.
*
* @return retry interval in milliseconds
*
* @see #getClientCommandSenderConfiguration()
*/
public long getClientSenderRetryInterval() {
long value = m_preferences.getLong(ServerConfigurationConstants.CLIENT_SENDER_RETRY_INTERVAL,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_RETRY_INTERVAL);
return value;
}
/**
* Returns the number of times a guaranteed message is retried, if it fails for a reason other than a "cannot
* connect" to server.
*
* @return maximum number of retry attempts that will be made to send a guaranteed delivery message when it fails
* for some reason other than being unable to communicate with the server.
*
* @see #getClientCommandSenderConfiguration()
*/
public int getClientSenderMaxRetries() {
int value = m_preferences.getInt(ServerConfigurationConstants.CLIENT_SENDER_MAX_RETRIES,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_MAX_RETRIES);
return value;
}
/**
* This will return the name of the command spool file (to be located in the
* {@link #getDataDirectory() data directory}). If this is not defined, <code>null</code> is returned to indicate
* that commands should not be spooled to disk (thus implicitly disabling guaranteed delivery).
*
* @return the command spool file name or <code>null</code> if it is not defined
*/
public String getClientSenderCommandSpoolFileName() {
String str = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_COMMAND_SPOOL_FILE_NAME,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_COMMAND_SPOOL_FILE_NAME);
if (str != null && str.length() == 0) {
str = null;
}
return str;
}
/**
* Returns an array of command spool file parameters. The first element of the array is the maximum file size
* threshold. The second element is the purge percentage. See {@link PersistentFifo} for the meanings of these
* settings.
*
* <p>Because this is a weakly typed method (i.e. you have to know what the elements in the returned array
* represent), it is recommended that you call {@link #getClientCommandSenderConfiguration()} because it will return
* all the configuration, including the spool file parameters, in a more strongly typed data object.</p>
*
* @return array of command spool file parameters
*
* @see #getClientCommandSenderConfiguration()
*/
public long[] getClientSenderCommandSpoolFileParams() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_COMMAND_SPOOL_FILE_PARAMS,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_COMMAND_SPOOL_FILE_PARAMS);
long[] ret_params = isClientSenderCommandSpoolFileParamsValueValid(value);
return ret_params;
}
/**
* Given a command spool file parameters value, will determine if its valid or not. If its valid, its individual
* parameter values are returned. If not valid, <code>null</code> is returned.
*
* @param pref_value the command spool file parameters value
*
* @return the individual parameters values or <code>null</code> if not valid
*/
public long[] isClientSenderCommandSpoolFileParamsValueValid(String pref_value) {
long[] ret_params = null;
try {
String[] numbers = pref_value.split("\\s*:\\s*");
if (numbers.length == 2) {
ret_params = new long[2];
ret_params[0] = Long.parseLong(numbers[0]);
ret_params[1] = Long.parseLong(numbers[1]);
if (ret_params[0] < 10000L) {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.COMMAND_SPOOL_INVALID_MAX_SIZE));
}
if ((ret_params[1] < 0L) || (ret_params[1] >= 100L)) {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.COMMAND_SPOOL_INVALID_PURGE_PERCENTAGE));
}
} else {
throw new NumberFormatException(LOG.getMsgString(ServerI18NResourceKeys.COMMAND_SPOOL_INVALID_FORMAT));
}
} catch (Exception e) {
ret_params = null;
LOG.warn(ServerI18NResourceKeys.BAD_COMMAND_SPOOL_PREF,
ServerConfigurationConstants.CLIENT_SENDER_COMMAND_SPOOL_FILE_PARAMS, pref_value, e);
}
return ret_params;
}
/**
* Returns the command spool file compression flag that, if true, indicates the data in the command spool file
* should be compressed.
*
* @return <code>true</code> if the command spool file should compress its data; <code>false</code> means the data
* should be stored in its uncompressed format.
*/
public boolean isClientSenderCommandSpoolFileCompressed() {
boolean flag = m_preferences.getBoolean(
ServerConfigurationConstants.CLIENT_SENDER_COMMAND_SPOOL_FILE_COMPRESSED,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_COMMAND_SPOOL_FILE_COMPRESSED);
return flag;
}
/**
* Returns an array of send throttling parameters or <code>null</code> if send throttling is to be disabled. The
* first element of the array is the maximum number of commands that can be sent before the quiet period must start.
* The second element is the length of time (in milliseconds) that each quiet period lasts. Once that time period
* expires, commands can again be sent, up to the maximum (and the cycle repeats).
*
* <p>Because this is a weakly typed method (i.e. you have to know what the elements in the returned array
* represent), it is recommended that you call {@link #getClientCommandSenderConfiguration()} because it will return
* all the configuration, including the throttling configuration, in a more strongly typed data object.</p>
*
* @return array of send throttling parameters, <code>null</code> if send throttling is disabled
*
* @see #getClientCommandSenderConfiguration()
*/
public long[] getClientSenderSendThrottling() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SEND_THROTTLING, null);
long[] ret_throttling_params = isClientSenderSendThrottlingValueValid(value);
return ret_throttling_params;
}
/**
* Given a send throttling parameters value, will determine if its valid or not. If its valid, its individual
* parameter values are returned. If not valid, <code>null</code> is returned. Note that if <code>pref_value</code>
* is <code>null</code>, then <code>null</code> will be immediately returned.
*
* @param pref_value the send throttling parameters value
*
* @return the individual parameters values or <code>null</code> if not valid or the preference value was <code>
* null</code>
*/
public long[] isClientSenderSendThrottlingValueValid(String pref_value) {
long[] ret_throttling_params = null;
if (pref_value != null) {
try {
String[] numbers = pref_value.split("\\s*:\\s*");
if (numbers.length == 2) {
ret_throttling_params = new long[2];
ret_throttling_params[0] = Long.parseLong(numbers[0]);
ret_throttling_params[1] = Long.parseLong(numbers[1]);
if (ret_throttling_params[0] <= 0L) {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.SEND_THROTTLE_INVALID_MAX));
}
if (ret_throttling_params[1] < 100L) {
throw new NumberFormatException(LOG.getMsgString(
ServerI18NResourceKeys.SEND_THROTTLE_INVALID_QUIET_PERIOD, 100L));
}
} else {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.SEND_THROTTLE_INVALID_FORMAT));
}
} catch (Exception e) {
ret_throttling_params = null;
LOG.warn(ServerI18NResourceKeys.BAD_SEND_THROTTLE_PREF,
ServerConfigurationConstants.CLIENT_SENDER_SEND_THROTTLING, pref_value, e);
}
}
return ret_throttling_params;
}
/**
* Returns an array of queue throttling parameters or <code>null</code> if queue throttling is to be disabled. The
* first element of the array is the maximum number of commands that can be dequeued in a burst period before the
* client sender must pause (i.e. cannot dequeue any more commands). The second element is the length of time (in
* milliseconds) that each burst period lasts. Once that time period expires, commands can again be dequeued, up to
* the maximum (and the cycle repeats).
*
* <p>Because this is a weakly typed method (i.e. you have to know what the elements in the returned array
* represent), it is recommended that you call {@link #getClientCommandSenderConfiguration()} because it will return
* all the configuration, including the throttling configuration, in a more strongly typed data object.</p>
*
* @return array of queue throttling parameters, <code>null</code> if queue throttling is disabled
*
* @see #getClientCommandSenderConfiguration()
*/
public int[] getClientSenderQueueThrottling() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_QUEUE_THROTTLING, null);
int[] ret_throttling_params = isClientSenderQueueThrottlingValueValid(value);
return ret_throttling_params;
}
/**
* Given a queue throttling parameters value, will determine if its valid or not. If its valid, its individual
* parameter values are returned. If not valid, <code>null</code> is returned. Note that if <code>pref_value</code>
* is <code>null</code>, then <code>null</code> will be immediately returned.
*
* @param pref_value the queue throttling parameters value
*
* @return the individual parameters values or <code>null</code> if not valid or the preference value was <code>
* null</code>
*/
public int[] isClientSenderQueueThrottlingValueValid(String pref_value) {
int[] ret_throttling_params = null;
if (pref_value != null) {
try {
String[] numbers = pref_value.split("\\s*:\\s*");
if (numbers.length == 2) {
ret_throttling_params = new int[2];
ret_throttling_params[0] = Integer.parseInt(numbers[0]);
ret_throttling_params[1] = Integer.parseInt(numbers[1]);
if (ret_throttling_params[0] <= 0L) {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.QUEUE_THROTTLE_INVALID_MAX));
}
if (ret_throttling_params[1] < 100L) {
throw new NumberFormatException(LOG.getMsgString(
ServerI18NResourceKeys.QUEUE_THROTTLE_INVALID_BURST_PERIOD, 100L));
}
} else {
throw new NumberFormatException(LOG
.getMsgString(ServerI18NResourceKeys.QUEUE_THROTTLE_INVALID_FORMAT));
}
} catch (Exception e) {
ret_throttling_params = null;
LOG.warn(ServerI18NResourceKeys.BAD_QUEUE_THROTTLE_PREF,
ServerConfigurationConstants.CLIENT_SENDER_QUEUE_THROTTLING, pref_value, e);
}
}
return ret_throttling_params;
}
/**
* This is a convienence method that returns the full client sender configuration. It combines all the
* getClientSenderXXX methods and puts all the data in the returned data object.
*
* @return the full client sender configuration
*/
public ClientCommandSenderConfiguration getClientCommandSenderConfiguration() {
ClientCommandSenderConfiguration config = new ClientCommandSenderConfiguration();
config.defaultTimeoutMillis = getClientSenderCommandTimeout();
config.maxConcurrent = getClientSenderMaxConcurrent();
config.queueSize = getClientSenderQueueSize();
config.dataDirectory = getDataDirectory();
config.serverPollingIntervalMillis = -1;
config.commandSpoolFileCompressData = isClientSenderCommandSpoolFileCompressed();
config.retryInterval = getClientSenderRetryInterval();
config.maxRetries = getClientSenderMaxRetries();
config.commandSpoolFileName = getClientSenderCommandSpoolFileName();
long[] cmd_spool_file_params = getClientSenderCommandSpoolFileParams();
config.commandSpoolFileMaxSize = cmd_spool_file_params[0];
config.commandSpoolFilePurgePercentage = (int) cmd_spool_file_params[1]; // cast is fine, we've ensured this is between 0 and 99
int[] queue_throttling = getClientSenderQueueThrottling();
if (queue_throttling != null) {
config.enableQueueThrottling = true;
config.queueThrottleMaxCommands = queue_throttling[0];
config.queueThrottleBurstPeriodMillis = queue_throttling[1];
} else {
config.enableQueueThrottling = false;
}
long[] send_throttling = getClientSenderSendThrottling();
if (send_throttling != null) {
config.enableSendThrottling = true;
config.sendThrottleMaxCommands = send_throttling[0];
config.sendThrottleQuietPeriodDurationMillis = send_throttling[1];
} else {
config.enableSendThrottling = false;
}
// get the security settings - the client sender probably won't need these
// these are actually set as part of the RemoteCommunicator configuration (which is passed to remoting Client)
config.securityServerAuthMode = isClientSenderSecurityServerAuthMode();
config.securityKeystoreFile = getClientSenderSecurityKeystoreFile();
config.securityKeystoreType = getClientSenderSecurityKeystoreType();
config.securityKeystoreAlgorithm = getClientSenderSecurityKeystoreAlgorithm();
config.securityKeystorePassword = getClientSenderSecurityKeystorePassword();
config.securityKeystoreKeyPassword = getClientSenderSecurityKeystoreKeyPassword();
config.securityKeystoreAlias = getClientSenderSecurityKeystoreAlias();
config.securityTruststoreFile = getClientSenderSecurityTruststoreFile();
config.securityTruststoreType = getClientSenderSecurityTruststoreType();
config.securityTruststoreAlgorithm = getClientSenderSecurityTruststoreAlgorithm();
config.securityTruststorePassword = getClientSenderSecurityTruststorePassword();
config.securitySecureSocketProtocol = getClientSenderSecuritySocketProtocol();
return config;
}
/**
* Returns the protocol used over the secure socket.
*
* @return protocol name
*/
public String getClientSenderSecuritySocketProtocol() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_SOCKET_PROTOCOL, "TLS");
return value;
}
/**
* Returns the alias to the client's key in the keystore.
*
* @return alias name
*/
public String getClientSenderSecurityKeystoreAlias() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_ALIAS, "rhq");
return value;
}
/**
* Returns the path to the keystore file. This returns a <code>String</code> as opposed to <code>File</code> since
* some underlying remoting code may allow for this filepath to be relative to a jar inside the classloader.
*
* @return keystore file path
*/
public String getClientSenderSecurityKeystoreFile() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_FILE, null);
if (value == null) {
value = new File(getDataDirectory(), "keystore.dat").getAbsolutePath();
}
return value;
}
/**
* Returns the algorithm used to manage the keys in the keystore.
*
* @return algorithm name
*/
public String getClientSenderSecurityKeystoreAlgorithm() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_ALGORITHM,
(isIBM() ? "IbmX509" : "SunX509"));
return value;
}
/**
* Returns the type of the keystore file.
*
* @return keystore file type
*/
public String getClientSenderSecurityKeystoreType() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_TYPE, "JKS");
return value;
}
/**
* Returns the password of the keystore file itself.
*
* @return keystore file password
*/
public String getClientSenderSecurityKeystorePassword() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_PASSWORD,
"rhqpwd");
return value;
}
/**
* Returns the password to gain access to the key in the keystore. If no key password is configured, this returns
* the {@link #getClientSenderSecurityKeystorePassword() keystore password}.
*
* @return password to the key
*/
public String getClientSenderSecurityKeystoreKeyPassword() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_KEYSTORE_KEY_PASSWORD,
null);
if (value == null) {
value = getClientSenderSecurityKeystorePassword();
}
return value;
}
/**
* Returns the path to the truststore file. This returns a <code>String</code> as opposed to <code>File</code> since
* some underlying remoting code may allow for this filepath to be relative to a jar inside the classloader.
*
* @return truststore file path
*/
public String getClientSenderSecurityTruststoreFile() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_TRUSTSTORE_FILE, null);
if (value == null) {
value = new File(getDataDirectory(), "truststore.dat").getAbsolutePath();
}
return value;
}
/**
* Returns the algorithm used to manage the keys in the truststore.
*
* @return algorithm name
*/
public String getClientSenderSecurityTruststoreAlgorithm() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_TRUSTSTORE_ALGORITHM,
(isIBM() ? "IbmX509" : "SunX509"));
return value;
}
/**
* Returns the type of the truststore file.
*
* @return truststore file type
*/
public String getClientSenderSecurityTruststoreType() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_TRUSTSTORE_TYPE, "JKS");
return value;
}
/**
* Returns the password of the truststore file itself.
*
* @return truststore file password
*/
public String getClientSenderSecurityTruststorePassword() {
String value = m_preferences.get(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_TRUSTSTORE_PASSWORD, null);
return value;
}
/**
* Returns <code>true</code> if the server authentication mode is enabled. If this is enabled, it means when using
* secure communications, the agents' certificates will be authenticated with the certificates found in the server's
* truststore. If this is <code>false</code>, the agents do not have to authenticate themselves with a trusted
* certificate; the server will trust any remote agent (in other words, the secure communications repo will only
* be used for encryption and not authentication).
*
* @return server authenticate mode
*/
public boolean isClientSenderSecurityServerAuthMode() {
boolean flag = m_preferences.getBoolean(ServerConfigurationConstants.CLIENT_SENDER_SECURITY_SERVER_AUTH_MODE,
ServerConfigurationConstants.DEFAULT_CLIENT_SENDER_SECURITY_SERVER_AUTH_MODE);
return flag;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuffer buf = new StringBuffer(m_preferences.absolutePath());
buf.append('[');
try {
String[] keys = m_preferences.keys();
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
buf.append(key);
buf.append('=');
if (key.toLowerCase().contains("password")) {
buf.append("***");
} else {
buf.append(m_preferences.get(key, LOG.getMsgString(ServerI18NResourceKeys.UNKNOWN)));
}
if ((i + 1) < keys.length) {
buf.append(',');
}
}
} catch (BackingStoreException e) {
buf.append(LOG.getMsgString(ServerI18NResourceKeys.CANNOT_GET_PREFERENCES, e));
}
buf.append(']');
return buf.toString();
}
private boolean isIBM() {
return System.getProperty("java.vendor", "").contains("IBM");
}
}