/* * Copyright 2001-2016 the original author or authors. * * 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.springframework.integration.ip.udp; import java.io.IOException; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.SocketAddress; import java.net.URI; import org.springframework.expression.Expression; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; /** * A {@link MessageHandler} implementation that maps a Message into * a UDP datagram packet and sends that to the specified multicast address * (224.0.0.0 to 239.255.255.255) and port. * * The only difference between this and its super class is the * ability to specify how many acknowledgments are required to * determine success. * * @author Gary Russell * @since 2.0 */ public class MulticastSendingMessageHandler extends UnicastSendingMessageHandler { private int timeToLive = -1; private String localAddress; private volatile MulticastSocket multicastSocket; /** * Constructs a MulticastSendingMessageHandler to send data to the multicast address/port. * @param address The multicast address. * @param port The port. */ public MulticastSendingMessageHandler(String address, int port) { super(address, port); } /** * Constructs a MulticastSendingMessageHandler to send data to the multicast address/port * and enables setting the lengthCheck option (if set, a length is prepended to the packet and checked * at the destination). * @param address The multicast address. * @param port The port. * @param lengthCheck Enable the lengthCheck option. */ public MulticastSendingMessageHandler(String address, int port, boolean lengthCheck) { super(address, port, lengthCheck); } /** * Constructs a MulticastSendingMessageHandler to send data to the multicast address/port * and enables setting the acknowledge option, where the destination sends a receipt acknowledgment. * @param address The multicast address. * @param port The port. * @param acknowledge Whether or not acknowledgments are required. * @param ackHost The host to which acknowledgments should be sent; required if acknowledge is true. * @param ackPort The port to which acknowledgments should be sent; required if acknowledge is true. * @param ackTimeout How long to wait (milliseconds) for an acknowledgment. */ public MulticastSendingMessageHandler(String address, int port, boolean acknowledge, String ackHost, int ackPort, int ackTimeout) { super(address, port, acknowledge, ackHost, ackPort, ackTimeout); } /** * Constructs a MulticastSendingMessageHandler to send data to the multicast address/port * and enables setting the acknowledge option, where the destination sends a receipt acknowledgment. * @param address The multicast address. * @param port The port. * @param lengthCheck Enable the lengthCheck option. * @param acknowledge Whether or not acknowledgments are required. * @param ackHost The host to which acknowledgments should be sent; required if acknowledge is true. * @param ackPort The port to which acknowledgments should be sent; required if acknowledge is true. * @param ackTimeout How long to wait (milliseconds) for an acknowledgment. */ public MulticastSendingMessageHandler(String address, int port, boolean lengthCheck, boolean acknowledge, String ackHost, int ackPort, int ackTimeout) { super(address, port, lengthCheck, acknowledge, ackHost, ackPort, ackTimeout); } /** * Construct MulticastSendingMessageHandler based on the destination SpEL expression to * determine the target destination at runtime against requestMessage. * @param destinationExpression the SpEL expression to evaluate the target destination * at runtime. Must evaluate to {@link String}, {@link URI} or {@link SocketAddress}. * @since 5.0 */ public MulticastSendingMessageHandler(Expression destinationExpression) { super(destinationExpression); } /** * Construct MulticastSendingMessageHandler based on the destination SpEL expression to * determine the target destination at runtime against requestMessage. * @param destinationExpression the SpEL expression to evaluate the target destination * at runtime. Must evaluate to {@link String}, {@link URI} or {@link SocketAddress}. * @since 5.0 */ public MulticastSendingMessageHandler(String destinationExpression) { super(destinationExpression); } @Override protected DatagramSocket getSocket() throws IOException { if (this.getTheSocket() == null) { synchronized (this) { createSocket(); } } return this.getTheSocket(); } private void createSocket() throws IOException { if (this.getTheSocket() == null) { MulticastSocket socket; if (this.isAcknowledge()) { int ackPort = this.getAckPort(); if (this.localAddress == null) { socket = ackPort == 0 ? new MulticastSocket() : new MulticastSocket(ackPort); } else { InetAddress whichNic = InetAddress.getByName(this.localAddress); socket = new MulticastSocket(new InetSocketAddress(whichNic, ackPort)); } if (getSoReceiveBufferSize() > 0) { socket.setReceiveBufferSize(this.getSoReceiveBufferSize()); } if (logger.isDebugEnabled()) { logger.debug("Listening for acks on port: " + socket.getLocalPort()); } setSocket(socket); updateAckAddress(); } else { socket = new MulticastSocket(); setSocket(socket); } if (this.timeToLive >= 0) { socket.setTimeToLive(this.timeToLive); } setSocketAttributes(socket); if (this.localAddress != null) { InetAddress whichNic = InetAddress.getByName(this.localAddress); socket.setInterface(whichNic); } this.multicastSocket = socket; } } /** * If acknowledge = true; how many acks needed for success. * * @param minAcksForSuccess The minimum number of acks that will represent success. */ public void setMinAcksForSuccess(int minAcksForSuccess) { this.setAckCounter(minAcksForSuccess); } /** * Set the underlying {@link MulticastSocket} time to live property. * * @param timeToLive {@link MulticastSocket#setTimeToLive(int)} */ public void setTimeToLive(int timeToLive) { this.timeToLive = timeToLive; } @Override public void setLocalAddress(String localAddress) { this.localAddress = localAddress; } @Override protected void convertAndSend(Message<?> message) throws Exception { super.convertAndSend(message); if (logger.isDebugEnabled()) { logger.debug("Sent packet to " + this.multicastSocket.getInterface()); } } }