/**
* Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP).
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package net.ctdp.rfdynhud.plugins.datasender;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* Connects to the editor via a socket and sends/receives data (client side).
*
* @author Marvin Froehlich (CTDP)
*/
public abstract class AbstractTCPClientCommunicator extends AbstractClientCommunicator implements Runnable
{
private String connectionString = null;
private String host = null;
private int port = 0;
private volatile boolean running = false;
private volatile boolean connected = false;
private volatile boolean closeRequested = false;
private volatile boolean waitingForConnection = false;
private boolean commandInProgress = false;
private short currentCommand = 0;
private boolean commandEnded = false;
public final String getLastConnectionString()
{
return ( connectionString );
}
@Override
public final boolean isRunning()
{
return ( running );
}
@Override
public final boolean isConnected()
{
return ( connected );
}
@Override
protected void startCommandImpl( short code )
{
synchronized ( eventsBuffer )
{
if ( commandInProgress )
throw new IllegalStateException( "Another command (" + currentCommand + ") has been started, but not ended." );
currentCommand = code;
commandInProgress = true;
try
{
eventsBuffer.writeShort( code );
}
catch ( IOException e )
{
log( e );
}
}
}
@Override
protected void endCommandImpl()
{
synchronized ( eventsBuffer )
{
if ( !commandInProgress )
throw new IllegalStateException( "No command had been started." );
currentCommand = 0;
commandInProgress = false;
commandEnded = true;
}
}
@Override
public void run()
{
running = true;
closeRequested = false;
Socket socket = null;
DataInputStream in = null;
OutputStream out = null;
try
{
waitingForConnection = true;
socket = new Socket( host, port );
waitingForConnection = false;
in = new DataInputStream( new BufferedInputStream( socket.getInputStream() ) );
out = socket.getOutputStream();
out.write( ( CONNECTION_REQUEST >>> 8 ) & 0xFF );
out.write( ( CONNECTION_REQUEST >>> 0 ) & 0xFF );
}
catch ( UnknownHostException e )
{
waitingForConnection = false;
onConnectionRefused( "Connection refused (unknown host)" );
running = false;
return;
}
catch ( IOException e )
{
waitingForConnection = false;
onConnectionRefused( "Connection refused" );
running = false;
return;
}
catch ( Throwable t )
{
waitingForConnection = false;
log( t );
running = false;
return;
}
connected = true;
while ( running && socket.isConnected() )
{
synchronized ( eventsBuffer )
{
if ( ( eventsBuffer0.size() > 0 ) )
{
try
{
//System.out.println( "Sending " + eventsBuffer0.size() + " bytes." );
eventsBuffer0.writeTo( out );
if ( commandEnded )
out.flush();
eventsBuffer0.reset();
}
catch ( IOException e )
{
log( e );
}
}
}
if ( closeRequested )
{
try
{
out.write( ( CONNECTION_CLOSED >>> 8 ) & 0xFF );
out.write( ( CONNECTION_CLOSED >>> 0 ) & 0xFF );
running = false;
}
catch ( IOException e )
{
running = false;
log( e );
}
}
try
{
if ( in.available() >= 2 )
{
running = readInput( in ) && running;
}
}
catch ( IOException e )
{
log( e );
}
try
{
Thread.sleep( 10L );
}
catch ( InterruptedException e )
{
log( e );
}
}
running = false;
connected = false;
try
{
in.close();
out.close();
socket.close();
}
catch ( IOException e )
{
log( e );
}
synchronized ( eventsBuffer )
{
eventsBuffer0.reset();
}
onConnectionClosed();
}
public static Object[] parseConnectionString( String connectionString )
{
String host = connectionString;
int port = 9876;
int p = host.indexOf( ':' );
if ( p >= 0 )
{
port = Integer.parseInt( host.substring( p + 1 ) );
host = host.substring( 0, p );
}
return ( new Object[] { host, port } );
}
@Override
public void connect( String connectionString )
{
Object[] parsed = parseConnectionString( connectionString );
this.connectionString = connectionString;
this.host = (String)parsed[0];
this.port = (Integer)parsed[1];
if ( !running )
{
new Thread( this ).start();
try
{
Thread.sleep( 100L );
}
catch ( InterruptedException e )
{
}
while ( running && !connected )
{
try
{
Thread.sleep( 10L );
}
catch ( InterruptedException e )
{
}
}
}
}
@Override
protected void close( boolean restart )
{
if ( connected )
{
closeRequested = true;
}
if ( waitingForConnection )
{
/*
try
{
// Create dummy connection to close the waiting socket.
Socket socket2 = new Socket( "localhost", port );
socket2.close();
}
catch ( IOException e )
{
manager.log( e );
}
*/
}
}
public AbstractTCPClientCommunicator()
{
}
}