org.jboss.ejb.client.legacy.JBossEJBProperties Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2017 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.ejb.client.legacy;
import static java.security.AccessController.doPrivileged;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import javax.security.auth.callback.CallbackHandler;
import org.jboss.ejb._private.Logs;
import org.jboss.ejb.client.ClusterNodeSelector;
import org.jboss.ejb.client.DeploymentNodeSelector;
import org.wildfly.common.Assert;
import org.wildfly.common.annotation.NotNull;
import org.wildfly.common.context.ContextManager;
import org.wildfly.common.context.Contextual;
import org.wildfly.common.expression.Expression;
import org.wildfly.common.function.ExceptionBiFunction;
import org.wildfly.common.function.ExceptionFunction;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.common.iteration.CodePointIterator;
import org.xnio.OptionMap;
import org.xnio.Options;
/**
* An object model of the legacy {@code jboss-ejb.properties} file format.
*
* @author Jaikiran Pai
* @author Tomasz Adamski
* @author David M. Lloyd
*/
public final class JBossEJBProperties implements Contextual {
public static final String DEFAULT_PATH_NAME = "jboss-ejb-client.properties";
public static final String PROPERTY_KEY_CLUSTERS = "remote.clusters";
private static final ContextManager CONTEXT_MANAGER = new ContextManager<>(JBossEJBProperties.class, "org.jboss.ejb.client.legacy-properties");
private static final Supplier SUPPLIER = doPrivileged((PrivilegedAction>) CONTEXT_MANAGER::getPrivilegedSupplier);
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 PROPERTY_KEY_DEFAULT_COMPRESSION = "default.compression";
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, Boolean.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_HOST = "host";
private static final String PROPERTY_KEY_PORT = "port";
private static final String PROPERTY_KEY_PROTOCOL = "protocol";
private static final String DEFAULT_PROTOCOL = "http-remoting";
private static final boolean EXPAND_PASSWORDS = SecurityUtils.getBoolean(SystemProperties.EXPAND_PASSWORDS);
private static final String CONFIGURED_PATH_NAME = SecurityUtils.getString(SystemProperties.PROPERTIES_FILE_PATH);
private static final String PROPERTY_KEY_HTTP_CONNECTIONS = "http.connections";
private static final String PROPERTY_KEY_URI = "uri";
static {
final AtomicReference onceRef = new AtomicReference<>();
CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> {
JBossEJBProperties value = onceRef.get();
if (value == null) {
synchronized (onceRef) {
value = onceRef.get();
if (value == null) {
try {
if (CONFIGURED_PATH_NAME != null) try {
File propertiesFile = new File(CONFIGURED_PATH_NAME);
if (! propertiesFile.isAbsolute()) {
propertiesFile = new File(SecurityUtils.getString(SystemProperties.USER_DIR), propertiesFile.toString());
}
value = JBossEJBProperties.fromFile(propertiesFile);
} catch (IOException e) {
Logs.MAIN.failedToFindEjbClientConfigFileSpecifiedBySysProp(SystemProperties.PROPERTIES_FILE_PATH, e);
value = JBossEJBProperties.fromClassPath();
} else {
value = JBossEJBProperties.fromClassPath();
}
} catch (IOException e) {
}
onceRef.set(value);
}
}
}
return value;
});
}
// Remoting-specific properties
private final String endpointName;
private final AuthenticationConfiguration authenticationConfiguration;
private final String defaultCallbackHandlerClassName;
private final OptionMap endpointCreationOptions;
private final OptionMap remoteConnectionProviderCreationOptions;
// Connections
private final List connectionList;
// Security-specific properties
private final ExceptionSupplier callbackHandlerSupplier;
// Enterprise Bean discovery and clustering properties
private final ExceptionSupplier deploymentNodeSelectorSupplier;
private final Map clusterConfigurations;
// Other Enterprise Bean parameters
private final long invocationTimeout;
private final long reconnectTimeout;
private final String deploymentNodeSelectorClassName;
private final boolean defaultConnectEagerly;
// HTTP connections
private final List httpConnectionList;
JBossEJBProperties(final Builder builder) {
this.endpointName = builder.endpointName;
this.defaultCallbackHandlerClassName = builder.callbackHandlerClassName;
this.authenticationConfiguration = builder.authenticationConfiguration;
this.endpointCreationOptions = builder.endpointCreationOptions;
this.remoteConnectionProviderCreationOptions = builder.remoteConnectionProviderCreationOptions;
this.callbackHandlerSupplier = builder.callbackHandlerSupplier;
this.deploymentNodeSelectorSupplier = builder.deploymentNodeSelectorSupplier;
this.clusterConfigurations = builder.clusterConfigurations;
this.invocationTimeout = builder.invocationTimeout;
this.reconnectTimeout = builder.reconnectTimeout;
this.deploymentNodeSelectorClassName = builder.deploymentNodeSelectorClassName;
this.connectionList = builder.connectionList;
this.defaultConnectEagerly = builder.connectEagerly;
this.httpConnectionList = builder.httpConnectionList;
}
public String getEndpointName() {
return endpointName;
}
public String getDefaultCallbackHandlerClassName() {
return defaultCallbackHandlerClassName;
}
public AuthenticationConfiguration getAuthenticationConfiguration() {
return authenticationConfiguration;
}
public OptionMap getEndpointCreationOptions() {
return endpointCreationOptions;
}
public OptionMap getRemoteConnectionProviderCreationOptions() {
return remoteConnectionProviderCreationOptions;
}
public List getConnectionList() {
return connectionList;
}
public List getHttpConnectionList() {
return httpConnectionList;
}
public ExceptionSupplier getDefaultCallbackHandlerSupplier() {
return callbackHandlerSupplier;
}
public ExceptionSupplier getDeploymentNodeSelectorSupplier() {
return deploymentNodeSelectorSupplier;
}
public Map getClusterConfigurations() {
return clusterConfigurations;
}
public long getInvocationTimeout() {
return invocationTimeout;
}
public long getReconnectTimeout() {
return reconnectTimeout;
}
public String getDeploymentNodeSelectorClassName() {
return deploymentNodeSelectorClassName;
}
public boolean isDefaultConnectEagerly() {
return defaultConnectEagerly;
}
/**
* Get the context manager.
*
* @return the context manager (not {@code null})
*/
@NotNull
public ContextManager getInstanceContextManager() {
return getContextManager();
}
/**
* Get the context manager.
*
* @return the context manager (not {@code null})
*/
@NotNull
public static ContextManager getContextManager() {
return CONTEXT_MANAGER;
}
// Factories
private static OptionMap getOptionMapFromProperties(final Properties properties, final String propertyPrefix, final ClassLoader classLoader) {
return OptionMap.builder().parseAll(properties, propertyPrefix, classLoader).getMap();
}
private static long getLongValueFromProperties(final Properties properties, final String propertyName, final long defVal) {
final String str = getProperty(properties, propertyName, null, true);
if (str == null) {
return defVal;
}
try {
return Long.parseLong(str);
} catch (NumberFormatException e) {
return defVal;
}
}
private static int getIntValueFromProperties(final Properties properties, final String propertyName, final int defVal) {
final String str = getProperty(properties, propertyName, null, true);
if (str == null) {
return defVal;
}
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
return defVal;
}
}
private static String getProperty(final Properties properties, final String propertyName, final String defaultValue, final boolean expand) {
final String str = properties.getProperty(propertyName);
if (str == null) {
return defaultValue;
}
if (expand) {
final Expression expression = Expression.compile(str, Expression.Flag.LENIENT_SYNTAX);
return expression.evaluateWithPropertiesAndEnvironment(false);
} else {
return str.trim();
}
}
public static JBossEJBProperties fromProperties(final String fileName, final Properties properties) {
Assert.checkNotNullParam("fileName", fileName);
Assert.checkNotNullParam("properties", properties);
final ClassLoader classLoader = JBossEJBProperties.class.getClassLoader();
final Builder builder = new Builder();
builder.populateFromProperties(properties, "", classLoader, null);
// if it's null, leave it null so that Remoting can pick a unique (hopefully) name based on our standard properties
builder.setEndpointName(getProperty(properties, PROPERTY_KEY_ENDPOINT_NAME, null, true));
// default callback handler class
final String defaultCallbackHandlerClassName = getProperty(properties, PROPERTY_KEY_CALLBACK_HANDLER_CLASS, null, true);
builder.setCallbackHandlerClassName(defaultCallbackHandlerClassName);
builder.setCallbackHandlerSupplier(() ->
Class.forName(defaultCallbackHandlerClassName, true, classLoader).asSubclass(CallbackHandler.class).getConstructor().newInstance());
// endpoint creation options
builder.setEndpointCreationOptions(getOptionMapFromProperties(properties, ENDPOINT_CREATION_OPTIONS_PREFIX, classLoader));
// remote connection provider options
builder.setRemoteConnectionProviderCreationOptions(getOptionMapFromProperties(properties, REMOTE_CONNECTION_PROVIDER_CREATE_OPTIONS_PREFIX, classLoader));
// invocation timeout
builder.setInvocationTimeout(getLongValueFromProperties(properties, PROPERTY_KEY_INVOCATION_TIMEOUT, -1L));
// reconnect timeout
builder.setReconnectTimeout(getLongValueFromProperties(properties, PROPERTY_KEY_RECONNECT_TASKS_TIMEOUT, -1L));
builder.setDefaultCompression(getIntValueFromProperties(properties, PROPERTY_KEY_DEFAULT_COMPRESSION,-1));
// deployment node selector
final String deploymentNodeSelectorClassName = getProperty(properties, PROPERTY_KEY_DEPLOYMENT_NODE_SELECTOR, null, true);
if (deploymentNodeSelectorClassName != null) {
builder.setDeploymentNodeSelectorClassName(deploymentNodeSelectorClassName);
builder.setDeploymentNodeSelectorSupplier(() ->
Class.forName(deploymentNodeSelectorClassName, true, classLoader).asSubclass(DeploymentNodeSelector.class).getConstructor().newInstance());
}
// connections
final String connectionsString = getProperty(properties, PROPERTY_KEY_REMOTE_CONNECTIONS, "", true).trim();
final List connectionList;
if (! connectionsString.isEmpty()) {
final ArrayList mutableList = new ArrayList<>();
// Parse this the same way as the legacy code.
final StringTokenizer tokenizer = new StringTokenizer(connectionsString, ",");
while (tokenizer.hasMoreTokens()) {
final String connectionName = tokenizer.nextToken().trim();
if (! connectionName.isEmpty()) {
final ConnectionConfiguration.Builder connBuilder = new ConnectionConfiguration.Builder();
String prefix = "remote.connection." + connectionName + ".";
if (! connBuilder.populateFromProperties(properties, prefix, classLoader, builder, connectionName)) {
continue;
}
mutableList.add(new ConnectionConfiguration(connBuilder));
}
}
if (mutableList.isEmpty()) {
connectionList = Collections.emptyList();
} else {
mutableList.trimToSize();
connectionList = Collections.unmodifiableList(mutableList);
}
} else {
connectionList = Collections.emptyList();
}
builder.setConnectionList(connectionList);
// clusters
final String clustersString = getProperty(properties, PROPERTY_KEY_CLUSTERS, "", true).trim();
final Map clusterMap;
if (! clustersString.isEmpty()) {
final HashMap map = new HashMap<>();
final StringTokenizer tokenizer = new StringTokenizer(clustersString, ",");
while (tokenizer.hasMoreTokens()) {
final String clusterName = tokenizer.nextToken().trim();
if (! clusterName.isEmpty()) {
String prefix = "remote.cluster." + clusterName + ".";
final ClusterConfiguration.Builder clusterBuilder = new ClusterConfiguration.Builder();
clusterBuilder.populateFromProperties(clusterName, properties, prefix, classLoader, builder);
map.put(clusterName, new ClusterConfiguration(clusterBuilder));
}
}
if (map.isEmpty()) {
clusterMap = Collections.emptyMap();
} else {
clusterMap = Collections.unmodifiableMap(map);
}
} else {
clusterMap = Collections.emptyMap();
}
builder.setClusterConfigurations(clusterMap);
//http-connections
final String httpConnectionsString = getProperty(properties, PROPERTY_KEY_HTTP_CONNECTIONS, "", true).trim();
final List httpConnectionList;
if (!httpConnectionsString.isEmpty()) {
final ArrayList mutableList = new ArrayList<>();
// Parse this the same way as the legacy code.
final StringTokenizer tokenizer = new StringTokenizer(httpConnectionsString, ",");
while (tokenizer.hasMoreTokens()) {
final String connectionName = tokenizer.nextToken().trim();
if (!connectionName.isEmpty()) {
final HttpConnectionConfiguration.Builder connBuilder = new HttpConnectionConfiguration.Builder();
String prefix = "http.connection." + connectionName + ".";
if (!connBuilder.populateFromProperties(properties, prefix, connectionName)) {
continue;
}
mutableList.add(new HttpConnectionConfiguration(connBuilder));
}
}
if (mutableList.isEmpty()) {
httpConnectionList = Collections.emptyList();
} else {
mutableList.trimToSize();
httpConnectionList = Collections.unmodifiableList(mutableList);
}
} else {
httpConnectionList = Collections.emptyList();
}
builder.setHttpConnectionList(httpConnectionList);
return new JBossEJBProperties(builder);
}
public static JBossEJBProperties fromResource(final String fileName, final ExceptionBiFunction streamSupplier, T param1, U param2) throws IOException {
Assert.checkNotNullParam("fileName", fileName);
Assert.checkNotNullParam("streamSupplier", streamSupplier);
final InputStream stream;
try {
stream = streamSupplier.apply(param1, param2);
} catch (FileNotFoundException | NoSuchFileException e) {
return null;
}
if (stream == null) {
return null;
}
return fromResource(fileName, stream);
}
private static JBossEJBProperties fromResource(String fileName, InputStream stream) throws IOException {
try (InputStream inputStream = stream) {
try (BufferedInputStream bis = new BufferedInputStream(inputStream)) {
try (InputStreamReader reader = new InputStreamReader(bis, StandardCharsets.UTF_8)) {
final Properties properties = new Properties();
properties.load(reader);
return fromProperties(fileName, properties);
}
}
}
}
public static JBossEJBProperties fromResource(final String fileName, final ExceptionFunction streamSupplier, T param) throws IOException {
return fromResource(fileName, ExceptionFunction::apply, streamSupplier, param);
}
public static JBossEJBProperties fromResource(final String fileName, final ExceptionSupplier streamSupplier) throws IOException {
return fromResource(fileName, ExceptionSupplier::get, streamSupplier);
}
public static JBossEJBProperties fromFile(final File propertiesFile) throws IOException {
Assert.checkNotNullParam("propertiesFile", propertiesFile);
return fromResource(propertiesFile.getPath(), FileInputStream::new, propertiesFile);
}
public static JBossEJBProperties fromPath(final Path propertiesFile) throws IOException {
Assert.checkNotNullParam("propertiesFile", propertiesFile);
return fromResource(propertiesFile.toString(), Files::newInputStream, propertiesFile);
}
public static JBossEJBProperties fromClassPath(final ClassLoader classLoader, final String pathName) throws IOException {
if (classLoader == null) {
return fromResource(pathName, ClassLoader.getSystemResourceAsStream(pathName));
}
return fromResource(pathName, ClassLoader::getResourceAsStream, classLoader, pathName);
}
public static JBossEJBProperties fromClassPath() throws IOException {
return fromClassPath(JBossEJBProperties.class.getClassLoader(), DEFAULT_PATH_NAME);
}
static JBossEJBProperties getCurrent() {
return SUPPLIER.get();
}
static final class Builder extends CommonSubconfiguration.Builder {
String endpointName;
OptionMap endpointCreationOptions;
OptionMap remoteConnectionProviderCreationOptions;
List connectionList;
List httpConnectionList;
Map clusterConfigurations;
long invocationTimeout;
long reconnectTimeout;
String deploymentNodeSelectorClassName;
int defaultCompression;
ExceptionSupplier deploymentNodeSelectorSupplier;
Builder() {
}
Builder setEndpointName(final String endpointName) {
this.endpointName = endpointName;
return this;
}
Builder setEndpointCreationOptions(final OptionMap endpointCreationOptions) {
this.endpointCreationOptions = endpointCreationOptions;
return this;
}
Builder setRemoteConnectionProviderCreationOptions(final OptionMap remoteConnectionProviderCreationOptions) {
this.remoteConnectionProviderCreationOptions = remoteConnectionProviderCreationOptions;
return this;
}
Builder setConnectionList(final List connectionList) {
this.connectionList = connectionList;
return this;
}
Builder setClusterConfigurations(final Map clusterConfigurations) {
this.clusterConfigurations = clusterConfigurations;
return this;
}
Builder setInvocationTimeout(final long invocationTimeout) {
this.invocationTimeout = invocationTimeout;
return this;
}
Builder setReconnectTimeout(final long reconnectTimeout) {
this.reconnectTimeout = reconnectTimeout;
return this;
}
Builder setDeploymentNodeSelectorClassName(final String deploymentNodeSelectorClassName) {
this.deploymentNodeSelectorClassName = deploymentNodeSelectorClassName;
return this;
}
Builder setDeploymentNodeSelectorSupplier(final ExceptionSupplier deploymentNodeSelectorSupplier) {
this.deploymentNodeSelectorSupplier = deploymentNodeSelectorSupplier;
return this;
}
Builder setHttpConnectionList(final List httpConnectionList) {
this.httpConnectionList = httpConnectionList;
return this;
}
Builder setDefaultCompression(final int defaultCompression) {
this.defaultCompression = defaultCompression;
return this;
}
}
abstract static class CommonSubconfiguration {
private final OptionMap connectionOptions;
private final String callbackHandlerClassName;
private final ExceptionSupplier callbackHandlerSupplier;
private final long connectionTimeout;
private final OptionMap channelOptions;
private final boolean connectEagerly;
private final AuthenticationConfiguration authenticationConfiguration;
CommonSubconfiguration(Builder builder) {
this.connectionOptions = builder.connectionOptions;
this.callbackHandlerClassName = builder.callbackHandlerClassName;
this.callbackHandlerSupplier = builder.callbackHandlerSupplier;
this.connectionTimeout = builder.connectionTimeout;
this.channelOptions = builder.channelOptions;
this.connectEagerly = builder.connectEagerly;
this.authenticationConfiguration = builder.authenticationConfiguration;
}
public OptionMap getConnectionOptions() {
return connectionOptions;
}
public long getConnectionTimeout() {
return connectionTimeout;
}
public boolean isConnectEagerly() {
return connectEagerly;
}
public String getCallbackHandlerClassName() {
return callbackHandlerClassName;
}
public AuthenticationConfiguration getAuthenticationConfiguration() {
return authenticationConfiguration;
}
public OptionMap getChannelOptions() {
return channelOptions;
}
public ExceptionSupplier getCallbackHandlerSupplier() {
return callbackHandlerSupplier;
}
abstract static class Builder {
OptionMap connectionOptions;
String callbackHandlerClassName;
ExceptionSupplier callbackHandlerSupplier;
long connectionTimeout;
OptionMap channelOptions;
boolean connectEagerly;
AuthenticationConfiguration authenticationConfiguration;
Builder() {
}
Builder setConnectionOptions(final OptionMap connectionOptions) {
this.connectionOptions = connectionOptions;
return this;
}
Builder setCallbackHandlerClassName(final String callbackHandlerClassName) {
this.callbackHandlerClassName = callbackHandlerClassName;
return this;
}
Builder setCallbackHandlerSupplier(final ExceptionSupplier callbackHandlerSupplier) {
this.callbackHandlerSupplier = callbackHandlerSupplier;
return this;
}
Builder setConnectionTimeout(final long connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
Builder setChannelOptions(final OptionMap channelOptions) {
this.channelOptions = channelOptions;
return this;
}
Builder setConnectEagerly(final boolean connectEagerly) {
this.connectEagerly = connectEagerly;
return this;
}
Builder setAuthenticationConfiguration(final AuthenticationConfiguration authenticationConfiguration) {
this.authenticationConfiguration = authenticationConfiguration;
return this;
}
boolean populateFromProperties(final Properties properties, final String prefix, final ClassLoader classLoader, final Builder defaultsBuilder) {
// connection options
String connectOptionsPrefix = prefix + "connect.options" + ".";
setConnectionOptions(getOptionMapFromProperties(properties, connectOptionsPrefix, classLoader));
// connection timeout
setConnectionTimeout(getLongValueFromProperties(properties, prefix + "connect.timeout", defaultsBuilder == null ? 5000L : defaultsBuilder.connectionTimeout));
// connect eagerly
setConnectEagerly(Boolean.parseBoolean(getProperty(properties, prefix + "connect.eager", Boolean.toString(defaultsBuilder == null || defaultsBuilder.connectEagerly), true).trim()));
// callback handler class
final String callbackHandlerClassName = getProperty(properties, prefix + PROPERTY_KEY_CALLBACK_HANDLER_CLASS, null, true);
setCallbackHandlerClassName(callbackHandlerClassName);
final AuthenticationConfiguration.Builder authBuilder = new AuthenticationConfiguration.Builder();
if (authBuilder.populateFromProperties(properties, prefix, classLoader)) {
setAuthenticationConfiguration(new AuthenticationConfiguration(authBuilder));
} else {
if (defaultsBuilder != null) {
setAuthenticationConfiguration(defaultsBuilder.authenticationConfiguration);
}
}
setChannelOptions(getOptionMapFromProperties(properties, prefix + "channel.options" + ".", classLoader));
final ExceptionSupplier callbackHandlerSupplier =
() -> Class.forName(callbackHandlerClassName, true, classLoader).asSubclass(CallbackHandler.class).getConstructor().newInstance();
return true;
}
}
}
public static class ConnectionConfiguration extends CommonSubconfiguration {
private final String host;
private final int port;
private final String protocol;
ConnectionConfiguration(Builder builder) {
super(builder);
this.host = builder.host;
this.port = builder.port;
this.protocol = builder.protocol;
}
public String getHost() {
return host;
}
public int getPort() {
return port;
}
public String getProtocol() {
return protocol;
}
static final class Builder extends CommonSubconfiguration.Builder {
String host;
int port;
String protocol;
Builder() {
}
boolean populateFromProperties(final Properties properties, final String prefix, final ClassLoader classLoader, final CommonSubconfiguration.Builder defaultsBuilder) {
// just to ensure this overload isn't used by mistake
throw Assert.unsupported();
}
boolean populateFromProperties(final Properties properties, final String prefix, final ClassLoader classLoader, final CommonSubconfiguration.Builder defaultsBuilder, final String connectionName) {
super.populateFromProperties(properties, prefix, classLoader, defaultsBuilder);
// connection host name
String host = getProperty(properties,prefix + PROPERTY_KEY_HOST, "", true).trim();
if (host.isEmpty()) {
Logs.MAIN.skippingConnectionCreationDueToMissingHostOrPort(connectionName);
return false;
}
setHost(host);
// connection port#
String portStr = getProperty(properties,prefix + PROPERTY_KEY_PORT, "", true).trim();
if (portStr.isEmpty()) {
Logs.MAIN.skippingConnectionCreationDueToMissingHostOrPort(connectionName);
return false;
}
int port;
try {
port = Integer.parseInt(portStr);
} catch (NumberFormatException e) {
Logs.MAIN.skippingConnectionCreationDueToInvalidPortNumber(portStr, connectionName);
return false;
}
setPort(port);
String protocol = getProperty(properties, prefix + PROPERTY_KEY_PROTOCOL, DEFAULT_PROTOCOL, true).trim();
this.protocol = protocol;
return true;
}
Builder setHost(final String host) {
this.host = host;
return this;
}
Builder setPort(final int port) {
this.port = port;
return this;
}
}
}
public static class ClusterConfiguration extends CommonSubconfiguration {
private final String clusterName;
private final long maximumAllowedConnectedNodes;
private final String clusterNodeSelectorClassName;
private final ExceptionSupplier clusterNodeSelectorSupplier;
private final List nodeConfigurations;
ClusterConfiguration(final Builder builder) {
super(builder);
this.clusterName = builder.clusterName;
this.maximumAllowedConnectedNodes = builder.maximumAllowedConnectedNodes;
this.clusterNodeSelectorClassName = builder.clusterNodeSelectorClassName;
this.clusterNodeSelectorSupplier = builder.clusterNodeSelectorSupplier;
this.nodeConfigurations = builder.nodeConfigurations;
}
public String getClusterName() {
return clusterName;
}
public long getMaximumAllowedConnectedNodes() {
return maximumAllowedConnectedNodes;
}
public String getClusterNodeSelectorClassName() {
return clusterNodeSelectorClassName;
}
public ExceptionSupplier getClusterNodeSelectorSupplier() {
return clusterNodeSelectorSupplier;
}
public List getNodeConfigurations() {
return nodeConfigurations;
}
static final class Builder extends CommonSubconfiguration.Builder {
String clusterName;
long maximumAllowedConnectedNodes;
String clusterNodeSelectorClassName;
ExceptionSupplier clusterNodeSelectorSupplier;
List nodeConfigurations = new ArrayList();
Builder() {
}
Builder setClusterName(final String clusterName) {
this.clusterName = clusterName;
return this;
}
Builder setMaximumAllowedConnectedNodes(final long maximumAllowedConnectedNodes) {
this.maximumAllowedConnectedNodes = maximumAllowedConnectedNodes;
return this;
}
Builder setClusterNodeSelectorClassName(final String clusterNodeSelectorClassName) {
this.clusterNodeSelectorClassName = clusterNodeSelectorClassName;
return this;
}
Builder setClusterNodeSelectorSupplier(final ExceptionSupplier clusterNodeSelectorSupplier) {
this.clusterNodeSelectorSupplier = clusterNodeSelectorSupplier;
return this;
}
Builder setNodeConfigurations(final List nodeConfigurations) {
this.nodeConfigurations = nodeConfigurations;
return this;
}
boolean populateFromProperties(final String clusterName, final Properties properties, final String prefix, final ClassLoader classLoader, final CommonSubconfiguration.Builder defaultsBuilder) {
if (! super.populateFromProperties(properties, prefix, classLoader, defaultsBuilder)) {
return false;
}
if (clusterName == null) {
return false;
}
setClusterName(clusterName);
setMaximumAllowedConnectedNodes(getLongValueFromProperties(properties, prefix + "max-allowed-connected-nodes", -1L));
final String clusterNodeSelectorClassName = getProperty(properties, prefix + "clusternode.selector", null, true);
if (clusterNodeSelectorClassName != null) {
setClusterNodeSelectorClassName(clusterNodeSelectorClassName);
setClusterNodeSelectorSupplier(() ->
Class.forName(clusterNodeSelectorClassName, true, classLoader).asSubclass(ClusterNodeSelector.class).getConstructor().newInstance()
);
}
final HashSet nodeNames = new HashSet<>();
// the cluster prefix already has a trailing dot
final String nodePrefix = prefix + "node" + ".";
final int prefixLen = nodePrefix.length();
final List nodes = new ArrayList();
String nodeName;
for (String propertyName : properties.stringPropertyNames()) {
if (propertyName.startsWith(nodePrefix)) {
int idx = propertyName.indexOf('.', prefixLen);
if (idx != -1) {
nodeName = propertyName.substring(prefixLen, idx);
} else {
nodeName = propertyName.substring(prefixLen);
}
if (nodeNames.add(nodeName)) {
final ClusterNodeConfiguration.Builder builder = new ClusterNodeConfiguration.Builder();
builder.setNodeName(nodeName);
if (builder.populateFromProperties(properties, nodePrefix + nodeName + ".", classLoader, this)) {
nodes.add(new ClusterNodeConfiguration(builder));
}
}
}
// otherwise ignore it
}
setNodeConfigurations(nodes);
return true;
}
}
}
public static final class ClusterNodeConfiguration extends CommonSubconfiguration {
private final String nodeName;
ClusterNodeConfiguration(final Builder builder) {
super(builder);
this.nodeName = builder.nodeName;
}
public String getNodeName() {
return nodeName;
}
static final class Builder extends CommonSubconfiguration.Builder {
String nodeName;
Builder() {
}
Builder setNodeName(final String nodeName) {
this.nodeName = nodeName;
return this;
}
}
}
public static final class AuthenticationConfiguration {
private final String userName;
private final String password;
private final String mechanismRealm;
private final String callbackHandlerClassName;
private final ExceptionSupplier callbackHandlerSupplier;
AuthenticationConfiguration(Builder builder) {
userName = builder.userName;
password = builder.password;
mechanismRealm = builder.mechanismRealm;
callbackHandlerClassName = builder.callbackHandlerClassName;
callbackHandlerSupplier = builder.callbackHandlerSupplier;
}
public String getUserName() {
return userName;
}
public String getPassword() {
return password;
}
public String getMechanismRealm() {
return mechanismRealm;
}
public String getCallbackHandlerClassName() {
return callbackHandlerClassName;
}
public ExceptionSupplier getCallbackHandlerSupplier() {
return callbackHandlerSupplier;
}
static final class Builder {
String userName;
String password;
String mechanismRealm;
String callbackHandlerClassName;
ExceptionSupplier callbackHandlerSupplier;
Builder() {
}
Builder setUserName(final String userName) {
this.userName = userName;
return this;
}
Builder setPassword(final String password) {
this.password = password;
return this;
}
Builder setMechanismRealm(final String mechanismRealm) {
this.mechanismRealm = mechanismRealm;
return this;
}
Builder setCallbackHandlerClassName(final String callbackHandlerClassName) {
this.callbackHandlerClassName = callbackHandlerClassName;
return this;
}
Builder setCallbackHandlerSupplier(final ExceptionSupplier callbackHandlerSupplier) {
this.callbackHandlerSupplier = callbackHandlerSupplier;
return this;
}
boolean populateFromProperties(final Properties properties, final String prefix, final ClassLoader classLoader) {
final String userName = getProperty(properties, prefix + PROPERTY_KEY_USERNAME, null, true);
if (userName != null) {
setUserName(userName);
}
final String mechanismRealm = getProperty(properties, prefix + PROPERTY_KEY_REALM, null, true);
if (mechanismRealm != null) {
setMechanismRealm(mechanismRealm);
}
final String finalPassword;
final String b64Password = getProperty(properties, prefix + PROPERTY_KEY_PASSWORD_BASE64, null, EXPAND_PASSWORDS);
if (b64Password != null) {
setPassword(CodePointIterator.ofString(b64Password).base64Decode().asUtf8String().drainToString());
} else {
final String password = getProperty(properties, prefix + PROPERTY_KEY_PASSWORD, null, EXPAND_PASSWORDS);
if (password != null) {
setPassword(password);
} else {
final String callbackHandlerClassName = getProperty(properties, prefix + PROPERTY_KEY_CALLBACK_HANDLER_CLASS, null, true);
if (callbackHandlerClassName != null) {
setCallbackHandlerClassName(callbackHandlerClassName);
setCallbackHandlerSupplier(() ->
Class.forName(callbackHandlerClassName, true, classLoader).asSubclass(CallbackHandler.class).getConstructor().newInstance());
} else {
if (userName == null) {
return false;
}
}
}
}
return true;
}
}
}
public static class HttpConnectionConfiguration {
private final String uri;
HttpConnectionConfiguration(Builder builder) {
this.uri = builder.uri;
}
public String getUri() {
return uri;
}
static final class Builder {
String uri;
Builder() {
}
boolean populateFromProperties(final Properties properties, final String prefix, final String connectionName) {
// connection host name
String uri = getProperty(properties, prefix + PROPERTY_KEY_URI, "", true).trim();
if (uri.isEmpty()) {
Logs.MAIN.skippingHttpConnectionCreationDueToMissingUri(connectionName);
return false;
}
setUri(uri);
return true;
}
Builder setUri(final String uri) {
this.uri = uri;
return this;
}
}
}
}