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.model.message.OutgoingDatagramMessage;
19  import org.fourthline.cling.transport.Router;
20  import org.fourthline.cling.transport.spi.DatagramIO;
21  import org.fourthline.cling.transport.spi.DatagramProcessor;
22  import org.fourthline.cling.transport.spi.InitializationException;
23  import org.fourthline.cling.model.UnsupportedDataException;
24  
25  import java.net.DatagramPacket;
26  import java.net.InetAddress;
27  import java.net.InetSocketAddress;
28  import java.net.MulticastSocket;
29  import java.net.SocketException;
30  import java.util.logging.Level;
31  import java.util.logging.Logger;
32  
33  /**
34   * Default implementation based on a single shared (receive/send) UDP <code>MulticastSocket</code>.
35   * <p>
36   * Although we do not receive multicast datagrams with this service, sending multicast
37   * datagrams with a configuration time-to-live requires a <code>MulticastSocket</code>.
38   * </p>
39   * <p>
40   * Thread-safety is guaranteed through synchronization of methods of this service and
41   * by the thread-safe underlying socket.
42   * </p>
43   * @author Christian Bauer
44   */
45  public class DatagramIOImpl implements DatagramIO<DatagramIOConfigurationImpl> {
46  
47      private static Logger log = Logger.getLogger(DatagramIO.class.getName());
48  
49      /* Implementation notes for unicast/multicast UDP:
50  
51      http://forums.sun.com/thread.jspa?threadID=771852
52      http://mail.openjdk.java.net/pipermail/net-dev/2008-December/000497.html
53      https://jira.jboss.org/jira/browse/JGRP-978
54      http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4701650
55  
56       */
57  
58      final protected DatagramIOConfigurationImpl configuration;
59  
60      protected Router router;
61      protected DatagramProcessor datagramProcessor;
62  
63      protected InetSocketAddress localAddress;
64      protected MulticastSocket socket; // For sending unicast & multicast, and reveiving unicast
65  
66      public DatagramIOImpl(DatagramIOConfigurationImpl configuration) {
67          this.configuration = configuration;
68      }
69  
70      public DatagramIOConfigurationImpl getConfiguration() {
71          return configuration;
72      }
73  
74      synchronized public void init(InetAddress bindAddress, Router router, DatagramProcessor datagramProcessor) throws InitializationException {
75  
76          this.router = router;
77          this.datagramProcessor = datagramProcessor;
78  
79          try {
80  
81              // TODO: UPNP VIOLATION: The spec does not prohibit using the 1900 port here again, however, the
82              // Netgear ReadyNAS miniDLNA implementation will no longer answer if it has to send search response
83              // back via UDP unicast to port 1900... so we use an ephemeral port
84              log.info("Creating bound socket (for datagram input/output) on: " + bindAddress);
85              localAddress = new InetSocketAddress(bindAddress, 0);
86              socket = new MulticastSocket(localAddress);
87              socket.setTimeToLive(configuration.getTimeToLive());
88              socket.setReceiveBufferSize(262144); // Keep a backlog of incoming datagrams if we are not fast enough
89          } catch (Exception ex) {
90              throw new InitializationException("Could not initialize " + getClass().getSimpleName() + ": " + ex);
91          }
92      }
93  
94      synchronized public void stop() {
95          if (socket != null && !socket.isClosed()) {
96              socket.close();
97          }
98      }
99  
100     public void run() {
101         log.fine("Entering blocking receiving loop, listening for UDP datagrams on: " + socket.getLocalAddress());
102 
103         while (true) {
104 
105             try {
106                 byte[] buf = new byte[getConfiguration().getMaxDatagramBytes()];
107                 DatagramPacket datagram = new DatagramPacket(buf, buf.length);
108 
109                 socket.receive(datagram);
110 
111                 log.fine(
112                         "UDP datagram received from: "
113                                 + datagram.getAddress().getHostAddress()
114                                 + ":" + datagram.getPort()
115                                 + " on: " + localAddress
116                 );
117 
118 
119                 router.received(datagramProcessor.read(localAddress.getAddress(), datagram));
120 
121             } catch (SocketException ex) {
122                 log.fine("Socket closed");
123                 break;
124             } catch (UnsupportedDataException ex) {
125                 log.info("Could not read datagram: " + ex.getMessage());
126             } catch (Exception ex) {
127                 throw new RuntimeException(ex);
128             }
129         }
130         try {
131             if (!socket.isClosed()) {
132                 log.fine("Closing unicast socket");
133                 socket.close();
134             }
135         } catch (Exception ex) {
136             throw new RuntimeException(ex);
137         }
138     }
139 
140     synchronized public void send(OutgoingDatagramMessage message) {
141         if (log.isLoggable(Level.FINE)) {
142             log.fine("Sending message from address: " + localAddress);
143         }
144         DatagramPacket packet = datagramProcessor.write(message);
145 
146         if (log.isLoggable(Level.FINE)) {
147             log.fine("Sending UDP datagram packet to: " + message.getDestinationAddress() + ":" + message.getDestinationPort());
148         }
149         
150         send(packet);
151     }
152 
153     synchronized public void send(DatagramPacket datagram) {
154         if (log.isLoggable(Level.FINE)) {
155             log.fine("Sending message from address: " + localAddress);
156         }
157             
158         try {
159             socket.send(datagram);
160         } catch (SocketException ex) {
161             log.fine("Socket closed, aborting datagram send to: " + datagram.getAddress());
162         } catch (RuntimeException ex) {
163             throw ex;
164         } catch (Exception ex) {
165             log.log(Level.SEVERE, "Exception sending datagram to: " + datagram.getAddress() + ": " + ex, ex);
166         }
167     }
168 }