001package org.apache.commons.jcs3.utils.discovery; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.io.ByteArrayOutputStream; 023import java.io.IOException; 024import java.net.DatagramPacket; 025import java.net.InetAddress; 026import java.net.MulticastSocket; 027import java.net.NetworkInterface; 028import java.util.ArrayList; 029 030import org.apache.commons.jcs3.engine.CacheInfo; 031import org.apache.commons.jcs3.engine.behavior.IElementSerializer; 032import org.apache.commons.jcs3.log.Log; 033import org.apache.commons.jcs3.log.LogManager; 034import org.apache.commons.jcs3.utils.discovery.UDPDiscoveryMessage.BroadcastType; 035import org.apache.commons.jcs3.utils.net.HostNameUtil; 036import org.apache.commons.jcs3.utils.serialization.StandardSerializer; 037 038/** 039 * This is a generic sender for the UDPDiscovery process. 040 */ 041public class UDPDiscoverySender implements AutoCloseable 042{ 043 /** The logger. */ 044 private static final Log log = LogManager.getLog( UDPDiscoverySender.class ); 045 046 /** The socket */ 047 private final MulticastSocket localSocket; 048 049 /** The address */ 050 private final InetAddress multicastAddress; 051 052 /** The port */ 053 private final int multicastPort; 054 055 /** Used to serialize messages */ 056 private final IElementSerializer serializer; 057 058 /** 059 * Constructor for the UDPDiscoverySender object 060 * <p> 061 * This sender can be used to send multiple messages. 062 * <p> 063 * When you are done sending, you should destroy the socket sender. 064 * <p> 065 * @param host 066 * @param port 067 * @param udpTTL the Datagram packet time-to-live 068 * @throws IOException 069 * @deprecated Specify serializer implementation explicitly 070 */ 071 @Deprecated 072 public UDPDiscoverySender( final String host, final int port, final int udpTTL ) 073 throws IOException 074 { 075 this(null, host, port, udpTTL, new StandardSerializer()); 076 } 077 078 /** 079 * Constructor for the UDPDiscoverySender object 080 * <p> 081 * This sender can be used to send multiple messages. 082 * <p> 083 * When you are done sending, you should destroy the socket sender. 084 * <p> 085 * @param udpDiscoveryAttributes configuration object 086 * @param serializer the Serializer to use when sending messages 087 * @throws IOException 088 * @since 3.1 089 */ 090 public UDPDiscoverySender(final UDPDiscoveryAttributes udpDiscoveryAttributes, final IElementSerializer serializer) 091 throws IOException 092 { 093 this(udpDiscoveryAttributes.getUdpDiscoveryInterface(), 094 udpDiscoveryAttributes.getUdpDiscoveryAddr(), 095 udpDiscoveryAttributes.getUdpDiscoveryPort(), 096 udpDiscoveryAttributes.getUdpTTL(), 097 serializer); 098 } 099 100 /** 101 * Constructor for the UDPDiscoverySender object 102 * <p> 103 * This sender can be used to send multiple messages. 104 * <p> 105 * When you are done sending, you should destroy the socket sender. 106 * <p> 107 * @param mcastInterface the Multicast interface name to use, if null, try to autodetect 108 * @param host 109 * @param port 110 * @param udpTTL the Datagram packet time-to-live 111 * @param serializer the Serializer to use when sending messages 112 * @throws IOException 113 * @since 3.1 114 */ 115 public UDPDiscoverySender(final String mcastInterface, final String host, 116 final int port, final int udpTTL, IElementSerializer serializer) 117 throws IOException 118 { 119 try 120 { 121 log.debug( "Constructing socket for sender on port [{0}]", port ); 122 localSocket = new MulticastSocket( port ); 123 124 if (udpTTL > 0) 125 { 126 log.debug( "Setting datagram TTL to [{0}]", udpTTL ); 127 localSocket.setTimeToLive(udpTTL); 128 } 129 130 // Remote address. 131 multicastAddress = InetAddress.getByName( host ); 132 133 // Use dedicated interface if specified 134 NetworkInterface multicastInterface = null; 135 if (mcastInterface != null) 136 { 137 multicastInterface = NetworkInterface.getByName(mcastInterface); 138 } 139 else 140 { 141 multicastInterface = HostNameUtil.getMulticastNetworkInterface(); 142 } 143 if (multicastInterface != null) 144 { 145 log.info("Sending multicast via network interface {0}", 146 multicastInterface::getDisplayName); 147 localSocket.setNetworkInterface(multicastInterface); 148 } 149 } 150 catch ( final IOException e ) 151 { 152 log.error( "Could not bind to multicast address [{0}]", host, e ); 153 throw e; 154 } 155 156 this.multicastPort = port; 157 this.serializer = serializer; 158 } 159 160 /** 161 * Closes the socket connection. 162 */ 163 @Override 164 public void close() 165 { 166 if ( this.localSocket != null && !this.localSocket.isClosed() ) 167 { 168 this.localSocket.close(); 169 } 170 } 171 172 /** 173 * Send messages. 174 * <p> 175 * @param message 176 * @throws IOException 177 */ 178 public void send( final UDPDiscoveryMessage message ) 179 throws IOException 180 { 181 if ( this.localSocket == null ) 182 { 183 throw new IOException( "Socket is null, cannot send message." ); 184 } 185 186 if ( this.localSocket.isClosed() ) 187 { 188 throw new IOException( "Socket is closed, cannot send message." ); 189 } 190 191 log.debug( "sending UDPDiscoveryMessage, address [{0}], port [{1}], " 192 + "message = {2}", multicastAddress, multicastPort, message ); 193 194 final byte[] bytes = serializer.serialize( message ); 195 196 // put the byte array in a packet 197 final DatagramPacket packet = new DatagramPacket( bytes, bytes.length, multicastAddress, multicastPort ); 198 199 log.debug( "Sending DatagramPacket with {0} bytes to {1}:{2}", 200 bytes.length, multicastAddress, multicastPort ); 201 202 localSocket.send( packet ); 203 } 204 205 /** 206 * Ask other to broadcast their info the multicast address. If a lateral is non receiving it 207 * can use this. This is also called on startup so we can get info. 208 * <p> 209 * @throws IOException 210 */ 211 public void requestBroadcast() 212 throws IOException 213 { 214 log.debug( "sending requestBroadcast" ); 215 216 final UDPDiscoveryMessage message = new UDPDiscoveryMessage(); 217 message.setHost(multicastAddress.getHostAddress()); 218 message.setPort(multicastPort); 219 message.setRequesterId( CacheInfo.listenerId ); 220 message.setMessageType( BroadcastType.REQUEST ); 221 send( message ); 222 } 223 224 /** 225 * This sends a message broadcasting out that the host and port is available for connections. 226 * <p> 227 * It uses the vmid as the requesterDI 228 * @param host 229 * @param port 230 * @param cacheNames 231 * @throws IOException 232 */ 233 public void passiveBroadcast( final String host, final int port, final ArrayList<String> cacheNames ) 234 throws IOException 235 { 236 passiveBroadcast( host, port, cacheNames, CacheInfo.listenerId ); 237 } 238 239 /** 240 * This allows you to set the sender id. This is mainly for testing. 241 * <p> 242 * @param host 243 * @param port 244 * @param cacheNames names of the cache regions 245 * @param listenerId 246 * @throws IOException 247 */ 248 protected void passiveBroadcast( final String host, final int port, final ArrayList<String> cacheNames, final long listenerId ) 249 throws IOException 250 { 251 log.debug( "sending passiveBroadcast" ); 252 253 final UDPDiscoveryMessage message = new UDPDiscoveryMessage(); 254 message.setHost( host ); 255 message.setPort( port ); 256 message.setCacheNames( cacheNames ); 257 message.setRequesterId( listenerId ); 258 message.setMessageType( BroadcastType.PASSIVE ); 259 send( message ); 260 } 261 262 /** 263 * This sends a message broadcasting our that the host and port is no longer available. 264 * <p> 265 * It uses the vmid as the requesterID 266 * <p> 267 * @param host host 268 * @param port port 269 * @param cacheNames names of the cache regions 270 * @throws IOException on error 271 */ 272 public void removeBroadcast( final String host, final int port, final ArrayList<String> cacheNames ) 273 throws IOException 274 { 275 removeBroadcast( host, port, cacheNames, CacheInfo.listenerId ); 276 } 277 278 /** 279 * This allows you to set the sender id. This is mainly for testing. 280 * <p> 281 * @param host host 282 * @param port port 283 * @param cacheNames names of the cache regions 284 * @param listenerId listener ID 285 * @throws IOException on error 286 */ 287 protected void removeBroadcast( final String host, final int port, final ArrayList<String> cacheNames, final long listenerId ) 288 throws IOException 289 { 290 log.debug( "sending removeBroadcast" ); 291 292 final UDPDiscoveryMessage message = new UDPDiscoveryMessage(); 293 message.setHost( host ); 294 message.setPort( port ); 295 message.setCacheNames( cacheNames ); 296 message.setRequesterId( listenerId ); 297 message.setMessageType( BroadcastType.REMOVE ); 298 send( message ); 299 } 300} 301 302/** 303 * This allows us to get the byte array from an output stream. 304 * 305 * @deprecated No longer used 306 */ 307@Deprecated 308class MyByteArrayOutputStream 309 extends ByteArrayOutputStream 310{ 311 /** 312 * Gets the bytes attribute of the MyByteArrayOutputStream object 313 * @return The bytes value 314 */ 315 public byte[] getBytes() 316 { 317 return buf; 318 } 319}