/**
* Squidy Interaction Library 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 of the License,
* or (at your option) any later version.
*
* Squidy Interaction Library 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 Squidy Interaction Library. If not, see
* <http://www.gnu.org/licenses/>.
*
* 2009 Human-Computer Interaction Group, University of Konstanz.
* <http://hci.uni-konstanz.de>
*
* Please contact info@squidy-lib.de or visit our website
* <http://www.squidy-lib.de> for further information.
*/
package org.squidy.manager.protocol.tcp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* <code>AsyncSocket</code>.
*
* <pre>
* Date: Jan 3, 2009
* Time: 4:44:07 PM
* </pre>
*
* @author Roman Rädle, <a
* href="mailto:Roman.Raedle@uni-konstanz.de">Roman.
* Raedle@uni-konstanz.de</a>, University of Konstanz
* @version $Id: AsyncSocket.java 772 2011-09-16 15:39:44Z raedle $
* @since 2.0
*/
public class AsyncSocket extends Socket {
// Logger to log info, error, debug,... messages.
private static final Log LOG = LogFactory.getLog(AsyncSocket.class);
public static final byte[] CRLF = new byte[] { 0x0D, 0x0A };
private final Queue<Enum<?>> tagQueue = new ConcurrentLinkedQueue<Enum<?>>();
private ByteArrayOutputStream outputBuffer;
private InputStream inputStream;
private OutputStream outputStream;
// private byte[] cachedBuffer;
private static final int MAX_BUFFER_SIZE = 1024;
public AsyncSocket(AsyncSocketCallback callback, InetAddress address, int port) throws IOException {
super(address, port);
setTcpNoDelay(true);
inputStream = getInputStream();
outputStream = getOutputStream();
callback.ready(this);
}
/**
* @param sequence
* @throws IOException
*/
public void readToByteSequence(final AsyncSocketCallback callback, final byte[] sequence, final Enum<?> tag) {
// Add current tag to tag queue to identify current read job.
tagQueue.add(tag);
new Thread() {
@Override
public void run() {
// Length of the sequence.
int sequenceSize = sequence.length;
int currentSequencePosition = 0;
outputBuffer = new ByteArrayOutputStream();
// if (cachedBuffer != null) {
// boolean processFinished = processReadToByteSequenceBuffer(
// callback, sequence, cachedBuffer,
// cachedBuffer.length, sequenceSize,
// currentSequencePosition);
//
// if (processFinished) {
// return;
// } else {
// cachedBuffer = null;
// }
// }
// int bufferSize = MAX_BUFFER_SIZE;
// byte[] buffer = new byte[bufferSize];
int read;
try {
while ((read = inputStream.read()) != -1) {
// boolean processFinished =
// processReadToByteSequenceBuffer(callback, sequence,
// buffer, readBytes, sequenceSize,
// currentSequencePosition);
//
// if (processFinished) {
// return;
// }
if (sequence[currentSequencePosition] == read) {
currentSequencePosition++;
}
else {
currentSequencePosition = 0;
}
outputBuffer.write(read);
// System.out.println("STREAM: " + new
// String(outputBuffer.toByteArray()));
if (sequenceSize == currentSequencePosition) {
byte[] data = outputBuffer.toByteArray();
outputBuffer = null;
callback.didReadToByteSequence(AsyncSocket.this, data, tagQueue.poll());
return;
}
}
}
catch (IOException e) {
callback.disconnected(AsyncSocket.this);
}
}
}.start();
}
// /**
// * @param callback
// * @param sequence
// * @param buffer
// * @param readBytes
// * @param sequenceSize
// * @param currentSequencePosition
// * @return
// */
// private boolean processReadToByteSequenceBuffer(AsyncSocketCallback
// callback, byte[] sequence, byte[] buffer, int readBytes, int
// sequenceSize, int currentSequencePosition) {
// for (int i = 0; i < readBytes; i++) {
// if (sequence[currentSequencePosition] == buffer[i]) {
// currentSequencePosition++;
// } else {
// currentSequencePosition = 0;
// }
//
// outputBuffer.write(buffer[i]);
//
// if (sequenceSize == currentSequencePosition) {
// int restBytes = readBytes - i - 1;
// if (restBytes > 0) {
// cachedBuffer = new byte[restBytes];
// System.arraycopy(buffer, i, cachedBuffer, 0, restBytes);
// }
//
// byte[] data = outputBuffer.toByteArray();
// outputBuffer = null;
// callback.didReadToByteSequence(AsyncSocket.this, data);
//
// return true;
// }
// }
// return false;
// }
public void readBytes(final AsyncSocketCallback callback, final int bytesToRead, final Enum<?> tag) {
// Add current tag to tag queue to identify current read job.
tagQueue.add(tag);
new Thread() {
@Override
public void run() {
// Length of the sequence.
int hasToRead = bytesToRead;
outputBuffer = new ByteArrayOutputStream();
int bufferSize = MAX_BUFFER_SIZE;
byte[] buffer = new byte[bufferSize];
int readBytes;
try {
while ((readBytes = inputStream.read(buffer, 0, (hasToRead < MAX_BUFFER_SIZE) ? hasToRead
: MAX_BUFFER_SIZE)) != -1) {
outputBuffer.write(buffer, 0, readBytes);
hasToRead -= readBytes;
if (hasToRead == 0) {
byte[] data = outputBuffer.toByteArray();
outputBuffer = null;
callback.didReadBytes(AsyncSocket.this, data, tagQueue.poll());
return;
}
}
}
catch (IOException e) {
callback.disconnected(AsyncSocket.this);
}
catch (ArrayIndexOutOfBoundsException e) {
try {
if (LOG.isWarnEnabled()) {
LOG.warn("Clearing already streamed data caused by: ", e);
}
while (inputStream.read() != -1) {
callback.ready(AsyncSocket.this);
}
}
catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
}.start();
}
/**
* @param b
* @throws IOException
*/
public void write(int b) throws IOException {
outputStream.write(b);
}
/**
* @param b
* @throws IOException
*/
public void write(byte[] b) throws IOException {
outputStream.write(b);
}
public void write(byte[] b, int off, int len) throws IOException {
outputStream.write(b, off, len);
}
/**
* @throws IOException
*/
public void flush() throws IOException {
outputStream.flush();
}
}