/* * Copyright (c) 2009, 2012 IBM Corp. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Dave Locke - initial API and implementation and/or initial documentation */ package org.eclipse.paho.client.mqttv3.internal; import org.eclipse.paho.client.mqttv3.MqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.internal.trace.Trace; import org.eclipse.paho.client.mqttv3.internal.wire.MqttAck; import org.eclipse.paho.client.mqttv3.internal.wire.MqttPublish; import org.eclipse.paho.client.mqttv3.internal.wire.MqttWireMessage; public class MqttDeliveryTokenImpl implements MqttDeliveryToken { private Object responseLock = new Object(); private Object sentLock = new Object(); private MqttMessage message; private MqttWireMessage response = null; private MqttException exception = null; private boolean sent = false; private boolean completed = false; private int msgId = 0; private Trace trace; MqttDeliveryTokenImpl(Trace trace) { this.message = null; this.trace = trace; } /** * Constructs a new delivery token. * * @param wait whether or not a wait is planned on this token. This is needed * to eliminate a timing window where a response is received before the call * to {@link #waitForCompletion()} is made. */ MqttDeliveryTokenImpl(Trace trace, MqttPublish send) { this.trace = trace; this.message = send.getMessage(); this.msgId = send.getMessageId(); } public void waitForCompletion(long timeout) throws MqttException { MqttWireMessage response = (MqttWireMessage)waitForResponse(timeout); if (response == null && !completed) { if (trace.isOn()) { //@TRACE 406=waitForCompletion timed out timeout={0} trace.trace(Trace.FINE,406,new Object[]{new Long(timeout)}); } throw new MqttException(MqttException.REASON_CODE_CLIENT_TIMEOUT); } } public void waitForCompletion() throws MqttException { waitForCompletion(-1); } /** * Waits for the message delivery to complete, but doesn't throw an exception * in the case of a NACK. It does still throw an exception if something else * goes wrong (e.g. an IOException). This is used for packets like CONNECT, * which have useful information in the ACK that needs to be accessed. */ protected MqttWireMessage waitForResponse() throws MqttException { return waitForResponse(-1); } protected MqttWireMessage waitForResponse(long timeout) throws MqttException { synchronized (responseLock) { if (trace.isOn()) { //@TRACE 400=waitForResponse token={0} timeout={1} sent={2} completed={3} hasException={4} response={5} trace.trace(Trace.FINE,400,new Object[]{this, new Long(timeout),new Boolean(sent),new Boolean(completed),(exception==null)?"false":"true",response},exception); } if (this.completed) { return this.response; } if (this.exception == null) { try { if (timeout == -1) { responseLock.wait(); } else { responseLock.wait(timeout); } } catch (InterruptedException e) { } } if (!this.completed) { if (this.exception != null) { MqttException e = this.exception; this.exception = null; //@TRACE 401=waitForResponse exception trace.trace(Trace.FINE,401,null,exception); throw e; } } } //@TRACE 402=waitForResponse response={0} trace.trace(Trace.FINE,402,new Object[]{this.response}); return this.response; } protected void waitUntilSent() throws MqttException { synchronized (sentLock) { synchronized (responseLock) { if (this.exception != null) { throw this.exception; } } if (!sent) { try { sentLock.wait(); } catch (InterruptedException e) { } } if (!sent) { if (this.exception == null) { throw ExceptionHelper.createMqttException(MqttException.REASON_CODE_UNEXPECTED_ERROR); } else { throw this.exception; } } } } /** * Notifies this token that the associated message has been sent * (i.e. written to the TCP/IP socket). */ protected void notifySent() { //@TRACE 403=notifySent token={0} trace.trace(Trace.FINE,403,new Object[]{this}); synchronized (responseLock) { this.response = null; this.completed = false; } synchronized (sentLock) { sent = true; sentLock.notifyAll(); } } /** * Notifies this token that a response message (an ACK or NACK) has been * received. */ protected void notifyReceived(MqttWireMessage msg) { //@TRACE 404=notifyReceived token={0} response={1} trace.trace(Trace.FINE,404,new Object[]{this,msg}); synchronized (responseLock) { // ACK means that everything was OK, so mark the message for garbage collection. if (msg instanceof MqttAck) { this.message = null; } this.response = msg; this.completed = true; responseLock.notifyAll(); } } /** * Notifies this token that an exception has occurred. This is only * used for things like IOException, and not for MQTT NACKs. */ protected void notifyException(MqttException exception) { //@TRACE 405=notifyException token={0} trace.trace(Trace.FINE,405,new Object[]{this},exception); synchronized (responseLock) { this.exception = exception; responseLock.notifyAll(); } synchronized (sentLock) { sentLock.notifyAll(); } } public MqttMessage getMessage() throws MqttException { return message; } public boolean isComplete() { return completed; } public int getMessageId() { return this.msgId; } }