/** * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ * * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). * * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 3.0 of the License, or (at your option) any later * version. * * BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. * */ package org.bigbluebutton.deskshare.client.net; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.UnknownHostException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import net.jcip.annotations.ThreadSafe; import org.bigbluebutton.deskshare.client.ExitCode; import org.bigbluebutton.deskshare.client.blocks.BlockManager; import org.bigbluebutton.deskshare.common.Dimension; @ThreadSafe public class NetworkStreamSender implements NextBlockRetriever, NetworkStreamListener { public static final String NAME = "NETWORKSTREAMSENDER: "; private ExecutorService executor; private final BlockingQueue<Message> blockDataQ = new LinkedBlockingQueue<Message>(); private final int numThreads; private final String host; private final int port; private final boolean useTLS; private final String room; private final boolean httpTunnel; private final boolean useSVC2; private NetworkSocketStreamSender[] socketSenders; private NetworkHttpStreamSender[] httpSenders; private boolean tunneling = false; private boolean stopped = true; private int numRunningThreads = 0; private Dimension screenDim; private Dimension blockDim; private BlockManager blockManager; private NetworkConnectionListener listener; private final SequenceNumberGenerator seqNumGenerator = new SequenceNumberGenerator(); public NetworkStreamSender(BlockManager blockManager, String host, int port, boolean useTLS , String room, Dimension screenDim, Dimension blockDim, boolean httpTunnel, boolean useSVC2) { this.blockManager = blockManager; this.host = host; this.port = port; this.useTLS = useTLS; this.room = room; this.screenDim = screenDim; this.blockDim = blockDim; this.httpTunnel = httpTunnel; this.useSVC2 = useSVC2; //numThreads = Runtime.getRuntime().availableProcessors() * 3; //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Use one thread per row of tiles //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //numThreads = screenDim.getHeight() / blockDim.getHeight(); // Use one single thread to save resources numThreads = 1; System.out.println(NAME + "Starting up " + numThreads + " sender threads."); executor = Executors.newFixedThreadPool(numThreads); } public void addNetworkConnectionListener(NetworkConnectionListener listener) { this.listener = listener; } private void notifyNetworkConnectionListener(ExitCode reason) { if (listener != null) listener.networkConnectionException(reason); } private boolean trySocketConnection(String host, int port) { try { Socket socket = new Socket(); InetSocketAddress endpoint = new InetSocketAddress(host, port); socket.connect(endpoint, 5000); socket.close(); return true; } catch (UnknownHostException e) { System.out.println("Unknown host [" + host + "]"); } catch (IOException e) { System.out.println("Cannot connect to [" + host + ":" + port + "]"); } return false; } public boolean connect() { //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // If the requested server port is nonzero, then try to connect to the // requested port. Otherwise, tunnel the connection to the web server. //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ if ((port != 0) && trySocketConnection(host, port)) { socketSenders = new NetworkSocketStreamSender[numThreads]; for (int i = 0; i < numThreads; i++) { try { createSender(i); numRunningThreads++; } catch (ConnectionException e) { System.out.println("Failed to connect using socket."); } } } else { if (httpTunnel) { System.out.println(NAME + "Trying http tunneling"); numRunningThreads = 0; if (tryHttpTunneling()) { tunneling = true; System.out.println(NAME + "Will use http tunneling"); httpSenders = new NetworkHttpStreamSender[numThreads]; for (int i = 0; i < numThreads; i++) { try { createHttpSender(i); numRunningThreads++; } catch (ConnectionException e) { System.out.println("Failed to connect using http."); } } } } } if (numRunningThreads != numThreads) { try { stop(); } catch (ConnectionException e) { System.out.println("Failed to stop deskshare applet."); } return false; } return true; } private void createSender(int i) throws ConnectionException { socketSenders[i] = new NetworkSocketStreamSender(i, this, room, screenDim, blockDim, seqNumGenerator, useSVC2); socketSenders[i].addListener(this); socketSenders[i].connect(host, port, useTLS); } private void createHttpSender(int i) throws ConnectionException { httpSenders[i] = new NetworkHttpStreamSender(i, this, room, screenDim, blockDim, seqNumGenerator, useSVC2); httpSenders[i].addListener(this); httpSenders[i].connect(host); } public void send(Message message) { boolean added = blockDataQ.offer(message); // System.out.println("Offered to queue: res="+added+" size="+blockDataQ.size()+" remaining_capacity="+blockDataQ.remainingCapacity()); } public void start() { System.out.println(NAME + "Starting network sender."); if (tunneling) { // NEW httpSenders[0].sendStartStreamMessage(); for (int i = 0; i < numRunningThreads; i++) { executor.execute(httpSenders[i]); } } else { for (int i = 0; i < numRunningThreads; i++) { try { socketSenders[i].sendStartStreamMessage(); executor.execute(socketSenders[i]); } catch (ConnectionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } stopped = false; } private volatile boolean clearQ = true; public void stop() throws ConnectionException { stopped = true; System.out.println(NAME + "Stopping network sender"); // NEW if (tunneling) { if (httpSenders == null) return; if (httpSenders[0] != null) httpSenders[0].disconnect(); } if (socketSenders == null) return; for (int i = 0; i < numRunningThreads; i++) { try { if (tunneling) { if (httpSenders[i] != null) httpSenders[i].stopProcessingBlocks(); } else { //socketSenders[i].disconnect(); if (clearQ) { clearQ = false; blockDataQ.clear(); } send(new PoisonMessage()); // LRP changed 06-06-2012 Thread.yield(); //Thread.sleep(1000); socketSenders[i].disconnect(); } } catch (Exception e) { e.printStackTrace(); } } System.out.println("Shutting down executor"); executor.shutdownNow(); System.out.println("Shutting down executor [DONE]"); httpSenders = null; socketSenders = null; } private boolean tryHttpTunneling() { NetworkHttpStreamSender httpSender = new NetworkHttpStreamSender(0, this, room, screenDim, blockDim, seqNumGenerator, useSVC2); try { httpSender.connect(host); return true; } catch (ConnectionException e) { System.out.println(NAME + "Problem connecting to " + host); } return false; } public void blockSent(int position) { blockManager.blockSent(position); } public EncodedBlockData getBlockToSend(int position) { return blockManager.getBlock(position).encode(); } public Message getNextMessageToSend() throws InterruptedException { try { return (Message) blockDataQ.take(); } catch (InterruptedException e) { if (!stopped) e.printStackTrace(); throw e; } } @Override public void networkException(int id, ExitCode reason) { try { numRunningThreads--; if (tunneling) { // httpSenders[id].disconnect(); System.out.println(NAME + "Failed to use http tunneling. Stopping."); stop(); notifyNetworkConnectionListener(reason); } else { socketSenders[id].disconnect(); } if (numRunningThreads < 1) { System.out.println(NAME + "No more sender threads. Stopping."); stop(); notifyNetworkConnectionListener(reason); } else { System.out.println(NAME + "Sender thread stopped. " + numRunningThreads + " sender threads remaining."); } } catch (ConnectionException e) { // TODO Auto-generated catch block e.printStackTrace(); if (numRunningThreads < 1) { System.out.println(NAME + "No more sender threads. Stopping."); notifyNetworkConnectionListener(reason); } else { System.out.println(NAME + "Sender thread stopped. " + numRunningThreads + " sender threads remaining."); } } } }