org.fabric3.binding.zeromq.runtime.broker.ZeroMQWireBrokerImpl Maven / Gradle / Ivy
The newest version!
/*
* Fabric3
* Copyright (c) 2009-2015 Metaform Systems
*
* 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.
* Portions originally based on Apache Tuscany 2007
* licensed under the Apache 2.0 license.
*/
package org.fabric3.binding.zeromq.runtime.broker;
import java.net.InetAddress;
import java.net.URI;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
import org.fabric3.api.annotation.Source;
import org.fabric3.api.annotation.monitor.Monitor;
import org.fabric3.api.binding.zeromq.model.SocketAddressDefinition;
import org.fabric3.api.binding.zeromq.model.ZeroMQMetadata;
import org.fabric3.api.host.Fabric3Exception;
import org.fabric3.api.host.runtime.HostInfo;
import org.fabric3.api.model.type.contract.DataType;
import org.fabric3.binding.zeromq.runtime.MessagingMonitor;
import org.fabric3.binding.zeromq.runtime.ZeroMQWireBroker;
import org.fabric3.binding.zeromq.runtime.context.ContextManager;
import org.fabric3.binding.zeromq.runtime.interceptor.OneWayInterceptor;
import org.fabric3.binding.zeromq.runtime.interceptor.RequestReplyInterceptor;
import org.fabric3.binding.zeromq.runtime.interceptor.UnwrappingInterceptor;
import org.fabric3.binding.zeromq.runtime.interceptor.WrappingInterceptor;
import org.fabric3.binding.zeromq.runtime.management.ZeroMQManagementService;
import org.fabric3.binding.zeromq.runtime.message.DelegatingOneWaySender;
import org.fabric3.binding.zeromq.runtime.message.DynamicOneWaySender;
import org.fabric3.binding.zeromq.runtime.message.NonReliableOneWayReceiver;
import org.fabric3.binding.zeromq.runtime.message.NonReliableOneWaySender;
import org.fabric3.binding.zeromq.runtime.message.NonReliableRequestReplyReceiver;
import org.fabric3.binding.zeromq.runtime.message.NonReliableRequestReplySender;
import org.fabric3.binding.zeromq.runtime.message.OneWaySender;
import org.fabric3.binding.zeromq.runtime.message.Receiver;
import org.fabric3.binding.zeromq.runtime.message.RequestReplySender;
import org.fabric3.binding.zeromq.runtime.message.Sender;
import org.fabric3.spi.container.invocation.WorkContext;
import org.fabric3.spi.container.wire.Interceptor;
import org.fabric3.spi.container.wire.InvocationChain;
import org.fabric3.spi.container.wire.TransformerInterceptorFactory;
import org.fabric3.spi.discovery.DiscoveryAgent;
import org.fabric3.spi.discovery.EntryChange;
import org.fabric3.spi.discovery.ServiceEntry;
import org.fabric3.binding.zeromq.runtime.SocketAddress;
import org.fabric3.spi.host.Port;
import org.fabric3.spi.host.PortAllocator;
import org.fabric3.spi.model.physical.PhysicalOperation;
import org.fabric3.spi.model.type.java.JavaType;
import org.fabric3.spi.runtime.event.EventService;
import org.fabric3.spi.runtime.event.Fabric3EventListener;
import org.fabric3.spi.runtime.event.RuntimeStop;
import org.fabric3.spi.util.UriHelper;
import org.oasisopen.sca.annotation.Init;
import org.oasisopen.sca.annotation.Property;
import org.oasisopen.sca.annotation.Reference;
import org.oasisopen.sca.annotation.Service;
import static java.util.stream.Collectors.toList;
/**
*
*/
@Service(ZeroMQWireBroker.class)
public class ZeroMQWireBrokerImpl implements ZeroMQWireBroker, DynamicOneWaySender, Fabric3EventListener {
private static final JavaType BYTE_TYPE = new JavaType(byte[].class);
private static final JavaType EMPTY_TYPE = new JavaType(Void.class);
List TRANSPORT_TYPES; // the transport type is a byte array
private static final String ZMQ = "zmq";
private ContextManager manager;
private DiscoveryAgent discoveryAgent;
private PortAllocator allocator;
private EventService eventService;
private HostInfo info;
private ZeroMQManagementService managementService;
private ExecutorService executorService;
private MessagingMonitor monitor;
private long pollTimeout = 10000000;
private TransformerInterceptorFactory interceptorFactory;
private String host;
private String hostAddress;
private Map senders = new HashMap<>();
private Map receivers = new HashMap<>();
public ZeroMQWireBrokerImpl(@Reference ContextManager manager,
@Reference(required = false) DiscoveryAgent discoveryAgent,
@Reference PortAllocator allocator,
@Reference(name = "executorService") ExecutorService executorService,
@Reference ZeroMQManagementService managementService,
@Reference EventService eventService,
@Reference TransformerInterceptorFactory interceptorFactory,
@Reference HostInfo info,
@Monitor MessagingMonitor monitor) throws UnknownHostException {
this.manager = manager;
this.discoveryAgent = discoveryAgent;
this.allocator = allocator;
this.executorService = executorService;
this.managementService = managementService;
this.eventService = eventService;
this.interceptorFactory = interceptorFactory;
this.info = info;
this.monitor = monitor;
this.host = InetAddress.getLocalHost().getHostAddress();
TRANSPORT_TYPES = new ArrayList<>();
TRANSPORT_TYPES.add(BYTE_TYPE);
this.hostAddress = InetAddress.getLocalHost().getHostAddress();
}
/**
* Sets the timeout in milliseconds for polling operations.
*
* @param timeout the timeout in milliseconds for polling operations
*/
@Property(required = false)
@Source("$systemConfig//f3:zeromq.binding/@poll.timeout")
public void setPollTimeout(long timeout) {
this.pollTimeout = timeout * 1000; // convert milliseconds to microseconds
}
/**
* Sets this host to bind the publisher to.
*
* @param host the host
*/
@Property(required = false)
@Source("$systemConfig/f3:runtime/@host.address")
public void setHost(String host) {
this.host = host;
}
@Init
public void init() {
eventService.subscribe(RuntimeStop.class, this);
}
public void connectToSender(String id, URI uri, List chains, ZeroMQMetadata metadata, ClassLoader loader) throws Fabric3Exception {
SenderHolder holder;
if (ZMQ.equals(uri.getScheme())) {
DelegatingOneWaySender sender = new DelegatingOneWaySender(id, this, metadata);
holder = new SenderHolder(sender);
} else {
holder = senders.get(uri.toString());
}
if (holder == null) {
boolean oneWay = isOneWay(chains, uri);
holder = createSender(uri.toString(), oneWay, metadata);
managementService.registerSender(id, holder.getSender());
}
for (int i = 0, chainsSize = chains.size(); i < chainsSize; i++) {
InvocationChain chain = chains.get(i);
PhysicalOperation physicalOperation = chain.getPhysicalOperation();
List sourceTypes = createTypes(physicalOperation);
Interceptor interceptor = interceptorFactory.createInterceptor(physicalOperation, sourceTypes, TRANSPORT_TYPES, loader, loader);
chain.addInterceptor(interceptor);
chain.addInterceptor(new UnwrappingInterceptor());
interceptor = createInterceptor(holder, i);
chain.addInterceptor(interceptor);
}
holder.getIds().add(id);
}
public void releaseSender(String id, URI uri) throws Fabric3Exception {
SenderHolder holder = senders.get(uri.toString());
if (holder == null) {
if (!ZMQ.equals(uri.getScheme())) {
// callback holders are dynamically created and it is possible for a sender to be released before an invocation is dispatched to it
throw new Fabric3Exception("Sender not found for " + uri);
} else {
return;
}
}
holder.getIds().remove(id);
if (holder.getIds().isEmpty()) {
senders.remove(uri.toString());
Sender sender = holder.getSender();
sender.stop();
managementService.unregisterSender(id);
}
}
public void connectToReceiver(URI uri, List chains, ZeroMQMetadata metadata, ClassLoader loader) throws Fabric3Exception {
if (receivers.containsKey(uri.toString())) {
throw new Fabric3Exception("Receiver already defined for " + uri);
}
String endpointId = UriHelper.getDefragmentedNameAsString(uri);
SocketAddress address;
if (metadata.getSocketAddresses() != null && !metadata.getSocketAddresses().isEmpty()) {
// bind using specified address and port
if (metadata.getSocketAddresses().size() != 1) {
throw new Fabric3Exception("Only one socket address can be specified");
}
SocketAddressDefinition addressDefinition = metadata.getSocketAddresses().get(0);
String specifiedHost = addressDefinition.getHost();
if ("localhost".equals(specifiedHost)) {
specifiedHost = hostAddress;
}
int portNumber = addressDefinition.getPort();
Port port = allocator.reserve(endpointId, ZMQ, portNumber);
address = new SocketAddress("tcp", specifiedHost, port);
} else {
// bind to a randomly allocated port
Port port = allocator.allocate(endpointId, ZMQ);
address = new SocketAddress("tcp", host, port);
}
addTransformer(chains, loader);
boolean oneWay = isOneWay(chains, uri);
Receiver receiver;
if (oneWay) {
receiver = new NonReliableOneWayReceiver(manager, address, chains, executorService, metadata, monitor);
} else {
receiver = new NonReliableRequestReplyReceiver(manager, address, chains, executorService, pollTimeout, metadata, monitor);
}
receiver.start();
ServiceEntry entry = new ServiceEntry();
entry.setName(endpointId);
entry.setAddress(address.getAddress());
entry.setPort(address.getPort().getNumber());
entry.setTransport("tcp");
if (discoveryAgent != null) {
discoveryAgent.register(entry);
}
receivers.put(uri.toString(), receiver);
String id = createReceiverId(uri);
managementService.registerReceiver(id, receiver);
monitor.onProvisionEndpoint(id);
}
public void releaseReceiver(URI uri) throws Fabric3Exception {
Receiver receiver = receivers.remove(UriHelper.getDefragmentedNameAsString(uri));
if (receiver == null) {
throw new Fabric3Exception("Receiver not found for " + uri);
}
String endpointId = uri.toString();
if (discoveryAgent != null) {
discoveryAgent.unregisterService(endpointId);
}
receiver.stop();
allocator.release(endpointId);
String id = createReceiverId(uri);
managementService.unregisterReceiver(id);
monitor.onRemoveEndpoint(id);
}
public void send(byte[] message, int index, WorkContext context, ZeroMQMetadata metadata) {
String callbackReference = context.peekCallbackReference();
if (callbackReference == null) {
monitor.error("Callback reference not found");
return;
}
SenderHolder holder = senders.get(callbackReference);
if (holder == null) {
holder = createSender(callbackReference, true, metadata);
}
Sender sender = holder.getSender();
if (sender instanceof OneWaySender) {
((OneWaySender) sender).send(message, index, context);
} else {
monitor.error("Callback sender is not a one-way type: " + holder.getClass().getName());
}
}
public void startAll() {
receivers.values().forEach(Receiver::start);
for (SenderHolder holder : senders.values()) {
holder.getSender().start();
}
}
public void stopAll() {
receivers.values().forEach(Receiver::stop);
for (SenderHolder holder : senders.values()) {
holder.getSender().stop();
}
}
public void start() {
// no-op
}
public void stop() {
// no-op
}
public String getId() {
return "ZeroMQWireBroker";
}
public void accept(EntryChange change, ServiceEntry serviceEntry) {
// no-op
}
public void onEvent(RuntimeStop event) {
stopAll();
}
private SenderHolder createSender(String endpointId, boolean oneWay, ZeroMQMetadata metadata) {
List addresses = new ArrayList<>();
boolean refresh;
if (metadata.getSocketAddresses() != null) {
// service addresses to connect to are explicitly configured in the binding definition
refresh = false;
for (SocketAddressDefinition addressDefinition : metadata.getSocketAddresses()) {
Port port = new SpecifiedPort(addressDefinition.getPort());
String specifiedHost = addressDefinition.getHost();
if ("localhost".equals(specifiedHost)) {
specifiedHost = hostAddress;
}
SocketAddress socketAddress = new SocketAddress("tcp", specifiedHost, port);
addresses.add(socketAddress);
}
} else {
// services addresses to connect to are not specified in the binding, retrieve them from the federation layer
refresh = true;
if (discoveryAgent == null) {
throw new Fabric3Exception("Discovery extension must be installed for dynamic wire addresses");
}
List entries = discoveryAgent.getServiceEntries(endpointId);
addresses = entries.stream().
map(e -> new SocketAddress(e.getTransport(), e.getAddress(), new SpecifiedPort(e.getPort()))).collect(toList());
}
Sender sender;
if (oneWay) {
sender = new NonReliableOneWaySender(endpointId, manager, addresses, pollTimeout, metadata, monitor);
} else {
sender = new NonReliableRequestReplySender(endpointId, manager, addresses, pollTimeout, metadata, monitor);
}
SenderHolder holder = new SenderHolder(sender);
sender.start();
if (refresh && discoveryAgent != null) {
// don't subscribe for updates if the sockets are explicitly configured
discoveryAgent.registerServiceListener(endpointId, sender);
}
senders.put(endpointId, holder);
return holder;
}
/**
* Determines if the wire is one-way or request-reply. The first operation is used to determine if the contract is one-way as the binding does not support
* mixing one-way and request-response operations on a service contract.
*
* @param chains the wire invocation chains
* @param uri thr service URI.
* @return true if the wire is one-way
*/
private boolean isOneWay(List chains, URI uri) {
if (chains.size() < 1) {
throw new AssertionError("Contract must have at least one operation: " + uri);
}
return chains.get(0).getPhysicalOperation().isOneWay();
}
private void addTransformer(List chains, ClassLoader loader) throws Fabric3Exception {
for (InvocationChain chain : chains) {
PhysicalOperation physicalOperation = chain.getPhysicalOperation();
List targetTypes = createTypes(physicalOperation);
Interceptor interceptor = interceptorFactory.createInterceptor(physicalOperation, TRANSPORT_TYPES, targetTypes, loader, loader);
chain.addInterceptor(new WrappingInterceptor());
chain.addInterceptor(interceptor);
}
}
@SuppressWarnings({"unchecked"})
private List createTypes(PhysicalOperation physicalOperation) throws Fabric3Exception {
List dataTypes = new ArrayList<>();
if (physicalOperation.getSourceParameterTypes().isEmpty()) {
// no params
dataTypes.add(EMPTY_TYPE);
} else {
List> types = physicalOperation.getSourceParameterTypes();
dataTypes.addAll(types.stream().map(type -> new JavaType((type))).collect(Collectors.toList()));
}
return dataTypes;
}
private Interceptor createInterceptor(SenderHolder holder, int i) {
Sender sender = holder.getSender();
if (sender instanceof NonReliableRequestReplySender) {
return new RequestReplyInterceptor(i, (RequestReplySender) sender);
} else if (sender instanceof OneWaySender) {
return new OneWayInterceptor(i, (OneWaySender) sender);
} else {
throw new AssertionError("Unknown sender type: " + sender.getClass().getName());
}
}
private String createReceiverId(URI uri) {
if ("zmq".equals(uri.getScheme())) {
// callback ids are of the form zmq://
return uri.getAuthority();
}
return uri.getPath().substring(1);
}
private class SenderHolder {
private Sender sender;
private List ids;
private SenderHolder(Sender sender) {
this.sender = sender;
ids = new ArrayList<>();
}
public Sender getSender() {
return sender;
}
public List getIds() {
return ids;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy