View Javadoc
1   /*
2    * Copyright (C) 2013 4th Line GmbH, Switzerland
3    *
4    * The contents of this file are subject to the terms of either the GNU
5    * Lesser General Public License Version 2 or later ("LGPL") or the
6    * Common Development and Distribution License Version 1 or later
7    * ("CDDL") (collectively, the "License"). You may not use this file
8    * except in compliance with the License. See LICENSE.txt for more
9    * information.
10   *
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14   */
15  
16  package org.fourthline.cling.transport.impl;
17  
18  import org.fourthline.cling.transport.Router;
19  import org.fourthline.cling.transport.spi.DatagramProcessor;
20  import org.fourthline.cling.transport.spi.InitializationException;
21  import org.fourthline.cling.transport.spi.MulticastReceiver;
22  import org.fourthline.cling.model.UnsupportedDataException;
23  import org.fourthline.cling.transport.spi.NetworkAddressFactory;
24  
25  import java.net.DatagramPacket;
26  import java.net.Inet6Address;
27  import java.net.InetAddress;
28  import java.net.InetSocketAddress;
29  import java.net.MulticastSocket;
30  import java.net.NetworkInterface;
31  import java.net.SocketException;
32  import java.util.logging.Logger;
33  
34  /**
35   * Default implementation based on a UDP <code>MulticastSocket</code>.
36   * <p>
37   * Thread-safety is guaranteed through synchronization of methods of this service and
38   * by the thread-safe underlying socket.
39   * </p>
40   * @author Christian Bauer
41   */
42  public class MulticastReceiverImpl implements MulticastReceiver<MulticastReceiverConfigurationImpl> {
43  
44      private static Logger log = Logger.getLogger(MulticastReceiver.class.getName());
45  
46      final protected MulticastReceiverConfigurationImpl configuration;
47  
48      protected Router router;
49      protected NetworkAddressFactory networkAddressFactory;
50      protected DatagramProcessor datagramProcessor;
51  
52      protected NetworkInterface multicastInterface;
53      protected InetSocketAddress multicastAddress;
54      protected MulticastSocket socket;
55  
56      public MulticastReceiverImpl(MulticastReceiverConfigurationImpl configuration) {
57          this.configuration = configuration;
58      }
59  
60      public MulticastReceiverConfigurationImpl getConfiguration() {
61          return configuration;
62      }
63  
64      synchronized public void init(NetworkInterface networkInterface,
65                                    Router router,
66                                    NetworkAddressFactory networkAddressFactory,
67                                    DatagramProcessor datagramProcessor) throws InitializationException {
68  
69          this.router = router;
70          this.networkAddressFactory = networkAddressFactory;
71          this.datagramProcessor = datagramProcessor;
72          this.multicastInterface = networkInterface;
73  
74          try {
75  
76              log.info("Creating wildcard socket (for receiving multicast datagrams) on port: " + configuration.getPort());
77              multicastAddress = new InetSocketAddress(configuration.getGroup(), configuration.getPort());
78  
79              socket = new MulticastSocket(configuration.getPort());
80              socket.setReuseAddress(true);
81              socket.setReceiveBufferSize(32768); // Keep a backlog of incoming datagrams if we are not fast enough
82  
83              log.info("Joining multicast group: " + multicastAddress + " on network interface: " + multicastInterface.getDisplayName());
84              socket.joinGroup(multicastAddress, multicastInterface);
85  
86          } catch (Exception ex) {
87              throw new InitializationException("Could not initialize " + getClass().getSimpleName() + ": " + ex);
88          }
89      }
90  
91      synchronized public void stop() {
92          if (socket != null && !socket.isClosed()) {
93              try {
94                  log.fine("Leaving multicast group");
95                  socket.leaveGroup(multicastAddress, multicastInterface);
96                  // Well this doesn't work and I have no idea why I get "java.net.SocketException: Can't assign requested address"
97              } catch (Exception ex) {
98                  log.fine("Could not leave multicast group: " + ex);
99              }
100             // So... just close it and ignore the log messages
101             socket.close();
102         }
103     }
104 
105     public void run() {
106 
107         log.fine("Entering blocking receiving loop, listening for UDP datagrams on: " + socket.getLocalAddress());
108         while (true) {
109 
110             try {
111                 byte[] buf = new byte[getConfiguration().getMaxDatagramBytes()];
112                 DatagramPacket datagram = new DatagramPacket(buf, buf.length);
113 
114                 socket.receive(datagram);
115 
116                 InetAddress receivedOnLocalAddress =
117                         networkAddressFactory.getLocalAddress(
118                             multicastInterface,
119                             multicastAddress.getAddress() instanceof Inet6Address,
120                             datagram.getAddress()
121                         );
122 
123                 log.fine(
124                         "UDP datagram received from: " + datagram.getAddress().getHostAddress() 
125                                 + ":" + datagram.getPort()
126                                 + " on local interface: " + multicastInterface.getDisplayName()
127                                 + " and address: " + receivedOnLocalAddress.getHostAddress()
128                 );
129 
130                 router.received(datagramProcessor.read(receivedOnLocalAddress, datagram));
131 
132             } catch (SocketException ex) {
133                 log.fine("Socket closed");
134                 break;
135             } catch (UnsupportedDataException ex) {
136                 log.info("Could not read datagram: " + ex.getMessage());
137             } catch (Exception ex) {
138                 throw new RuntimeException(ex);
139             }
140         }
141         try {
142             if (!socket.isClosed()) {
143                 log.fine("Closing multicast socket");
144                 socket.close();
145             }
146         } catch (Exception ex) {
147             throw new RuntimeException(ex);
148         }
149     }
150 
151 
152 }
153