001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of Granite Data Services.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or (at your
010 * option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 * for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020 package org.granite.client.messaging.channel;
021
022 import java.util.ArrayList;
023 import java.util.List;
024 import java.util.TimerTask;
025 import java.util.concurrent.ExecutionException;
026 import java.util.concurrent.TimeoutException;
027
028 import org.granite.client.messaging.ResponseListener;
029 import org.granite.client.messaging.ResponseListenerDispatcher;
030 import org.granite.client.messaging.events.CancelledEvent;
031 import org.granite.client.messaging.events.Event;
032 import org.granite.client.messaging.events.FailureEvent;
033 import org.granite.client.messaging.events.FaultEvent;
034 import org.granite.client.messaging.events.ResultEvent;
035 import org.granite.client.messaging.events.TimeoutEvent;
036 import org.granite.client.messaging.messages.RequestMessage;
037 import org.granite.client.messaging.messages.ResponseMessage;
038 import org.granite.client.messaging.messages.responses.FaultMessage;
039 import org.granite.client.messaging.messages.responses.ResultMessage;
040
041 /**
042 * @author Franck WOLFF
043 */
044 public class AsyncToken extends TimerTask implements ResponseMessageFuture {
045
046 private final RequestMessage request;
047 private final List<ResponseListener> listeners = new ArrayList<ResponseListener>();
048
049 private Event event = null;
050
051 private ResponseListener channelListener = null;
052
053 public AsyncToken(RequestMessage request) {
054 this(request, (ResponseListener[])null);
055 }
056
057 public AsyncToken(RequestMessage request, ResponseListener listener) {
058 this(request, (listener == null ? null : new ResponseListener[]{listener}));
059 }
060
061 public AsyncToken(RequestMessage request, ResponseListener[] listeners) {
062 if (request == null)
063 throw new NullPointerException("request cannot be null");
064 this.request = request;
065
066 if (listeners != null) {
067 for (ResponseListener listener : listeners) {
068 if (listener == null)
069 throw new NullPointerException("listeners cannot contain null values");
070 this.listeners.add(listener);
071 }
072 }
073 }
074
075 public String getId() {
076 return request.getId();
077 }
078
079 public RequestMessage getRequest() {
080 return request;
081 }
082
083 public synchronized Event setChannelListener(ResponseListener channelListener) {
084 if (event == null)
085 this.channelListener = channelListener;
086 return event;
087 }
088
089 @Override
090 public void run() {
091 // Try to dispatch a TimeoutEvent.
092 dispatchTimeout(System.currentTimeMillis());
093 }
094
095 @Override
096 public boolean cancel() {
097 // Try to dispatch a CancelledEvent.
098 return dispatchCancelled();
099 }
100
101 @Override
102 public ResponseMessage get() throws InterruptedException, ExecutionException, TimeoutException {
103 synchronized (this) {
104 if (event == null) {
105 try {
106 wait();
107 }
108 catch (InterruptedException e) {
109 if (dispatchCancelled())
110 throw e;
111 }
112 }
113 }
114
115 return ResponseListenerDispatcher.getResponseMessage(event);
116 }
117
118 @Override
119 public synchronized boolean isCancelled() {
120 return event instanceof CancelledEvent;
121 }
122
123 @Override
124 public synchronized boolean isDone() {
125 return event != null;
126 }
127
128 public boolean dispatchResult(ResultMessage result) {
129 return dispatch(new ResultEvent(request, result));
130 }
131
132 public boolean dispatchFault(FaultMessage fault) {
133 return dispatch(new FaultEvent(request, fault));
134 }
135
136 public boolean dispatchFailure(Exception e) {
137 return dispatch(new FailureEvent(request, e));
138 }
139
140 public boolean dispatchTimeout(long millis) {
141 return dispatch(new TimeoutEvent(request, millis));
142 }
143
144 public boolean dispatchCancelled() {
145 return dispatch(new CancelledEvent(request));
146 }
147
148 private boolean dispatch(Event event) {
149
150 // Cancel this TimerTask.
151 super.cancel();
152
153 synchronized (this) {
154
155 // Make sure we didn't dispatch a previous event.
156 if (this.event != null)
157 return false;
158
159 // Create the corresponding event.
160 this.event = event;
161
162 if (channelListener != null)
163 ResponseListenerDispatcher.dispatch(channelListener, event);
164
165 // Wake up all threads waiting on the get() method.
166 notifyAll();
167 }
168
169 // Call all listeners.
170 for (ResponseListener listener : listeners)
171 ResponseListenerDispatcher.dispatch(listener, event);
172
173 // Release references on listeners to help gc
174 channelListener = null;
175 listeners.clear();
176
177 return true;
178 }
179
180 @Override
181 public boolean equals(Object obj) {
182 if (obj == this)
183 return true;
184 return (obj instanceof AsyncToken) && request.getId().equals(((AsyncToken)obj).request.getId());
185 }
186
187 @Override
188 public int hashCode() {
189 return request.getId().hashCode();
190 }
191
192 @Override
193 public String toString() {
194 return getClass().getName() + " {request=" + request + "}";
195 }
196 }