Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.jboss.ejb.client.PropertiesBasedEJBClientConfiguration Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.ejb.client;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.sasl.RealmCallback;
import javax.xml.bind.DatatypeConverter;
import org.jboss.ejb.client.remoting.RemotingConnectionUtil;
import org.jboss.logging.Logger;
import org.xnio.Option;
import org.xnio.OptionMap;
import org.xnio.Options;
/**
* A {@link EJBClientConfiguration} which is configured through {@link Properties}. Some well known
* properties will be looked for in the {@link Properties} that is passed to the {@link #PropertiesBasedEJBClientConfiguration(java.util.Properties) constructor},
* for setting up the configurations
*
* @author Jaikiran Pai
*/
public class PropertiesBasedEJBClientConfiguration implements EJBClientConfiguration {
private static final Logger logger = Logger.getLogger(PropertiesBasedEJBClientConfiguration.class);
private static final String PROPERTY_KEY_ENDPOINT_NAME = "endpoint.name";
private static final String DEFAULT_ENDPOINT_NAME = "config-based-ejb-client-endpoint";
private static final String PROPERTY_KEY_INVOCATION_TIMEOUT = "invocation.timeout";
private static final String PROPERTY_KEY_RECONNECT_TASKS_TIMEOUT = "reconnect.tasks.timeout";
private static final String PROPERTY_KEY_DEPLOYMENT_NODE_SELECTOR = "deployment.node.selector";
private static final String ENDPOINT_CREATION_OPTIONS_PREFIX = "endpoint.create.options.";
// The default options that will be used (unless overridden by the config file) for endpoint creation
private static final OptionMap DEFAULT_ENDPOINT_CREATION_OPTIONS = OptionMap.create(Options.THREAD_DAEMON, true);
// The default options that will be used (unless overridden by the config file) while adding a remote connection
// provider to the endpoint
private static final OptionMap DEFAULT_CONNECTION_PROVIDER_CREATION_OPTIONS = OptionMap.EMPTY;
private static final String REMOTE_CONNECTION_PROVIDER_CREATE_OPTIONS_PREFIX = "remote.connectionprovider.create.options.";
private static final String PROPERTY_KEY_REMOTE_CONNECTIONS = "remote.connections";
private static final String PROPERTY_KEY_REMOTE_CONNECTIONS_CONNECT_EAGER = "remote.connections.connect.eager";
// The default options that will be used (unless overridden by the config file) while creating a connection
private static final OptionMap DEFAULT_CONNECTION_CREATION_OPTIONS = OptionMap.EMPTY;
private static final long DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS = 5000;
private static final String PROPERTY_KEY_USERNAME = "username";
private static final String PROPERTY_KEY_PASSWORD = "password";
private static final String PROPERTY_KEY_PASSWORD_BASE64 = "password.base64";
private static final String PROPERTY_KEY_REALM = "realm";
private static final String PROPERTY_KEY_CALLBACK_HANDLER_CLASS = "callback.handler.class";
private static final String PROPERTY_KEY_CLUSTERS = "remote.clusters";
private static final String DEFAULT_PROTOCOL = "http-remoting";
private final Properties ejbReceiversConfigurationProperties;
private String endPointName;
private OptionMap endPointCreationOptions;
private OptionMap remoteConnectionProviderCreationOptions;
private CallbackHandler callbackHandler;
private Collection remotingConnectionConfigurations = new ArrayList();
private Map clusterConfigurations = new HashMap();
private long invocationTimeout = 0;
private long reconnectTasksTimeout = 0;
private DeploymentNodeSelector deploymentNodeSelector = new RandomDeploymentNodeSelector();
private static final boolean expandPasswords = Boolean.valueOf(
System.getProperty("jboss-ejb-client.expandPasswords", "false")).booleanValue();
public PropertiesBasedEJBClientConfiguration(final Properties properties) {
final Properties resolvedProperties = new Properties();
if (properties != null) {
for (Map.Entry entry : properties.entrySet()) {
Object value = entry.getValue();
if (value instanceof String) {
boolean propertyIsAPassword = ((String)entry.getKey()).indexOf(PROPERTY_KEY_PASSWORD) >= 0 ? true : false;
// if its not a password...expand it
// if it is a password and we're supposed to expand it...then do so
if( !propertyIsAPassword || ( propertyIsAPassword && expandPasswords ) ) {
value = PropertiesValueResolver.replaceProperties((String) value);
}
}
resolvedProperties.put(entry.getKey(), value);
}
}
this.ejbReceiversConfigurationProperties = resolvedProperties;
// parse the properties and setup this configuration
this.parseProperties();
}
@Override
public String getEndpointName() {
return this.endPointName;
}
@Override
public OptionMap getEndpointCreationOptions() {
return this.endPointCreationOptions;
}
@Override
public OptionMap getRemoteConnectionProviderCreationOptions() {
return this.remoteConnectionProviderCreationOptions;
}
@Override
public CallbackHandler getCallbackHandler() {
return this.callbackHandler;
}
@Override
public Iterator getConnectionConfigurations() {
return this.remotingConnectionConfigurations.iterator();
}
@Override
public ClusterConfiguration getClusterConfiguration(String clusterName) {
return this.clusterConfigurations.get(clusterName);
}
@Override
public long getInvocationTimeout() {
return this.invocationTimeout;
}
@Override
public long getReconnectTasksTimeout() {
return this.reconnectTasksTimeout;
}
@Override
public DeploymentNodeSelector getDeploymentNodeSelector() {
return this.deploymentNodeSelector;
}
private void parseProperties() {
// endpoint name
this.endPointName = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_ENDPOINT_NAME, DEFAULT_ENDPOINT_NAME);
// callback handler
this.callbackHandler = this.getDefaultCallbackHandler();
// endpoint creation options
final OptionMap endPointCreationOptionsFromConfiguration = getOptionMapFromProperties(ejbReceiversConfigurationProperties, ENDPOINT_CREATION_OPTIONS_PREFIX, getClientClassLoader());
// merge with defaults
this.endPointCreationOptions = mergeWithDefaults(DEFAULT_ENDPOINT_CREATION_OPTIONS, endPointCreationOptionsFromConfiguration);
// invocation timeout
final String invocationTimeoutValue = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_INVOCATION_TIMEOUT);
// if a invocation timeout is specified, use it
if (invocationTimeoutValue != null && !invocationTimeoutValue.trim().isEmpty()) {
try {
invocationTimeout = Long.parseLong(invocationTimeoutValue.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectInvocationTimeoutValue(invocationTimeoutValue, String.valueOf(this.invocationTimeout));
}
}
// reconnect tasks timeout
final String reconnectTasksTimeoutValue = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_RECONNECT_TASKS_TIMEOUT);
if (reconnectTasksTimeoutValue != null && !reconnectTasksTimeoutValue.trim().isEmpty()) {
try {
this.reconnectTasksTimeout = Long.parseLong(reconnectTasksTimeoutValue.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectReconnectTasksTimeoutValue(reconnectTasksTimeoutValue, String.valueOf(this.reconnectTasksTimeout));
}
}
// deployment node selector
final String deploymentNodeSelectorClassName = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_DEPLOYMENT_NODE_SELECTOR);
if (deploymentNodeSelectorClassName != null) {
final ClassLoader classLoader = getClientClassLoader();
try {
final Class> deploymentNodeSelectorClass = Class.forName(deploymentNodeSelectorClassName.trim(), true, classLoader);
if (!DeploymentNodeSelector.class.isAssignableFrom(deploymentNodeSelectorClass)) {
throw Logs.MAIN.unexpectedDeploymentNodeSelectorClassType(deploymentNodeSelectorClass);
}
this.deploymentNodeSelector = (DeploymentNodeSelector) deploymentNodeSelectorClass.newInstance();
} catch (Exception e) {
throw Logs.MAIN.couldNotCreateDeploymentNodeSelector(e);
}
}
// remote connection provider creation options
final OptionMap remoteConnectionProviderOptionsFromConfiguration = getOptionMapFromProperties(ejbReceiversConfigurationProperties, REMOTE_CONNECTION_PROVIDER_CREATE_OPTIONS_PREFIX, getClientClassLoader());
// merge with defaults
this.remoteConnectionProviderCreationOptions = mergeWithDefaults(DEFAULT_CONNECTION_PROVIDER_CREATION_OPTIONS, remoteConnectionProviderOptionsFromConfiguration);
// connection configurations
this.parseConnectionConfigurations();
// cluster configurations
this.parseClusterConfigurations();
}
private OptionMap getOptionMapFromProperties(final Properties properties, final String propertyPrefix, final ClassLoader classLoader) {
final OptionMap.Builder optionMapBuilder = OptionMap.builder().parseAll(properties, propertyPrefix, classLoader);
final OptionMap optionMap = optionMapBuilder.getMap();
logger.debugf("%s has the following options %s", propertyPrefix, optionMap);
return optionMap;
}
/**
* Merges the passed defaults
and the overrides
to return a combined
* {@link OptionMap}. If the passed overrides
has a {@link org.xnio.Option} for
* which matches the one in defaults
then the default option value is ignored and instead the
* overridden one is added to the combined {@link OptionMap}. If however, the overrides
doesn't
* contain a option which is present in the defaults
, then the default option is added to the
* combined {@link OptionMap}
*
* @param defaults The default options
* @param overrides The overridden options
* @return
*/
private OptionMap mergeWithDefaults(final OptionMap defaults, final OptionMap overrides) {
// copy all the overrides
final OptionMap.Builder combinedOptionsBuilder = OptionMap.builder().addAll(overrides);
// Skip all the defaults which have been overridden and just add the rest of the defaults
// to the combined options
for (final Option defaultOption : defaults) {
if (combinedOptionsBuilder.getMap().contains(defaultOption)) {
continue;
}
final Object defaultValue = defaults.get(defaultOption);
combinedOptionsBuilder.set(defaultOption, defaultValue);
}
final OptionMap combinedOptions = combinedOptionsBuilder.getMap();
if (logger.isTraceEnabled()) {
logger.trace("Options " + overrides + " have been merged with defaults " + defaults + " to form " + combinedOptions);
}
return combinedOptions;
}
/**
* If {@link Thread#getContextClassLoader()} is null then returns the classloader which loaded
* {@link PropertiesBasedEJBClientConfiguration} class. Else returns the {@link Thread#getContextClassLoader()}
*
* @return
*/
private static ClassLoader getClientClassLoader() {
final ClassLoader tccl = SecurityActions.getContextClassLoader();
if (tccl != null) {
return tccl;
}
return PropertiesBasedEJBClientConfiguration.class.getClassLoader();
}
private void parseClusterConfigurations() {
final String clusterNames = (String) ejbReceiversConfigurationProperties.get(PROPERTY_KEY_CLUSTERS);
// no clusters configured, nothing to do!
if (clusterNames == null || clusterNames.trim().isEmpty()) {
logger.debug("No clusters configured in properties");
return;
}
// parse the comma separated string of cluster names
final StringTokenizer tokenizer = new StringTokenizer(clusterNames, ",");
while (tokenizer.hasMoreTokens()) {
final String clusterName = tokenizer.nextToken().trim();
if (clusterName.isEmpty()) {
continue;
}
ClusterConfiguration clusterConfiguration = null;
try {
clusterConfiguration = this.createClusterConfiguration(clusterName);
} catch (Exception e) {
logger.warn("Could not create cluster configuration for cluster named " + clusterName, e);
}
if (clusterConfiguration == null) {
continue;
}
logger.debugf("Cluster configuration for cluster %s successfully created", clusterName);
// add it to the cluster configuration map
this.clusterConfigurations.put(clusterName, clusterConfiguration);
}
}
private ClusterConfiguration createClusterConfiguration(final String clusterName) {
final String clusterSpecificPrefix = this.getClusterSpecificPrefix(clusterName);
final Map clusterSpecificProperties = this.getPropertiesWithPrefix(clusterSpecificPrefix);
if (clusterSpecificProperties.isEmpty()) {
return null;
}
// get "max-connected-nodes" for the cluster
final String maxConnectedNodesStringVal = clusterSpecificProperties.get("max-allowed-connected-nodes");
long maxAllowedConnectedNodes = 10; // default to 10
if (maxConnectedNodesStringVal != null && !maxConnectedNodesStringVal.trim().isEmpty()) {
try {
maxAllowedConnectedNodes = Long.parseLong(maxConnectedNodesStringVal.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectMaxAllowedConnectedNodesValueForCluster(maxConnectedNodesStringVal, clusterName, String.valueOf(maxAllowedConnectedNodes));
}
}
// get the connection creation options applicable for all the nodes (unless explicitly overridden) in this
// cluster
final String connectOptionsPrefix = this.getClusterSpecificConnectOptionsPrefix(clusterName);
final OptionMap connectOptionsFromConfiguration = getOptionMapFromProperties(ejbReceiversConfigurationProperties, connectOptionsPrefix, getClientClassLoader());
// merge with defaults
OptionMap connectOptions = mergeWithDefaults(DEFAULT_CONNECTION_CREATION_OPTIONS, connectOptionsFromConfiguration);
// get the connection timeout applicable for all nodes (unless explicitly overridden) in this cluster
long connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS;
final String connectionTimeoutValue = clusterSpecificProperties.get("connect.timeout");
// if a connection timeout is specified, use it
if (connectionTimeoutValue != null && !connectionTimeoutValue.trim().isEmpty()) {
try {
connectionTimeout = Long.parseLong(connectionTimeoutValue.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectConnectionTimeoutValueForCluster(connectionTimeoutValue, clusterName, String.valueOf(DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS));
}
}
// cluster node selector for this cluster
final String clusterNodeSelectorClassName = clusterSpecificProperties.get("clusternode.selector");
final ClusterNodeSelector clusterNodeSelector;
if (clusterNodeSelectorClassName != null) {
final ClassLoader classLoader = getClientClassLoader();
try {
final Class> clusterNodeSelectorClass = Class.forName(clusterNodeSelectorClassName.trim(), true, classLoader);
if (!ClusterNodeSelector.class.isAssignableFrom(clusterNodeSelectorClass)) {
throw Logs.MAIN.unexpectedClusterNodeSelectorClassType(clusterNodeSelectorClass, clusterName);
}
clusterNodeSelector = (ClusterNodeSelector) clusterNodeSelectorClass.newInstance();
} catch (Exception e) {
throw Logs.MAIN.couldNotCreateClusterNodeSelector(e, clusterName);
}
} else {
clusterNodeSelector = null;
}
// create the CallbackHandler applicable for all nodes (unless explicitly overridden) in this cluster
final CallbackHandler callbackHandler = createCallbackHandler(clusterSpecificProperties, this.getDefaultCallbackHandler());
connectOptions = RemotingConnectionUtil.addSilentLocalAuthOptionsIfApplicable(callbackHandler, connectOptions);
// Channel creation options for this cluster
final String channelOptionsPrefix = this.getClusterSpecificChannelOptionsPrefix(clusterName);
final OptionMap channelOptions = getOptionMapFromProperties(ejbReceiversConfigurationProperties, channelOptionsPrefix, getClientClassLoader());
final ClusterConfigurationImpl clusterConfiguration = new ClusterConfigurationImpl(clusterName, maxAllowedConnectedNodes,
connectOptions, callbackHandler, connectionTimeout, clusterNodeSelector, channelOptions);
// parse the node configurations for this cluster
final Collection nodeConfigurations = this.parseClusterNodeConfigurations(clusterConfiguration, clusterSpecificProperties);
// add them to the cluster configuration
clusterConfiguration.addNodeConfigurations(nodeConfigurations);
// return the cluster configuration
return clusterConfiguration;
}
private Collection parseClusterNodeConfigurations(final ClusterConfiguration clusterConfiguration, final Map clusterSpecificProperties) {
final Collection nodeConfigurations = new ArrayList();
final Collection parsedNodes = new HashSet();
for (final String key : clusterSpecificProperties.keySet()) {
if (!key.startsWith("node.")) {
continue;
}
final String keyWithoutNodeDotPrefix = key.substring("node.".length());
final int nextDotIndex = keyWithoutNodeDotPrefix.indexOf(".");
if (nextDotIndex == -1) {
continue;
}
final String nodeName = keyWithoutNodeDotPrefix.substring(0, nextDotIndex);
// already parsed, so skip
if (parsedNodes.contains(nodeName)) {
continue;
}
// create a node configuration for the node name
final ClusterNodeConfiguration nodeConfiguration = this.createClusterNodeConfiguration(clusterConfiguration, nodeName);
if (nodeConfiguration == null) {
continue;
}
// add it to the collection to be returned
nodeConfigurations.add(nodeConfiguration);
// mark the node as parsed
parsedNodes.add(nodeConfiguration.getNodeName());
}
return nodeConfigurations;
}
private ClusterNodeConfiguration createClusterNodeConfiguration(final ClusterConfiguration clusterConfiguration, final String nodeName) {
final String clusterName = clusterConfiguration.getClusterName();
final String nodeSpecificPrefix = this.getClusterSpecificPrefix(clusterName) + "node." + nodeName + ".";
// get the cluster node specific properties
final Map nodeSpecificProperties = this.getPropertiesWithPrefix(nodeSpecificPrefix);
if (nodeSpecificProperties.isEmpty()) {
return null;
}
// get the connection creation options for the cluster node
final String connectOptionsPrefix = this.getClusterNodeSpecificConnectOptionsPrefix(clusterName, nodeName);
final OptionMap connectOptionsFromConfiguration = getOptionMapFromProperties(ejbReceiversConfigurationProperties, connectOptionsPrefix, getClientClassLoader());
// merge with defaults (== connection creation options applicable to the entire cluster)
final OptionMap connectOptions = mergeWithDefaults(clusterConfiguration.getConnectionCreationOptions(), connectOptionsFromConfiguration);
// get the connection timeout applicable for the cluster node
long connectionTimeout = clusterConfiguration.getConnectionTimeout(); // default to the timeout applicable to the entire cluster
final String connectionTimeoutValue = nodeSpecificProperties.get("connect.timeout");
// if a connection timeout is specified, use it
if (connectionTimeoutValue != null && !connectionTimeoutValue.trim().isEmpty()) {
try {
connectionTimeout = Long.parseLong(connectionTimeoutValue.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectConnectionTimeoutValueForNodeInCluster(connectionTimeoutValue, nodeName, clusterName, String.valueOf(connectionTimeout));
}
}
// create the CallbackHandler applicable for the cluster node (default to the callback handler applicable to the entire cluster)
final CallbackHandler callbackHandler = createCallbackHandler(nodeSpecificProperties, clusterConfiguration.getCallbackHandler());
// Channel creation options for this cluster node
final String channelOptionsPrefix = this.getClusterNodeSpecificChannelOptionsPrefix(clusterName, nodeName);
final OptionMap channelOptions = getOptionMapFromProperties(ejbReceiversConfigurationProperties, channelOptionsPrefix, getClientClassLoader());
return new ClusterNodeConfigurationImpl(nodeName, connectOptions, callbackHandler, connectionTimeout, channelOptions);
}
private void parseConnectionConfigurations() {
final String remoteConnectionNames = (String) ejbReceiversConfigurationProperties.get(PROPERTY_KEY_REMOTE_CONNECTIONS);
// no connections configured, nothing to do!
if (remoteConnectionNames == null || remoteConnectionNames.trim().isEmpty()) {
logger.debug("No remoting connections configured in properties");
return;
}
// make note of whether the connection attempts are to be eager or lazy for all listed connections (unless overridden at the specific connection configuration)
final Object connectEagerValue = ejbReceiversConfigurationProperties.get(PROPERTY_KEY_REMOTE_CONNECTIONS_CONNECT_EAGER);
final boolean connectEager;
if (connectEagerValue == null) {
// by default we connect eagerly
connectEager = true;
} else {
if (connectEagerValue instanceof String) {
connectEager = Boolean.valueOf(((String) connectEagerValue).trim());
} else {
// default to true
connectEager = true;
}
}
// parse the comma separated string of connection names
final StringTokenizer tokenizer = new StringTokenizer(remoteConnectionNames, ",");
while (tokenizer.hasMoreTokens()) {
final String connectionName = tokenizer.nextToken().trim();
if (connectionName.isEmpty()) {
continue;
}
RemotingConnectionConfiguration connectionConfiguration = null;
try {
connectionConfiguration = this.createConnectionConfiguration(connectionName, connectEager);
} catch (Exception e) {
logger.warn("Could not create connection for connection named " + connectionName, e);
}
if (connectionConfiguration == null) {
continue;
}
logger.debugf("Connection %s successfully created for connection named %s", connectionConfiguration, connectionName);
// add it to the collection of connection configurations
this.remotingConnectionConfigurations.add(connectionConfiguration);
}
}
private RemotingConnectionConfiguration createConnectionConfiguration(final String connectionName, final boolean defaultConnectEager) throws IOException, URISyntaxException {
final String connectionSpecificPrefix = this.getConnectionSpecificPrefix(connectionName);
final Map connectionSpecificProps = this.getPropertiesWithPrefix(connectionSpecificPrefix);
if (connectionSpecificProps.isEmpty()) {
return null;
}
// get "host" for the connection
final String host = connectionSpecificProps.get("host");
if (host == null || host.trim().isEmpty()) {
Logs.MAIN.skippingConnectionCreationDueToMissingHostOrPort(connectionName);
return null;
}
// get "port" for the connection
final String portStringVal = connectionSpecificProps.get("port");
if (portStringVal == null || portStringVal.trim().isEmpty()) {
Logs.MAIN.skippingConnectionCreationDueToMissingHostOrPort(connectionName);
return null;
}
final Integer port;
try {
port = Integer.parseInt(portStringVal.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.skippingConnectionCreationDueToInvalidPortNumber(portStringVal, connectionName);
return null;
}
String protocol = connectionSpecificProps.get("protocol");
if(protocol == null) {
protocol = DEFAULT_PROTOCOL;
}
// get connect options for the connection
final String connectOptionsPrefix = this.getConnectionSpecificConnectOptionsPrefix(connectionName);
final OptionMap connectOptionsFromConfiguration = getOptionMapFromProperties(ejbReceiversConfigurationProperties, connectOptionsPrefix, getClientClassLoader());
// merge with defaults
OptionMap connectOptions = mergeWithDefaults(DEFAULT_CONNECTION_CREATION_OPTIONS, connectOptionsFromConfiguration);
long connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS;
final String connectionTimeoutValue = connectionSpecificProps.get("connect.timeout");
// if a connection timeout is specified, use it
if (connectionTimeoutValue != null && !connectionTimeoutValue.trim().isEmpty()) {
try {
connectionTimeout = Long.parseLong(connectionTimeoutValue.trim());
} catch (NumberFormatException nfe) {
Logs.MAIN.incorrectConnectionTimeoutValueForConnection(connectionTimeoutValue, connectionName, String.valueOf(DEFAULT_CONNECTION_TIMEOUT_IN_MILLIS));
}
}
// connect eagerly or lazily
final String connectEagerValue = connectionSpecificProps.get("connect.eager");
final boolean connectEagerly;
if (connectEagerValue == null || connectEagerValue.trim().isEmpty()) {
// default to the value that may have been set for all connections
connectEagerly = defaultConnectEager;
} else {
connectEagerly = Boolean.valueOf(connectEagerValue.trim());
}
// create the CallbackHandler for this connection configuration
final CallbackHandler callbackHandler = createCallbackHandler(connectionSpecificProps, this.getDefaultCallbackHandler());
connectOptions = RemotingConnectionUtil.addSilentLocalAuthOptionsIfApplicable(callbackHandler, connectOptions);
// Channel creation options for this connection
final String channelOptionsPrefix = this.getConnectionSpecificChannelOptionsPrefix(connectionName);
final OptionMap channelOptions = getOptionMapFromProperties(ejbReceiversConfigurationProperties, channelOptionsPrefix, getClientClassLoader());
return new RemotingConnectionConfigurationImpl(protocol, host, port, connectOptions, connectionTimeout, callbackHandler, channelOptions, connectEagerly);
}
private String getConnectionSpecificPrefix(final String connectionName) {
return "remote.connection." + connectionName + ".";
}
private String getConnectionSpecificConnectOptionsPrefix(final String connectionName) {
return "remote.connection." + connectionName + ".connect.options.";
}
private String getConnectionSpecificChannelOptionsPrefix(final String connectionName) {
return "remote.connection." + connectionName + ".channel.options.";
}
private Map getPropertiesWithPrefix(final String prefix) {
final Map propertiesWithPrefix = new HashMap();
for (final String fullPropName : this.ejbReceiversConfigurationProperties.stringPropertyNames()) {
if (fullPropName.startsWith(prefix)) {
// strip the "prefix" from the full property name and just get the trailing part.
// Example, If remote.cluster.foo.bar is the full property name,
// then this step will return "bar" as the property name for the prefix "remote.cluster.foo.".
String propName = fullPropName.substring(prefix.length());
// get the value of the (full) property name
final String propValue = this.ejbReceiversConfigurationProperties.getProperty(fullPropName);
propertiesWithPrefix.put(propName, propValue);
}
}
return propertiesWithPrefix;
}
private String getClusterSpecificPrefix(final String clusterName) {
return "remote.cluster." + clusterName + ".";
}
private String getClusterSpecificConnectOptionsPrefix(final String clusterName) {
return "remote.cluster." + clusterName + ".connect.options.";
}
private String getClusterSpecificChannelOptionsPrefix(final String clusterName) {
return "remote.cluster." + clusterName + ".channel.options.";
}
private String getClusterNodeSpecificConnectOptionsPrefix(final String clusterName, final String nodeName) {
return "remote.cluster." + clusterName + ".node." + nodeName + ".connect.options.";
}
private String getClusterNodeSpecificChannelOptionsPrefix(final String clusterName, final String nodeName) {
return "remote.cluster." + clusterName + ".node." + nodeName + ".channel.options.";
}
/**
* Creates a callback handler
*
* @param properties
* @return The CallbackHandler
*/
private CallbackHandler createCallbackHandler(final Map properties, final CallbackHandler defaultCallbackHandler) {
String callbackClass = properties.get(PROPERTY_KEY_CALLBACK_HANDLER_CLASS);
String userName = properties.get(PROPERTY_KEY_USERNAME);
String password = properties.get(PROPERTY_KEY_PASSWORD);
String passwordBase64 = properties.get(PROPERTY_KEY_PASSWORD_BASE64);
String realm = properties.get(PROPERTY_KEY_REALM);
CallbackHandler handler = resolveCallbackHandler(callbackClass, userName, password, passwordBase64, realm);
if (handler != null) {
return handler;
}
return defaultCallbackHandler;
}
private CallbackHandler resolveCallbackHandler(final String callbackClass, final String userName, final String password, final String passwordBase64, final String realm) {
if (callbackClass != null && (userName != null || password != null)) {
throw Logs.MAIN.cannotSpecifyBothCallbackHandlerAndUserPass();
}
if (callbackClass != null) {
final ClassLoader classLoader = getClientClassLoader();
try {
final Class> clazz = Class.forName(callbackClass, true, classLoader);
return (CallbackHandler) clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (userName != null) {
if (password != null && passwordBase64 != null) {
throw Logs.MAIN.cannotSpecifyBothPlainTextAndEncodedPassword();
}
final String decodedPassword;
if (passwordBase64 != null) {
try {
decodedPassword = DatatypeConverter.printBase64Binary(passwordBase64.getBytes());
} catch (Exception e) {
throw Logs.MAIN.couldNotDecodeBase64Password(e);
}
} else if (password != null) {
decodedPassword = password;
} else {
decodedPassword = null;
}
return new AuthenticationCallbackHandler(userName, decodedPassword == null ? null : decodedPassword.toCharArray(), realm);
}
return null;
}
private CallbackHandler getDefaultCallbackHandler() {
final String callbackClass = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_CALLBACK_HANDLER_CLASS);
final String userName = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_USERNAME);
final String password = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_PASSWORD);
final String passwordBase64 = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_PASSWORD_BASE64);
final String realm = this.ejbReceiversConfigurationProperties.getProperty(PROPERTY_KEY_REALM);
CallbackHandler handler = resolveCallbackHandler(callbackClass, userName, password, passwordBase64, realm);
if (handler != null) {
return handler;
}
// no auth specified, just use the default
return new DefaultCallbackHandler();
}
private class AuthenticationCallbackHandler implements CallbackHandler {
private final String realm;
private final String username;
private final char[] password;
private AuthenticationCallbackHandler(final String username, final char[] password, final String realm) {
this.username = username;
this.password = password;
this.realm = realm;
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback current : callbacks) {
if (current instanceof RealmCallback) {
RealmCallback rcb = (RealmCallback) current;
if (realm == null) {
String defaultText = rcb.getDefaultText();
rcb.setText(defaultText); // For now just use the realm suggested.
} else {
rcb.setText(realm);
}
} else if (current instanceof NameCallback) {
NameCallback ncb = (NameCallback) current;
ncb.setName(username);
} else if (current instanceof PasswordCallback) {
PasswordCallback pcb = (PasswordCallback) current;
pcb.setPassword(password);
} else {
throw new UnsupportedCallbackException(current);
}
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AuthenticationCallbackHandler that = (AuthenticationCallbackHandler) o;
if (!Arrays.equals(password, that.password)) return false;
if (realm != null ? !realm.equals(that.realm) : that.realm != null) return false;
if (username != null ? !username.equals(that.username) : that.username != null) return false;
return true;
}
@Override
public int hashCode() {
int result = realm != null ? realm.hashCode() : 0;
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? Arrays.hashCode(password) : 0);
return result;
}
}
private class RemotingConnectionConfigurationImpl implements RemotingConnectionConfiguration {
final String protocol;
final String host;
final int port;
final OptionMap connectionCreationOptions;
final long connectionTimeout;
final CallbackHandler callbackHandler;
final OptionMap channelCreationOptions;
final boolean connectEagerly;
RemotingConnectionConfigurationImpl(final String protocol, final String host, final int port, final OptionMap connectionCreationOptions,
final long connectionTimeout, final CallbackHandler callbackHandler, final OptionMap channelCreationOptions,
final boolean connectEagerly) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.connectionCreationOptions = connectionCreationOptions;
this.connectionTimeout = connectionTimeout;
this.callbackHandler = callbackHandler;
this.channelCreationOptions = channelCreationOptions == null ? OptionMap.EMPTY : channelCreationOptions;
this.connectEagerly = connectEagerly;
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public long getConnectionTimeout() {
return this.connectionTimeout;
}
@Override
public OptionMap getConnectionCreationOptions() {
return this.connectionCreationOptions;
}
@Override
public CallbackHandler getCallbackHandler() {
return this.callbackHandler;
}
@Override
public OptionMap getChannelCreationOptions() {
return this.channelCreationOptions;
}
@Override
public boolean isConnectEagerly() {
return connectEagerly;
}
@Override
public String getProtocol() {
return protocol;
}
}
private class ClusterConfigurationImpl implements ClusterConfiguration {
private final String clusterName;
private final long maxAllowedConnectedNodes;
private final Map nodeConfigurations = new HashMap();
private final CallbackHandler callbackHandler;
private final OptionMap connectionCreationOptions;
private final long connectionTimeout;
private final ClusterNodeSelector clusterNodeSelector;
private final OptionMap channelCreationOptions;
ClusterConfigurationImpl(final String clusterName, final long maxAllowedConnectedNodes, final OptionMap connectionCreationOptions,
final CallbackHandler callbackHandler, final long connectionTimeout, final ClusterNodeSelector clusterNodeSelector,
final OptionMap channelCreationOptions) {
this.clusterName = clusterName;
this.maxAllowedConnectedNodes = maxAllowedConnectedNodes;
this.connectionCreationOptions = connectionCreationOptions;
this.callbackHandler = callbackHandler;
this.connectionTimeout = connectionTimeout;
this.clusterNodeSelector = clusterNodeSelector;
this.channelCreationOptions = channelCreationOptions == null ? OptionMap.EMPTY : channelCreationOptions;
}
@Override
public String getClusterName() {
return this.clusterName;
}
@Override
public long getMaximumAllowedConnectedNodes() {
return this.maxAllowedConnectedNodes;
}
@Override
public ClusterNodeConfiguration getNodeConfiguration(String nodeName) {
return this.nodeConfigurations.get(nodeName);
}
@Override
public OptionMap getConnectionCreationOptions() {
return this.connectionCreationOptions;
}
@Override
public CallbackHandler getCallbackHandler() {
return this.callbackHandler;
}
@Override
public long getConnectionTimeout() {
return this.connectionTimeout;
}
@Override
public ClusterNodeSelector getClusterNodeSelector() {
return this.clusterNodeSelector;
}
void addNodeConfigurations(final Collection nodeConfigurations) {
if (nodeConfigurations != null) {
for (final ClusterNodeConfiguration nodeConfiguration : nodeConfigurations) {
this.nodeConfigurations.put(nodeConfiguration.getNodeName(), nodeConfiguration);
}
}
}
@Override
public OptionMap getChannelCreationOptions() {
return this.channelCreationOptions;
}
@Override
public boolean isConnectEagerly() {
// connecting to cluster nodes is always on-demand and not eager. So return false.
return false;
}
}
private class ClusterNodeConfigurationImpl implements ClusterNodeConfiguration {
private final String nodeName;
private final OptionMap connectionCreationOptions;
private final CallbackHandler callbackHandler;
private final long connectionTimeout;
private final OptionMap channelCreationOptions;
ClusterNodeConfigurationImpl(final String nodeName, final OptionMap connectionCreationOptions, final CallbackHandler callbackHandler,
final long connectionTimeout, OptionMap channelCreationOptions) {
this.nodeName = nodeName;
this.connectionCreationOptions = connectionCreationOptions;
this.callbackHandler = callbackHandler;
this.connectionTimeout = connectionTimeout;
this.channelCreationOptions = channelCreationOptions == null ? OptionMap.EMPTY : channelCreationOptions;
}
@Override
public String getNodeName() {
return this.nodeName;
}
@Override
public OptionMap getConnectionCreationOptions() {
return this.connectionCreationOptions;
}
@Override
public CallbackHandler getCallbackHandler() {
return this.callbackHandler;
}
@Override
public long getConnectionTimeout() {
return this.connectionTimeout;
}
@Override
public OptionMap getChannelCreationOptions() {
return this.channelCreationOptions;
}
@Override
public boolean isConnectEagerly() {
// connecting to cluster node is always on-demand and not eager. So return false.
return false;
}
}
}