diff options
Diffstat (limited to 'src/javax/jmdns/impl/DNSStatefulObject.java')
-rw-r--r-- | src/javax/jmdns/impl/DNSStatefulObject.java | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/src/javax/jmdns/impl/DNSStatefulObject.java b/src/javax/jmdns/impl/DNSStatefulObject.java new file mode 100644 index 0000000..aadd6f1 --- /dev/null +++ b/src/javax/jmdns/impl/DNSStatefulObject.java @@ -0,0 +1,559 @@ +// Licensed under Apache License version 2.0 +package javax.jmdns.impl; + +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.jmdns.impl.constants.DNSState; +import javax.jmdns.impl.tasks.DNSTask; + +/** + * Sets of methods to manage the state machine.<br/> + * <b>Implementation note:</b> This interface is accessed from multiple threads. The implementation must be thread safe. + * + * @author Pierre Frisch + */ +public interface DNSStatefulObject { + + /** + * This class define a semaphore. On this multiple threads can wait the arrival of one event. Thread wait for a maximum defined by the timeout. + * <p> + * Implementation note: this class is based on {@link java.util.concurrent.Semaphore} so that they can be released by the timeout timer. + * </p> + * + * @author Pierre Frisch + */ + public static final class DNSStatefulObjectSemaphore { + private static Logger logger = Logger.getLogger(DNSStatefulObjectSemaphore.class.getName()); + + private final String _name; + + private final ConcurrentMap<Thread, Semaphore> _semaphores; + + /** + * @param name + * Semaphore name for debugging purposes. + */ + public DNSStatefulObjectSemaphore(String name) { + super(); + _name = name; + _semaphores = new ConcurrentHashMap<Thread, Semaphore>(50); + } + + /** + * Blocks the current thread until the event arrives or the timeout expires. + * + * @param timeout + * wait period for the event + */ + public void waitForEvent(long timeout) { + Thread thread = Thread.currentThread(); + Semaphore semaphore = _semaphores.get(thread); + if (semaphore == null) { + semaphore = new Semaphore(1, true); + semaphore.drainPermits(); + _semaphores.putIfAbsent(thread, semaphore); + } + semaphore = _semaphores.get(thread); + try { + semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS); + } catch (InterruptedException exception) { + logger.log(Level.FINER, "Exception ", exception); + } + } + + /** + * Signals the semaphore when the event arrives. + */ + public void signalEvent() { + Collection<Semaphore> semaphores = _semaphores.values(); + for (Semaphore semaphore : semaphores) { + semaphore.release(); + semaphores.remove(semaphore); + } + } + + @Override + public String toString() { + StringBuilder aLog = new StringBuilder(1000); + aLog.append("Semaphore: "); + aLog.append(this._name); + if (_semaphores.size() == 0) { + aLog.append(" no semaphores."); + } else { + aLog.append(" semaphores:\n"); + for (Thread thread : _semaphores.keySet()) { + aLog.append("\tThread: "); + aLog.append(thread.getName()); + aLog.append(' '); + aLog.append(_semaphores.get(thread)); + aLog.append('\n'); + } + } + return aLog.toString(); + } + + } + + public static class DefaultImplementation extends ReentrantLock implements DNSStatefulObject { + private static Logger logger = Logger.getLogger(DefaultImplementation.class.getName()); + + private static final long serialVersionUID = -3264781576883412227L; + + private volatile JmDNSImpl _dns; + + protected volatile DNSTask _task; + + protected volatile DNSState _state; + + private final DNSStatefulObjectSemaphore _announcing; + + private final DNSStatefulObjectSemaphore _canceling; + + public DefaultImplementation() { + super(); + _dns = null; + _task = null; + _state = DNSState.PROBING_1; + _announcing = new DNSStatefulObjectSemaphore("Announce"); + _canceling = new DNSStatefulObjectSemaphore("Cancel"); + } + + /** + * {@inheritDoc} + */ + @Override + public JmDNSImpl getDns() { + return this._dns; + } + + protected void setDns(JmDNSImpl dns) { + this._dns = dns; + } + + /** + * {@inheritDoc} + */ + @Override + public void associateWithTask(DNSTask task, DNSState state) { + if (this._task == null && this._state == state) { + this.lock(); + try { + if (this._task == null && this._state == state) { + this.setTask(task); + } + } finally { + this.unlock(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public void removeAssociationWithTask(DNSTask task) { + if (this._task == task) { + this.lock(); + try { + if (this._task == task) { + this.setTask(null); + } + } finally { + this.unlock(); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAssociatedWithTask(DNSTask task, DNSState state) { + this.lock(); + try { + return this._task == task && this._state == state; + } finally { + this.unlock(); + } + } + + protected void setTask(DNSTask task) { + this._task = task; + } + + /** + * @param state + * the state to set + */ + protected void setState(DNSState state) { + this.lock(); + try { + this._state = state; + if (this.isAnnounced()) { + _announcing.signalEvent(); + } + if (this.isCanceled()) { + _canceling.signalEvent(); + // clear any waiting announcing + _announcing.signalEvent(); + } + } finally { + this.unlock(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean advanceState(DNSTask task) { + boolean result = true; + if (this._task == task) { + this.lock(); + try { + if (this._task == task) { + this.setState(this._state.advance()); + } else { + logger.warning("Trying to advance state whhen not the owner. owner: " + this._task + " perpetrator: " + task); + } + } finally { + this.unlock(); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean revertState() { + boolean result = true; + if (!this.willCancel()) { + this.lock(); + try { + if (!this.willCancel()) { + this.setState(this._state.revert()); + this.setTask(null); + } + } finally { + this.unlock(); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean cancelState() { + boolean result = false; + if (!this.willCancel()) { + this.lock(); + try { + if (!this.willCancel()) { + this.setState(DNSState.CANCELING_1); + this.setTask(null); + result = true; + } + } finally { + this.unlock(); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean closeState() { + boolean result = false; + if (!this.willClose()) { + this.lock(); + try { + if (!this.willClose()) { + this.setState(DNSState.CLOSING); + this.setTask(null); + result = true; + } + } finally { + this.unlock(); + } + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean recoverState() { + boolean result = false; + this.lock(); + try { + this.setState(DNSState.PROBING_1); + this.setTask(null); + } finally { + this.unlock(); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isProbing() { + return this._state.isProbing(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAnnouncing() { + return this._state.isAnnouncing(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAnnounced() { + return this._state.isAnnounced(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCanceling() { + return this._state.isCanceling(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isCanceled() { + return this._state.isCanceled(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isClosing() { + return this._state.isClosing(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isClosed() { + return this._state.isClosed(); + } + + private boolean willCancel() { + return this._state.isCanceled() || this._state.isCanceling(); + } + + private boolean willClose() { + return this._state.isClosed() || this._state.isClosing(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean waitForAnnounced(long timeout) { + if (!this.isAnnounced() && !this.willCancel()) { + _announcing.waitForEvent(timeout); + } + if (!this.isAnnounced()) { + if (this.willCancel() || this.willClose()) { + logger.fine("Wait for announced cancelled: " + this); + } else { + logger.warning("Wait for announced timed out: " + this); + } + } + return this.isAnnounced(); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean waitForCanceled(long timeout) { + if (!this.isCanceled()) { + _canceling.waitForEvent(timeout); + } + if (!this.isCanceled() && !this.willClose()) { + logger.warning("Wait for canceled timed out: " + this); + } + return this.isCanceled(); + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return (_dns != null ? "DNS: " + _dns.getName() : "NO DNS") + " state: " + _state + " task: " + _task; + } + + } + + /** + * Returns the DNS associated with this object. + * + * @return DNS resolver + */ + public JmDNSImpl getDns(); + + /** + * Sets the task associated with this Object. + * + * @param task + * associated task + * @param state + * state of the task + */ + public void associateWithTask(DNSTask task, DNSState state); + + /** + * Remove the association of the task with this Object. + * + * @param task + * associated task + */ + public void removeAssociationWithTask(DNSTask task); + + /** + * Checks if this object is associated with the task and in the same state. + * + * @param task + * associated task + * @param state + * state of the task + * @return <code>true</code> is the task is associated with this object, <code>false</code> otherwise. + */ + public boolean isAssociatedWithTask(DNSTask task, DNSState state); + + /** + * Sets the state and notifies all objects that wait on the ServiceInfo. + * + * @param task + * associated task + * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise. + * @see DNSState#advance() + */ + public boolean advanceState(DNSTask task); + + /** + * Sets the state and notifies all objects that wait on the ServiceInfo. + * + * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise. + * @see DNSState#revert() + */ + public boolean revertState(); + + /** + * Sets the state and notifies all objects that wait on the ServiceInfo. + * + * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise. + */ + public boolean cancelState(); + + /** + * Sets the state and notifies all objects that wait on the ServiceInfo. + * + * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise. + */ + public boolean closeState(); + + /** + * Sets the state and notifies all objects that wait on the ServiceInfo. + * + * @return <code>true</code if the state was changed by this thread, <code>false</code> otherwise. + */ + public boolean recoverState(); + + /** + * Returns true, if this is a probing state. + * + * @return <code>true</code> if probing state, <code>false</code> otherwise + */ + public boolean isProbing(); + + /** + * Returns true, if this is an announcing state. + * + * @return <code>true</code> if announcing state, <code>false</code> otherwise + */ + public boolean isAnnouncing(); + + /** + * Returns true, if this is an announced state. + * + * @return <code>true</code> if announced state, <code>false</code> otherwise + */ + public boolean isAnnounced(); + + /** + * Returns true, if this is a canceling state. + * + * @return <code>true</code> if canceling state, <code>false</code> otherwise + */ + public boolean isCanceling(); + + /** + * Returns true, if this is a canceled state. + * + * @return <code>true</code> if canceled state, <code>false</code> otherwise + */ + public boolean isCanceled(); + + /** + * Returns true, if this is a closing state. + * + * @return <code>true</code> if closing state, <code>false</code> otherwise + */ + public boolean isClosing(); + + /** + * Returns true, if this is a closed state. + * + * @return <code>true</code> if closed state, <code>false</code> otherwise + */ + public boolean isClosed(); + + /** + * Waits for the object to be announced. + * + * @param timeout + * the maximum time to wait in milliseconds. + * @return <code>true</code> if the object is announced, <code>false</code> otherwise + */ + public boolean waitForAnnounced(long timeout); + + /** + * Waits for the object to be canceled. + * + * @param timeout + * the maximum time to wait in milliseconds. + * @return <code>true</code> if the object is canceled, <code>false</code> otherwise + */ + public boolean waitForCanceled(long timeout); + +} |