diff --git a/lib/runtime/jar/logback-classic-0.9.26.jar b/lib/runtime/jar/logback-classic-0.9.26.jar new file mode 100644 index 0000000..b900b64 Binary files /dev/null and b/lib/runtime/jar/logback-classic-0.9.26.jar differ diff --git a/lib/runtime/jar/logback-core-0.9.26.jar b/lib/runtime/jar/logback-core-0.9.26.jar new file mode 100644 index 0000000..d50f3cd Binary files /dev/null and b/lib/runtime/jar/logback-core-0.9.26.jar differ diff --git a/src/main/com/jdbernard/net/ParameterizedSocket.java b/src/main/com/jdbernard/net/ParameterizedSocket.java new file mode 100644 index 0000000..ebf4e26 --- /dev/null +++ b/src/main/com/jdbernard/net/ParameterizedSocket.java @@ -0,0 +1,89 @@ +/** + * ParameterizedSocket + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +package com.jdbernard.net; + +import java.io.IOException; +import java.net.Socket; +import java.net.UnknownHostException; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +public class ParameterizedSocket { + + public static final String START_TOKEN = "\u0001"; + public static final String END_TOKEN = "\u0004"; + public static final String RECORD_SEPARATOR = "\u001E"; + + public static final byte START_TOKEN_BYTE = 0x01; + public static final byte END_TOKEN_BYTE = 0x04; + public static final byte RECORD_SEPARATOR_BYTE = 0x1E; + + private Socket socket; + private byte[] buffer; + private Charset charset; + + public ParameterizedSocket(Socket socket, int bufferSize) { + this.socket = socket; + this.buffer = new byte[bufferSize]; + this.charset = Charset.forName("US-ASCII"); } + + public ParameterizedSocket(String ipAddress, int port, int bufferSize) + throws UnknownHostException, IOException { + this(new Socket(ipAddress, port), bufferSize); } + + public ParameterizedSocket(Socket socket) + throws UnknownHostException, IOException { + this(socket, 2048); } + + public ParameterizedSocket(String ipAddress, int port) + throws UnknownHostException, IOException { + this(ipAddress, port, 2048); } + + public boolean getClosed() { return socket.isClosed(); } + public boolean getConnected() { return socket.isConnected(); } + + public void close() throws IOException { socket.close(); } + + public void writeMessage(String... message) throws IOException { + if (message == null || message.length == 0) return; + + byte[] messageBytes = formatMessage(message); + socket.getOutputStream().write(messageBytes); } + + public String[] readMessage() throws IOException { + List messageList = new ArrayList(); + + if (socket.getInputStream().read() != START_TOKEN_BYTE) { + byte[] errMsg = formatMessage("ERROR", "Invalid command (expected START_TOKEN)."); + socket.getOutputStream().write(errMsg); } + + int bufIdx= 0; + int nextByte = socket.getInputStream().read(); + + while (nextByte != END_TOKEN_BYTE) { + if (nextByte == -1) { + byte[] errMsg = formatMessage("ERROR", "Invalid command: stream ended before END_TOKEN was read."); + socket.getOutputStream().write(errMsg); + return null; } + + else if (nextByte == RECORD_SEPARATOR_BYTE) { + messageList.add(new String(buffer, 0, bufIdx, charset)); + bufIdx = 0; } + + else { buffer[bufIdx++] = (byte) nextByte; } + } + + if (bufIdx > 0) messageList.add(new String(buffer, 0, bufIdx, charset)); + + return (String[]) messageList.toArray(); } + + protected byte[] formatMessage(String... message) { + String fullMessage = START_TOKEN + message[0]; + for (int i = 1; i < message.length; i++) { + fullMessage += RECORD_SEPARATOR + message[i]; } + fullMessage += END_TOKEN; + return fullMessage.getBytes(charset); } +} diff --git a/src/main/com/jdbernard/slf4j/LoggerOutputStream.java b/src/main/com/jdbernard/slf4j/LoggerOutputStream.java new file mode 100644 index 0000000..c1da96a --- /dev/null +++ b/src/main/com/jdbernard/slf4j/LoggerOutputStream.java @@ -0,0 +1,88 @@ +/** + * LoggerOutputStream + * @author Jonathan Bernard (jdbernard@gmail.com) + */ +package com.jdbernard.slf4j; + +import java.io.IOException; +import java.io.OutputStream; +import org.slf4j.Logger; + +public class LoggerOutputStream { + + public enum Level { TRACE, DEBUG, INFO, WARN, ERROR } + + private Logger logger; + private Level level; + private boolean closed = false; + private byte[] buffer; + private int count; + + private static final int DEFAULT_BUFFER_LENGTH = 2048; + + public LoggerOutputStream(Logger logger, Level level) { + this.logger = logger; + this.level = level; } + + /** {@inheritDoc} */ + public void write(final byte[] b, final int offset, final int length) + throws IOException { + if (closed) + throw new IOException("The output stream has been closed."); + + // Grow the buffer if we will reach the limit. + if (count + length >= buffer.length) { + final int newLength = + Math.max(buffer.length + DEFAULT_BUFFER_LENGTH, count + length); + final byte[] newBuffer = new byte[newLength]; + + System.arraycopy(buffer, 0, newBuffer, 0, count); + buffer = newBuffer; } + + System.arraycopy(buffer, count, b, offset, length); + count += length; + } + + /** {@inheritDoc} */ + public void write(final int b) throws IOException { + if (closed) + throw new IOException("The output stream has been closed."); + + // Grow the buffer if we have reached the limit. + if (count == buffer.length) { + final int newLength = buffer.length + DEFAULT_BUFFER_LENGTH; + final byte[] newBuffer = new byte[newLength]; + + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; } + + buffer[count++] = (byte) b; + } + + /** {@inheritDoc} */ + public void flush() throws IOException { + if (closed) + throw new IOException("The output stream has been closed."); + + final byte[] messageBytes = new byte[count]; + System.arraycopy(buffer, 0, messageBytes, 0, count); + final String message = new String(messageBytes); + synchronized(logger) { + switch (level) { + default: + case TRACE: logger.trace(message); break; + case DEBUG: logger.debug(message); break; + case INFO: logger.info(message); break; + case WARN: logger.warn(message); break; + case ERROR: logger.error(message); break; + } + } + count = 0; + } + + /** {@inheritDoc} */ + public void close() throws IOException { + if (closed) return; + flush(); + closed = true; } +}