From a23d13bc69b714371933a4f183f784ef55fc0228 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Mon, 23 Jan 2017 12:56:44 -0600 Subject: [PATCH 1/2] Restore requireCurrent() method --- src/main/java/org/jboss/ejb/client/EJBClientContext.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/jboss/ejb/client/EJBClientContext.java b/src/main/java/org/jboss/ejb/client/EJBClientContext.java index d8fbb78ec..8691373df 100644 --- a/src/main/java/org/jboss/ejb/client/EJBClientContext.java +++ b/src/main/java/org/jboss/ejb/client/EJBClientContext.java @@ -297,6 +297,15 @@ public static EJBClientContext getCurrent() { return clientContext; } + /** + * Get the current client context for this thread. Delegates to {@link #getCurrent()}. + * + * @return the current client context for this thread + */ + public static EJBClientContext requireCurrent() { + return getCurrent(); + } + StatefulEJBLocator createSession(final StatelessEJBLocator statelessLocator) throws Exception { final LocatedAction, StatelessEJBLocator, T> action = (receiver, originalLocator, newAffinity) -> receiver.createSession(originalLocator.withNewAffinity(newAffinity)); From c810cca2c0c22d6d636d4520816260fdec72c257 Mon Sep 17 00:00:00 2001 From: "David M. Lloyd" Date: Tue, 24 Jan 2017 10:20:47 -0600 Subject: [PATCH 2/2] Completed legacy configuration initial implementation --- pom.xml | 15 +- .../java/org/jboss/ejb/_private/Logs.java | 31 +- ...gurationBasedEJBClientContextSelector.java | 23 +- .../legacy/CommonLegacyConfiguration.java | 56 ++ .../legacy/DiscoveryLegacyConfiguration.java | 61 ++ .../legacy/ElytronLegacyConfiguration.java | 142 +++ .../ejb/client/legacy/JBossEJBProperties.java | 913 ++++++++++++++++++ .../legacy/LegacyPropertiesConfiguration.java | 567 +---------- .../client/legacy/LegacyPropertiesLoader.java | 151 --- .../legacy/PropertiesValueResolver.java | 174 ---- .../RemotingConnectionConfiguration.java | 97 -- .../legacy/RemotingLegacyConfiguration.java | 87 ++ 12 files changed, 1331 insertions(+), 986 deletions(-) create mode 100644 src/main/java/org/jboss/ejb/client/legacy/CommonLegacyConfiguration.java create mode 100644 src/main/java/org/jboss/ejb/client/legacy/DiscoveryLegacyConfiguration.java create mode 100644 src/main/java/org/jboss/ejb/client/legacy/ElytronLegacyConfiguration.java create mode 100644 src/main/java/org/jboss/ejb/client/legacy/JBossEJBProperties.java delete mode 100644 src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesLoader.java delete mode 100644 src/main/java/org/jboss/ejb/client/legacy/PropertiesValueResolver.java delete mode 100644 src/main/java/org/jboss/ejb/client/legacy/RemotingConnectionConfiguration.java create mode 100644 src/main/java/org/jboss/ejb/client/legacy/RemotingLegacyConfiguration.java diff --git a/pom.xml b/pom.xml index c92b67f98..78415370b 100644 --- a/pom.xml +++ b/pom.xml @@ -39,8 +39,8 @@ 1.5.0.Beta3 - 3.1.4.GA - 1.1.0.Final + 3.3.0.Final + 2.0.1.Final 1.5.2.Final 2.0.0.Beta2 1.4.3.Final @@ -49,7 +49,7 @@ 1.0.0.Final 3.4.2.Final 1.7 - 1.2.0.Beta3 + 1.2.0.Beta4 1.0.0.Beta8 1.0.0.Beta5 1.1.0.Beta17 @@ -151,7 +151,14 @@ org.jboss.logging jboss-logging-processor - ${version.org.jboss.logging.jboss-logging-processor} + ${version.org.jboss.logging.jboss-logging-tools} + provided + + + + org.jboss.logging + jboss-logging-annotations + ${version.org.jboss.logging.jboss-logging-tools} provided diff --git a/src/main/java/org/jboss/ejb/_private/Logs.java b/src/main/java/org/jboss/ejb/_private/Logs.java index 86abdca17..d7021c01c 100644 --- a/src/main/java/org/jboss/ejb/_private/Logs.java +++ b/src/main/java/org/jboss/ejb/_private/Logs.java @@ -34,6 +34,7 @@ import org.jboss.logging.Logger; import org.jboss.logging.annotations.Message; import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.Once; import org.jboss.logging.annotations.Property; import org.jboss.remoting3.Channel; @@ -242,7 +243,9 @@ public interface Logs extends BasicLogger { IllegalStateException cannotSpecifyBothPlainTextAndEncodedPassword(); // @Message(id = 57, value = "%s not of type org.jboss.ejb.client.DeploymentNodeSelector") - // @Message(id = 58, value = "Could not create the deployment node selector") + + @Message(id = 58, value = "Failed to instantiate deployment node selector class \"%s\"") + IllegalArgumentException cannotInstantiateDeploymentNodeSelector(String name, @Cause ReflectiveOperationException e); @LogMessage(level = WARN) @Message(id = 59, value = "Could not send a message over remoting channel, to cancel invocation for invocation id %s") @@ -274,6 +277,32 @@ public interface Logs extends BasicLogger { @Message(id = 67, value = "Cannot convert %s to stateful") IllegalArgumentException cannotConvertToStateful(EJBLocator locator); + @Message(id = 68, value = "Failed to instantiate callback handler class \"%s\"") + IllegalArgumentException cannotInstantiateCallbackHandler(String name, @Cause ReflectiveOperationException e); + + @Once + @LogMessage + @Message(id = 69, value = "Using legacy jboss-ejb-client.properties security configuration") + void legacyEJBPropertiesSecurityConfigurationInUse(); + + @Once + @LogMessage + @Message(id = 70, value = "Using legacy jboss-ejb-client.properties Remoting configuration") + void legacyEJBPropertiesRemotingConfigurationInUse(); + + @Once + @LogMessage + @Message(id = 71, value = "Using legacy jboss-ejb-client.properties discovery configuration") + void legacyEJBPropertiesDiscoveryConfigurationInUse(); + + @Once + @LogMessage + @Message(id = 72, value = "Using legacy jboss-ejb-client.properties EJB client configuration") + void legacyEJBPropertiesEJBConfigurationInUse(); + + @Message(id = 73, value = "Failed to construct Remoting endpoint") + IllegalStateException failedToConstructEndpoint(@Cause IOException e); + // Proxy API errors @Message(id = 100, value = "Object '%s' is not a valid proxy object") diff --git a/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java b/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java index e53f080cc..ad1e3e237 100644 --- a/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java +++ b/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java @@ -22,21 +22,18 @@ package org.jboss.ejb.client; -import static javax.xml.stream.XMLStreamConstants.*; +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; import java.net.URI; -import java.net.URISyntaxException; import java.util.Collections; import java.util.Iterator; -import java.util.Properties; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.function.Supplier; import org.jboss.ejb._private.Logs; import org.jboss.ejb.client.legacy.LegacyPropertiesConfiguration; -import org.jboss.ejb.client.legacy.LegacyPropertiesLoader; -import org.jboss.ejb.client.legacy.RemotingConnectionConfiguration; import org.jboss.modules.Module; import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoadException; @@ -63,27 +60,15 @@ private static EJBClientContext loadConfiguration() { final ClientConfiguration clientConfiguration = ClientConfiguration.getInstance(); final ClassLoader classLoader = ConfigurationBasedEJBClientContextSelector.class.getClassLoader(); final EJBClientContext.Builder builder = new EJBClientContext.Builder(); + loadTransportProviders(builder, classLoader); if (clientConfiguration != null) try { try (final ConfigurationXMLStreamReader streamReader = clientConfiguration.readConfiguration(Collections.singleton(NS_EJB_CLIENT_3_0))) { parseEJBClientConfiguration(streamReader, builder); } - loadTransportProviders(builder, classLoader); } catch (ConfigXMLParseException e) { throw new IllegalStateException(e); } - final Properties props = LegacyPropertiesLoader.loadEJBClientProperties(); - final LegacyPropertiesConfiguration configuration = new LegacyPropertiesConfiguration(props); - for (RemotingConnectionConfiguration connectionConfiguration : configuration.getConnectionConfigurations()) { - final String uriString = connectionConfiguration.getProtocol() + "://" + connectionConfiguration.getHost() + ":" + connectionConfiguration.getPort(); - try { - URI uri = new URI(uriString); - final EJBClientConnection.Builder connectionBuilder = new EJBClientConnection.Builder(); - connectionBuilder.setDestination(uri); - builder.addClientConnection(connectionBuilder.build()); - } catch (URISyntaxException e) { - new IllegalStateException(e); - } - } + LegacyPropertiesConfiguration.configure(builder); return builder.build(); } diff --git a/src/main/java/org/jboss/ejb/client/legacy/CommonLegacyConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/CommonLegacyConfiguration.java new file mode 100644 index 000000000..ec7d30807 --- /dev/null +++ b/src/main/java/org/jboss/ejb/client/legacy/CommonLegacyConfiguration.java @@ -0,0 +1,56 @@ +/* + * 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 java.net.URI; +import java.net.URISyntaxException; + +import org.xnio.OptionMap; +import org.xnio.Options; + +/** + * @author David M. Lloyd + */ +final class CommonLegacyConfiguration { + private CommonLegacyConfiguration() { + } + + static URI getUri(final JBossEJBProperties.ConnectionConfiguration connectionConfiguration, final OptionMap connectionOptions) { + final String host = connectionConfiguration.getHost(); + if (host == null) { + return null; + } + final int port = connectionConfiguration.getPort(); + if (port == -1) { + return null; + } + final String protocol; + if (connectionOptions.get(Options.SECURE, false) && connectionOptions.get(Options.SSL_ENABLED, false)) { + protocol = "remote+https"; + } else { + protocol = "remote+http"; + } + try { + return new URI(protocol, null, host, port, null, null, null); + } catch (URISyntaxException e) { + return null; + } + + } +} diff --git a/src/main/java/org/jboss/ejb/client/legacy/DiscoveryLegacyConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/DiscoveryLegacyConfiguration.java new file mode 100644 index 000000000..03846f3e6 --- /dev/null +++ b/src/main/java/org/jboss/ejb/client/legacy/DiscoveryLegacyConfiguration.java @@ -0,0 +1,61 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.jboss.ejb._private.Logs; +import org.kohsuke.MetaInfServices; +import org.wildfly.discovery.ServiceURL; +import org.wildfly.discovery.impl.StaticDiscoveryProvider; +import org.wildfly.discovery.spi.DiscoveryProvider; +import org.wildfly.discovery.spi.ExternalDiscoveryConfigurator; +import org.wildfly.discovery.spi.RegistryProvider; + +/** + * The interface to merge EJB properties into the discovery configuration. + * + * @author David M. Lloyd + */ +@MetaInfServices +public final class DiscoveryLegacyConfiguration implements ExternalDiscoveryConfigurator { + public void configure(final Consumer discoveryProviderConsumer, final Consumer registryProviderConsumer) { + final JBossEJBProperties ejbProperties = JBossEJBProperties.getCurrent(); + if (ejbProperties == null) { + return; + } + + final List list = new ArrayList<>(); + + for (Map.Entry entry : ejbProperties.getClusterConfigurations().entrySet()) { + final String name = entry.getKey(); + final JBossEJBProperties.ClusterConfiguration configuration = entry.getValue(); + + // todo: construct URI and map cluster:name to it + } + + if (! list.isEmpty()) { + Logs.MAIN.legacyEJBPropertiesDiscoveryConfigurationInUse(); + discoveryProviderConsumer.accept(new StaticDiscoveryProvider(list)); + } + } +} diff --git a/src/main/java/org/jboss/ejb/client/legacy/ElytronLegacyConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/ElytronLegacyConfiguration.java new file mode 100644 index 000000000..cb0623b34 --- /dev/null +++ b/src/main/java/org/jboss/ejb/client/legacy/ElytronLegacyConfiguration.java @@ -0,0 +1,142 @@ +/* + * 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 java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +import org.jboss.ejb._private.Logs; +import org.kohsuke.MetaInfServices; +import org.wildfly.common.function.ExceptionSupplier; +import org.wildfly.security.auth.client.AuthenticationConfiguration; +import org.wildfly.security.auth.client.AuthenticationContext; +import org.wildfly.security.auth.client.LegacyConfiguration; +import org.wildfly.security.auth.client.MatchRule; +import org.wildfly.security.sasl.localuser.LocalUserClient; +import org.xnio.OptionMap; +import org.xnio.Options; +import org.xnio.sasl.SaslUtils; + +/** + * The interface to merge EJB properties into the Elytron configuration. + * + * @author David M. Lloyd + */ +@MetaInfServices +public final class ElytronLegacyConfiguration implements LegacyConfiguration { + + private static final boolean useQuietAuth = AccessController.doPrivileged((PrivilegedAction) () -> Boolean.valueOf(System.getProperty("jboss.sasl.local-user.quiet-auth", "true"))).booleanValue(); + private static final String[] NO_STRINGS = new String[0]; + + public AuthenticationContext getConfiguredAuthenticationContext() { + AuthenticationContext context = AuthenticationContext.empty(); + + final JBossEJBProperties properties = JBossEJBProperties.getCurrent(); + + if (properties == null) { + return context; + } + + Logs.MAIN.legacyEJBPropertiesSecurityConfigurationInUse(); + + for (JBossEJBProperties.ConnectionConfiguration configuration : properties.getConnectionList()) { + // we don't actually care about the protocol for Elytron configuration + MatchRule rule = MatchRule.ALL.matchAbstractType("ejb", "jboss"); + AuthenticationConfiguration config = AuthenticationConfiguration.EMPTY; + + final String host = configuration.getHost(); + if (host == null) { + // skip + continue; + } + rule = rule.matchHost(host); + final int port = configuration.getPort(); + if (port != -1) { + rule = rule.matchPort(port); + } + + config = configureCommon(properties, configuration, config); + + context = context.with( + rule, + config + ); + } + + for (Map.Entry entry : properties.getClusterConfigurations().entrySet()) { + final String clusterName = entry.getKey(); + final JBossEJBProperties.ClusterConfiguration configuration = entry.getValue(); + + for (JBossEJBProperties.ClusterNodeConfiguration nodeConfiguration : configuration.getNodeConfigurations()) { + MatchRule rule = MatchRule.ALL.matchAbstractType("ejb", "jboss"); + AuthenticationConfiguration config = AuthenticationConfiguration.EMPTY; + + rule = rule.matchProtocol("node"); + rule = rule.matchUrnName(nodeConfiguration.getNodeName()); + + config = configureCommon(properties, configuration, config); + + context = context.with( + rule, + config + ); + } + } + return context; + } + + private static AuthenticationConfiguration configureCommon(final JBossEJBProperties properties, final JBossEJBProperties.CommonSubconfiguration configuration, AuthenticationConfiguration config) { + final JBossEJBProperties.AuthenticationConfiguration authenticationConfiguration = configuration.getAuthenticationConfiguration(); + final String userName = authenticationConfiguration.getUserName(); + if (userName != null) config = config.useName(userName); + final String realm = authenticationConfiguration.getMechanismRealm(); + if (realm != null) config = config.useRealm(realm); + final ExceptionSupplier callbackHandlerSupplier = configuration.getCallbackHandlerSupplier(); + if (callbackHandlerSupplier != null) { + final CallbackHandler callbackHandler; + try { + callbackHandler = callbackHandlerSupplier.get(); + } catch (ReflectiveOperationException e) { + throw Logs.MAIN.cannotInstantiateCallbackHandler(properties.getDefaultCallbackHandlerClassName(), e); + } + config = config.useCallbackHandler(callbackHandler); + } + final String password = authenticationConfiguration.getPassword(); + if (password != null) config = config.usePassword(password); + + final OptionMap options = configuration.getConnectionOptions(); + @SuppressWarnings({"unchecked", "rawtypes"}) + final Map props = (Map) SaslUtils.createPropertyMap(options, false); + if (props.isEmpty()) { + config = config.useMechanismProperties(props); + } else { + config = config.useMechanismProperties(Collections.singletonMap(LocalUserClient.QUIET_AUTH, Boolean.toString(useQuietAuth))); + } + if (options.contains(Options.SASL_DISALLOWED_MECHANISMS)) { + config = config.forbidSaslMechanisms(options.get(Options.SASL_DISALLOWED_MECHANISMS).toArray(NO_STRINGS)); + } else if (options.contains(Options.SASL_MECHANISMS)) { + config = config.allowSaslMechanisms(options.get(Options.SASL_MECHANISMS).toArray(NO_STRINGS)); + } + return config; + } +} diff --git a/src/main/java/org/jboss/ejb/client/legacy/JBossEJBProperties.java b/src/main/java/org/jboss/ejb/client/legacy/JBossEJBProperties.java new file mode 100644 index 000000000..585f682b6 --- /dev/null +++ b/src/main/java/org/jboss/ejb/client/legacy/JBossEJBProperties.java @@ -0,0 +1,913 @@ +/* + * 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.security.util.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"; + + 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 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 DEFAULT_PROTOCOL = "http-remoting"; + + private static final String PROPERTY_KEY_CLUSTERS = "remote.clusters"; + + private static final boolean expandPasswords; + + static { + expandPasswords = doPrivileged((PrivilegedAction) () -> + Boolean.valueOf(System.getProperty("jboss-ejb-client.expandPasswords", "false"))).booleanValue(); + final AtomicReference onceRef = new AtomicReference<>(); + CONTEXT_MANAGER.setGlobalDefaultSupplier(() -> { + JBossEJBProperties value = onceRef.get(); + if (value == null) { + synchronized (onceRef) { + value = onceRef.get(); + if (value == null) { + try { + 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; + + // EJB discovery and clustering properties + + private final ExceptionSupplier deploymentNodeSelectorSupplier; + private final Map clusterConfigurations; + + // Other EJB parameters + + private final long invocationTimeout; + private final long reconnectTimeout; + private final String deploymentNodeSelectorClassName; + private final boolean defaultConnectEagerly; + + 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; + } + + 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 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 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)); + + // deployment node selector + final String deploymentNodeSelectorClassName = getProperty(properties, PROPERTY_KEY_DEPLOYMENT_NODE_SELECTOR, null, true); + 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()) { + String connectionName = tokenizer.nextToken(); + final String trimmed = connectionName.trim(); + if (! trimmed.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(); + final String trimmed = clusterName.trim(); + if (! trimmed.isEmpty()) { + String prefix = "remote.cluster." + clusterName + "."; + + final ClusterConfiguration.Builder clusterBuilder = new ClusterConfiguration.Builder(); + clusterBuilder.populateFromProperties(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); + + 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; + } + 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 { + 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; + Map clusterConfigurations; + long invocationTimeout; + long reconnectTimeout; + String deploymentNodeSelectorClassName; + ExceptionSupplier deploymentNodeSelectorSupplier; + + Builder() { + } + + void setEndpointName(final String endpointName) { + this.endpointName = endpointName; + } + + void setEndpointCreationOptions(final OptionMap endpointCreationOptions) { + this.endpointCreationOptions = endpointCreationOptions; + } + + void setRemoteConnectionProviderCreationOptions(final OptionMap remoteConnectionProviderCreationOptions) { + this.remoteConnectionProviderCreationOptions = remoteConnectionProviderCreationOptions; + } + + void setConnectionList(final List connectionList) { + this.connectionList = connectionList; + } + + void setClusterConfigurations(final Map clusterConfigurations) { + this.clusterConfigurations = clusterConfigurations; + } + + void setInvocationTimeout(final long invocationTimeout) { + this.invocationTimeout = invocationTimeout; + } + + void setReconnectTimeout(final long reconnectTimeout) { + this.reconnectTimeout = reconnectTimeout; + } + + void setDeploymentNodeSelectorClassName(final String deploymentNodeSelectorClassName) { + this.deploymentNodeSelectorClassName = deploymentNodeSelectorClassName; + } + + void setDeploymentNodeSelectorSupplier(final ExceptionSupplier deploymentNodeSelectorSupplier) { + this.deploymentNodeSelectorSupplier = deploymentNodeSelectorSupplier; + } + } + + 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() { + } + + void setConnectionOptions(final OptionMap connectionOptions) { + this.connectionOptions = connectionOptions; + } + + void setCallbackHandlerClassName(final String callbackHandlerClassName) { + this.callbackHandlerClassName = callbackHandlerClassName; + } + + void setCallbackHandlerSupplier(final ExceptionSupplier callbackHandlerSupplier) { + this.callbackHandlerSupplier = callbackHandlerSupplier; + } + + void setConnectionTimeout(final long connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + + void setChannelOptions(final OptionMap channelOptions) { + this.channelOptions = channelOptions; + } + + void setConnectEagerly(final boolean connectEagerly) { + this.connectEagerly = connectEagerly; + } + + void setAuthenticationConfiguration(final AuthenticationConfiguration authenticationConfiguration) { + this.authenticationConfiguration = authenticationConfiguration; + } + + 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, connectOptionsPrefix + "connect.timeout", defaultsBuilder == null ? 5000L : defaultsBuilder.connectionTimeout)); + + // connect eagerly + setConnectEagerly(Boolean.parseBoolean(getProperty(properties, connectOptionsPrefix + "connect.eager", Boolean.toString(defaultsBuilder == null || defaultsBuilder.connectEagerly), true).trim())); + + // callback handler class + final String callbackHandlerClassName = getProperty(properties, connectOptionsPrefix + 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; + + ConnectionConfiguration(Builder builder) { + super(builder); + this.host = builder.host; + this.port = builder.port; + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + static final class Builder extends CommonSubconfiguration.Builder { + String host; + int port; + + 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 + "host", "", true).trim(); + if (host.isEmpty()) { + Logs.MAIN.skippingConnectionCreationDueToMissingHostOrPort(connectionName); + return false; + } + setHost(host); + + // connection port# + String portStr = getProperty(properties,prefix + "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); + return true; + } + + void setHost(final String host) { + this.host = host; + } + + void setPort(final int port) { + this.port = port; + } + } + } + + 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; + + Builder() { + } + + void setClusterName(final String clusterName) { + this.clusterName = clusterName; + } + + void setMaximumAllowedConnectedNodes(final long maximumAllowedConnectedNodes) { + this.maximumAllowedConnectedNodes = maximumAllowedConnectedNodes; + } + + void setClusterNodeSelectorClassName(final String clusterNodeSelectorClassName) { + this.clusterNodeSelectorClassName = clusterNodeSelectorClassName; + } + + void setClusterNodeSelectorSupplier(final ExceptionSupplier clusterNodeSelectorSupplier) { + this.clusterNodeSelectorSupplier = clusterNodeSelectorSupplier; + } + + void setNodeConfigurations(final List nodeConfigurations) { + this.nodeConfigurations = nodeConfigurations; + } + + boolean populateFromProperties(final Properties properties, final String prefix, final ClassLoader classLoader, final CommonSubconfiguration.Builder defaultsBuilder) { + if (! super.populateFromProperties(properties, prefix, classLoader, defaultsBuilder)) { + return false; + } + final String clusterName = getProperty(properties, prefix, null, true); + if (clusterName == null) { + return false; + } + setClusterName(clusterName); + setMaximumAllowedConnectedNodes(getLongValueFromProperties(properties, prefix + "max-allowed-connected-nodes", 1000L)); + 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<>(); + final String nodePrefix = prefix + ".node."; + final int prefixLen = nodePrefix.length(); + 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(); + if (builder.populateFromProperties(properties, prefix + "." + nodeName + ".", classLoader, this)) { + nodeConfigurations.add(new ClusterNodeConfiguration(builder)); + } + } + } + // otherwise ignore it + } + 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() { + } + + void setNodeName(final String nodeName) { + this.nodeName = nodeName; + } + } + } + + 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() { + } + + void setUserName(final String userName) { + this.userName = userName; + } + + void setPassword(final String password) { + this.password = password; + } + + void setMechanismRealm(final String mechanismRealm) { + this.mechanismRealm = mechanismRealm; + } + + void setCallbackHandlerClassName(final String callbackHandlerClassName) { + this.callbackHandlerClassName = callbackHandlerClassName; + } + + void setCallbackHandlerSupplier(final ExceptionSupplier callbackHandlerSupplier) { + this.callbackHandlerSupplier = callbackHandlerSupplier; + } + + 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 finalPassword; + final String b64Password = getProperty(properties, prefix + PROPERTY_KEY_PASSWORD_BASE64, null, expandPasswords); + if (b64Password != null) { + setPassword(CodePointIterator.ofString(b64Password).base64Decode().asUtf8String().drainToString()); + } else { + final String password = getProperty(properties, prefix + PROPERTY_KEY_PASSWORD, null, expandPasswords); + 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; + } + } + } +} diff --git a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java index 838409309..38292aef2 100644 --- a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java +++ b/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesConfiguration.java @@ -22,560 +22,47 @@ package org.jboss.ejb.client.legacy; -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; +import java.net.URI; +import java.util.List; 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.logging.Logger; -import org.xnio.Option; +import org.jboss.ejb._private.Logs; +import org.jboss.ejb.client.EJBClientConnection; +import org.jboss.ejb.client.EJBClientContext; import org.xnio.OptionMap; -import org.xnio.Options; /** - * Client configuration which is configured through {@link Properties}. Some well known - * properties will be looked for in the {@link Properties} that is passed to the {@link #LegacyPropertiesConfiguration(java.util.Properties) constructor}, - * for setting up the configurations + * Client configuration which is configured through {@link Properties}. * - * @author Jaikiran Pai - * @author Tomasz Adamski -1 - + * @author David M. Lloyd */ public class LegacyPropertiesConfiguration { - private static final Logger logger = Logger.getLogger(LegacyPropertiesConfiguration.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 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 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 long invocationTimeout = 0; - private long reconnectTasksTimeout = 0; - - private static final boolean expandPasswords = Boolean.valueOf( - System.getProperty("jboss-ejb-client.expandPasswords", "false")).booleanValue(); - - public LegacyPropertiesConfiguration(final Properties properties) { - final Properties resolvedProperties = new Properties(); + public static void configure(final EJBClientContext.Builder builder) { + final JBossEJBProperties properties = JBossEJBProperties.getCurrent(); 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(); - } - - public String getEndpointName() { - return this.endPointName; - } - - public OptionMap getEndpointCreationOptions() { - return this.endPointCreationOptions; - } - - public OptionMap getRemoteConnectionProviderCreationOptions() { - return this.remoteConnectionProviderCreationOptions; - } - - public CallbackHandler getCallbackHandler() { - return this.callbackHandler; - } - - public Collection getConnectionConfigurations() { - return this.remotingConnectionConfigurations; - } - - public long getInvocationTimeout() { - return this.invocationTimeout; - } - - public long getReconnectTasksTimeout() { - return this.reconnectTasksTimeout; - } - + Logs.MAIN.legacyEJBPropertiesEJBConfigurationInUse(); - - 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)); - } - } - - // 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(); - } - - 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 LegacyPropertiesConfiguration} class. Else returns the {@link Thread#getContextClassLoader()} - * - * @return - */ - private static ClassLoader getClientClassLoader() { - if (System.getSecurityManager() == null) { - return Thread.currentThread().getContextClassLoader(); - } else { - return AccessController.doPrivileged((PrivilegedAction)() -> Thread.currentThread().getContextClassLoader()); - } - } - - 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()); - } - - // 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; - } - - /** - * 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); - throw e; + final List connectionList = properties.getConnectionList(); + for (JBossEJBProperties.ConnectionConfiguration connectionConfiguration : connectionList) { + final String host = connectionConfiguration.getHost(); + if (host == null) { + continue; } - } 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); + final int port = connectionConfiguration.getPort(); + if (port == -1) { + continue; + } + final String protocol; + final OptionMap connectionOptions = connectionConfiguration.getConnectionOptions(); + final URI uri = CommonLegacyConfiguration.getUri(connectionConfiguration, connectionOptions); + if (uri == null) { + continue; } + final EJBClientConnection.Builder connectionBuilder = new EJBClientConnection.Builder(); + connectionBuilder.setDestination(uri); + builder.addClientConnection(connectionBuilder.build()); } } - - @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; - } } } diff --git a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesLoader.java b/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesLoader.java deleted file mode 100644 index bbe07723b..000000000 --- a/src/main/java/org/jboss/ejb/client/legacy/LegacyPropertiesLoader.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2017, 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.legacy; - -import org.jboss.ejb._private.Logs; -import org.jboss.logging.Logger; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.AccessController; -import java.security.PrivilegedAction; -import java.util.Properties; - -/** - * A {@link LegacyPropertiesLoader} loads a EJB client properties file based on the following algorithm: - *
    - *
  1. Checks if the jboss.ejb.client.properties.file.path system property is set. If it's set then this - * {@link LegacyPropertiesLoader} uses that as the file path to the properties file and loads and returns the properties. - *
  2. - *
  3. If the jboss.ejb.client.properties.file.path system property is not set then this {@link LegacyPropertiesLoader} - * then looks for a file named jboss-ejb-client.properties using an appropriate {@link ClassLoader}. If the - * {@link Thread#getContextClassLoader() thread context classloader} is set, then it uses that to find the jboss-ejb-client.properties - * file. Else it uses the {@link ClassLoader} which loaded the {@link LegacyPropertiesLoader} class. - *

    - * If such a file is found by the classloader, then this {@link LegacyPropertiesLoader} loads and returns - * those properties. Else it returns null

    . - *

    - * This step of looking for a jboss-ejb-client.properties file using a {@link ClassLoader} can be - * completely skipped by setting the jboss.ejb.client.properties.skip.classloader.scan system property - * to true - *

    - *
  4. - *
- * - * @author Jaikiran Pai - * @author Tomasz Adamski - */ -public class LegacyPropertiesLoader { - - private static final Logger logger = Logger.getLogger(LegacyPropertiesLoader.class); - - private static final String EJB_CLIENT_PROPS_FILE_SYS_PROPERTY = "jboss.ejb.client.properties.file.path"; - private static final String EJB_CLIENT_PROPS_SKIP_CLASSLOADER_SCAN_SYS_PROPERTY = "jboss.ejb.client.properties.skip.classloader.scan"; - - private static final String EJB_CLIENT_PROPS_FILE_NAME = "jboss-ejb-client.properties"; - - - static public Properties loadEJBClientProperties() { - final String ejbClientPropsFilePath = getSystemProperty(EJB_CLIENT_PROPS_FILE_SYS_PROPERTY); - if (ejbClientPropsFilePath != null) { - // - InputStream fileStream = null; - try { - fileStream = new FileInputStream(ejbClientPropsFilePath); - - final Properties ejbClientProps = new Properties(); - ejbClientProps.load(fileStream); - return ejbClientProps; - - } catch (FileNotFoundException e) { - throw Logs.MAIN.failedToFindEjbClientConfigFileSpecifiedBySysProp(e, EJB_CLIENT_PROPS_FILE_SYS_PROPERTY); - } catch (IOException e) { - throw Logs.MAIN.failedToReadEjbClientConfigFile(e, ejbClientPropsFilePath); - } finally { - if (fileStream != null) { - try { - fileStream.close(); - } catch (IOException e) { - logger.error("Failed to close file " + ejbClientPropsFilePath, e); - } - } - } - } - // if classpath scan is disabled then skip looking for jboss-ejb-client.properties file in the classpath - final String skipClasspathScan = getSystemProperty(EJB_CLIENT_PROPS_SKIP_CLASSLOADER_SCAN_SYS_PROPERTY); - if (skipClasspathScan != null && Boolean.valueOf(skipClasspathScan.trim())) { - logger.debugf("%s system property is set. Skipping classloader search for %s", EJB_CLIENT_PROPS_SKIP_CLASSLOADER_SCAN_SYS_PROPERTY, EJB_CLIENT_PROPS_FILE_NAME); - return null; - } - final ClassLoader classLoader = getClientClassLoader(); - logger.debugf("Looking for %s using classloader %s", EJB_CLIENT_PROPS_FILE_NAME, classLoader); - // find from classloader - final InputStream clientPropsInputStream = classLoader.getResourceAsStream(EJB_CLIENT_PROPS_FILE_NAME); - if (clientPropsInputStream != null) { - logger.debugf("Found %s using classloader %s", EJB_CLIENT_PROPS_FILE_NAME, classLoader); - final Properties clientProps = new Properties(); - try { - clientProps.load(clientPropsInputStream); - return clientProps; - - } catch (IOException e) { - throw Logs.MAIN.failedToReadEjbClientConfigFile(e, EJB_CLIENT_PROPS_FILE_NAME); - } finally { - try { - clientPropsInputStream.close(); - } catch (IOException e) { - logger.error("Failed to close stream", e); - } - } - } - return null; - } - - private static ClassLoader getClientClassLoader(){ - final SecurityManager sm = System.getSecurityManager(); - ClassLoader classLoader; - if (sm != null) { - classLoader = AccessController.doPrivileged((PrivilegedAction) LegacyPropertiesLoader::doGetContextClassLoader); - } else { - classLoader = doGetContextClassLoader(); - } - return classLoader; - } - - private static ClassLoader doGetContextClassLoader() { - return LegacyPropertiesLoader.class.getClassLoader(); - } - - private static String getSystemProperty(final String key){ - final SecurityManager sm = System.getSecurityManager(); - final String value; - if (sm != null) { - value = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty(key)); - } else { - value = System.getProperty(key); - } - return value; - } -} diff --git a/src/main/java/org/jboss/ejb/client/legacy/PropertiesValueResolver.java b/src/main/java/org/jboss/ejb/client/legacy/PropertiesValueResolver.java deleted file mode 100644 index 76cd12e32..000000000 --- a/src/main/java/org/jboss/ejb/client/legacy/PropertiesValueResolver.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * JBoss, Home of Professional Open Source. - * Copyright 2017, 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.legacy; - -import java.io.File; - -/** - * Parses a string and replaces any references to system properties or environment variables in the string - * - * @author Jaikiran Pai (copied from JBoss DMR project) - * @author David M. Lloyd - * @author Tomasz Adamski - */ -class PropertiesValueResolver { - - private static final int INITIAL = 0; - private static final int GOT_DOLLAR = 1; - private static final int GOT_OPEN_BRACE = 2; - private static final int RESOLVED = 3; - private static final int DEFAULT = 4; - - /** - * Replace properties of the form: - * ${<[env.]name>[,<[env.]name2>[,<[env.]name3>...]][:<default>]} - * - * @param value - either a system property or environment variable reference - * @return the value of the system property or environment variable referenced if - * it exists - */ - public static String replaceProperties(final String value) { - final StringBuilder builder = new StringBuilder(); - final int len = value.length(); - int state = INITIAL; - int start = -1; - int nameStart = -1; - String resolvedValue = null; - for (int i = 0; i < len; i = value.offsetByCodePoints(i, 1)) { - final int ch = value.codePointAt(i); - switch (state) { - case INITIAL: { - switch (ch) { - case '$': { - state = GOT_DOLLAR; - continue; - } - default: { - builder.appendCodePoint(ch); - continue; - } - } - // not reachable - } - case GOT_DOLLAR: { - switch (ch) { - case '$': { - builder.appendCodePoint(ch); - state = INITIAL; - continue; - } - case '{': { - start = i + 1; - nameStart = start; - state = GOT_OPEN_BRACE; - continue; - } - default: { - // invalid; emit and resume - builder.append('$').appendCodePoint(ch); - state = INITIAL; - continue; - } - } - // not reachable - } - case GOT_OPEN_BRACE: { - switch (ch) { - case ':': - case '}': - case ',': { - final String name = value.substring(nameStart, i).trim(); - if ("/".equals(name)) { - builder.append(File.separator); - state = ch == '}' ? INITIAL : RESOLVED; - continue; - } else if (":".equals(name)) { - builder.append(File.pathSeparator); - state = ch == '}' ? INITIAL : RESOLVED; - continue; - } - // First check for system property, then env variable - String val = System.getProperty(name); - if (val == null && name.startsWith("env.")) - val = System.getenv(name.substring(4)); - - if (val != null) { - builder.append(val); - resolvedValue = val; - state = ch == '}' ? INITIAL : RESOLVED; - continue; - } else if (ch == ',') { - nameStart = i + 1; - continue; - } else if (ch == ':') { - start = i + 1; - state = DEFAULT; - continue; - } else { - throw new IllegalStateException("Failed to resolve expression: " + value.substring(start - 2, i + 1)); - } - } - default: { - continue; - } - } - // not reachable - } - case RESOLVED: { - if (ch == '}') { - state = INITIAL; - } - continue; - } - case DEFAULT: { - if (ch == '}') { - state = INITIAL; - builder.append(value.substring(start, i)); - } - continue; - } - default: - throw new IllegalStateException("Unexpected char seen: " + ch); - } - } - switch (state) { - case GOT_DOLLAR: { - builder.append('$'); - break; - } - case DEFAULT: { - builder.append(value.substring(start - 2)); - break; - } - case GOT_OPEN_BRACE: { - // We had a reference that was not resolved, throw ISE - if (resolvedValue == null) - throw new IllegalStateException("Incomplete expression: " + builder.toString()); - break; - } - } - return builder.toString(); - } - -} - diff --git a/src/main/java/org/jboss/ejb/client/legacy/RemotingConnectionConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/RemotingConnectionConfiguration.java deleted file mode 100644 index a9e157221..000000000 --- a/src/main/java/org/jboss/ejb/client/legacy/RemotingConnectionConfiguration.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.jboss.ejb.client.legacy; - -/* - * JBoss, Home of Professional Open Source. - * Copyright 2017, 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. - */ - -import org.xnio.OptionMap; - -import javax.security.auth.callback.CallbackHandler; - -public interface RemotingConnectionConfiguration { - - /** - * Returns the {@link OptionMap options} that will be used during connection creation. This method must - * not return null - * - * @return - */ - OptionMap getConnectionCreationOptions(); - - /** - * Returns the {@link CallbackHandler} that will be used during connection creation. This method must - * not return null - * - * @return - */ - CallbackHandler getCallbackHandler(); - - /** - * Returns the connection timeout in milliseconds, that will be used during connection creation - * - * @return - */ - long getConnectionTimeout(); - - /** - * Returns the {@link OptionMap options} that will be used during creation of a {@link org.jboss.remoting3.Channel} - * for the connection - * - * @return - */ - OptionMap getChannelCreationOptions(); - - /** - * If this method returns true, then the EJB client API will try and connect to the destination host "eagerly". when the {@link EJBClientContext} - * is being created out of the {@link EJBClientConfiguration} to which this connection configuration belongs. - *

- * On the other hand, if this method returns false, then the EJB client API will try to connect to the destination host only if no other node/EJBReceiver within the EJB client context - * can handle a EJB invocation request. i.e. it tries to establish the connection lazily/on-demand. - * - * @return - */ - boolean isConnectEagerly(); - - - /** - * Returns the host name/IP address to be used during connection creation. This method must not - * return null - * - * @return - */ - String getHost(); - - /** - * Returns the port that will be used during connection creation - * - * @return - */ - int getPort(); - - /** - * The protocol to use. Can be remoting, http-remoting or https-remoting - * - * - * @return The protocol - */ - String getProtocol(); - -} diff --git a/src/main/java/org/jboss/ejb/client/legacy/RemotingLegacyConfiguration.java b/src/main/java/org/jboss/ejb/client/legacy/RemotingLegacyConfiguration.java new file mode 100644 index 000000000..4f5986baf --- /dev/null +++ b/src/main/java/org/jboss/ejb/client/legacy/RemotingLegacyConfiguration.java @@ -0,0 +1,87 @@ +/* + * 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 java.io.IOException; +import java.net.URI; +import java.util.List; + +import org.jboss.ejb._private.Logs; +import org.jboss.remoting3.ConnectionBuilder; +import org.jboss.remoting3.Endpoint; +import org.jboss.remoting3.EndpointBuilder; +import org.jboss.remoting3.spi.EndpointConfigurator; +import org.kohsuke.MetaInfServices; +import org.xnio.OptionMap; +import org.xnio.Options; + +/** + * The interface to merge EJB properties into the Remoting configuration. + * + * @author David M. Lloyd + */ +@MetaInfServices +public final class RemotingLegacyConfiguration implements EndpointConfigurator { + + public Endpoint getConfiguredEndpoint() { + final JBossEJBProperties properties = JBossEJBProperties.getCurrent(); + + if (properties == null) { + return null; + } + + Logs.MAIN.legacyEJBPropertiesRemotingConfigurationInUse(); + + final EndpointBuilder endpointBuilder = Endpoint.builder(); + final String endpointName = properties.getEndpointName(); + if (endpointName != null) { + endpointBuilder.setEndpointName(endpointName); + } + OptionMap endpointCreationOptions = properties.getEndpointCreationOptions(); + if (endpointCreationOptions != null && endpointCreationOptions.size() > 0) { + if (! endpointCreationOptions.contains(Options.THREAD_DAEMON)) { + endpointCreationOptions = OptionMap.builder().addAll(endpointCreationOptions).set(Options.THREAD_DAEMON, true).getMap(); + } + endpointBuilder.setXnioWorkerOptions(endpointCreationOptions); + } + + // we ignore the connection provider options + + final List connectionList = properties.getConnectionList(); + for (JBossEJBProperties.ConnectionConfiguration connectionConfiguration : connectionList) { + final OptionMap connectionOptions = connectionConfiguration.getConnectionOptions(); + + final URI uri = CommonLegacyConfiguration.getUri(connectionConfiguration, connectionOptions); + if (uri == null) { + continue; + } + + final ConnectionBuilder connectionBuilder = endpointBuilder.addConnection(uri); + connectionBuilder.addAllOptions(connectionOptions); + connectionBuilder.setImmediate(connectionConfiguration.isConnectEagerly()); + connectionBuilder.setAbstractType("ejb"); + connectionBuilder.setAbstractTypeAuthority("jboss"); + } + try { + return endpointBuilder.build(); + } catch (IOException e) { + throw Logs.MAIN.failedToConstructEndpoint(e); + } + } +}