package io.muoncore.extension.amqp; import io.muoncore.Discovery; import io.muoncore.ServiceDescriptor; import io.muoncore.channel.Channel; import io.muoncore.channel.ChannelConnection; import io.muoncore.channel.Channels; import io.muoncore.channel.impl.KeepAliveChannel; import io.muoncore.channel.support.Scheduler; import io.muoncore.codec.Codecs; import io.muoncore.exception.MuonTransportFailureException; import io.muoncore.exception.NoSuchServiceException; import io.muoncore.message.MuonInboundMessage; import io.muoncore.message.MuonOutboundMessage; import io.muoncore.protocol.ServerStacks; import io.muoncore.transport.MuonTransport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class AMQPMuonTransport implements MuonTransport { private Logger log = LoggerFactory.getLogger(AMQPMuonTransport.class.getName()); private String rabbitUrl; private List<AmqpChannel> channels; private ServiceQueue serviceQueue; private AmqpChannelFactory channelFactory; private Discovery discovery; private Codecs codecs; private Scheduler scheduler; public AMQPMuonTransport( String url, ServiceQueue serviceQueue, AmqpChannelFactory channelFactory) { channels = new ArrayList<>(); this.channelFactory = channelFactory; this.rabbitUrl = url; this.serviceQueue = serviceQueue; log.info("Connecting to AMQP host at " + rabbitUrl); } @Override public void shutdown() { new ArrayList<>(channels).stream().forEach(AmqpChannel::shutdown); serviceQueue.shutdown(); } @Override public boolean canConnectToService(String name) { Optional<ServiceDescriptor> descriptor = discovery.findService(svc -> svc.getIdentifier().equals(name)); return descriptor.map(serviceDescriptor -> serviceDescriptor.getSchemes() .stream() .anyMatch(url -> url.equals(getUrlScheme()))) .orElse(false); } @Override public ChannelConnection<MuonOutboundMessage, MuonInboundMessage> openClientChannel(String serviceName, String protocol) { if (!discovery.findService( svc -> svc.getIdentifier().equals(serviceName)) .isPresent()) { throw new NoSuchServiceException(serviceName); } AmqpChannel channel = channelFactory.createChannel(); channel.onShutdown(msg -> { channels.remove(channel); }); channel.initiateHandshake(serviceName, protocol); channels.add(channel); Channel<MuonOutboundMessage, MuonInboundMessage> intermediate = new KeepAliveChannel(Channels.EVENT_DISPATCHER, protocol, scheduler); Channels.connect(intermediate.right(), channel); return intermediate.left(); } public void start(Discovery discovery, final ServerStacks serverStacks, Codecs codecs, Scheduler scheduler) { this.discovery = discovery; this.codecs = codecs; this.scheduler = scheduler; channelFactory.initialiseEnvironment(codecs, discovery, scheduler); log.info("Booting up transport with stack " + serverStacks); serviceQueue.onHandshake( handshake -> { log.debug("opening new server channel with " + serverStacks); ChannelConnection<MuonInboundMessage, MuonOutboundMessage> serverChannelConnection = serverStacks.openServerChannel(handshake.getProtocol()); Channel<MuonOutboundMessage, MuonInboundMessage> keepAliveChannel = new KeepAliveChannel(Channels.EVENT_DISPATCHER, handshake.getProtocol(), scheduler); AmqpChannel amqpChannel = channelFactory.createChannel(); Channels.connect(amqpChannel, keepAliveChannel.right()); Channels.connect(serverChannelConnection, keepAliveChannel.left()); amqpChannel.respondToHandshake(handshake); channels.add(amqpChannel); }); } @Override public String getUrlScheme() { return "amqp"; } @Override public URI getLocalConnectionURI() { try { return new URI(rabbitUrl); } catch (URISyntaxException e) { throw new MuonTransportFailureException("Invalid URI is provided: " + rabbitUrl, e); } } public int getNumberOfActiveChannels() { return channels.size(); } }