jcifs.util.transport.Transport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-krb5-jdk7 Show documentation
Show all versions of jcifs-krb5-jdk7 Show documentation
JCIFS is an Open Source client library that implements the CIFS/SMB networking protocol in 100% Java
package jcifs.util.transport;
import java.io.*;
import java.net.*;
import java.util.*;
import jcifs.util.LogStream;
/**
* This class simplifies communication for protocols that support
* multiplexing requests. It encapsulates a stream and some protocol
* knowledge (provided by a concrete subclass) so that connecting,
* disconnecting, sending, and receiving can be syncronized
* properly. Apparatus is provided to send and receive requests
* concurrently.
*/
public abstract class Transport implements Runnable {
static int id = 0;
static LogStream log = LogStream.getInstance();
public static int readn( InputStream in,
byte[] b,
int off,
int len ) throws IOException {
int i = 0, n = -5;
while (i < len) {
n = in.read( b, off + i, len - i );
if (n <= 0) {
break;
}
i += n;
}
return i;
}
/* state values
* 0 - not connected
* 1 - connecting
* 2 - run connected
* 3 - connected
* 4 - error
*/
int state = 0;
String name = "Transport" + id++;
Thread thread;
TransportException te;
protected HashMap response_map = new HashMap( 4 );
protected abstract void makeKey( Request request ) throws IOException;
protected abstract Request peekKey() throws IOException;
protected abstract void doSend( Request request ) throws IOException;
protected abstract void doRecv( Response response ) throws IOException;
protected abstract void doSkip() throws IOException;
public synchronized void sendrecv( Request request,
Response response,
long timeout ) throws IOException {
makeKey( request );
response.isReceived = false;
try {
response_map.put( request, response );
doSend( request );
response.expiration = System.currentTimeMillis() + timeout;
while (!response.isReceived) {
wait( timeout );
timeout = response.expiration - System.currentTimeMillis();
if (timeout <= 0) {
throw new TransportException( name +
" timedout waiting for response to " +
request );
}
}
} catch( IOException ioe ) {
if (log.level > 2)
ioe.printStackTrace( log );
try {
disconnect( true );
} catch( IOException ioe2 ) {
ioe2.printStackTrace( log );
}
throw ioe;
} catch( InterruptedException ie ) {
throw new TransportException( ie );
} finally {
response_map.remove( request );
}
}
private void loop() {
while( thread == Thread.currentThread() ) {
try {
Request key = peekKey();
if (key == null)
throw new IOException( "end of stream" );
synchronized (this) {
Response response = (Response)response_map.get( key );
if (response == null) {
if (log.level >= 4)
log.println( "Invalid key, skipping message" );
doSkip();
} else {
doRecv( response );
response.isReceived = true;
notifyAll();
}
}
} catch( Exception ex ) {
String msg = ex.getMessage();
boolean timeout = msg != null && msg.equals( "Read timed out" );
/* If just a timeout, try to disconnect gracefully
*/
boolean hard = timeout == false;
if (!timeout && log.level >= 3)
ex.printStackTrace( log );
try {
disconnect( hard );
} catch( IOException ioe ) {
ioe.printStackTrace( log );
}
}
}
}
/* Build a connection. Only one thread will ever call this method at
* any one time. If this method throws an exception or the connect timeout
* expires an encapsulating TransportException will be thrown from connect
* and the transport will be in error.
*/
protected abstract void doConnect() throws Exception;
/* Tear down a connection. If the hard parameter is true, the diconnection
* procedure should not initiate or wait for any outstanding requests on
* this transport.
*/
protected abstract void doDisconnect( boolean hard ) throws IOException;
public synchronized void connect( long timeout ) throws TransportException {
try {
switch (state) {
case 0:
break;
case 3:
return; // already connected
case 4:
state = 0;
throw new TransportException( "Connection in error", te );
default:
TransportException te = new TransportException( "Invalid state: " + state );
state = 0;
throw te;
}
state = 1;
te = null;
thread = new Thread( this, name );
thread.setDaemon( true );
synchronized (thread) {
thread.start();
thread.wait( timeout ); /* wait for doConnect */
switch (state) {
case 1: /* doConnect never returned */
state = 0;
thread = null;
throw new TransportException( "Connection timeout" );
case 2:
if (te != null) { /* doConnect throw Exception */
state = 4; /* error */
thread = null;
throw te;
}
state = 3; /* Success! */
return;
}
}
} catch( InterruptedException ie ) {
state = 0;
thread = null;
throw new TransportException( ie );
} finally {
/* This guarantees that we leave in a valid state
*/
if (state != 0 && state != 3 && state != 4) {
if (log.level >= 1)
log.println("Invalid state: " + state);
state = 0;
thread = null;
}
}
}
public synchronized void disconnect( boolean hard ) throws IOException {
IOException ioe = null;
switch (state) {
case 0: /* not connected - just return */
return;
case 2:
hard = true;
case 3: /* connected - go ahead and disconnect */
if (response_map.size() != 0 && !hard) {
break; /* outstanding requests */
}
try {
doDisconnect( hard );
} catch (IOException ioe0) {
ioe = ioe0;
}
case 4: /* in error - reset the transport */
thread = null;
state = 0;
break;
default:
if (log.level >= 1)
log.println("Invalid state: " + state);
thread = null;
state = 0;
break;
}
if (ioe != null)
throw ioe;
}
public void run() {
Thread run_thread = Thread.currentThread();
Exception ex0 = null;
try {
/* We cannot synchronize (run_thread) here or the caller's
* thread.wait( timeout ) cannot reaquire the lock and
* return which would render the timeout effectively useless.
*/
doConnect();
} catch( Exception ex ) {
ex0 = ex; // Defer to below where we're locked
return;
} finally {
synchronized (run_thread) {
if (run_thread != thread) {
/* Thread no longer the one setup for this transport --
* doConnect returned too late, just ignore.
*/
if (ex0 != null) {
if (log.level >= 2)
ex0.printStackTrace(log);
}
return;
}
if (ex0 != null) {
te = new TransportException( ex0 );
}
state = 2; // run connected
run_thread.notify();
}
}
/* Proccess responses
*/
loop();
}
public String toString() {
return name;
}
}