summaryrefslogtreecommitdiffstats
path: root/simple/simple-common/src/main/java/org/simpleframework
diff options
context:
space:
mode:
Diffstat (limited to 'simple/simple-common/src/main/java/org/simpleframework')
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/KeyMap.java93
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/Allocator.java55
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayAllocator.java111
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayBuffer.java397
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/Buffer.java129
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferAllocator.java229
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferException.java43
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileAllocator.java137
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileBuffer.java622
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileWatcher.java179
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/buffer/FilterAllocator.java123
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64Encoder.java166
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64InputStream.java123
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64OutputStream.java138
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/Cleaner.java44
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/Contract.java77
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractController.java84
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractLease.java119
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractMaintainer.java115
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractQueue.java44
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/Expiration.java163
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/Lease.java85
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseCleaner.java155
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseException.java52
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseManager.java93
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseMap.java83
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseProvider.java60
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/parse/MapParser.java251
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/parse/ParseBuffer.java247
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/parse/Parser.java197
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentExecutor.java109
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentScheduler.java122
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/Daemon.java164
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/DaemonFactory.java147
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/ExecutorQueue.java128
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/Scheduler.java57
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/SchedulerQueue.java127
-rw-r--r--simple/simple-common/src/main/java/org/simpleframework/common/thread/SynchronousExecutor.java43
38 files changed, 5311 insertions, 0 deletions
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/KeyMap.java b/simple/simple-common/src/main/java/org/simpleframework/common/KeyMap.java
new file mode 100644
index 00000000..6f450bbd
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/KeyMap.java
@@ -0,0 +1,93 @@
+/*
+ * KeyMap.java May 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The <code>KeyMap</code> object is used to represent a map of values
+ * keyed using a known string. This also ensures that the keys and
+ * the values added to this hash map can be acquired in an independent
+ * list of values, ensuring that modifications to the map do not have
+ * an impact on the lists provided, and vice versa. The key map can
+ * also be used in a fore each look using the string keys.
+ *
+ * @author Niall Gallagher
+ */
+public class KeyMap<T> extends LinkedHashMap<String, T> implements Iterable<String> {
+
+ /**
+ * Constructor for the <code>KeyMap</code> object. This creates
+ * a hash map that can expose the keys and values of the map as
+ * an independent <code>List</code> containing the values. This
+ * can also be used within a for loop for convenience.
+ */
+ public KeyMap() {
+ super();
+ }
+
+ /**
+ * This is used to produce an <code>Iterator</code> of values
+ * that can be used to acquire the contents of the key map within
+ * a for each loop. The key map can be modified while it is been
+ * iterated as the iterator is an independent list of values.
+ *
+ * @return this returns an iterator of the keys in the map
+ */
+ public Iterator<String> iterator() {
+ return getKeys().iterator();
+ }
+
+ /**
+ * This is used to produce a <code>List</code> of the keys in
+ * the map. The list produced is a copy of the internal keys and
+ * so can be modified and used without affecting this map object.
+ *
+ * @return this returns an independent list of the key values
+ */
+ public List<String> getKeys() {
+ Set<String> keys = keySet();
+
+ if(keys == null) {
+ return new ArrayList<String>();
+ }
+ return new ArrayList<String>(keys);
+ }
+
+ /**
+ * This is used to produce a <code>List</code> of the values in
+ * the map. The list produced is a copy of the internal values and
+ * so can be modified and used without affecting this map object.
+ *
+ * @return this returns an independent list of the values
+ */
+ public List<T> getValues() {
+ Collection<T> values = values();
+
+ if(values == null) {
+ return new ArrayList<T>();
+ }
+ return new ArrayList<T>(values);
+ }
+ } \ No newline at end of file
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Allocator.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Allocator.java
new file mode 100644
index 00000000..aa20b76c
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Allocator.java
@@ -0,0 +1,55 @@
+/*
+ * Allocator.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+
+/**
+ * The <code>Allocator</code> interface is used to describe a resource
+ * that can allocate a buffer. This is used so that memory allocation
+ * can be implemented as a strategy allowing many different sources of
+ * memory. Typically memory will be allocated as an array of bytes but
+ * can be a mapped region of shared memory or a file.
+ *
+ * @author Niall Gallagher
+ */
+public interface Allocator {
+
+ /**
+ * This method is used to allocate a default buffer. Typically this
+ * will allocate a buffer of predetermined size, allowing it to
+ * grow to an upper limit to accommodate extra data. If the buffer
+ * can not be allocated for some reason this throws an exception.
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ Buffer allocate() throws IOException;
+
+ /**
+ * This method is used to allocate a default buffer. This is used
+ * to allocate a buffer of the specified size, allowing it to
+ * grow to an upper limit to accommodate extra data. If the buffer
+ * can not be allocated for some reason this throws an exception.
+ *
+ * @param size this is the initial capacity the buffer should have
+ *
+ * @return this returns an allocated buffer with a specified size
+ */
+ Buffer allocate(long size) throws IOException;
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayAllocator.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayAllocator.java
new file mode 100644
index 00000000..e2dbb3c7
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayAllocator.java
@@ -0,0 +1,111 @@
+/*
+ * ArrayAllocator.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+
+/**
+ * The <code>ArrayAllocator</code> object is used to provide a means
+ * to allocate buffers using a single byte array. This essentially uses
+ * the heap to allocate all buffers. As a result the performance of the
+ * resulting buffers is good, however for very large buffers this will
+ * use quote allot of the usable heap space. For very large buffers a
+ * mapped region of shared memory of a file should be considered.
+ *
+ * @author Niall Gallagher
+ */
+public class ArrayAllocator implements Allocator {
+
+ /**
+ * This represents the largest portion of memory that is allowed.
+ */
+ private int limit;
+
+ /**
+ * This represents the default capacity of all allocated buffers.
+ */
+ private int size;
+
+ /**
+ * Constructor for the <code>ArrayAllocator</code> object. This is
+ * used to instantiate the allocator with a default buffer size of
+ * half a kilobyte. This ensures that it can be used for general
+ * purpose byte storage and for minor I/O tasks.
+ */
+ public ArrayAllocator() {
+ this(512);
+ }
+
+ /**
+ * Constructor for the <code>ArrayAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param size the initial capacity of the allocated buffers
+ */
+ public ArrayAllocator(int size) {
+ this(size, 1048576);
+ }
+
+ /**
+ * Constructor for the <code>ArrayAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param size the initial capacity of the allocated buffers
+ * @param limit this is the maximum buffer size created by this
+ */
+ public ArrayAllocator(int size, int limit) {
+ this.limit = Math.max(size, limit);
+ this.size = size;
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ public Buffer allocate() throws IOException {
+ return allocate(size);
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @param size the initial capacity of the allocated buffer
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ public Buffer allocate(long size) throws IOException {
+ int required = (int)size;
+
+ if(size > limit) {
+ throw new BufferException("Specified size %s beyond limit", size);
+ }
+ return new ArrayBuffer(required, limit);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayBuffer.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayBuffer.java
new file mode 100644
index 00000000..972ad924
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/ArrayBuffer.java
@@ -0,0 +1,397 @@
+/*
+ * ArrayBuffer.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The <code>ArrayBuffer</code> is intended to be a general purpose
+ * byte buffer that stores bytes in an single internal byte array. The
+ * intended use of this buffer is to provide a simple buffer object to
+ * read and write bytes with. In particular this provides a high
+ * performance buffer that can be used to read and write bytes fast.
+ * <p>
+ * This provides several convenience methods which make the use of the
+ * buffer easy and useful. This buffer allows an initial capacity to be
+ * specified however if there is a need for extra space to be added to
+ * buffer then the <code>append</code> methods will expand the capacity
+ * of the buffer as needed.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.buffer.ArrayAllocator
+ */
+public class ArrayBuffer implements Buffer {
+
+ /**
+ * This is the internal array used to store the buffered bytes.
+ */
+ private byte[] buffer;
+
+ /**
+ * This is used to determine whether this buffer has been closed.
+ */
+ private boolean closed;
+
+ /**
+ * This is the count of the number of bytes buffered.
+ */
+ private int count;
+
+ /**
+ * This is the maximum allowable buffer capacity for this.
+ */
+ private int limit;
+
+ /**
+ * Constructor for the <code>ArrayBuffer</code> object. The initial
+ * capacity of the default buffer object is set to 16, the capacity
+ * will be expanded when the append methods are used and there is
+ * not enough space to accommodate the extra bytes.
+ */
+ public ArrayBuffer() {
+ this(16);
+ }
+
+ /**
+ * Constructor for the <code>ArrayBuffer</code> object. The initial
+ * capacity of the buffer object is set to given size, the capacity
+ * will be expanded when the append methods are used and there is
+ * not enough space to accommodate the extra bytes.
+ *
+ * @param size the initial capacity of this buffer instance
+ */
+ public ArrayBuffer(int size) {
+ this(size, size);
+ }
+
+ /**
+ * Constructor for the <code>ArrayBuffer</code> object. The initial
+ * capacity of the buffer object is set to given size, the capacity
+ * will be expanded when the append methods are used and there is
+ * not enough space to accommodate the extra bytes.
+ *
+ * @param size the initial capacity of this buffer instance
+ * @param limit this is the maximum allowable buffer capacity
+ */
+ public ArrayBuffer(int size, int limit) {
+ this.buffer = new byte[size];
+ this.limit = limit;
+ }
+
+ /**
+ * This method is used so that the buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ public InputStream open() {
+ return new ByteArrayInputStream(buffer, 0, count);
+ }
+
+ /**
+ * This method is used to allocate a segment of this buffer as a
+ * separate buffer object. This allows the buffer to be sliced in
+ * to several smaller independent buffers, while still allowing the
+ * parent buffer to manage a single buffer. This is useful if the
+ * parent is split in to logically smaller segments.
+ *
+ * @return this returns a buffer which is a segment of this buffer
+ */
+ public Buffer allocate() throws IOException {
+ return new Segment(this,count);
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ public String encode() throws IOException {
+ return encode("UTF-8");
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ public String encode(String charset) throws IOException {
+ return new String(buffer,0,count, charset);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ *
+ * @return this returns this buffer for another operation
+ */
+ public Buffer append(byte[] array) throws IOException {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ * @param off this is the offset to begin reading the bytes from
+ * @param size the number of bytes to be read from the array
+ *
+ * @return this returns this buffer for another operation
+ */
+ public Buffer append(byte[] array, int off, int size) throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer is closed");
+ }
+ if(size + count > buffer.length) {
+ expand(count + size);
+ }
+ if(size > 0) {
+ System.arraycopy(array, off, buffer, count, size);
+ count += size;
+ }
+ return this;
+ }
+
+ /**
+ * This is used to ensure that there is enough space in the buffer
+ * to allow for more bytes to be added. If the buffer is already
+ * larger than the required capacity the this will do nothing.
+ *
+ * @param capacity the minimum size needed for this buffer object
+ */
+ private void expand(int capacity) throws IOException {
+ if(capacity > limit) {
+ throw new BufferException("Capacity limit %s exceeded", limit);
+ }
+ int resize = buffer.length * 2;
+ int size = Math.max(capacity, resize);
+ byte[] temp = new byte[size];
+
+ System.arraycopy(buffer, 0, temp, 0, count);
+ buffer = temp;
+ }
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ public void clear() throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer is closed");
+ }
+ count = 0;
+ }
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ public void close() throws IOException {
+ closed = true;
+ }
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ public long length() {
+ return count;
+ }
+
+ /**
+ * A <code>Segment</code> represents a segment within a buffer. It
+ * is used to allow a buffer to be split in to several logical parts
+ * without the need to create several separate buffers. This means
+ * that the buffer can be represented in a single memory space, as
+ * both a single large buffer and as several individual buffers.
+ */
+ private class Segment implements Buffer {
+
+ /**
+ * This is the parent buffer which is used for collecting data.
+ */
+ private Buffer parent;
+
+ /**
+ * This is used to determine if the buffer has closed or not.
+ */
+ private boolean closed;
+
+ /**
+ * This represents the start of the segment within the buffer.
+ */
+ private int start;
+
+ /**
+ * This represents the number of bytes this segment contains.
+ */
+ private int length;
+
+ /**
+ * Constructor for the <code>Segment</code> object. This is used
+ * to create a buffer within a buffer. A segment is a region of
+ * bytes within the original buffer. It allows the buffer to be
+ * split in to several logical parts of a single buffer.
+ *
+ * @param parent this is the parent buffer used to append to
+ * @param start this is the start within the buffer to read
+ */
+ public Segment(Buffer parent, int start) {
+ this.parent = parent;
+ this.start = start;
+ }
+
+ /**
+ * This method is used so that the buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ public InputStream open() throws IOException {
+ return new ByteArrayInputStream(buffer,start,length);
+ }
+
+ /**
+ * This method is used to allocate a segment of this buffer as a
+ * separate buffer object. This allows the buffer to be sliced in
+ * to several smaller independent buffers, while still allowing the
+ * parent buffer to manage a single buffer. This is useful if the
+ * parent is split in to logically smaller segments.
+ *
+ * @return this returns a buffer which is a segment of this buffer
+ */
+ public Buffer allocate() throws IOException {
+ return new Segment(this,count);
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ public String encode() throws IOException {
+ return encode("UTF-8");
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ public String encode(String charset) throws IOException {
+ return new String(buffer,start,length, charset);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ */
+ public Buffer append(byte[] array) throws IOException {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ * @param off this is the offset to begin reading the bytes from
+ * @param size the number of bytes to be read from the array
+ */
+ public Buffer append(byte[] array, int off, int size) throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer is closed");
+ }
+ if(size > 0) {
+ parent.append(array, off, size);
+ length += size;
+ }
+ return this;
+ }
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ public void clear() throws IOException {
+ length = 0;
+ }
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ public void close() throws IOException {
+ closed = true;
+ }
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ public long length() {
+ return length;
+ }
+ }
+}
+
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Buffer.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Buffer.java
new file mode 100644
index 00000000..ebde2807
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/Buffer.java
@@ -0,0 +1,129 @@
+/*
+ * Buffer.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The <code>Buffer</code> interface represents a collection of bytes
+ * that can be written to and later read. This is used to provide a
+ * region of memory is such a way that the underlying representation
+ * of that memory is independent of its use. Typically buffers are
+ * implemented as either allocated byte arrays or files.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.buffer.Allocator
+ */
+public interface Buffer {
+
+ /**
+ * This method is used to allocate a segment of this buffer as a
+ * separate buffer object. This allows the buffer to be sliced in
+ * to several smaller independent buffers, while still allowing the
+ * parent buffer to manage a single buffer. This is useful if the
+ * parent is split in to logically smaller segments.
+ *
+ * @return this returns a buffer which is a segment of this buffer
+ */
+ Buffer allocate() throws IOException;
+
+ /**
+ * This method is used so that a buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ InputStream open() throws IOException;
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ String encode() throws IOException;
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @param charset this is the charset to encode the data with
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ String encode(String charset) throws IOException;
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ *
+ * @return this returns this buffer for another operation
+ */
+ Buffer append(byte[] array) throws IOException;
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ * @param len the number of bytes to be read from the array
+ * @param off this is the offset to begin reading the bytes from
+ *
+ * @return this returns this buffer for another operation
+ */
+ Buffer append(byte[] array, int off, int len) throws IOException;
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ void clear() throws IOException;
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ void close() throws IOException;
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ long length();
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferAllocator.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferAllocator.java
new file mode 100644
index 00000000..033ba3ab
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferAllocator.java
@@ -0,0 +1,229 @@
+/*
+ * BufferAllocator.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The <code>BufferAllocator</code> object is used to provide a means
+ * to allocate buffers using a single underlying buffer. This uses a
+ * buffer from a existing allocator to create the region of memory to
+ * use to allocate all other buffers. As a result this allows a single
+ * buffer to acquire the bytes in a number of associated buffers. This
+ * has the advantage of allowing bytes to be read in sequence without
+ * joining data from other buffers or allocating multiple regions.
+ *
+ * @author Niall Gallagher
+ */
+public class BufferAllocator extends FilterAllocator implements Buffer {
+
+ /**
+ * This is the underlying buffer all other buffers are within.
+ */
+ private Buffer buffer;
+
+ /**
+ * Constructor for the <code>BufferAllocator</code> object. This is
+ * used to instantiate the allocator with a default buffer size of
+ * half a kilobyte. This ensures that it can be used for general
+ * purpose byte storage and for minor I/O tasks.
+ *
+ * @param source this is where the underlying buffer is allocated
+ */
+ public BufferAllocator(Allocator source) {
+ super(source);
+ }
+
+ /**
+ * Constructor for the <code>BufferAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param source this is where the underlying buffer is allocated
+ * @param capacity the initial capacity of the allocated buffers
+ */
+ public BufferAllocator(Allocator source, long capacity) {
+ super(source, capacity);
+ }
+
+ /**
+ * Constructor for the <code>BufferAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param source this is where the underlying buffer is allocated
+ * @param capacity the initial capacity of the allocated buffers
+ * @param limit this is the maximum buffer size created by this
+ */
+ public BufferAllocator(Allocator source, long capacity, long limit) {
+ super(source, capacity, limit);
+ }
+
+ /**
+ * This method is used so that a buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ public InputStream open() throws IOException {
+ if(buffer == null) {
+ allocate();
+ }
+ return buffer.open();
+ }
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ public String encode() throws IOException {
+ if(buffer == null) {
+ allocate();
+ }
+ return buffer.encode();
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ public String encode(String charset) throws IOException {
+ if(buffer == null) {
+ allocate();
+ }
+ return buffer.encode(charset);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ *
+ * @return this returns this buffer for another operation
+ */
+ public Buffer append(byte[] array) throws IOException {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * This method is used to append bytes to the end of the buffer.
+ * This will expand the capacity of the buffer if there is not
+ * enough space to accommodate the extra bytes.
+ *
+ * @param array this is the byte array to append to this buffer
+ * @param size the number of bytes to be read from the array
+ * @param off this is the offset to begin reading the bytes from
+ *
+ * @return this returns this buffer for another operation
+ */
+ public Buffer append(byte[] array, int off, int size) throws IOException {
+ if(buffer == null) {
+ allocate(size);
+ }
+ return buffer.append(array, off, size);
+ }
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ public void clear() throws IOException {
+ if(buffer != null) {
+ buffer.clear();
+ }
+ }
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ public void close() throws IOException {
+ if(buffer == null) {
+ allocate();
+ }
+ buffer.close();
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ @Override
+ public Buffer allocate() throws IOException {
+ return allocate(capacity);
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @param size the initial capacity of the allocated buffer
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ @Override
+ public Buffer allocate(long size) throws IOException {
+ if(size > limit) {
+ throw new BufferException("Specified size %s beyond limit", size);
+ }
+ if(capacity > size) { // lazily create backing buffer
+ size = capacity;
+ }
+ if(buffer == null) {
+ buffer = source.allocate(size);
+ }
+ return buffer.allocate();
+ }
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ public long length() {
+ return buffer.length();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferException.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferException.java
new file mode 100644
index 00000000..4ec2019e
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/BufferException.java
@@ -0,0 +1,43 @@
+/*
+ * BufferException.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+
+/**
+ * The <code>BufferException</code> is used to report problems that
+ * can occur during the use or allocation of a buffer. Typically
+ * this is thrown if the upper capacity limit is exceeded.
+ *
+ * @author Niall Gallagher
+ */
+public class BufferException extends IOException {
+
+ /**
+ * Constructor for the <code>BufferException</code> object. The
+ * exception can be provided with a message describing the issue
+ * that has arisen in the use or allocation of the buffer.
+ *
+ * @param format this is the template for the exception
+ * @param values these are the values to be added to the template
+ */
+ public BufferException(String format, Object... values) {
+ super(String.format(format, values));
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileAllocator.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileAllocator.java
new file mode 100644
index 00000000..c91b1dc5
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileAllocator.java
@@ -0,0 +1,137 @@
+/*
+ * FileAllocator.java February 2008
+ *
+ * Copyright (C) 2008, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * The <code>FileAllocator</code> object is used to create buffers
+ * that can be written to the file system. This creates buffers as
+ * files if they are larger than the specified limit. This ensures
+ * that buffers of arbitrary large size can be created. All buffer
+ * sizes under the limit are created using byte arrays allocated
+ * on the executing VM heap. This ensures that optimal performance
+ * is maintained for buffers of reasonable size.
+ *
+ * @author Niall Gallagher
+ */
+public class FileAllocator implements Allocator {
+
+ /**
+ * This is the default prefix used when none has been specified.
+ */
+ private static final String PREFIX = "temp";
+
+ /**
+ * This is the file manager used to create the buffer files.
+ */
+ private FileWatcher manager;
+
+ /**
+ * This is the limit up to which buffers are allocated in memory.
+ */
+ private int limit;
+
+ /**
+ * Constructor for the <code>FileAllocator</code> object. This is
+ * used to create buffers in memory up to a threshold size. If a
+ * buffer is required over the threshold size then the data is
+ * written to a file, where it can be retrieved at a later point.
+ */
+ public FileAllocator() {
+ this(1048576);
+ }
+
+ /**
+ * Constructor for the <code>FileAllocator</code> object. This is
+ * used to create buffers in memory up to a threshold size. If a
+ * buffer is required over the threshold size then the data is
+ * written to a file, where it can be retrieved at a later point.
+ *
+ * @param limit this is the maximum size for a heap buffer
+ */
+ public FileAllocator(int limit) {
+ this(PREFIX, limit);
+ }
+
+ /**
+ * Constructor for the <code>FileAllocator</code> object. This is
+ * used to create buffers in memory up to a threshold size. If a
+ * buffer is required over the threshold size then the data is
+ * written to a file, where it can be retrieved at a later point.
+ *
+ * @param prefix this is the file prefix for the file buffers
+ */
+ public FileAllocator(String prefix) {
+ this(prefix, 1048576);
+ }
+
+ /**
+ * Constructor for the <code>FileAllocator</code> object. This is
+ * used to create buffers in memory up to a threshold size. If a
+ * buffer is required over the threshold size then the data is
+ * written to a file, where it can be retrieved at a later point.
+ *
+ * @param prefix this is the file prefix for the file buffers
+ * @param limit this is the maximum size for a heap buffer
+ */
+ public FileAllocator(String prefix, int limit) {
+ this.manager = new FileWatcher(prefix);
+ this.limit = limit;
+ }
+
+ /**
+ * This will allocate a file buffer which will write data for the
+ * buffer to a file. Buffers allocated by this method can be of
+ * arbitrary size as data is appended directly to a temporary
+ * file. This ensures there is no upper limit for appended data.
+ *
+ * @return a buffer which will write to a temporary file
+ */
+ public Buffer allocate() throws IOException {
+ File file = manager.create();
+
+ if(!file.exists()) {
+ throw new BufferException("Could not create file %s", file);
+ }
+ return new FileBuffer(file);
+ }
+
+ /**
+ * This will allocate a file buffer which will write data for the
+ * buffer to a file. Buffers allocated by this method can be of
+ * arbitrary size as data is appended directly to a temporary
+ * file. This ensures there is no upper limit for appended data.
+ * If the size required is less than the limit then the buffer
+ * is an in memory array which provides optimal performance.
+ *
+ * @param size this is the size of the buffer to be created
+ *
+ * @return a buffer which will write to a created temporary file
+ */
+ public Buffer allocate(long size) throws IOException {
+ int required = (int)size;
+
+ if(size <= limit) {
+ return new ArrayBuffer(required);
+ }
+ return allocate();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileBuffer.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileBuffer.java
new file mode 100644
index 00000000..8fcea77e
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileBuffer.java
@@ -0,0 +1,622 @@
+/*
+ * FileBuffer.java February 2008
+ *
+ * Copyright (C) 2008, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The <code>FileBuffer</code> object is used to create a buffer
+ * which will write the appended data to an underlying file. This
+ * is typically used for buffers that are too large for to allocate
+ * in memory. Data appended to the buffer can be retrieved at a
+ * later stage by acquiring the <code>InputStream</code> for the
+ * underlying file. To ensure that excessive file system space is
+ * not occupied the buffer files are cleaned every five minutes.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.buffer.FileAllocator
+ */
+class FileBuffer implements Buffer {
+
+ /**
+ * This is the file output stream used for this buffer object.
+ */
+ private OutputStream buffer;
+
+ /**
+ * This represents the last file segment that has been created.
+ */
+ private Segment segment;
+
+ /**
+ * This is the path for the file that this buffer appends to.
+ */
+ private File file;
+
+ /**
+ * This is the number of bytes currently appended to the buffer.
+ */
+ private long count;
+
+ /**
+ * This is used to determine if this buffer has been closed.
+ */
+ private boolean closed;
+
+ /**
+ * Constructor for the <code>FileBuffer</code> object. This will
+ * create a buffer using the provided file. All data appended to
+ * this buffer will effectively written to the underlying file.
+ * If the appended data needs to be retrieved at a later stage
+ * then it can be acquired using the buffers input stream.
+ *
+ * @param file this is the file used for the file buffer
+ */
+ public FileBuffer(File file) throws IOException {
+ this.buffer = new FileOutputStream(file);
+ this.file = file;
+ }
+
+ /**
+ * This is used to allocate a segment within this buffer. If the
+ * buffer is closed this will throw an exception, if however the
+ * buffer is still open then a segment is created which will
+ * write all appended data to this buffer. However it can be
+ * treated as an independent source of data.
+ *
+ * @return this returns a buffer which is a segment of this
+ */
+ public Buffer allocate() throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer has been closed");
+ }
+ if(segment != null) {
+ segment.close();
+ }
+ if(!closed) {
+ segment = new Segment(this, count);
+ }
+ return segment;
+ }
+
+ /**
+ * This is used to append the specified data to the underlying
+ * file. All bytes appended to the file can be consumed at a
+ * later stage by acquiring the <code>InputStream</code> from
+ * this buffer. Also if require the data can be encoded as a
+ * string object in a required character set.
+ *
+ * @param array this is the array to write the the file
+ *
+ * @return this returns this buffer for further operations
+ */
+ public Buffer append(byte[] array) throws IOException {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * This is used to append the specified data to the underlying
+ * file. All bytes appended to the file can be consumed at a
+ * later stage by acquiring the <code>InputStream</code> from
+ * this buffer. Also if require the data can be encoded as a
+ * string object in a required character set.
+ *
+ * @param array this is the array to write the the file
+ * @param off this is the offset within the array to write
+ * @param size this is the number of bytes to be appended
+ *
+ * @return this returns this buffer for further operations
+ */
+ public Buffer append(byte[] array, int off, int size) throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer has been closed");
+ }
+ if(size > 0) {
+ buffer.write(array, off, size);
+ count += size;
+ }
+ return this;
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ public String encode() throws IOException {
+ return encode("UTF-8");
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @param charset this is the charset to encode the data with
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ public String encode(String charset) throws IOException {
+ InputStream source = open();
+ int size = (int)count;
+
+ if(count <= 0) {
+ return new String();
+ }
+ return convert(source, charset, size);
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @param source this is the source stream that is to be encoded
+ * @param charset this is the charset to encode the data with
+ * @param count this is the number of bytes to be encoded
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ private String convert(InputStream source, String charset, int count) throws IOException {
+ byte[] buffer = new byte[count];
+ int left = count;
+
+ while(left > 0) {
+ int size = source.read(buffer, 0, left);
+
+ if(size == -1) {
+ throw new BufferException("Could not read buffer");
+ }
+ left -= count;
+ }
+ return new String(buffer, charset);
+ }
+
+ /**
+ * This method is used so that a buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ public InputStream open() throws IOException {
+ if(!closed) {
+ close();
+ }
+ return open(file);
+ }
+
+ /**
+ * This method is used so that a buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @param file this is the file used to create the input stream
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ private InputStream open(File file) throws IOException {
+ InputStream source = new FileInputStream(file);
+
+ if(count <= 0) {
+ source.close(); // release file descriptor
+ }
+ return new Range(source, count);
+ }
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ public void clear() throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer has been closed");
+ }
+ }
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ public void close() throws IOException {
+ if(!closed) {
+ buffer.close();
+ closed = true;
+ }
+ if(segment != null) {
+ segment.close();
+ }
+ }
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ public long length() {
+ return count;
+ }
+
+ /**
+ * The <code>Segment</code> object is used to create a segment of
+ * the parent buffer. The segment will write to the parent however
+ * if can be read as a unique range of bytes starting with the
+ * first sequence of bytes appended to the segment. A segment can
+ * be used to create a collection of buffers backed by the same
+ * underlying file, as is require with multipart uploads.
+ */
+ private class Segment implements Buffer {
+
+ /**
+ * This is an internal segment created from this buffer object.
+ */
+ private Segment segment;
+
+ /**
+ * This is the parent buffer that bytes are to be appended to.
+ */
+ private Buffer parent;
+
+ /**
+ * This is the offset of the first byte within the sequence.
+ */
+ private long first;
+
+ /**
+ * This is the last byte within the segment for this segment.
+ */
+ private long last;
+
+ /**
+ * This determines if the segment is currently open or closed.
+ */
+ private boolean closed;
+
+ /**
+ * Constructor for the <code>Segment</code> object. This is used
+ * to create a segment from a parent buffer. A segment is a part
+ * of the parent buffer and appends its bytes to the parent. It
+ * can however be treated as an independent source of bytes.
+ *
+ * @param parent this is the parent buffer to be appended to
+ * @param first this is the offset for the first byte in this
+ */
+ public Segment(Buffer parent, long first) {
+ this.parent = parent;
+ this.first = first;
+ this.last = first;
+ }
+
+ /**
+ * This is used to allocate a segment within this buffer. If the
+ * buffer is closed this will throw an exception, if however the
+ * buffer is still open then a segment is created which will
+ * write all appended data to this buffer. However it can be
+ * treated as an independent source of data.
+ *
+ * @return this returns a buffer which is a segment of this
+ */
+ public Buffer allocate() throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer has been closed");
+ }
+ if(segment != null) {
+ segment.close();
+ }
+ if(!closed) {
+ segment = new Segment(this, last);
+ }
+ return segment;
+ }
+
+ /**
+ * This is used to append the specified data to the underlying
+ * file. All bytes appended to the file can be consumed at a
+ * later stage by acquiring the <code>InputStream</code> from
+ * this buffer. Also if require the data can be encoded as a
+ * string object in a required character set.
+ *
+ * @param array this is the array to write the the file
+ *
+ * @return this returns this buffer for further operations
+ */
+ public Buffer append(byte[] array) throws IOException {
+ return append(array, 0, array.length);
+ }
+
+ /**
+ * This is used to append the specified data to the underlying
+ * file. All bytes appended to the file can be consumed at a
+ * later stage by acquiring the <code>InputStream</code> from
+ * this buffer. Also if require the data can be encoded as a
+ * string object in a required character set.
+ *
+ * @param array this is the array to write the the file
+ * @param off this is the offset within the array to write
+ * @param size this is the number of bytes to be appended
+ *
+ * @return this returns this buffer for further operations
+ */
+ public Buffer append(byte[] array, int off, int size) throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer has been closed");
+ }
+ if(size > 0) {
+ parent.append(array, off, size);
+ last += size;
+ }
+ return this;
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. If the UTF-8
+ * content encoding is not supported the platform default is
+ * used, however this is unlikely as UTF-8 should be supported.
+ *
+ * @return this returns a UTF-8 encoding of the buffer contents
+ */
+ public String encode() throws IOException {
+ return encode("UTF-8");
+ }
+
+ /**
+ * This method is used to acquire the buffered bytes as a string.
+ * This is useful if the contents need to be manipulated as a
+ * string or transferred into another encoding. This will convert
+ * the bytes using the specified character encoding format.
+ *
+ * @param charset this is the charset to encode the data with
+ *
+ * @return this returns the encoding of the buffer contents
+ */
+ public String encode(String charset) throws IOException {
+ InputStream source = open();
+ long count = last - first;
+ int size = (int)count;
+
+ if(count <= 0) {
+ return new String();
+ }
+ return convert(source, charset, size);
+ }
+
+ /**
+ * This method is used so that a buffer can be represented as a
+ * stream of bytes. This provides a quick means to access the data
+ * that has been written to the buffer. It wraps the buffer within
+ * an input stream so that it can be read directly.
+ *
+ * @return a stream that can be used to read the buffered bytes
+ */
+ public InputStream open() throws IOException {
+ InputStream source = new FileInputStream(file);
+ long length = last - first;
+
+ if(first > 0) {
+ source.skip(first);
+ }
+ return new Range(source, length);
+ }
+
+ /**
+ * This will clear all data from the buffer. This simply sets the
+ * count to be zero, it will not clear the memory occupied by the
+ * instance as the internal buffer will remain. This allows the
+ * memory occupied to be reused as many times as is required.
+ */
+ public void clear() throws IOException {
+ if(closed) {
+ throw new BufferException("Buffer is closed");
+ }
+ }
+
+ /**
+ * This method is used to ensure the buffer can be closed. Once
+ * the buffer is closed it is an immutable collection of bytes and
+ * can not longer be modified. This ensures that it can be passed
+ * by value without the risk of modification of the bytes.
+ */
+ public void close() throws IOException {
+ if(!closed) {
+ closed = true;
+ }
+ if(segment != null) {
+ segment.close();
+ }
+ }
+
+ /**
+ * This determines how much space is left in the buffer. If there
+ * is no limit to the buffer size this will return the maximum
+ * long value. Typically this is the capacity minus the length.
+ *
+ * @return this is the space that is available within the buffer
+ */
+ public long space() {
+ return Long.MAX_VALUE;
+ }
+
+ /**
+ * This is used to provide the number of bytes that have been
+ * written to the buffer. This increases as bytes are appended
+ * to the buffer. if the buffer is cleared this resets to zero.
+ *
+ * @return this returns the number of bytes within the buffer
+ */
+ public long length() {
+ return last - first;
+ }
+
+ }
+
+ /**
+ * The <code>Range</code> object is used to provide a stream that
+ * can read a range of bytes from a provided input stream. This
+ * allows buffer segments to be allocated from the main buffer.
+ * Providing a range in this manner ensures that only one backing
+ * file is needed for the primary buffer allocated.
+ */
+ private class Range extends FilterInputStream {
+
+ /**
+ * This is the length of the bytes that exist in the range.
+ */
+ private long length;
+
+ /**
+ * This is used to close the stream once it has been read.
+ */
+ private boolean closed;
+
+ /**
+ * Constructor for the <code>Range</code> object. This ensures
+ * that only a limited number of bytes can be consumed from a
+ * backing input stream giving the impression of an independent
+ * stream of bytes for a segmented region of the parent buffer.
+ *
+ * @param source this is the input stream used to read data
+ * @param length this is the number of bytes that can be read
+ */
+ public Range(InputStream source, long length) {
+ super(source);
+ this.length = length;
+ }
+
+ /**
+ * This will read data from the underlying stream up to the
+ * number of bytes this range is allowed to read. When all of
+ * the bytes are exhausted within the stream this returns -1.
+ *
+ * @return this returns the octet from the underlying stream
+ */
+ @Override
+ public int read() throws IOException {
+ if(length-- > 0) {
+ return in.read();
+ }
+ if(length <= 0) {
+ close();
+ }
+ return -1;
+ }
+
+ /**
+ * This will read data from the underlying stream up to the
+ * number of bytes this range is allowed to read. When all of
+ * the bytes are exhausted within the stream this returns -1.
+ *
+ * @param array this is the array to read the bytes in to
+ * @param off this is the start offset to append the bytes to
+ * @param size this is the number of bytes that are required
+ *
+ * @return this returns the number of bytes that were read
+ */
+ @Override
+ public int read(byte[] array, int off, int size) throws IOException {
+ int left = (int)Math.min(length, size);
+
+ if(left > 0) {
+ int count = in.read(array, off, left);
+
+ if(count > 0){
+ length -= count;
+ }
+ if(length <= 0) {
+ close();
+ }
+ return count;
+ }
+ return -1;
+ }
+
+ /**
+ * This returns the number of bytes that can be read from the
+ * range. This will be the actual number of bytes the range
+ * contains as the underlying file will not block reading.
+ *
+ * @return this returns the number of bytes within the range
+ */
+ @Override
+ public int available() throws IOException {
+ return (int)length;
+ }
+
+ /**
+ * This is the number of bytes to skip from the buffer. This
+ * will allow up to the number of remaining bytes within the
+ * range to be read. When all the bytes have been read this
+ * will return zero indicating no bytes were skipped.
+ *
+ * @param size this returns the number of bytes to skip
+ *
+ * @return this returns the number of bytes that were skipped
+ */
+ @Override
+ public long skip(long size) throws IOException {
+ long left = Math.min(length, size);
+ long skip = in.skip(left);
+
+ if(skip > 0) {
+ length -= skip;
+ }
+ if(length <= 0) {
+ close();
+ }
+ return skip;
+ }
+
+ /**
+ * This is used to close the range once all of the content has
+ * been fully read. The <code>Range</code> object forces the
+ * close of the stream once all the content has been consumed
+ * to ensure that excessive file descriptors are used. Also
+ * this will ensure that the files can be deleted.
+ */
+ @Override
+ public void close() throws IOException {
+ if(!closed) {
+ in.close();
+ closed =true;
+ }
+ }
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileWatcher.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileWatcher.java
new file mode 100644
index 00000000..6d1f3b27
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FileWatcher.java
@@ -0,0 +1,179 @@
+/*
+ * FileWatcher.java February 2008
+ *
+ * Copyright (C) 2008, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+
+/**
+ * The <code>FileWatcher</code> object is used to create files that
+ * are to be used for file buffers. All files created by this are
+ * created in the <code>java.io.tmpdir</code> path. Temporary files
+ * created in this directory last for a configurable length of time
+ * before they are deleted.
+ *
+ * @author Niall Gallagher
+ */
+class FileWatcher implements FileFilter {
+
+ /**
+ * This is the prefix for the temporary files created.
+ */
+ private final String prefix;
+
+ /**
+ * This is the duration the files created will exist for.
+ */
+ private final long duration;
+
+ /**
+ * Constructor for the <code>FileWatcher</code> object. This will
+ * allow temporary files to exist for five minutes. After this
+ * time the will be removed from the underlying directory. Any
+ * request for a new file will result in a sweep of the temporary
+ * directory for all matching files, if they have expired they
+ * will be deleted.
+ *
+ * @param prefix this is the file name prefix for the files
+ */
+ public FileWatcher(String prefix) {
+ this(prefix, 300000);
+ }
+
+ /**
+ * Constructor for the <code>FileWatcher</code> object. This will
+ * allow temporary files to exist for a configurable length of time.
+ * After this time the will be removed from the underlying directory.
+ * Any request for a new file will result in a sweep of the temporary
+ * directory for all matching files, if they have expired they
+ * will be deleted.
+ *
+ * @param prefix this is the file name prefix for the files
+ * @param duration this is the duration the files exist for
+ */
+ public FileWatcher(String prefix, long duration) {
+ this.duration = duration;
+ this.prefix = prefix;
+ }
+
+ /**
+ * This will create a temporary file which can be used as a buffer
+ * for <code>FileBuffer</code> objects. The file returned by this
+ * method will be created before it is returned, which ensures it
+ * can be used as a means to buffer bytes. All files are created
+ * in the <code>java.io.tmpdir</code> location, which represents
+ * the underlying file system temporary file destination.
+ *
+ * @return this returns a created temporary file for buffers
+ */
+ public File create() throws IOException {
+ File path = create(prefix);
+
+ if(!path.isDirectory()) {
+ File parent = path.getParentFile();
+
+ if(parent.isDirectory()) {
+ clean(parent);
+ }
+ }
+ return path;
+ }
+
+ /**
+ * This will create a temporary file which can be used as a buffer
+ * for <code>FileBuffer</code> objects. The file returned by this
+ * method will be created before it is returned, which ensures it
+ * can be used as a means to buffer bytes. All files are created
+ * in the <code>java.io.tmpdir</code> location, which represents
+ * the underlying file system temporary file destination.
+ *
+ * @param prefix this is the prefix of the file to be created
+ *
+ * @return this returns a created temporary file for buffers
+ */
+ private File create(String prefix) throws IOException {
+ File file = File.createTempFile(prefix, null);
+
+ if(!file.exists()) {
+ file.createNewFile();
+ }
+ return file;
+ }
+
+ /**
+ * When this method is invoked the files that match the pattern
+ * of the temporary files are evaluated for deletion. Only those
+ * files that have not been modified in the duration period can
+ * be deleted. This ensures the file system is not exhausted.
+ *
+ * @param path this is the path of the file to be evaluated
+ */
+ private void clean(File path) throws IOException {
+ File[] list = path.listFiles(this);
+
+ for(File next : list) {
+ for(int i = 0; i < 3; i++) {
+ if(next.delete()) {
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * This determines if the file provided is an acceptable file for
+ * deletion. Acceptable files are those that match the pattern
+ * of files created by this file system object. If the file is
+ * a matching file then it is a candidate for deletion.
+ *
+ * @param file this is the file to evaluate for deletion
+ *
+ * @return this returns true if the file matches the pattern
+ */
+ public boolean accept(File file) {
+ String name = file.getName();
+
+ if(file.isDirectory()) {
+ return false;
+ }
+ return accept(file, name);
+ }
+
+ /**
+ * This determines if the file provided is an acceptable file for
+ * deletion. Acceptable files are those that match the pattern
+ * of files created by this file system object. If the file is
+ * a matching file then it is a candidate for deletion.
+ *
+ * @param file this is the file to evaluate for deletion
+ * @param name this is the name of the file to be evaluated
+ *
+ * @return this returns true if the file matches the pattern
+ */
+ private boolean accept(File file, String name) {
+ long time = System.currentTimeMillis();
+ long modified = file.lastModified();
+
+ if(modified + duration > time) { // not yet expired
+ return false;
+ }
+ return name.startsWith(prefix);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FilterAllocator.java b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FilterAllocator.java
new file mode 100644
index 00000000..ab974235
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/buffer/FilterAllocator.java
@@ -0,0 +1,123 @@
+/*
+ * FilterAllocator.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.buffer;
+
+import java.io.IOException;
+
+/**
+ * The <code>FilterAllocator</code> object is used to provide a means
+ * to provide a general set of constraints around buffer allocation.
+ * It can ensure that a minimum capacity is used for default allocation
+ * and that an upper limit is used for allocation. In general this can
+ * be used in conjunction with another <code>Allocator</code> which may
+ * not have such constraints. It ensures that a set of requirements can
+ * be observed when allocating buffers.
+ *
+ * @author Niall Gallagher
+ */
+public class FilterAllocator implements Allocator {
+
+ /**
+ * This is the allocator the underlying buffer is allocated with.
+ */
+ protected Allocator source;
+
+ /**
+ * This is the default initial minimum capacity of the buffer.
+ */
+ protected long capacity;
+
+ /**
+ * This is the maximum number of bytes that can be allocated.
+ */
+ protected long limit;
+
+ /**
+ * Constructor for the <code>FilterAllocator</code> object. This is
+ * used to instantiate the allocator with a default buffer size of
+ * half a kilobyte. This ensures that it can be used for general
+ * purpose byte storage and for minor I/O tasks.
+ *
+ * @param source this is where the underlying buffer is allocated
+ */
+ public FilterAllocator(Allocator source) {
+ this(source, 512, 1048576);
+ }
+
+ /**
+ * Constructor for the <code>FilterAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param source this is where the underlying buffer is allocated
+ * @param capacity the initial capacity of the allocated buffers
+ */
+ public FilterAllocator(Allocator source, long capacity) {
+ this(source, capacity, 1048576);
+ }
+
+ /**
+ * Constructor for the <code>FilterAllocator</code> object. This is
+ * used to instantiate the allocator with a specified buffer size.
+ * This is typically used when a very specific buffer capacity is
+ * required, for example a request body with a known length.
+ *
+ * @param source this is where the underlying buffer is allocated
+ * @param capacity the initial capacity of the allocated buffers
+ * @param limit this is the maximum buffer size created by this
+ */
+ public FilterAllocator(Allocator source, long capacity, long limit) {
+ this.limit = Math.max(capacity, limit);
+ this.capacity = capacity;
+ this.source = source;
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ public Buffer allocate() throws IOException {
+ return allocate(capacity);
+ }
+
+ /**
+ * This method is used to allocate a default buffer. This will
+ * allocate a buffer of predetermined size, allowing it to grow
+ * to an upper limit to accommodate extra data. If the buffer
+ * requested is larger than the limit an exception is thrown.
+ *
+ * @param size the initial capacity of the allocated buffer
+ *
+ * @return this returns an allocated buffer with a default size
+ */
+ public Buffer allocate(long size) throws IOException {
+ if(size > limit) {
+ throw new BufferException("Specified size %s beyond limit", size);
+ }
+ if(capacity > size) {
+ size = capacity;
+ }
+ return source.allocate(size);
+ }
+} \ No newline at end of file
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64Encoder.java b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64Encoder.java
new file mode 100644
index 00000000..54c820ae
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64Encoder.java
@@ -0,0 +1,166 @@
+/*
+ * Base64Encoder.java February 2014
+ *
+ * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.encode;
+
+/**
+ * The <code>Base64Encoder</code> is used to encode and decode base64
+ * content. The implementation used here provides a reasonably fast
+ * memory efficient encoder for use with input and output streams. It
+ * is possible to achieve higher performance, however, ease of use
+ * and convenience are the priorities with this implementation. This
+ * can only decode complete blocks.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.encode.Base64OutputStream
+ * @see org.simpleframework.common.encode.Base64InputStream
+ */
+public class Base64Encoder {
+
+ /**
+ * This maintains reference data used to fast decoding.
+ */
+ private static final int[] REFERENCE = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
+ 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0,};
+
+ /**
+ * This contains the base64 alphabet used for encoding.
+ */
+ private static final char[] ALPHABET = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', };
+
+ /**
+ * This method is used to encode the specified byte array of binary
+ * data in to base64 data. The block is complete and must be decoded
+ * as a complete block.
+ *
+ * @param buf this is the binary data to be encoded
+ *
+ * @return this is the base64 encoded value of the data
+ */
+ public static char[] encode(byte[] buf) {
+ return encode(buf, 0, buf.length);
+ }
+
+ /**
+ * This method is used to encode the specified byte array of binary
+ * data in to base64 data. The block is complete and must be decoded
+ * as a complete block.
+ *
+ * @param buf this is the binary data to be encoded
+ * @param off this is the offset to read the binary data from
+ * @param len this is the length of data to encode from the array
+ *
+ * @return this is the base64 encoded value of the data
+ */
+ public static char[] encode(byte[] buf, int off, int len) {
+ char[] text = new char[((len + 2) / 3) * 4];
+ int last = off + len;
+ int a = 0;
+ int i = 0;
+
+ while (i < last) {
+ byte one = buf[i++];
+ byte two = (i < len) ? buf[i++] : 0;
+ byte three = (i < len) ? buf[i++] : 0;
+
+ int mask = 0x3F;
+ text[a++] = ALPHABET[(one >> 2) & mask];
+ text[a++] = ALPHABET[((one << 4) | ((two & 0xFF) >> 4)) & mask];
+ text[a++] = ALPHABET[((two << 2) | ((three & 0xFF) >> 6)) & mask];
+ text[a++] = ALPHABET[three & mask];
+ }
+ switch (len % 3) {
+ case 1:
+ text[--a] = '=';
+ case 2:
+ text[--a] = '=';
+ }
+ return text;
+ }
+
+ /**
+ * This is used to decode the provide base64 data back in to an
+ * array of binary data. The data provided here must be a full block
+ * of base 64 data in order to be decoded.
+ *
+ * @param text this is the base64 text to be decoded
+ *
+ * @return this returns the resulting byte array
+ */
+ public static byte[] decode(char[] text) {
+ return decode(text, 0, text.length);
+ }
+
+ /**
+ * This is used to decode the provide base64 data back in to an
+ * array of binary data. The data provided here must be a full block
+ * of base 64 data in order to be decoded.
+ *
+ * @param text this is the base64 text to be decoded
+ * @param off this is the offset to read the text data from
+ * @param len this is the length of data to decode from the text
+ *
+ * @return this returns the resulting byte array
+ */
+ public static byte[] decode(char[] text, int off, int len) {
+ int delta = 0;
+
+ if (text[off + len - 1] == '=') {
+ delta = text[off + len - 2] == '=' ? 2 : 1;
+ }
+ byte[] buf = new byte[len * 3 / 4 - delta];
+ int mask = 0xff;
+ int index = 0;
+
+ for (int i = 0; i < len; i += 4) {
+ int pos = off + i;
+ int one = REFERENCE[text[pos]];
+ int two = REFERENCE[text[pos + 1]];
+
+ buf[index++] = (byte) (((one << 2) | (two >> 4)) & mask);
+
+ if (index >= buf.length) {
+ return buf;
+ }
+ int three = REFERENCE[text[pos + 2]];
+
+ buf[index++] = (byte) (((two << 4) | (three >> 2)) & mask);
+
+ if (index >= buf.length) {
+ return buf;
+ }
+ int four = REFERENCE[text[pos + 3]];
+ buf[index++] = (byte) (((three << 6) | four) & mask);
+ }
+ return buf;
+ }
+
+} \ No newline at end of file
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64InputStream.java b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64InputStream.java
new file mode 100644
index 00000000..8aa28af5
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64InputStream.java
@@ -0,0 +1,123 @@
+/*
+ * Base64InputStream.java February 2014
+ *
+ * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.encode;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * The <code>Base64InputStream</code> is used to read base64 text in
+ * the form of a string through a conventional input stream. This is
+ * provided for convenience so that it is possible to encode and
+ * decode binary data as base64 for implementations that would
+ * normally use a binary format.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.encode.Base64Encoder
+ */
+public class Base64InputStream extends InputStream {
+
+ /**
+ * This is that original base64 text that is to be decoded.
+ */
+ private char[] encoded;
+
+ /**
+ * This is used to accumulate the decoded text as an array.
+ */
+ private byte[] decoded;
+
+ /**
+ * This is a temporary buffer used to read one byte at a time.
+ */
+ private byte[] temp;
+
+ /**
+ * This is the total number of bytes that have been read.
+ */
+ private int count;
+
+ /**
+ * Constructor for the <code>Base64InputStream</code> object.
+ * This takes an encoded string and reads it as binary data.
+ *
+ * @param source this string containing the encoded data
+ */
+ public Base64InputStream(String source) {
+ this.encoded = source.toCharArray();
+ this.temp = new byte[1];
+ }
+
+ /**
+ * This is used to read the next byte decoded from the text. If
+ * the data has been fully consumed then this will return the
+ * standard -1.
+ *
+ * @return this returns the next octet decoded
+ */
+ @Override
+ public int read() throws IOException {
+ int count = read(temp);
+
+ if (count == -1) {
+ return -1;
+ }
+ return temp[0] & 0xff;
+ }
+
+ /**
+ * This is used to read the next byte decoded from the text. If
+ * the data has been fully consumed then this will return the
+ * standard -1.
+ *
+ * @param array this is the array to decode the text to
+ * @param offset this is the offset to decode in to the array
+ * @param this is the number of bytes available to decode to
+ *
+ * @return this returns the number of octets decoded
+ */
+ @Override
+ public int read(byte[] array, int offset, int length) throws IOException {
+ if (decoded == null) {
+ decoded = Base64Encoder.decode(encoded);
+ }
+ if (count >= decoded.length) {
+ return -1;
+ }
+ int size = Math.min(length, decoded.length - count);
+
+ if (size > 0) {
+ System.arraycopy(decoded, count, array, offset, size);
+ count += size;
+ }
+ return size;
+ }
+
+ /**
+ * This returns the original base64 text that was encoded. This
+ * is useful for debugging purposes to see the source data.
+ *
+ * @return this returns the original base64 text to decode
+ */
+ @Override
+ public String toString() {
+ return new String(encoded);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64OutputStream.java b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64OutputStream.java
new file mode 100644
index 00000000..a8f425e3
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/encode/Base64OutputStream.java
@@ -0,0 +1,138 @@
+/*
+ * Base64OutputStream.java February 2014
+ *
+ * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.encode;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * The <code>Base64OutputStream</code> is used to write base64 text
+ * in the form of a string through a conventional output stream. This
+ * is provided for convenience so that it is possible to encode and
+ * decode binary data as base64 for implementations that would
+ * normally use a binary format.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.encode.Base64Encoder
+ */
+public class Base64OutputStream extends OutputStream {
+
+ private char[] encoded;
+ private byte[] buffer;
+ private byte[] temp;
+ private int count;
+
+ /**
+ * Constructor for the <code>Base64OutputStream</code> object. A
+ * stream created with this constructor uses an initial capacity
+ * of one kilobyte, the capacity is increased as bytes are written.
+ */
+ public Base64OutputStream() {
+ this(1024);
+ }
+
+ /**
+ * Constructor for the <code>Base64OutputStream</code> object. A
+ * stream created with this constructor can have an initial capacity
+ * specified. Typically it is a good rule of thumb to use a capacity
+ * that is just over an additional third of the source binary data.
+ *
+ * @param capacity this is the initial capacity of the buffer
+ */
+ public Base64OutputStream(int capacity) {
+ this.buffer = new byte[capacity];
+ this.temp = new byte[1];
+ }
+
+ /**
+ * This method is used to write data as base64 to an internal buffer.
+ * The <code>toString</code> method can be used to acquire the text
+ * encoded from the written binary data.
+ *
+ * @param octet the octet to encode in to the internal buffer
+ */
+ @Override
+ public void write(int octet) throws IOException {
+ temp[0] = (byte) octet;
+ write(temp);
+ }
+
+ /**
+ * This method is used to write data as base64 to an internal buffer.
+ * The <code>toString</code> method can be used to acquire the text
+ * encoded from the written binary data.
+ *
+ * @param array the octets to encode to the internal buffer
+ * @param offset this is the offset in the array to encode from
+ * @param length this is the number of bytes to be encoded
+ */
+ @Override
+ public void write(byte[] array, int offset, int length) throws IOException {
+ if (encoded != null) {
+ throw new IOException("Stream has been closed");
+ }
+ if (count + length > buffer.length) {
+ expand(count + length);
+ }
+ System.arraycopy(array, offset, buffer, count, length);
+ count += length;
+ }
+
+ /**
+ * This will expand the size of the internal buffer. To allow for
+ * a variable length number of bytes to be written the internal
+ * buffer can grow as demand exceeds space available.
+ *
+ * @param capacity this is the minimum capacity required
+ */
+ private void expand(int capacity) throws IOException {
+ int length = Math.max(buffer.length * 2, capacity);
+
+ if (buffer.length < capacity) {
+ buffer = Arrays.copyOf(buffer, length);
+ }
+ }
+
+ /**
+ * This is used to close the stream and encode the buffered bytes
+ * to base64. Once this method is invoked no further data can be
+ * encoded with the stream. The <code>toString</code> method can
+ * be used to acquire the base64 encoded text.
+ */
+ @Override
+ public void close() throws IOException {
+ if (encoded == null) {
+ encoded = Base64Encoder.encode(buffer, 0, count);
+ }
+ }
+
+ /**
+ * This returns the base64 text encoded from the bytes written to
+ * the stream. This is the primary means for acquiring the base64
+ * encoded text once the stream has been closed.
+ *
+ * @return this returns the base64 text encoded
+ */
+ @Override
+ public String toString() {
+ return new String(encoded);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/Cleaner.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Cleaner.java
new file mode 100644
index 00000000..08d7fb09
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Cleaner.java
@@ -0,0 +1,44 @@
+/*
+ * Cleaner.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+/**
+ * The <code>Cleaner</code> represents an object that is used to
+ * clean up after the keyed resource. Typically this is used when
+ * a <code>Lease</code> referring a resource has expired meaning
+ * that any memory, file descriptors, or other such limited data
+ * should be released for the keyed resource. The resource keys
+ * used should be distinct over time to avoid conflicts.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.lease.Lease
+ */
+public interface Cleaner<T> {
+
+ /**
+ * This method is used to clean up after a the keyed resource.
+ * To ensure that the leasing infrastructure operates properly
+ * this should not block releasing resources. If required this
+ * should spawn a thread to perform time consuming tasks.
+ *
+ * @param key this is the key for the resource to clean
+ */
+ void clean(T key) throws Exception;
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/Contract.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Contract.java
new file mode 100644
index 00000000..ac05682e
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Contract.java
@@ -0,0 +1,77 @@
+/*
+ * Contract.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A <code>Contract</code> is used to represent the contract a
+ * lease has been issued. This contains all relevant information
+ * regarding the lease, such as the keyed resource that has been
+ * leased and the duration of the lease. Delays for the contract
+ * can be measured in any <code>TimeUnit</code> for convinienct.
+ *
+ * @author Niall Gallagher
+ */
+interface Contract<T> extends Delayed {
+
+ /**
+ * This returns the key for the resource this represents.
+ * This is used when the contract has expired to clean resources
+ * associated with the lease. It is passed in to the cleaner as
+ * an parameter to the callback. The cleaner is then responsible
+ * for cleaning any resources associated with the lease.
+ *
+ * @return returns the resource key that this represents
+ */
+ T getKey();
+
+ /**
+ * This method will return the number of <code>TimeUnit</code>
+ * seconds that remain in the contract. If the value returned is
+ * less than or equal to zero then it should be assumed that the
+ * lease has expired, if greater than zero the lease is active.
+ *
+ * @return returns the duration in time unit remaining
+ */
+ long getDelay(TimeUnit unit);
+
+ /**
+ * This method is used to set the number of <code>TimeUnit</code>
+ * seconds that should remain within the contract. This is used
+ * when the contract is to be reissued. Once a new duration has
+ * been set the contract for the lease has been changed and the
+ * previous expiry time is ignores, so only one clean is called.
+ *
+ * @param delay this is the delay to be used for this contract
+ * @param unit this is the time unit measurment for the delay
+ */
+ void setDelay(long delay, TimeUnit unit);
+
+ /**
+ * This is used to provide a description of the contract that the
+ * instance represents. A description well contain the key owned
+ * by the contract as well as the expiry time expected for it.
+ * This is used to provide descriptive messages in the exceptions.
+ *
+ * @return a descriptive message describing the contract object
+ */
+ String toString();
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractController.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractController.java
new file mode 100644
index 00000000..03536479
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractController.java
@@ -0,0 +1,84 @@
+/*
+ * ContractController.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import org.simpleframework.common.lease.LeaseException;
+
+/**
+ * The <code>ContractController</code> forms the interface to the
+ * lease management system. There are two actions permitted for
+ * leased resources, these are lease issue and lease renewal. When
+ * the lease is first issued it is scheduled for the contract
+ * duration. Once issued the lease can be renewed with another
+ * duration, which can be less than the previous duration used.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.lease.ContractMaintainer
+ */
+interface ContractController<T> {
+
+ /**
+ * This method will establish a contract for the given duration.
+ * If the contract duration expires before it is renewed then a
+ * notification is sent, typically to a <code>Cleaner</code> to
+ * to signify that the resource should be released. The contract
+ * can also be cancelled by providing a zero length duration.
+ *
+ * @param contract a contract representing a leased resource
+ *
+ * @exception Exception if the lease could not be done
+ */
+ void issue(Contract<T> contract) throws LeaseException;
+
+ /**
+ * This ensures that the contract is renewed for the duration on
+ * the contract, which may have changed since it was issued or
+ * last renewed. If the duration on the contract has changed this
+ * will insure the previous contract duration is revoked and the
+ * new duration is used to maintain the leased resource.
+ *
+ * @param contract a contract representing a leased resource
+ *
+ * @exception Exception if the lease could not be done
+ */
+ void renew(Contract<T> contract) throws LeaseException;
+
+ /**
+ * This will cancel the lease and release the resource. This
+ * has the same effect as the <code>renew</code> method with
+ * a zero length duration. Once this has been called the
+ * <code>Cleaner</code> used should be notified immediately.
+ * If the lease has already expired this throws an exception.
+ *
+ * @param contract a contract representing a leased resource
+ *
+ * @exception Exception if the expiry has been passed
+ */
+ void cancel(Contract<T> contract) throws LeaseException;
+
+ /**
+ * This method is used to cancel all outstanding leases and to
+ * close the controller. Closing the controller ensures that it
+ * can no longer be used to issue or renew leases. All resources
+ * occupied by the controller are released, including threads,
+ * memory, and all leased resources occupied by the instance.
+ */
+ void close();
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractLease.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractLease.java
new file mode 100644
index 00000000..60cd4748
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractLease.java
@@ -0,0 +1,119 @@
+/*
+ * ContractLease.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>ContractLease</code> is used to maintain contracts by
+ * using a controller object. This will invoke the controller with
+ * the contract when a lease operation is performed. A lease is
+ * renewed by changing the contract duration and passing that to
+ * the controller which will reestablish the expiry time for it.
+ *
+ * @author Niall Gallagher
+ */
+class ContractLease<T> implements Lease<T> {
+
+ /**
+ * This is the controller object used to handle contracts.
+ */
+ private final ContractController<T> handler;
+
+ /**
+ * This is the contract object representing the lease.
+ */
+ private final Contract<T> contract;
+
+ /**
+ * Constructor for the <code>ContractLease</code> object. This is
+ * used to create a lease which will maintain a contract using a
+ * controller object. Lease renewals are performed by changing the
+ * expiry duration on the contract and notifying the controller.
+ *
+ * @param handler this is used to manage the contract expiration
+ * @param contract this is the contract representing the lease
+ */
+ public ContractLease(ContractController<T> handler, Contract<T> contract) {
+ this.handler = handler;
+ this.contract = contract;
+ }
+
+ /**
+ * Determines the duration remaining before the lease expires.
+ * The expiry is given as the number of <code>TimeUnit</code>
+ * seconds remaining before the lease expires. If this value is
+ * negative it should be assumed that the lease has expired.
+ *
+ * @param unit this is the time unit used for the duration
+ *
+ * @return the duration remaining within this lease instance
+ *
+ * @exception LeaseException if the lease expiry has passed
+ */
+ public long getExpiry(TimeUnit unit) throws LeaseException {
+ return contract.getDelay(unit);
+ }
+
+ /**
+ * This ensures that the leased resource is maintained for the
+ * specified number of <code>TimeUnit</code> seconds. Allowing
+ * the duration unit to be specified enables the lease system
+ * to maintain a resource with a high degree of accuracy. The
+ * accuracy of the leasing system is dependant on how long it
+ * takes to clean the resource associated with the lease.
+ *
+ * @param duration this is the length of time to renew for
+ * @param unit this is the time unit used for the duration
+ *
+ * @exception LeaseException if the expiry has been passed
+ */
+ public void renew(long duration, TimeUnit unit) throws LeaseException {
+ if(duration >= 0) {
+ contract.setDelay(duration, unit);
+ }
+ handler.renew(contract);
+ }
+
+ /**
+ * This will cancel the lease and release the resource. This
+ * has the same effect as the <code>renew</code> method with
+ * a zero length duration. Once this has been called the
+ * <code>Cleaner</code> used should be notified immediately.
+ * If the lease has already expired this throws an exception.
+ *
+ * @exception LeaseException if the expiry has been passed
+ */
+ public void cancel() throws LeaseException {
+ handler.cancel(contract);
+ }
+
+ /**
+ * Provides the key for the resource that this lease represents.
+ * This can be used to identify the resource should the need
+ * arise. Also, this provides a convenient means of identifying
+ * leases when using or storing it as an <code>Object</code>.
+ *
+ * @return this returns the key for the resource represented
+ */
+ public T getKey() {
+ return contract.getKey();
+ }
+}
+
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractMaintainer.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractMaintainer.java
new file mode 100644
index 00000000..3e650390
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractMaintainer.java
@@ -0,0 +1,115 @@
+/*
+ * ContractMaintainer.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+/**
+ * The <code>ContractMaintainer</code> is used provide a controller
+ * uses a cleaner. This simple delegates to the cleaner queue when
+ * a renewal is required. Renewals are performed by revoking the
+ * contract and then reissuing it. This will ensure that the delay
+ * for expiry of the contract is reestablished within the queue.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.lease.LeaseCleaner
+ */
+class ContractMaintainer<T> implements ContractController<T> {
+
+ /**
+ * The queue that is used to issue and revoke contracts.
+ */
+ private final LeaseCleaner<T> queue;
+
+ /**
+ * Constructor for the <code>ContractMaintainer</code> object. This
+ * is used to create a controller for contracts which will ensure
+ * that the lease expiry durations are met. All notifications of
+ * expiry will be delivered to the provided cleaner instance.
+ *
+ * @param cleaner this is used to receive expiry notifications
+ */
+ public ContractMaintainer(Cleaner<T> cleaner) {
+ this.queue = new LeaseCleaner<T>(cleaner);
+ }
+
+ /**
+ * This method will establish a contract for the given duration.
+ * If the contract duration expires before it is renewed then a
+ * notification is sent, typically to a <code>Cleaner</code> to
+ * to signify that the resource should be released. The contract
+ * can also be cancelled by providing a zero length duration.
+ *
+ * @param contract a contract representing a leased resource
+ */
+ public synchronized void issue(Contract<T> contract) {
+ queue.issue(contract);
+ }
+
+ /**
+ * This ensures that the contract is renewed for the duration on
+ * the contract, which may have changed since it was issued or
+ * last renewed. If the duration on the contract has changed this
+ * will insure the previous contract duration is revoked and the
+ * new duration is used to maintain the leased resource.
+ *
+ * @param contract a contract representing a leased resource
+ */
+ public synchronized void renew(Contract<T> contract) {
+ boolean active = queue.revoke(contract);
+
+ if(!active) {
+ throw new LeaseException("Lease has expired for " + contract);
+ }
+ queue.issue(contract);
+ }
+
+ /**
+ * This will cancel the lease and release the resource. This
+ * has the same effect as the <code>renew</code> method with
+ * a zero length duration. Once this has been called the
+ * <code>Cleaner</code> used should be notified immediately.
+ * If the lease has already expired this throws an exception.
+ *
+ * @param contract a contract representing a leased resource
+ */
+ public synchronized void cancel(Contract<T> contract) {
+ boolean active = queue.revoke(contract);
+
+ if(!active) {
+ throw new LeaseException("Lease has expired for " + contract);
+ }
+ contract.setDelay(0, MILLISECONDS);
+ queue.issue(contract);
+ }
+
+ /**
+ * This method is used to cancel all outstanding leases and to
+ * close the controller. Closing the controller ensures that it
+ * can no longer be used to issue or renew leases. All resources
+ * occupied by the controller are released, including threads,
+ * memory, and all leased resources occupied by the instance.
+ *
+ * @throws LeaseException if the controller can not be closed
+ */
+ public synchronized void close() {
+ queue.close();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractQueue.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractQueue.java
new file mode 100644
index 00000000..df881dcc
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/ContractQueue.java
@@ -0,0 +1,44 @@
+/*
+ * ContractQueue.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.DelayQueue;
+
+/**
+ * The <code>ContraceQueue</code> object is used to queue contracts
+ * between two asynchronous threads of execution. This allows the
+ * controller to schedule the lease contract for expiry. Taking the
+ * contracts from the queue is delayed for the contract duration.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.lease.Contract
+ */
+class ContractQueue<T> extends DelayQueue<Contract<T>> {
+
+ /**
+ * Constructor for the <code>ContractQueue</code> object. This
+ * is used to create a queue for passing contracts between two
+ * asynchronous threads of execution. This is used by the
+ * lease controller to schedule the lease contract for expiry.
+ */
+ public ContractQueue() {
+ super();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/Expiration.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Expiration.java
new file mode 100644
index 00000000..fca1c140
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Expiration.java
@@ -0,0 +1,163 @@
+/*
+ * Expiration.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import java.util.concurrent.Delayed;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A <code>Expiration</code> is used to represent the expiration
+ * for a lease. This contains all relevant information for the
+ * the lease, such as the keyed resource that has been leased and
+ * the duration of the lease. Durations for the contract can be
+ * measured in any <code>TimeUnit</code> for convenience.
+ *
+ * @author Niall Gallagher
+ */
+class Expiration<T> implements Contract<T> {
+
+ /**
+ * This is the expiration time in nanoseconds for this.
+ */
+ private volatile long time;
+
+ /**
+ * This is the key representing the resource being lease.
+ */
+ private T key;
+
+ /**
+ * Constructor for the <code>Expiration</code> object. This is used
+ * to create a contract with an initial expiry period. Once this
+ * is created the time is taken and the contract can be issued.
+ *
+ * @param key this is the key that this contract represents
+ * @param lease this is the initial lease duration to be used
+ * @param scale this is the time unit scale that is to be used
+ */
+ public Expiration(T key, long lease, TimeUnit scale) {
+ this.time = getTime() + scale.toNanos(lease);
+ this.key = key;
+ }
+
+ /**
+ * This returns the key for the resource this represents.
+ * This is used when the contract has expired to clean resources
+ * associated with the lease. It is passed in to the cleaner as
+ * an parameter to the callback. The cleaner is then responsible
+ * for cleaning any resources associated with the lease.
+ *
+ * @return returns the resource key that this represents
+ */
+ public T getKey() {
+ return key;
+ }
+
+ /**
+ * This method will return the number of <code>TimeUnit</code>
+ * seconds that remain in the contract. If the value returned is
+ * less than or equal to zero then it should be assumed that the
+ * lease has expired, if greater than zero the lease is active.
+ *
+ * @return returns the duration in the time unit remaining
+ */
+ public long getDelay(TimeUnit unit) {
+ return unit.convert(time - getTime(), NANOSECONDS);
+ }
+
+ /**
+ * This method is used to set the number of <code>TimeUnit</code>
+ * seconds that should remain within the contract. This is used
+ * when the contract is to be reissued. Once a new duration has
+ * been set the contract for the lease has been changed and the
+ * previous expiry time is ignores, so only one clean is called.
+ *
+ * @param delay this is the delay to be used for this contract
+ * @param unit this is the time unit measurment for the delay
+ */
+ public void setDelay(long delay, TimeUnit unit) {
+ this.time = getTime() + unit.toNanos(delay);
+ }
+
+ /**
+ * This method returns the current time in nanoseconds. This is
+ * used to allow the duration of the lease to be calculated with
+ * any given time unit which allows flexibility in setting and
+ * getting the current delay for the contract.
+ *
+ * @return returns the current time in nanoseconds remaining
+ */
+ private long getTime() {
+ return System.nanoTime();
+ }
+
+ /**
+ * This is used to compare the specified delay to this delay. The
+ * result of this operation is used to prioritize contracts in
+ * order of first to expire. Contracts that expire first reach
+ * the top of the contract queue and are taken off for cleaning.
+ *
+ * @param other this is the delay to be compared with this
+ *
+ * @return this returns zero if equal otherwise the difference
+ */
+ public int compareTo(Delayed other) {
+ Expiration value = (Expiration) other;
+
+ if(other == this) {
+ return 0;
+ }
+ return compareTo(value);
+ }
+
+ /**
+ * This is used to compare the specified delay to this delay. The
+ * result of this operation is used to prioritize contracts in
+ * order of first to expire. Contracts that expire first reach
+ * the top of the contract queue and are taken off for cleaning.
+ *
+ * @param value this is the expiration to be compared with this
+ *
+ * @return this returns zero if equal otherwise the difference
+ */
+ private int compareTo(Expiration value) {
+ long diff = time - value.time;
+
+ if(diff < 0) {
+ return -1;
+ } else if(diff > 0) {
+ return 1;
+ }
+ return 0;
+ }
+
+ /**
+ * This is used to provide a description of the contract that the
+ * instance represents. A description well contain the key owned
+ * by the contract as well as the expiry time expected for it.
+ * This is used to provide descriptive messages in the exceptions.
+ *
+ * @return a descriptive message describing the contract object
+ */
+ public String toString() {
+ return String.format("contract %s", key);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/Lease.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Lease.java
new file mode 100644
index 00000000..d2a97851
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/Lease.java
@@ -0,0 +1,85 @@
+/*
+ * Lease.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>Lease</code> object is used to keep a keyed resource
+ * active. This provides a very simple lease that can be used to
+ * track the activity of a resource or system. Keeping track of
+ * activity allows resources to be maintained until such time
+ * that they are no longer required, allowing the server to clean
+ * up any allocated memory, file descriptors, or other such data.
+ *
+ * @author Niall Gallagher
+ */
+public interface Lease<T> {
+
+ /**
+ * Determines the duration remaining before the lease expires.
+ * The expiry is given as the number of <code>TimeUnit</code>
+ * seconds remaining before the lease expires. If this value is
+ * negative it should be assumed that the lease has expired.
+ *
+ * @param unit this is the time unit used for the duration
+ *
+ * @return the duration remaining within this lease instance
+ *
+ * @exception Exception if the expiry could not be acquired
+ */
+ long getExpiry(TimeUnit unit) throws LeaseException;
+
+ /**
+ * This ensures that the leased resource is maintained for the
+ * specified number of <code>TimeUnit</code> seconds. Allowing
+ * the duration unit to be specified enables the lease system
+ * to maintain a resource with a high degree of accuracy. The
+ * accuracy of the leasing system is dependent on how long it
+ * takes to clean the resource associated with the lease.
+ *
+ * @param duration this is the length of time to renew for
+ * @param unit this is the time unit used for the duration
+ *
+ * @exception Exception if the lease could not be renewed
+ */
+ void renew(long duration, TimeUnit unit) throws LeaseException;
+
+ /**
+ * This will cancel the lease and release the resource. This
+ * has the same effect as the <code>renew</code> method with
+ * a zero length duration. Once this has been called the
+ * <code>Cleaner</code> used should be notified immediately.
+ * If the lease has already expired this throws an exception.
+ *
+ * @exception Exception if the expiry has been passed
+ */
+ void cancel() throws LeaseException;
+
+ /**
+ * Provides the key for the resource that this lease represents.
+ * This can be used to identify the resource should the need
+ * arise. Also, this provides a convenient means of identifying
+ * leases when using or storing it as an <code>Object</code>.
+ *
+ * @return this returns the key for the resource represented
+ */
+ T getKey();
+
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseCleaner.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseCleaner.java
new file mode 100644
index 00000000..d1fe912d
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseCleaner.java
@@ -0,0 +1,155 @@
+/*
+ * LeaseCleaner.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import org.simpleframework.common.thread.Daemon;
+
+/**
+ * The <code>LeaseCleaner</code> provides a means of providing
+ * callbacks to clean a leased resource once the contract duration
+ * has expired. This will acquire contracts from the queue and
+ * invoke the <code>Cleaner</code> notification method. This will
+ * wait until the current clean operation has completed before it
+ * attempts to clean the next contract.
+ *
+ * @author Niall Gallagher
+ */
+class LeaseCleaner<T> extends Daemon {
+
+ /**
+ * This is used to queue contracts that are to be cleaned.
+ */
+ private final ContractQueue<T> queue;
+
+ /**
+ * This is the cleaner that is invoked to clean contracts.
+ */
+ private final Cleaner<T> cleaner;
+
+ /**
+ * Constructor for the <code>LeaseCleaner</code> object. This
+ * can be used to issue, update, and expire leases. When a lease
+ * expires notification is sent to the <code>Cleaner</code>
+ * object provided. This allows an implementation independent
+ * means to clean up once a specific lease has expired.
+ *
+ * @param cleaner this will receive expiration notifications
+ */
+ public LeaseCleaner(Cleaner<T> cleaner) {
+ this.queue = new ContractQueue<T>();
+ this.cleaner = cleaner;
+ this.start();
+ }
+
+ /**
+ * This revokes a contract that has previously been issued. This
+ * is used when the contract duration has changed so that it can
+ * be reissued again with a new duration. This returns true if
+ * the contract was still active and false if it did not exist.
+ *
+ * @param contract this is the contract that contains details
+ */
+ public boolean revoke(Contract<T> contract) throws LeaseException {
+ if(!isActive()) {
+ throw new LeaseException("Lease can not be revoked");
+ }
+ return queue.remove(contract);
+ }
+
+ /**
+ * This method will establish a contract for a given resource.
+ * If the contract duration expires before it is renewed then
+ * a notification is sent, to the issued <code>Cleaner</code>
+ * implementation, to signify that the resource has expired.
+ *
+ * @param contract this is the contract that contains details
+ */
+ public boolean issue(Contract<T> contract) throws LeaseException {
+ if(!isActive()) {
+ throw new LeaseException("Lease can not be issued");
+ }
+ return queue.offer(contract);
+ }
+
+ /**
+ * This acquires expired lease contracts from the queue once the
+ * expiry duration has passed. This will deliver notification to
+ * the <code>Cleaner</code> object once the contract has been
+ * taken from the queue. This allows the cleaner to clean up any
+ * resources associated with the lease before the next expiration.
+ */
+ public void run() {
+ while(isActive()) {
+ try {
+ clean();
+ } catch(Throwable e) {
+ continue;
+ }
+ }
+ purge();
+ }
+
+ /**
+ * This method is used to take the lease from the queue and give
+ * it to the cleaner for expiry. This effectively waits until the
+ * next contract expiry has passed, once it has passed the key
+ * for that contract is given to the cleaner to clean up resources.
+ */
+ private void clean() throws Exception {
+ Contract<T> next = queue.take();
+ T key = next.getKey();
+
+ if(key != null) {
+ cleaner.clean(key);
+ }
+ }
+
+ /**
+ * Here all of the existing contracts are purged when the invoker
+ * is closed. This ensures that each leased resource has a chance
+ * to clean up after the lease manager has been closed. All of the
+ * contracts are given a zero delay and cleaned immediately such
+ * that once this method has finished the queue will be empty.
+ */
+ private void purge() {
+ for(Contract<T> next : queue) {
+ T key = next.getKey();
+
+ try {
+ next.setDelay(0L, NANOSECONDS);
+ cleaner.clean(key);
+ } catch(Throwable e) {
+ continue;
+ }
+ }
+ }
+
+ /**
+ * Here we shutdown the lease maintainer so that the thread will
+ * die. Shutting down the maintainer is done by interrupting the
+ * thread and setting the dead flag to true. Once this is invoked
+ * then the thread will no longer be running for this object.
+ */
+ public void close() {
+ stop();
+ interrupt();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseException.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseException.java
new file mode 100644
index 00000000..3a479493
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseException.java
@@ -0,0 +1,52 @@
+/*
+ * LeaseException.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+/**
+ * The <code>LeaseException</code> is used to indicate that some
+ * operation failed when using the lease after the lease duration
+ * has expired. Typically this will be thrown when the lease is
+ * renewed after the expiry period has passed.
+ *
+ * @author Niall Gallagher
+ */
+public class LeaseException extends RuntimeException {
+
+ /**
+ * This constructor is used if there is a description of the
+ * event that caused the exception required. This can be given
+ * a message used to describe the situation for the exception.
+ *
+ * @param message this is a description of the exception
+ */
+ public LeaseException(String template) {
+ super(template);
+ }
+
+ /**
+ * This constructor is used if there is a description of the
+ * event that caused the exception required. This can be given
+ * a message used to describe the situation for the exception.
+ *
+ * @param message this is a description of the exception
+ */
+ public LeaseException(String template, Throwable cause) {
+ super(template, cause);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseManager.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseManager.java
new file mode 100644
index 00000000..93459fdc
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseManager.java
@@ -0,0 +1,93 @@
+/*
+ * LeaseManager.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>LeaseManager</code> is used to issue a lease for a
+ * named resource. This is effectively used to issue a request
+ * for a keyed resource to be released when a lease has expired.
+ * The use of a <code>Lease</code> simplifies the interface to
+ * the notification and also enables other objects to manage the
+ * lease without any knowledge of the resource it represents.
+ *
+ * @author Niall Gallagher
+ */
+public class LeaseManager<T> implements LeaseProvider<T> {
+
+ /**
+ * This is the controller used to handle lease operations.
+ */
+ private ContractController<T> handler;
+
+ /**
+ * Constructor for the <code>LeaseManager</code> object. This
+ * instance is created using a specified notification handler.
+ * The specified <code>Cleaner</code> will be notified when
+ * the lease for a named resource expires, which will allow
+ * the cleaner object to perform a clean up for that resource.
+ *
+ * @param cleaner the cleaner object receiving notifications
+ */
+ public LeaseManager(Cleaner<T> cleaner) {
+ this.handler = new ContractMaintainer<T>(cleaner);
+ }
+
+ /**
+ * This method will issue a <code>Lease</code> object that
+ * can be used to manage the release of a keyed resource. If
+ * the lease duration expires before it is renewed then the
+ * notification is sent, typically to a <code>Cleaner</code>
+ * implementation, to signify that the resource should be
+ * recovered. The issued lease can also be canceled.
+ *
+ * @param key this is the key for the leased resource
+ * @param duration this is the duration of the issued lease
+ * @param unit this is the time unit to issue the lease with
+ *
+ * @return a lease that can be used to manage notification
+ */
+ public Lease<T> lease(T key, long duration, TimeUnit unit) {
+ Contract<T> contract = new Expiration<T>(key, duration, unit);
+
+ try {
+ handler.issue(contract);
+ } catch(Exception e) {
+ throw new LeaseException("Could not issue lease", e);
+ }
+ return new ContractLease<T>(handler, contract);
+ }
+
+ /**
+ * This is used to close the lease provider such that all of
+ * the outstanding leases are canceled. This also ensures the
+ * provider can no longer be used to issue new leases, such
+ * that further invocations of the <code>lease</code> method
+ * will result in null leases. Once the provider has been
+ * closes all threads and other such resources are released.
+ */
+ public void close() {
+ try {
+ handler.close();
+ } catch(Exception e) {
+ return;
+ }
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseMap.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseMap.java
new file mode 100644
index 00000000..711a466b
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseMap.java
@@ -0,0 +1,83 @@
+/*
+ * LeaseMap.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * The <code>LeaseMap</code> object is used to map lease keys to the
+ * lease objects managing those objects. This allows components that
+ * are using the leasing framework to associate an object with its
+ * lease and vice versa. Such a capability enables lease renewals to
+ * be performed without the need for a direct handle on the lease.
+ *
+ * @author Niall Gallagher
+ */
+public class LeaseMap<T> extends ConcurrentHashMap<T, Lease<T>> {
+
+ /**
+ * Constructor for the <code>LeaseMap</code> object. This will
+ * create a map for mapping leased resource keys to the leases
+ * that manage them. Having such a map allows leases to be
+ * maintained without having a direct handle on the lease.
+ */
+ public LeaseMap() {
+ super();
+ }
+
+ /**
+ * Constructor for the <code>LeaseMap</code> object. This will
+ * create a map for mapping leased resource keys to the leases
+ * that manage them. Having such a map allows leases to be
+ * maintained without having a direct handle on the lease.
+ *
+ * @param capacity this is the initial capacity of the map
+ */
+ public LeaseMap(int capacity) {
+ super(capacity);
+ }
+
+ /**
+ * This is used to acquire the <code>Lease</code> object that is
+ * mapped to the specified key. Overriding this method ensures
+ * that even without generic parameters a type safe method for
+ * acquiring the registered lease objects can be used.
+ *
+ * @param key this is the key used to acquire the lease object
+ *
+ * @return this is the lease that is associated with the key
+ */
+ public Lease<T> get(Object key) {
+ return super.get(key);
+ }
+
+ /**
+ * This is used to remove the <code>Lease</code> object that is
+ * mapped to the specified key. Overriding this method ensures
+ * that even without generic parameters a type safe method for
+ * removing the registered lease objects can be used.
+ *
+ * @param key this is the key used to remove the lease object
+ *
+ * @return this is the lease that is associated with the key
+ */
+ public Lease<T> remove(Object key) {
+ return super.remove(key);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseProvider.java b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseProvider.java
new file mode 100644
index 00000000..e5e7d8c9
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/lease/LeaseProvider.java
@@ -0,0 +1,60 @@
+/*
+ * LeaseProvider.java May 2004
+ *
+ * Copyright (C) 2004, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.lease;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>LeaseProvider</code> is used to issue a lease for a
+ * named resource. This is effectively used to issue a request
+ * for a keyed resource to be released when a lease has expired.
+ * The use of a <code>Lease</code> simplifies the interface to
+ * the notification and also enables other objects to manage the
+ * lease without any knowledge of the resource it represents.
+ *
+ * @author Niall Gallagher
+ */
+public interface LeaseProvider<T> {
+
+ /**
+ * This method will issue a <code>Lease</code> object that
+ * can be used to manage the release of a keyed resource. If
+ * the lease duration expires before it is renewed then the
+ * notification is sent, typically to a <code>Cleaner</code>
+ * implementation, to signify that the resource should be
+ * recovered. The issued lease can also be canceled.
+ *
+ * @param key this is the key for the leased resource
+ * @param duration this is the duration of the issued lease
+ * @param unit this is the time unit to issue the lease with
+ *
+ * @return a lease that can be used to manage notification
+ */
+ Lease<T> lease(T key, long duration, TimeUnit unit);
+
+ /**
+ * This is used to close the lease provider such that all of
+ * the outstanding leases are canceled. This also ensures the
+ * provider can no longer be used to issue new leases, such
+ * that further invocations of the <code>lease</code> method
+ * will result in null leases. Once the provider has been
+ * closes all threads and other such resources are released.
+ */
+ void close();
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/parse/MapParser.java b/simple/simple-common/src/main/java/org/simpleframework/common/parse/MapParser.java
new file mode 100644
index 00000000..d242937a
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/parse/MapParser.java
@@ -0,0 +1,251 @@
+/*
+ * MapParser.java February 2005
+ *
+ * Copyright (C) 2005, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.parse;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The <code>MapParser</code> object represents a parser for name
+ * value pairs. Any parser extending this will typically be parsing
+ * name=value tokens or the like, and inserting these pairs into
+ * the internal map. This type of parser is useful as it exposes all
+ * pairs extracted using the <code>java.util.Map</code> interface
+ * and as such can be used with the Java collections framework. The
+ * internal map used by this is a <code>Hashtable</code>, however
+ * subclasses are free to assign a different type to the map used.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class MapParser<T> extends Parser implements Map<T, T> {
+
+ /**
+ * Represents all values inserted to the map as a list of values.
+ */
+ protected Map<T, List<T>> all;
+
+ /**
+ * Represents the last value inserted into this map instance.
+ */
+ protected Map<T, T> map;
+
+ /**
+ * Constructor for the <code>MapParser</code> object. This is
+ * used to create a new parser that makes use of a thread safe
+ * map implementation. The <code>HashMap</code> is used so
+ * that the resulting parser can be accessed in a concurrent
+ * environment with the fear of data corruption.
+ */
+ protected MapParser(){
+ this.all = new HashMap<T, List<T>>();
+ this.map = new HashMap<T, T>();
+ }
+
+ /**
+ * This is used to determine whether a token representing the
+ * name of a pair has been inserted into the internal map. The
+ * object passed into this method should be a string, as all
+ * tokens stored within the map will be stored as strings.
+ *
+ * @param name this is the name of a pair within the map
+ *
+ * @return this returns true if the pair of that name exists
+ */
+ public boolean containsKey(Object name) {
+ return map.containsKey(name);
+ }
+
+ /**
+ * This method is used to determine whether any pair that has
+ * been inserted into the internal map had the presented value.
+ * If one or more pairs within the collected tokens contains
+ * the value provided then this method will return true.
+ *
+ * @param value this is the value that is to be searched for
+ *
+ * @return this returns true if any value is equal to this
+ */
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ /**
+ * This method is used to acquire the name and value pairs that
+ * have currently been collected by this parser. This is used
+ * to determine which tokens have been extracted from the
+ * source. It is useful when the tokens have to be gathered.
+ *
+ * @return this set of token pairs that have been extracted
+ */
+ public Set<Map.Entry<T, T>> entrySet() {
+ return map.entrySet();
+ }
+
+ /**
+ * The <code>get</code> method is used to acquire the value for
+ * a named pair. So if a pair of name=value has been parsed and
+ * inserted into the collection of tokens this will return the
+ * value given the name. The value returned will be a string.
+ *
+ * @param name this is a string used to search for the value
+ *
+ * @return this is the value, as a string, that has been found
+ */
+ public T get(Object name) {
+ return map.get(name);
+ }
+
+ /**
+ * This method is used to acquire a <code>List</code> for all of
+ * the values that have been put in to the map. The list allows
+ * all values associated with the specified key. This enables a
+ * parser to collect a number of associated tokens.
+ *
+ * @param key this is the key used to search for the value
+ *
+ * @return this is the list of values associated with the key
+ */
+ public List<T> getAll(Object key) {
+ return all.get(key);
+ }
+
+ /**
+ * This method is used to determine whether the parser has any
+ * tokens available. If the <code>size</code> is zero then the
+ * parser is empty and this returns true. The is acts as a
+ * proxy the the <code>isEmpty</code> of the internal map.
+ *
+ * @return this is true if there are no available tokens
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * This is used to acquire the names for all the tokens that
+ * have currently been collected by this parser. This is used
+ * to determine which tokens have been extracted from the
+ * source. It is useful when the tokens have to be gathered.
+ *
+ * @return the set of name tokens that have been extracted
+ */
+ public Set<T> keySet() {
+ return map.keySet();
+ }
+
+ /**
+ * The <code>put</code> method is used to insert the name and
+ * value provided into the collection of tokens. Although it is
+ * up to the parser to decide what values will be inserted it
+ * is generally the case that the inserted tokens will be text.
+ *
+ * @param name this is the name token from a name=value pair
+ * @param value this is the value token from a name=value pair
+ *
+ * @return this returns the previous value if there was any
+ */
+ public T put(T name, T value) {
+ List<T> list = all.get(name);
+ T first = map.get(name);
+
+ if(list == null) {
+ list = new ArrayList<T>();
+ all.put(name, list);
+ }
+ list.add(value);
+
+ if(first == null) {
+ return map.put(name, value);
+ }
+ return null;
+ }
+
+ /**
+ * This method is used to insert a collection of tokens into
+ * the parsers map. This is used when another source of tokens
+ * is required to populate the connection currently maintained
+ * within this parsers internal map. Any tokens that currently
+ * exist with similar names will be overwritten by this.
+ *
+ * @param data this is the collection of tokens to be added
+ */
+ public void putAll(Map<? extends T, ? extends T> data) {
+ Set<? extends T> keySet = data.keySet();
+
+ for(T key : keySet) {
+ T value = data.get(key);
+
+ if(value != null) {
+ put(key, value);
+ }
+ }
+ }
+
+ /**
+ * The <code>remove</code> method is used to remove the named
+ * token pair from the collection of tokens. This acts like a
+ * take, in that it will get the token value and remove if
+ * from the collection of tokens the parser has stored.
+ *
+ * @param name this is a string used to search for the value
+ *
+ * @return this is the value, as a string, that is removed
+ */
+ public T remove(Object name) {
+ return map.remove(name);
+ }
+
+ /**
+ * This obviously enough provides the number of tokens that
+ * have been inserted into the internal map. This acts as
+ * a proxy method for the internal map <code>size</code>.
+ *
+ * @return this returns the number of tokens are available
+ */
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * This method is used to acquire the value for all tokens that
+ * have currently been collected by this parser. This is used
+ * to determine which tokens have been extracted from the
+ * source. It is useful when the tokens have to be gathered.
+ *
+ * @return the list of value tokens that have been extracted
+ */
+ public Collection<T> values() {
+ return map.values();
+ }
+
+ /**
+ * The <code>clear</code> method is used to wipe out all the
+ * currently existing tokens from the collection. This is used
+ * to recycle the parser so that it can be used to parse some
+ * other source of tokens without any lingering state.
+ */
+ public void clear() {
+ all.clear();
+ map.clear();
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/parse/ParseBuffer.java b/simple/simple-common/src/main/java/org/simpleframework/common/parse/ParseBuffer.java
new file mode 100644
index 00000000..d680a08f
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/parse/ParseBuffer.java
@@ -0,0 +1,247 @@
+/*
+ * ParseBuffer.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.parse;
+
+/**
+ * This is primarily used to replace the <code>StringBuffer</code>
+ * class, as a way for the <code>Parser</code> to store the char's
+ * for a specific region within the parse data that constitutes a
+ * desired value. The methods are not synchronized so it enables
+ * the <code>char</code>'s to be taken quicker than the
+ * <code>StringBuffer</code> class.
+ *
+ * @author Niall Gallagher
+ */
+public class ParseBuffer {
+
+ /**
+ * This is used to quicken <code>toString</code>.
+ */
+ protected String cache;
+
+ /**
+ * The <code>char</code>'s this buffer accumulated.
+ */
+ protected char[] buf;
+
+ /**
+ * This is the number of <code>char</code>'s stored.
+ */
+ protected int count;
+
+ /**
+ * Constructor for <code>ParseBuffer</code>. The default
+ * <code>ParseBuffer</code> stores 16 <code>char</code>'s
+ * before a <code>resize</code> is needed to accommodate
+ * extra characters.
+ */
+ public ParseBuffer(){
+ this(16);
+ }
+
+ /**
+ * This creates a <code>ParseBuffer</code> with a specific
+ * default size. The buffer will be created the with the
+ * length specified. The <code>ParseBuffer</code> can grow
+ * to accommodate a collection of <code>char</code>'s larger
+ * the the size specified.
+ *
+ * @param size initial size of this <code>ParseBuffer</code>
+ */
+ public ParseBuffer(int size){
+ this.buf = new char[size];
+ }
+
+ /**
+ * This will add a <code>char</code> to the end of the buffer.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate more <code>char</code>'s.
+ *
+ * @param c the <code>char</code> to be appended
+ */
+ public void append(char c){
+ ensureCapacity(count+ 1);
+ buf[count++] = c;
+ }
+
+ /**
+ * This will add a <code>String</code> to the end of the buffer.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate large <code>String</code> objects.
+ *
+ * @param text the <code>String</code> to be appended to this
+ */
+ public void append(String text){
+ ensureCapacity(count+ text.length());
+ text.getChars(0,text.length(),buf,count);
+ count += text.length();
+ }
+
+ /**
+ * This will reset the buffer in such a way that the buffer is
+ * cleared of all contents and then has the given string appended.
+ * This is used when a value is to be set into the buffer value.
+ * See the <code>append(String)</code> method for reference.
+ *
+ * @param text this is the text that is to be appended to this
+ */
+ public void reset(String text) {
+ clear();
+ append(text);
+ }
+
+ /**
+ * This will add a <code>ParseBuffer</code> to the end of this.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate large <code>ParseBuffer</code> objects.
+ *
+ * @param text the <code>ParseBuffer</code> to be appended
+ */
+ public void append(ParseBuffer text){
+ append(text.buf, 0, text.count);
+ }
+
+ /**
+ * This will reset the buffer in such a way that the buffer is
+ * cleared of all contents and then has the given string appended.
+ * This is used when a value is to be set into the buffer value.
+ * See the <code>append(ParseBuffer)</code> method for reference.
+ *
+ * @param text this is the text that is to be appended to this
+ */
+ public void reset(ParseBuffer text) {
+ clear();
+ append(text);
+ }
+ /**
+ * This will add a <code>char</code> to the end of the buffer.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate large <code>char</code> arrays.
+ *
+ * @param c the <code>char</code> array to be appended to this
+ * @param off the read offset for the array
+ * @param len the number of <code>char</code>'s to add
+ */
+ public void append(char[] c, int off, int len){
+ ensureCapacity(count+ len);
+ System.arraycopy(c,off,buf,count,len);
+ count+=len;
+ }
+
+ /**
+ * This will add a <code>String</code> to the end of the buffer.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate large <code>String</code> objects.
+ *
+ * @param str the <code>String</code> to be appended to this
+ * @param off the read offset for the <code>String</code>
+ * @param len the number of <code>char</code>'s to add
+ */
+ public void append(String str, int off, int len){
+ ensureCapacity(count+ len);
+ str.getChars(off,len,buf,count);
+ count += len;
+ }
+
+
+ /**
+ * This will add a <code>ParseBuffer</code> to the end of this.
+ * The buffer will not overflow with repeated uses of the
+ * <code>append</code>, it uses an <code>ensureCapacity</code>
+ * method which will allow the buffer to dynamically grow in
+ * size to accommodate large <code>ParseBuffer</code> objects.
+ *
+ * @param text the <code>ParseBuffer</code> to be appended
+ * @param off the read offset for the <code>ParseBuffer</code>
+ * @param len the number of <code>char</code>'s to add
+ */
+ public void append(ParseBuffer text, int off, int len){
+ append(text.buf, off, len);
+ }
+
+ /**
+ * This ensure that there is enough space in the buffer to
+ * allow for more <code>char</code>'s to be added. If
+ * the buffer is already larger than min then the buffer
+ * will not be expanded at all.
+ *
+ * @param min the minimum size needed
+ */
+ protected void ensureCapacity(int min) {
+ if(buf.length < min) {
+ int size = buf.length * 2;
+ int max = Math.max(min, size);
+ char[] temp = new char[max];
+ System.arraycopy(buf, 0, temp, 0, count);
+ buf = temp;
+ }
+ }
+
+ /**
+ * This will empty the <code>ParseBuffer</code> so that the
+ * <code>toString</code> parameter will return <code>null</code>.
+ * This is used so that the same <code>ParseBuffer</code> can be
+ * recycled for different tokens.
+ */
+ public void clear(){
+ cache = null;
+ count = 0;
+ }
+
+ /**
+ * This will return the number of bytes that have been appended
+ * to the <code>ParseBuffer</code>. This will return zero after
+ * the clear method has been invoked.
+ *
+ * @return the number of <code>char</code>'s within the buffer
+ */
+ public int length(){
+ return count;
+ }
+
+ /**
+ * This will return the characters that have been appended to the
+ * <code>ParseBuffer</code> as a <code>String</code> object.
+ * If the <code>String</code> object has been created before then
+ * a cached <code>String</code> object will be returned. This
+ * method will return <code>null</code> after clear is invoked.
+ *
+ * @return the <code>char</code>'s appended as a <code>String</code>
+ */
+ public String toString(){
+ if(count <= 0) {
+ return null;
+ }
+ if(cache != null) {
+ return cache;
+ }
+ cache = new String(buf,0,count);
+ return cache;
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/parse/Parser.java b/simple/simple-common/src/main/java/org/simpleframework/common/parse/Parser.java
new file mode 100644
index 00000000..5ba7b522
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/parse/Parser.java
@@ -0,0 +1,197 @@
+/*
+ * Parser.java February 2001
+ *
+ * Copyright (C) 2001, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.parse;
+
+/**
+ * This <code>Parser</code> object is to be used as a simple template
+ * for parsing uncomplicated expressions. This object is used to parse
+ * a <code>String</code>. This provides a few methods that can be used
+ * to store and track the reading of data from a buffer. There are two
+ * abstract methods provided to allow this to be subclassed to create
+ * a <code>Parser</code> for a given <code>String</code>.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class Parser {
+
+ /**
+ * This is the buffer that is being parsed.
+ */
+ protected char[] buf;
+
+ /**
+ * This represents the current read offset.
+ */
+ protected int off;
+
+ /**
+ * This represents the length of the buffer.
+ */
+ protected int count;
+
+ /**
+ * This is a no argument constructor for the <code>Parser</code>.
+ * This will be invoked by each subclass of this object. It will
+ * set the buffer to a zero length buffer so that when the
+ * <code>ensureCapacity</code> method is used the buf's
+ * length can be checked.
+ */
+ protected Parser(){
+ this.buf = new char[0];
+ }
+
+ /**
+ * This is used to parse the <code>String</code> given to it. This
+ * will ensure that the <code>char</code> buffer has enough space
+ * to contain the characters from the <code>String</code>. This
+ * will firstly ensure that the buffer is resized if nessecary. The
+ * second step in this <code>parse</code> method is to initialize
+ * the <code>Parser</code> object so that multiple parse invocations
+ * can be made. The <code>init</code> method will reset this to an
+ * prepared state. Then finally the <code>parse</code> method is
+ * called to parse the <code>char</code> buffer.
+ *
+ * @param text the <code>String</code> to be parsed with this
+ * <code>Parser</code>
+ */
+ public void parse(String text){
+ if(text != null){
+ ensureCapacity(text.length());
+ count = text.length();
+ text.getChars(0, count, buf,0);
+ init();
+ parse();
+ }
+ }
+
+ /**
+ * This ensure that there is enough space in the buffer to allow
+ * for more <code>char</code>'s to be added. If the buffer is
+ * already larger than min then the buffer will not be expanded
+ * at all.
+ *
+ * @param min the minimum size needed to accommodate the characters
+ */
+ protected void ensureCapacity(int min) {
+ if(buf.length < min) {
+ int size = buf.length * 2;
+ int max = Math.max(min, size);
+ char[] temp = new char[max];
+ buf = temp;
+ }
+ }
+
+ /**
+ * This is used to determine if a given ISO-8859-1 character is
+ * a space character. That is a whitespace character this sees
+ * the, space, carriage return and line feed characters as
+ * whitespace characters.
+ *
+ * @param c the character that is being determined by this
+ *
+ * @return true if the character given it is a space character
+ */
+ protected boolean space(char c) {
+ switch(c){
+ case ' ': case '\t':
+ case '\n': case '\r':
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * This is used to determine weather or not a given character is
+ * a digit character. It assumes iso-8859-1 encoding to compare.
+ *
+ * @param c the character being determined by this method
+ *
+ * @return true if the character given is a digit character
+ */
+ protected boolean digit(char c){
+ return c <= '9' && '0' <= c;
+ }
+
+ /**
+ * This takes a unicode character and assumes an encoding of
+ * ISO-8859-1. This then checks to see if the given character
+ * is uppercase if it is it converts it into is ISO-8859-1
+ * lowercase char.
+ *
+ * @param c the <code>char</code> to be converted to lowercase
+ *
+ * @return the lowercase ISO-8859-1 of the given character
+ */
+ protected char toLower(char c) {
+ if(c >= 'A' && c <= 'Z') {
+ return (char)((c - 'A') + 'a');
+ }
+ return c;
+ }
+
+ /** This is used to skip an arbitrary <code>String</code> within the
+ * <code>char</code> buf. It checks the length of the <code>String</code>
+ * first to ensure that it will not go out of bounds. A comparison
+ * is then made with the buffers contents and the <code>String</code>
+ * if the reigon in the buffer matched the <code>String</code> then the
+ * offset within the buffer is increased by the <code>String</code>'s
+ * length so that it has effectively skipped it.
+ *
+ * @param text this is the <code>String</code> value to be skipped
+ *
+ * @return true if the <code>String</code> was skipped
+ */
+ protected boolean skip(String text){
+ int size = text.length();
+ int read = 0;
+
+ if(off + size > count){
+ return false;
+ }
+ while(read < size){
+ char a = text.charAt(read);
+ char b = buf[off + read++];
+
+ if(toLower(a) != toLower(b)){
+ return false;
+ }
+ }
+ off += size;
+ return true;
+ }
+
+ /**
+ * This will initialize the <code>Parser</code> when it is ready
+ * to parse a new <code>String</code>. This will reset the
+ * <code>Parser</code> to a ready state. The <code>init</code>
+ * method is invoked by the <code>Parser</code> when the
+ * <code>parse</code> method is invoked.
+ */
+ protected abstract void init();
+
+ /**
+ * This is the method that should be implemented to read
+ * the buf. This method should attempt to extract tokens
+ * from the buffer so that thes tokens may some how be
+ * used to determine the semantics. This method is invoked
+ * after the <code>init</code> method is invoked.
+ */
+ protected abstract void parse();
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentExecutor.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentExecutor.java
new file mode 100644
index 00000000..9f99025e
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentExecutor.java
@@ -0,0 +1,109 @@
+/*
+ * ConcurrentExecutor.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.Executor;
+
+/**
+ * The <code>ConcurrentExecutor</code> object is used to execute tasks
+ * in a thread pool. This creates a thread pool with an unbounded list
+ * of outstanding tasks, which ensures that any system requesting
+ * a task to be executed will not block when handing it over.
+ *
+ * @author Niall Gallagher
+ */
+public class ConcurrentExecutor implements Executor {
+
+ /**
+ * This is the queue used to enqueue the tasks for execution.
+ */
+ private final ExecutorQueue queue;
+
+ /**
+ * Constructor for the <code>ConcurrentExecutor</code> object. This
+ * is used to create a pool of threads that can be used to execute
+ * arbitrary <code>Runnable</code> tasks. If the threads are
+ * busy this will simply enqueue the tasks and return.
+ *
+ * @param type this is the type of runnable that this accepts
+ */
+ public ConcurrentExecutor(Class type) {
+ this(type, 10);
+ }
+
+ /**
+ * Constructor for the <code>ConcurrentExecutor</code> object. This
+ * is used to create a pool of threads that can be used to execute
+ * arbitrary <code>Runnable</code> tasks. If the threads are
+ * busy this will simply enqueue the tasks and return.
+ *
+ * @param type this is the type of runnable that this accepts
+ * @param size this is the number of threads to use in the pool
+ */
+ public ConcurrentExecutor(Class type, int size) {
+ this(type, size, size);
+ }
+
+ /**
+ * Constructor for the <code>ConcurrentExecutor</code> object. This
+ * is used to create a pool of threads that can be used to execute
+ * arbitrary <code>Runnable</code> tasks. If the threads are
+ * busy this will simply enqueue the tasks and return.
+ *
+ * @param type this is the type of runnable that this accepts
+ * @param rest this is the number of threads to use in the pool
+ * @param active this is the maximum size the pool can grow to
+ */
+ public ConcurrentExecutor(Class type, int rest, int active) {
+ this.queue = new ExecutorQueue(type, rest, active);
+ }
+
+ /**
+ * The <code>execute</code> method is used to queue the task for
+ * execution. If all threads are busy the provided task is queued
+ * and waits until all current and outstanding tasks are finished.
+ *
+ * @param task this is the task to be queued for execution
+ */
+ public void execute(Runnable task) {
+ queue.execute(task);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ */
+ public void stop() {
+ stop(60000);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ *
+ * @param wait the number of milliseconds to wait for it to stop
+ */
+ public void stop(long wait) {
+ queue.stop(wait);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentScheduler.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentScheduler.java
new file mode 100644
index 00000000..bb4a1174
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ConcurrentScheduler.java
@@ -0,0 +1,122 @@
+/*
+ * ConcurrentScheduler.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>ConcurrentScheduler</code> object is used to schedule tasks
+ * for execution. This queues the task for the requested period of
+ * time before it is executed. It ensures that the delay is adhered
+ * to such that tasks can be timed for execution in an accurate way.
+ *
+ * @author Niall Gallagher
+ */
+public class ConcurrentScheduler implements Scheduler {
+
+ /**
+ * This is the scheduler queue used to enque tasks to execute.
+ */
+ private final SchedulerQueue queue;
+
+ /**
+ * Constructor for the <code>ConcurrentScheduler</code> object.
+ * This will create a scheduler with a fixed number of threads to
+ * use before execution. Depending on the types of task that are
+ * to be executed this should be increased for accuracy.
+ *
+ * @param type this is the type of the worker threads
+ */
+ public ConcurrentScheduler(Class type) {
+ this(type, 10);
+ }
+
+ /**
+ * Constructor for the <code>ConcurrentScheduler</code> object.
+ * This will create a scheduler with a fixed number of threads to
+ * use before execution. Depending on the types of task that are
+ * to be executed this should be increased for accuracy.
+ *
+ * @param type this is the type of the worker threads
+ * @param size this is the number of threads for the scheduler
+ */
+ public ConcurrentScheduler(Class type, int size) {
+ this.queue = new SchedulerQueue(type, size);
+ }
+
+ /**
+ * This will execute the task within the executor immediately
+ * as it uses a delay duration of zero milliseconds. This can
+ * be used if the scheduler is to be used as a thread pool.
+ *
+ * @param task this is the task to schedule for execution
+ */
+ public void execute(Runnable task) {
+ queue.execute(task);
+ }
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay the time in milliseconds to wait for execution
+ */
+ public void execute(Runnable task, long delay) {
+ execute(task, delay, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay this is the delay to wait before execution
+ * @param unit this is the duration time unit to wait for
+ */
+ public void execute(Runnable task, long delay, TimeUnit unit) {
+ queue.execute(task, delay, unit);
+ }
+
+ /**
+ * This is used to stop the scheduler by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return immediately once it has been stopped, and not further
+ * tasks will be accepted by this pool for execution.
+ */
+ public void stop() {
+ stop(60000);
+ }
+
+ /**
+ * This is used to stop the scheduler by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ *
+ * @param wait the number of milliseconds to wait for it to stop
+ */
+ public void stop(long wait) {
+ queue.stop(wait);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/Daemon.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/Daemon.java
new file mode 100644
index 00000000..3b7b5bf9
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/Daemon.java
@@ -0,0 +1,164 @@
+/*
+ * Daemon.java February 2009
+ *
+ * Copyright (C) 2009, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * The <code>Daemon</code> object provides a named thread which will
+ * execute the <code>run</code> method when started. This offers
+ * some convenience in that it hides the normal thread methods and
+ * also allows the object extending this to provide the name of the
+ * internal thread, which is given an incrementing sequence number
+ * appended to the name provided.
+ *
+ * @author Niall Gallagher
+ */
+public abstract class Daemon implements Runnable {
+
+ /**
+ * This is the current thread executing this service.
+ */
+ private final AtomicReference<Thread> reference;
+
+ /**
+ * This is the internal thread used by this daemon instance.
+ */
+ private final DaemonFactory factory;
+
+ /**
+ * This is used to determine if the daemon is active.
+ */
+ private final AtomicBoolean active;
+
+ /**
+ * This is the internal thread that is executed by this.
+ */
+ private final Runnable delegate;
+
+ /**
+ * Constructor for the <code>Daemon</code> object. This will
+ * create the internal thread and ensure it is a daemon. When it
+ * is started the name of the internal thread is set using the
+ * name of the instance as taken from <code>getName</code>. If
+ * the name provided is null then no name is set for the thread.
+ */
+ protected Daemon() {
+ this.reference = new AtomicReference<Thread>();
+ this.delegate = new RunnableDelegate(this);
+ this.factory = new DaemonFactory();
+ this.active = new AtomicBoolean();
+ }
+
+ /**
+ * This is used to determine if the runner is active. If it is not
+ * active then it is assumed that no thread is executing. Also, if
+ * this is extended then any executing thread to stop as soon as
+ * this method returns false.
+ *
+ * @return this returns true if the runner is active
+ */
+ public boolean isActive() {
+ return active.get();
+ }
+
+ /**
+ * This is used to start the internal thread. Once started the
+ * internal thread will execute the <code>run</code> method of
+ * this instance. Aside from starting the thread this will also
+ * ensure the internal thread has a unique name.
+ */
+ public void start() {
+ Class type = getClass();
+
+ if (!active.get()) {
+ Thread thread = factory.newThread(delegate, type);
+
+ reference.set(thread);
+ active.set(true);
+ thread.start();
+ }
+ }
+
+ /**
+ * This is used to interrupt the internal thread. This is used
+ * when there is a need to wake the thread from a sleeping or
+ * waiting state so that some other operation can be performed.
+ * Typically this is required when killing the thread.
+ */
+ public void interrupt() {
+ Thread thread = reference.get();
+
+ if(thread != null) {
+ thread.interrupt();
+ }
+ }
+
+ /**
+ * This method is used to stop the thread without forcing it to
+ * stop. It acts as a means to deactivate it. It is up to the
+ * implementor to ensure that the <code>isActive</code> method
+ * is checked to determine whether it should continue to run.
+ */
+ public void stop() {
+ active.set(false);
+ }
+
+ /**
+ * The <code>RunnableDelegate</code> object is used to actually
+ * invoke the <code>run</code> method. A delegate is used to ensure
+ * that once the task has finished it is inactive so that it can
+ * be started again with a new thread.
+ */
+ private class RunnableDelegate implements Runnable {
+
+ /**
+ * This is the runnable that is to be executed.
+ */
+ private final Runnable task;
+
+ /**
+ * Constructor for the <code>RunnableDelegate</code> object. The
+ * delegate requires the actual runnable that is to be executed.
+ * As soon as the task has finished the runner becomes inactive.
+ *
+ * @param task this is the task to be executed
+ */
+ public RunnableDelegate(Runnable task) {
+ this.task = task;
+ }
+
+ /**
+ * This is used to execute the task. Once the task has finished
+ * the runner becomes inactive and any reference to the internal
+ * thread is set to null. This ensures the runner can be started
+ * again at a later time if desired.
+ */
+ public void run() {
+ try {
+ task.run();
+ } finally {
+ reference.set(null);
+ active.set(false);
+ }
+ }
+
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/DaemonFactory.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/DaemonFactory.java
new file mode 100644
index 00000000..d5da16a2
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/DaemonFactory.java
@@ -0,0 +1,147 @@
+/*
+ * DaemonFactory.java February 2009
+ *
+ * Copyright (C) 2009, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * The <code>DaemonFactory</code> object is used to build threads
+ * and prefix the thread with a type name. Prefixing the threads with
+ * the type that it represents allows the purpose of the thread to
+ * be determined and also provides better debug information.
+ *
+ * @author Niall Gallagher
+ */
+public class DaemonFactory implements ThreadFactory {
+
+ /**
+ * This is the type of the task this pool will execute.
+ */
+ private final Class type;
+
+ /**
+ * Constructor for the <code>DaemonFactory</code> object. This
+ * will provide a thread factory that names the threads based
+ * on the type of <code>Runnable</code> the pool executes.
+ */
+ public DaemonFactory() {
+ this(null);
+ }
+
+ /**
+ * Constructor for the <code>DaemonFactory</code> object. This
+ * will provide a thread factory that names the threads based
+ * on the type of <code>Runnable</code> the pool executes. Each
+ * of the threads is given a unique sequence number.
+ *
+ * @param type this is the type of runnable this will execute
+ */
+ public DaemonFactory(Class type) {
+ this.type = type;
+ }
+
+ /**
+ * This is used to create a thread from the provided runnable. The
+ * thread created will contain a unique name which is prefixed with
+ * the type of task it has been created to execute. This provides
+ * some detail as to what the thread should be doing.
+ *
+ * @param task this is the task that the thread is to execute
+ *
+ * @return this returns a thread that will executed the given task
+ */
+ public Thread newThread(Runnable task) {
+ Thread thread = newThread(task, type);
+ String name = createName(task, thread);
+
+ if(!thread.isAlive()) {
+ thread.setName(name);
+ }
+ return thread;
+ }
+
+ /**
+ * This is used to create a thread from the provided runnable. The
+ * thread created will contain a unique name which is prefixed with
+ * the type of task it has been created to execute. This provides
+ * some detail as to what the thread should be doing.
+ *
+ * @param task this is the task that the thread is to execute
+ * @param type this is the type of object the thread is to execute
+ *
+ * @return this returns a thread that will executed the given task
+ */
+ public Thread newThread(Runnable task, Class type) {
+ Thread thread = createThread(task);
+ String name = createName(type, thread);
+
+ if(!thread.isAlive()) {
+ thread.setName(name);
+ }
+ return thread;
+ }
+
+ /**
+ * This will create a thread name that is unique. The thread name
+ * is a combination of the original thread name with a prefix
+ * of the type of the object that will be running within it.
+ *
+ * @param task this is the task to be run within the thread
+ * @param thread this is the thread containing the original name
+ *
+ * @return this will return the new name of the thread
+ */
+ private String createName(Runnable task, Thread thread) {
+ Class type = task.getClass();
+ String prefix = type.getSimpleName();
+ String name = thread.getName();
+
+ return String.format("%s: %s", prefix, name);
+ }
+
+ /**
+ * This will create a thread name that is unique. The thread name
+ * is a combination of the original thread name with a prefix
+ * of the type of the object that will be running within it.
+ *
+ * @param type this is the type of object to be executed
+ * @param thread this is the thread containing the original name
+ *
+ * @return this will return the new name of the thread
+ */
+ private String createName(Class type, Thread thread) {
+ String prefix = type.getSimpleName();
+ String name = thread.getName();
+
+ return String.format("%s: %s", prefix, name);
+ }
+
+ /**
+ * This is used to create the thread that will be used to execute
+ * the provided task. The created thread will be renamed after
+ * it has been created and before it has been started.
+ *
+ * @param task this is the task that is to be executed
+ *
+ * @return this returns a thread to execute the given task
+ */
+ private Thread createThread(Runnable task) {
+ return new Thread(task);
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/ExecutorQueue.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ExecutorQueue.java
new file mode 100644
index 00000000..99e8fb21
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/ExecutorQueue.java
@@ -0,0 +1,128 @@
+/*
+ * ExecutorQueue.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>ExecutorQueue</code> object is used to queue tasks in
+ * a thread pool. This creates a thread pool with no limit to the
+ * number of tasks that can be enqueued, which ensures that any
+ * system requesting a task to be executed will not block when
+ * handing it over, it also means the user must use caution.
+ *
+ * @author Niall Gallagher
+ *
+ * @see org.simpleframework.common.thread.ConcurrentExecutor
+ */
+class ExecutorQueue {
+
+ /**
+ * This is the task queue that contains tasks due to execute.
+ */
+ private final BlockingQueue<Runnable> queue;
+
+ /**
+ * This is the actual thread pool implementation used.
+ */
+ private final ThreadPoolExecutor executor;
+
+ /**
+ * This is used to create the pool worker threads.
+ */
+ private final ThreadFactory factory;
+
+ /**
+ * Constructor for the <code>ExecutorQueue</code> object. This is
+ * used to create a pool of threads that can be used to execute
+ * arbitrary <code>Runnable</code> tasks. If the threads are
+ * busy this will simply enqueue the tasks and return.
+ *
+ * @param type this is the type of runnable that this accepts
+ * @param rest this is the number of threads to use in the pool
+ * @param active this is the maximum size the pool can grow to
+ */
+ public ExecutorQueue(Class type, int rest, int active) {
+ this(type, rest, active, 120, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Constructor for the <code>ExecutorQueue</code> object. This is
+ * used to create a pool of threads that can be used to execute
+ * arbitrary <code>Runnable</code> tasks. If the threads are
+ * busy this will simply enqueue the tasks and return.
+ *
+ * @param type this is the type of runnable that this accepts
+ * @param rest this is the number of threads to use in the pool
+ * @param active this is the maximum size the pool can grow to
+ * @param duration the duration active threads remain idle for
+ * @param unit this is the time unit used for the duration
+ */
+ public ExecutorQueue(Class type, int rest, int active, long duration, TimeUnit unit) {
+ this.queue = new LinkedBlockingQueue<Runnable>();
+ this.factory = new DaemonFactory(type);
+ this.executor = new ThreadPoolExecutor(rest, active, duration, unit, queue, factory);
+ }
+
+ /**
+ * The <code>execute</code> method is used to queue the task for
+ * execution. If all threads are busy the provided task is queued
+ * and waits until all current and outstanding tasks are finished.
+ *
+ * @param task this is the task to be queued for execution
+ */
+ public void execute(Runnable task) {
+ executor.execute(task);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ */
+ public void stop() {
+ stop(60000);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ *
+ * @param wait the number of milliseconds to wait for it to stop
+ */
+ public void stop(long wait) {
+ if(!executor.isTerminated()) {
+ try {
+ executor.shutdown();
+ executor.awaitTermination(wait, MILLISECONDS);
+ } catch(Exception e) {
+ throw new IllegalStateException("Could not stop pool", e);
+ }
+ }
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/Scheduler.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/Scheduler.java
new file mode 100644
index 00000000..d24fa17a
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/Scheduler.java
@@ -0,0 +1,57 @@
+/*
+ * Scheduler.java October 2014
+ *
+ * Copyright (C) 2014, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>Scheduler</code> interface represents a means to execute
+ * a task immediately or after a specified delay. This queues the
+ * task for the requested period of time before it is executed, if a
+ * delay is specified. How the task is executed is dependent on the
+ * implementation, however it will normally use a thread pool.
+ *
+ * @author Niall Gallagher
+ */
+public interface Scheduler extends Executor {
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay the time in milliseconds to wait for execution
+ */
+ void execute(Runnable task, long delay);
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay this is the delay to wait before execution
+ * @param unit this is the duration time unit to wait for
+ */
+ void execute(Runnable task, long delay, TimeUnit unit);
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/SchedulerQueue.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/SchedulerQueue.java
new file mode 100644
index 00000000..67385ed3
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/SchedulerQueue.java
@@ -0,0 +1,127 @@
+/*
+ * SchedulerQueue.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The <code>SchedulerQueue</code> object is used to schedule tasks
+ * for execution. This queues the task for the requested period of
+ * time before it is executed. It ensures that the delay is adhered
+ * to such that tasks can be timed for execution in an accurate way.
+ *
+ * @author Niall Gallagher
+ */
+class SchedulerQueue {
+
+ /**
+ * This is the actual scheduler used to schedule the tasks.
+ */
+ private final ScheduledThreadPoolExecutor executor;
+
+ /**
+ * This is the factory used to create the worker threads.
+ */
+ private final ThreadFactory factory;
+
+ /**
+ * Constructor for the <code>SchedulerQueue</code> object. This
+ * will create a scheduler with a fixed number of threads to use
+ * before execution. Depending on the types of task that are
+ * to be executed this should be increased for accuracy.
+ *
+ * @param type this is the type of task to execute
+ * @param size this is the number of threads for the scheduler
+ */
+ public SchedulerQueue(Class type, int size) {
+ this.factory = new DaemonFactory(type);
+ this.executor = new ScheduledThreadPoolExecutor(size, factory);
+ }
+
+ /**
+ * The <code>execute</code> method is used to queue the task for
+ * execution. If all threads are busy the provided task is queued
+ * and waits until all current and outstanding tasks are finished.
+ *
+ * @param task this is the task to be queued for execution
+ */
+ public void execute(Runnable task) {
+ executor.execute(task);
+ }
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay the time in milliseconds to wait for execution
+ */
+ public void execute(Runnable task, long delay) {
+ execute(task, delay, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * This will execute the task within the executor after the time
+ * specified has expired. If the time specified is zero then it
+ * will be executed immediately. Once the scheduler has been
+ * stopped then this method will no longer accept runnable tasks.
+ *
+ * @param task this is the task to schedule for execution
+ * @param delay this is the delay to wait before execution
+ * @param unit this is the duration time unit to wait for
+ */
+ public void execute(Runnable task, long delay, TimeUnit unit) {
+ executor.schedule(task, delay, unit);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ */
+ public void stop() {
+ stop(60000);
+ }
+
+ /**
+ * This is used to stop the executor by interrupting all running
+ * tasks and shutting down the threads within the pool. This will
+ * return once it has been stopped, and no further tasks will be
+ * accepted by this pool for execution.
+ *
+ * @param wait the number of milliseconds to wait for it to stop
+ */
+ public void stop(long wait) {
+ if(!executor.isTerminated()) {
+ try {
+ executor.shutdown();
+ executor.awaitTermination(wait, MILLISECONDS);
+ } catch(Exception e) {
+ throw new IllegalStateException("Could not stop pool", e);
+ }
+ }
+ }
+}
diff --git a/simple/simple-common/src/main/java/org/simpleframework/common/thread/SynchronousExecutor.java b/simple/simple-common/src/main/java/org/simpleframework/common/thread/SynchronousExecutor.java
new file mode 100644
index 00000000..decd41d7
--- /dev/null
+++ b/simple/simple-common/src/main/java/org/simpleframework/common/thread/SynchronousExecutor.java
@@ -0,0 +1,43 @@
+/*
+ * SynchronousExecutor.java February 2007
+ *
+ * Copyright (C) 2007, Niall Gallagher <niallg@users.sf.net>
+ *
+ * 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.simpleframework.common.thread;
+
+import java.util.concurrent.Executor;
+
+/**
+ * The <code>SynchronousExecutor</code> object is used for synchronous
+ * execution of tasks. This simple acts as an adapter for running
+ * a <code>Runnable</code> implementation and can be used wherever
+ * the executor interface is required.
+ *
+ * @author Niall Gallagher
+ */
+public class SynchronousExecutor implements Executor {
+
+ /**
+ * This will execute the provided <code>Runnable</code> within
+ * the current thread. This implementation will simple invoke
+ * the run method of the task and wait for it to complete.
+ *
+ * @param task this is the task that is to be executed
+ */
+ public void execute(Runnable task) {
+ task.run();
+ }
+} \ No newline at end of file