diff --git a/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java b/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java
index 5139dc2b7..9e545d6fc 100644
--- a/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java
+++ b/src/main/java/org/jboss/ejb/client/ConfigurationBasedEJBClientContextSelector.java
@@ -41,11 +41,11 @@
import org.wildfly.common.Assert;
/**
- * A one-time, configuration-based EJB client context selector.
+ * A one-time, configuration-based EJB client context configurator.
*
* @author David M. Lloyd
*/
-public final class ConfigurationBasedEJBClientContextSelector implements Supplier {
+final class ConfigurationBasedEJBClientContextSelector implements Supplier {
private static final EJBClientContext configuredContext;
private static final String NS_EJB_CLIENT_3_0 = "urn:jboss:ejb-client:3.0";
@@ -67,6 +67,7 @@ private static EJBClientContext loadConfiguration() {
} catch (ConfigXMLParseException e) {
throw new IllegalStateException(e);
}
+ // TODO: parse ejb-client.properties instead right here
// build a generic config instead
final EJBClientContext.Builder builder = new EJBClientContext.Builder();
loadTransportProviders(builder, classLoader);
@@ -228,6 +229,9 @@ private static void parseConnectionType(final ConfigurationXMLStreamReader strea
streamReader.skipContent();
}
} else if (next == END_ELEMENT) {
+ final EJBClientConnection.Builder connBuilder = new EJBClientConnection.Builder();
+ connBuilder.setDestination(uri);
+ builder.addClientConnection(connBuilder.build());
return;
} else {
throw Assert.unreachableCode();
diff --git a/src/main/java/org/jboss/ejb/client/EJBClientConnection.java b/src/main/java/org/jboss/ejb/client/EJBClientConnection.java
new file mode 100644
index 000000000..192568b73
--- /dev/null
+++ b/src/main/java/org/jboss/ejb/client/EJBClientConnection.java
@@ -0,0 +1,78 @@
+/*
+ * 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;
+
+import java.net.URI;
+
+import org.wildfly.common.Assert;
+
+/**
+ * Information about a configured connection on an EJB client context.
+ *
+ * @author David M. Lloyd
+ */
+public final class EJBClientConnection {
+ private final URI destination;
+
+ EJBClientConnection(final Builder builder) {
+ destination = builder.destination;
+ }
+
+ /**
+ * Get the connection destination URI.
+ *
+ * @return the connection destination URI (not {@code null})
+ */
+ public URI getDestination() {
+ return destination;
+ }
+
+ /**
+ * A builder for a client connection definition.
+ */
+ public static final class Builder {
+ URI destination;
+
+ /**
+ * Construct a new instance.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Set the destination URI.
+ *
+ * @param destination the destination URI (must not be {@code null})
+ */
+ public void setDestination(final URI destination) {
+ Assert.checkNotNullParam("destination", destination);
+ this.destination = destination;
+ }
+
+ /**
+ * Build a new {@link EJBClientConnection} instance based on the current contents of this builder.
+ *
+ * @return the new instance (not {@code null})
+ */
+ public EJBClientConnection build() {
+ Assert.checkNotNullParam("destination", destination);
+ return new EJBClientConnection(this);
+ }
+ }
+}
diff --git a/src/main/java/org/jboss/ejb/client/EJBClientContext.java b/src/main/java/org/jboss/ejb/client/EJBClientContext.java
index ab4edc8e1..014513f39 100644
--- a/src/main/java/org/jboss/ejb/client/EJBClientContext.java
+++ b/src/main/java/org/jboss/ejb/client/EJBClientContext.java
@@ -28,6 +28,7 @@
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
@@ -37,7 +38,6 @@
import org.wildfly.common.context.Contextual;
import org.wildfly.discovery.Discovery;
import org.wildfly.discovery.FilterSpec;
-import org.wildfly.discovery.ServiceRegistry;
import org.wildfly.discovery.ServiceType;
import org.wildfly.discovery.ServicesQueue;
@@ -106,8 +106,8 @@ public final class EJBClientContext extends Attachable implements Contextual configuredConnections;
EJBClientContext(Builder builder) {
final List builderInterceptors = builder.interceptors;
@@ -122,9 +122,16 @@ public final class EJBClientContext extends Attachable implements Contextual clientConnections = builder.clientConnections;
+ if (clientConnections == null || clientConnections.isEmpty()) {
+ configuredConnections = Collections.emptyList();
+ } else if (clientConnections.size() == 1) {
+ configuredConnections = Collections.singletonList(clientConnections.get(0));
+ } else {
+ configuredConnections = Collections.unmodifiableList(new ArrayList<>(clientConnections));
+ }
// this must be last
for (EJBTransportProvider transportProvider : transportProviders) {
transportProvider.notifyRegistered(receiverContext);
@@ -158,6 +165,16 @@ public long getInvocationTimeout() {
return invocationTimeout;
}
+ /**
+ * Get the pre-configured connections for this context. This information may not be used by some transport providers
+ * and mainly exists for legacy compatibility purposes.
+ *
+ * @return the pre-configured connections for this context (not {@code null})
+ */
+ public List getConfiguredConnections() {
+ return configuredConnections;
+ }
+
/**
* Get a copy of this context with the given interceptor(s) added. If the array is {@code null} or empty, the
* current context is returned as-is.
@@ -231,10 +248,6 @@ Discovery getDiscovery() {
return DISCOVERY_SUPPLIER.get();
}
- ServiceRegistry getServiceRegistry() {
- return serviceRegistry;
- }
-
/**
* A builder for EJB client contexts.
*/
@@ -242,7 +255,7 @@ public static final class Builder {
List interceptors;
List transportProviders;
- ServiceRegistry serviceRegistry = doPrivileged((PrivilegedAction) ServiceRegistry.getContextManager()::get);
+ List clientConnections;
/**
* Construct a new instance.
@@ -259,6 +272,7 @@ public Builder() {
if (transportProviders.length > 0) {
this.transportProviders = new ArrayList<>(Arrays.asList(transportProviders));
}
+ clientConnections = new ArrayList<>(ejbClientContext.getConfiguredConnections());
}
public void addInterceptor(EJBClientInterceptor interceptor) {
@@ -277,9 +291,12 @@ public void addTransportProvider(EJBTransportProvider provider) {
transportProviders.add(provider);
}
- public void setServiceRegistry(final ServiceRegistry serviceRegistry) {
- Assert.checkNotNullParam("serviceRegistry", serviceRegistry);
- this.serviceRegistry = serviceRegistry;
+ public void addClientConnection(EJBClientConnection connection) {
+ Assert.checkNotNullParam("connection", connection);
+ if (clientConnections == null) {
+ clientConnections = new ArrayList<>();
+ }
+ clientConnections.add(connection);
}
public EJBClientContext build() {
diff --git a/src/main/java/org/jboss/ejb/client/EJBReceiverContext.java b/src/main/java/org/jboss/ejb/client/EJBReceiverContext.java
index c754f66a2..59f1d2a8d 100644
--- a/src/main/java/org/jboss/ejb/client/EJBReceiverContext.java
+++ b/src/main/java/org/jboss/ejb/client/EJBReceiverContext.java
@@ -18,8 +18,6 @@
package org.jboss.ejb.client;
-import org.wildfly.discovery.ServiceRegistry;
-
/**
* A context which is provided to EJB receiver implementations in order to perform operations on the client context.
*
@@ -32,15 +30,6 @@ public final class EJBReceiverContext {
this.clientContext = clientContext;
}
- /**
- * Get the discovery service registry to use for server discovery events.
- *
- * @return the discovery service registry
- */
- public ServiceRegistry getServiceRegistry() {
- return clientContext.getServiceRegistry();
- }
-
/**
* Get the client context that corresponds to this receiver context.
*
diff --git a/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java b/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java
index a2da11b93..e2ee56167 100644
--- a/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java
+++ b/src/main/java/org/jboss/ejb/protocol/remote/EJBClientChannel.java
@@ -88,6 +88,8 @@
import org.wildfly.discovery.ServiceRegistration;
import org.wildfly.discovery.ServiceRegistry;
import org.wildfly.discovery.ServiceURL;
+import org.wildfly.discovery.impl.LocalRegistryAndDiscoveryProvider;
+import org.wildfly.discovery.spi.DiscoveryProvider;
import org.wildfly.security.auth.AuthenticationException;
import org.wildfly.transaction.client.ContextTransactionManager;
import org.wildfly.transaction.client.LocalTransaction;
@@ -122,6 +124,7 @@ class EJBClientChannel {
private final RemoteTransactionContext transactionContext;
private final AtomicReference> futureResultRef;
+ private final LocalRegistryAndDiscoveryProvider discoveryProvider = new LocalRegistryAndDiscoveryProvider();
EJBClientChannel(final Channel channel, final int version, final FutureResult futureResult) {
this.channel = channel;
@@ -139,7 +142,7 @@ class EJBClientChannel {
// server does not present v3 unless the transaction service is also present
}
transactionContext = RemoteTransactionContext.getInstance();
- this.serviceRegistry = REGISTRY_SUPPLIER.get();
+ this.serviceRegistry = ServiceRegistry.create(discoveryProvider);
this.configuration = configuration;
invocationTracker = new InvocationTracker(this.channel, channel.getOption(RemotingOptions.MAX_OUTBOUND_MESSAGES).intValue(), EJBClientChannel::mask);
futureResultRef = new AtomicReference<>(futureResult);
@@ -791,6 +794,10 @@ UserTransactionID allocateUserTransactionID() {
}
}
+ DiscoveryProvider getDiscoveryProvider() {
+ return discoveryProvider;
+ }
+
final class MethodInvocation extends Invocation {
private final EJBReceiverInvocationContext receiverInvocationContext;
private final AtomicInteger refCounter = new AtomicInteger(1);
diff --git a/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java b/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java
new file mode 100644
index 000000000..f60c81957
--- /dev/null
+++ b/src/main/java/org/jboss/ejb/protocol/remote/RemotingEJBDiscoveryProvider.java
@@ -0,0 +1,132 @@
+/*
+ * 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.protocol.remote;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.jboss.ejb.client.EJBClientConnection;
+import org.jboss.ejb.client.EJBClientContext;
+import org.jboss.remoting3.Connection;
+import org.jboss.remoting3.Endpoint;
+import org.wildfly.discovery.FilterSpec;
+import org.wildfly.discovery.ServiceType;
+import org.wildfly.discovery.ServiceURL;
+import org.wildfly.discovery.spi.DiscoveryProvider;
+import org.wildfly.discovery.spi.DiscoveryRequest;
+import org.wildfly.discovery.spi.DiscoveryResult;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+
+/**
+ * @author David M. Lloyd
+ */
+final class RemotingEJBDiscoveryProvider implements DiscoveryProvider {
+ static final RemotingEJBDiscoveryProvider INSTANCE = new RemotingEJBDiscoveryProvider();
+
+ private RemotingEJBDiscoveryProvider() {
+ }
+
+ public DiscoveryRequest discover(final ServiceType serviceType, final FilterSpec filterSpec, final DiscoveryResult result) {
+ if (! serviceType.implies(ServiceType.of("ejb", "jboss"))) {
+ // only respond to requests for JBoss EJB services
+ result.complete();
+ return DiscoveryRequest.NULL;
+ }
+ final EJBClientContext ejbClientContext = EJBClientContext.getCurrent();
+ final RemoteEJBReceiver ejbReceiver = ejbClientContext.getAttachment(RemoteTransportProvider.ATTACHMENT_KEY);
+ if (ejbReceiver == null) {
+ // ???
+ result.complete();
+ return DiscoveryRequest.NULL;
+ }
+ final Endpoint endpoint = Endpoint.getCurrent();
+ final List connections = ejbClientContext.getConfiguredConnections();
+ if (connections.isEmpty()) {
+ result.complete();
+ return DiscoveryRequest.NULL;
+ }
+ final AtomicInteger connectionCount = new AtomicInteger(connections.size() + 1);
+ final List cancellers = Collections.synchronizedList(new ArrayList<>());
+ for (EJBClientConnection connection : connections) {
+ final URI uri = connection.getDestination();
+ final String scheme = uri.getScheme();
+ if (scheme == null || ! ejbReceiver.getRemoteTransportProvider().supportsProtocol(scheme) || ! endpoint.isValidUriScheme(scheme)) {
+ continue;
+ }
+ final IoFuture future = endpoint.getConnection(uri);
+ cancellers.add(future::cancel);
+ future.addNotifier(new IoFuture.HandlingNotifier() {
+ public void handleCancelled(final DiscoveryResult discoveryResult) {
+ countDown(connectionCount, discoveryResult);
+ }
+
+ public void handleFailed(final IOException exception, final DiscoveryResult discoveryResult) {
+ countDown(connectionCount, discoveryResult);
+ }
+
+ public void handleDone(final Connection data, final DiscoveryResult discoveryResult) {
+ final IoFuture future = ejbReceiver.serviceHandle.getClientService(data, OptionMap.EMPTY);
+ cancellers.add(future::cancel);
+ future.addNotifier(new IoFuture.HandlingNotifier() {
+ public void handleCancelled(final DiscoveryResult discoveryResult) {
+ countDown(connectionCount, discoveryResult);
+ }
+
+ public void handleFailed(final IOException exception, final DiscoveryResult discoveryResult) {
+ countDown(connectionCount, discoveryResult);
+ }
+
+ public void handleDone(final EJBClientChannel clientChannel, final DiscoveryResult discoveryResult) {
+ final DiscoveryRequest request = clientChannel.getDiscoveryProvider().discover(serviceType, filterSpec, new DiscoveryResult() {
+ public void complete() {
+ countDown(connectionCount, discoveryResult);
+ }
+
+ public void addMatch(final ServiceURL serviceURL) {
+ discoveryResult.addMatch(serviceURL);
+ }
+ });
+ cancellers.add(request::cancel);
+ }
+ }, discoveryResult);
+ }
+
+ }, result);
+ }
+ countDown(connectionCount, result);
+ return () -> {
+ synchronized (cancellers) {
+ for (Runnable canceller : cancellers) {
+ canceller.run();
+ }
+ }
+ };
+ }
+
+ static void countDown(final AtomicInteger connectionCount, final DiscoveryResult discoveryResult) {
+ if (connectionCount.decrementAndGet() == 0) {
+ discoveryResult.complete();
+ }
+ }
+}