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 com.sun.net.httpserver.HttpExchange;
19  import com.sun.net.httpserver.HttpHandler;
20  import com.sun.net.httpserver.HttpServer;
21  import org.fourthline.cling.model.message.Connection;
22  import org.fourthline.cling.transport.Router;
23  import org.fourthline.cling.transport.spi.InitializationException;
24  import org.fourthline.cling.transport.spi.StreamServer;
25  
26  import java.io.IOException;
27  import java.net.InetAddress;
28  import java.net.InetSocketAddress;
29  import java.util.logging.Logger;
30  
31  /**
32   * Implementation based on the built-in SUN JDK 6.0 HTTP Server.
33   * <p>
34   * See <a href="http://download.oracle.com/javase/6/docs/jre/api/net/httpserver/spec/index.html?com/sun/net/httpserver/HttpServer.html">the
35   * documentation of the SUN JDK 6.0 HTTP Server</a>.
36   * </p>
37   * <p>
38   * This implementation <em>DOES NOT WORK</em> on Android. Read the Cling manual for
39   * alternatives for Android.
40   * </p>
41   * <p>
42   * This implementation does not support connection alive checking, as we can't send
43   * heartbeats to the client. We don't have access to the raw socket with the Sun API.
44   * </p>
45   *
46   * @author Christian Bauer
47   */
48  public class StreamServerImpl implements StreamServer<StreamServerConfigurationImpl> {
49  
50      private static Logger log = Logger.getLogger(StreamServer.class.getName());
51  
52      final protected StreamServerConfigurationImpl configuration;
53      protected HttpServer server;
54  
55      public StreamServerImpl(StreamServerConfigurationImpl configuration) {
56          this.configuration = configuration;
57      }
58  
59      synchronized public void init(InetAddress bindAddress, Router router) throws InitializationException {
60          try {
61              InetSocketAddress socketAddress = new InetSocketAddress(bindAddress, configuration.getListenPort());
62  
63              server = HttpServer.create(socketAddress, configuration.getTcpConnectionBacklog());
64              server.createContext("/", new RequestHttpHandler(router));
65  
66              log.info("Created server (for receiving TCP streams) on: " + server.getAddress());
67  
68          } catch (Exception ex) {
69              throw new InitializationException("Could not initialize " + getClass().getSimpleName() + ": " + ex.toString(), ex);
70          }
71      }
72  
73      synchronized public int getPort() {
74          return server.getAddress().getPort();
75      }
76  
77      public StreamServerConfigurationImpl getConfiguration() {
78          return configuration;
79      }
80  
81      synchronized public void run() {
82          log.fine("Starting StreamServer...");
83          // Starts a new thread but inherits the properties of the calling thread
84          server.start();
85      }
86  
87      synchronized public void stop() {
88          log.fine("Stopping StreamServer...");
89          if (server != null) server.stop(1);
90      }
91  
92      protected class RequestHttpHandler implements HttpHandler {
93  
94          private final Router router;
95  
96          public RequestHttpHandler(Router router) {
97              this.router = router;
98          }
99  
100         // This is executed in the request receiving thread!
101         public void handle(final HttpExchange httpExchange) throws IOException {
102             // And we pass control to the service, which will (hopefully) start a new thread immediately so we can
103             // continue the receiving thread ASAP
104             log.fine("Received HTTP exchange: " + httpExchange.getRequestMethod() + " " + httpExchange.getRequestURI());
105             router.received(
106                 new HttpExchangeUpnpStream(router.getProtocolFactory(), httpExchange) {
107                     @Override
108                     protected Connection createConnection() {
109                         return new HttpServerConnection(httpExchange);
110                     }
111                 }
112             );
113         }
114     }
115 
116     /**
117      * Logs a warning and returns <code>true</code>, we can't access the socket using the awful JDK webserver API.
118      * <p>
119      * Override this method if you know how to do it.
120      * </p>
121      */
122     protected boolean isConnectionOpen(HttpExchange exchange) {
123         log.warning("Can't check client connection, socket access impossible on JDK webserver!");
124         return true;
125     }
126 
127     protected class HttpServerConnection implements Connection {
128 
129         protected HttpExchange exchange;
130 
131         public HttpServerConnection(HttpExchange exchange) {
132             this.exchange = exchange;
133         }
134 
135         @Override
136         public boolean isOpen() {
137             return isConnectionOpen(exchange);
138         }
139 
140         @Override
141         public InetAddress getRemoteAddress() {
142             return exchange.getRemoteAddress() != null
143                 ? exchange.getRemoteAddress().getAddress()
144                 : null;
145         }
146 
147         @Override
148         public InetAddress getLocalAddress() {
149             return exchange.getLocalAddress() != null
150                 ? exchange.getLocalAddress().getAddress()
151                 : null;
152         }
153     }
154 }