/* Copyright (c) 2008 Bluendo S.r.L.
* See about.html for details about license.
*
* $Id: SocketChannel.java 1464 2009-05-12 14:56:52Z luca $
*/
package it.yup.transport;
// #debug
import it.yup.util.Logger;
import it.yup.util.Utils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.util.TimerTask;
import javax.microedition.io.Connector;
import javax.microedition.io.SocketConnection;
import javax.microedition.io.StreamConnection;
//#ifdef TLS
import org.bouncycastle.crypto.tls.AlwaysValidVerifyer;
import org.bouncycastle.crypto.tls.TlsInputStream;
//#endif
import org.bouncycastle.crypto.tls.TlsProtocolHandler;
// #ifdef COMPRESSION
import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZInputStream;
import com.jcraft.jzlib.ZOutputStream;
// #endif
public class SocketChannel extends BaseChannel {
/** String identifying the transport type */
public static final String TRANSPORT_TYPE = "DIRECT_SOCKET";
/** Keepalive interval for XML streams */
// Please note that if we are using WiFi this KEEP_ALIVE must be very short
// Many cellphone WiFi implementations in fact hangs after 20-30s of inactivity,
// and they cannot receive packets any more
public long KEEP_ALIVE = 300 * 1000;
protected String connectionUrl;
protected TransportListener listener;
protected StreamConnection connection;
protected boolean exiting = false;
// #ifdef COMPRESSION
private boolean compressed = false;
// #endif
private TimerTask ka_task = null;
/*
* The handler used to trasnport tls data
*/
protected static TlsProtocolHandler handler;
public SocketChannel(String connectionUrl,
TransportListener transportListener) {
this.connectionUrl = connectionUrl;
this.listener = transportListener;
this.transportType = TRANSPORT_TYPE;
inputStream = null;
outputStream = null;
}
public void open() {
exiting = false;
// create the opener and start the connection
Runnable starter = new Runnable() {
public void run() {
inputStream = null;
outputStream = null;
try {
// #debug
Logger.log("Connecting to " + connectionUrl);
connection = (SocketConnection) Connector
.open(connectionUrl);
// #debug
Logger.log("Connected ");
inputStream = connection.openInputStream();
outputStream = connection.openOutputStream();
// start the sender after each new connection
sender = new Sender(SocketChannel.this);
sender.start();
listener.connectionEstablished(SocketChannel.this);
} catch (IOException e) {
// #debug
Logger.log("Connection failed: " + e.getMessage());
listener.connectionFailed(SocketChannel.this);
} catch (Exception e) {
// #debug
Logger.log("Unexpected exception: " + e.getMessage());
listener.connectionFailed(SocketChannel.this);
// YUPMidlet.yup.reportException("Unexpected Exception on Channel start.", e, null);
}
}
};
new Thread(starter).start();
}
public void close() {
if (pollAlive() == false)
return;
exiting = true;
try {
inputStream.close();
outputStream.close();
connection.close();
} catch (IOException e) {
// #mdebug
System.out.println("In closing strean");
e.printStackTrace();
// #enddebug
} catch (Exception e) {
// #mdebug
System.out.println("In closing strean");
e.printStackTrace();
// #enddebug
}
}
public boolean isOpen() {
return inputStream != null;
}
public InputStream getInputStream() {
return this.inputStream;
}
public OutputStream getOutputStream() {
return this.outputStream;
}
public void sendContent(byte[] packetToSend) {
synchronized (packets) {
packets.addElement(packetToSend);
packets.notify();
}
if (ka_task != null) {
ka_task.cancel();
}
ka_task = new TimerTask() {
public void run() {
/* utf-8 space */
sendContent(new byte[] { 0x20 });
}
};
Utils.tasks.schedule(ka_task, KEEP_ALIVE);
}
protected boolean pollAlive() {
return !exiting;
}
/**
* Wrapper of the input stream capable of reading UTF characters
* (the default reader hangs at least on nokia phones)
*
*/
public class UTFReader extends Reader {
private InputStream is;
private byte buf[];
private int offset = -1;
private int available = -1;
public UTFReader(InputStream is) {
this.is = is;
this.buf = new byte[512];
}
public void close() throws IOException {
is.close();
}
public int read(char[] arg0, int arg1, int arg2) throws IOException {
throw new IOException("Unsupported method");
}
public int read(char[] arg0) throws IOException {
throw new IOException("Unsupported method");
}
private int getByte() throws IOException {
int b;
// buffered reading
if (false && offset > 0 && offset < available) {
System.out.println("+++:" + offset + "/" + available);
b = buf[offset++];
} else {
available = is.available();
available = 0;
if (available > 0) {
// do {
// Logger.log("read");
available = is.read(buf, 0,
(available < buf.length) ? available : buf.length);
// if(available == -1) {
// try {
//// Logger.log("tick");
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// }while(available == -1);
System.out.println(">>:" + available);
b = buf[0];
offset = 1;
} else {
// block waiting for the first char
b = is.read();
}
}
return b & 0xFF;
}
public int read() throws IOException {
int b;
int ch = 0;
b = getByte();
// #ifdef COMPRESSION
InputStream sockInstream = SocketChannel.this.getInputStream();
if (sockInstream instanceof ZInputStream) {
BaseChannel.bytes_received = (int) ((ZInputStream) sockInstream)
.getTotalIn();
}
else
{
// #ifndef TLS
SocketChannel.this.bytes_received++;
// #endif
// #ifdef TLS
if (sockInstream instanceof TlsInputStream == false) {
BaseChannel.bytes_received++;
} else if (sockInstream instanceof TlsInputStream == true) {
BaseChannel.bytes_received = SocketChannel.handler
.getBytes_received();
}
// #endif
}
// #endif
// #ifndef COMPRESSION
SocketChannel.bytes_received++;
// #endif
if (b == 0xff) {
throw new IOException("Invalid byte received on text stream: "
+ b);
//return -1;
} else if (b <= 0x07F) {
ch = b;
} else {
byte b1 = 0;
byte b2 = 0;
byte b3 = 0;
byte b4 = 0;
if (b >= 0x0C2 && b <= 0x0DF) {
b3 = (byte) (b & 0x1F);
b4 = (byte) (getByte() & 0x3F);
} else if (b >= 0x0E0 && b <= 0x0EF) {
b2 = (byte) (b & 0x0F);
b3 = (byte) (getByte() & 0x3F);
b4 = (byte) (getByte() & 0x3F);
} else if (b >= 0x0F0 && b <= 0x0F4) {
b1 = (byte) (b & 0x07);
b2 = (byte) (getByte() & 0x3F);
b3 = (byte) (getByte() & 0x3F);
b4 = (byte) (getByte() & 0x3F);
}
ch = (b1 << 18) + (b2 << 12) + (b3 << 6) + b4;
}
return ch;
}
}
public UTFReader getReader() {
return new UTFReader(getInputStream());
}
//#ifdef COMPRESSION
public void startCompression() {
synchronized (packets) {
compressed = true;
inputStream = new ZInputStream(inputStream);
outputStream = new ZOutputStream(outputStream,
JZlib.Z_DEFAULT_COMPRESSION);
((ZOutputStream) outputStream).setFlushMode(JZlib.Z_PARTIAL_FLUSH);
//sender.exiting = true;
//packets.notify();
}
}
// #endif
// #ifdef TLS
public void startTLS() throws IOException {
synchronized (packets) {
TlsProtocolHandler handler = new TlsProtocolHandler(inputStream,
outputStream);
handler.connect(new AlwaysValidVerifyer());
this.handler = handler;
outputStream = handler.getOutputStream();
inputStream = handler.getInputStream();
}
}
// #endif
}