/* * Copyright 2011-2012 Gregory P. Moyer * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.syphr.mythtv.commons.socket; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.regex.Pattern; public class DefaultPacket implements Packet { private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private static final int DEFAULT_PAYLOAD_BUFFER_SIZE = 8192; private static final String DEFAULT_MESSAGE_TERMINATOR_LITERAL = "\n"; private Charset charset = DEFAULT_CHARSET; private int bufferSize = DEFAULT_PAYLOAD_BUFFER_SIZE; private String messageTerminator = Pattern.quote(DEFAULT_MESSAGE_TERMINATOR_LITERAL); public void setCharset(Charset charset) { this.charset = charset; } public Charset getCharset() { return charset; } public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } public int getBufferSize() { return bufferSize; } /** * Set the regular expression that determines the boundary between two * messages. * * @param messageTerminator * the regular expression to set */ public void setMessageTerminator(String messageTerminator) { this.messageTerminator = messageTerminator; } /** * Retrieve the regular expression that determines the boundary between two * messages. * * @return the regular expression */ public String getMessageTerminator() { return messageTerminator; } @Override public List<String> read(ReadableByteChannel in) throws IOException { byte[] payloadBytes = new byte[getBufferSize()]; ByteBuffer payloadBuffer = ByteBuffer.wrap(payloadBytes); StringBuilder builder = new StringBuilder(); int read = 0; while (true) { if (Thread.interrupted()) { InterruptedIOException e = new InterruptedIOException(); e.bytesTransferred = read; throw e; } payloadBuffer.clear(); int justRead = in.read(payloadBuffer); if (justRead <= 0) { break; } read += justRead; builder.append(new String(payloadBytes, 0, justRead, getCharset())); } /* * Retain empty messages by sending a limit of -1 to the split method. */ String[] messages = builder.toString().split(getMessageTerminator(), -1); /* * Removing the tail message since it will be the empty string after the * last message terminator. */ LinkedList<String> list = new LinkedList<String>(Arrays.asList(messages)); list.pollLast(); return list; } @Override public void write(WritableByteChannel out, String data) throws IOException { byte[] payloadBytes = buildMessage(data).getBytes(getCharset()); ByteBuffer buffer = ByteBuffer.allocate(payloadBytes.length); buffer.put(payloadBytes); buffer.flip(); while (buffer.hasRemaining()) { if (Thread.interrupted()) { InterruptedIOException e = new InterruptedIOException(); e.bytesTransferred = buffer.position(); throw e; } out.write(buffer); } } protected String buildMessage(String data) { return data.concat(DEFAULT_MESSAGE_TERMINATOR_LITERAL); } }