001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2012 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
021 package org.granite.client.messaging.channel;
022
023 import java.util.ArrayList;
024 import java.util.List;
025 import java.util.TimerTask;
026 import java.util.concurrent.ExecutionException;
027 import java.util.concurrent.TimeoutException;
028
029 import org.granite.client.messaging.ResponseListener;
030 import org.granite.client.messaging.ResponseListenerDispatcher;
031 import org.granite.client.messaging.events.CancelledEvent;
032 import org.granite.client.messaging.events.Event;
033 import org.granite.client.messaging.events.FailureEvent;
034 import org.granite.client.messaging.events.FaultEvent;
035 import org.granite.client.messaging.events.ResultEvent;
036 import org.granite.client.messaging.events.TimeoutEvent;
037 import org.granite.client.messaging.messages.RequestMessage;
038 import org.granite.client.messaging.messages.ResponseMessage;
039 import org.granite.client.messaging.messages.responses.FaultMessage;
040 import org.granite.client.messaging.messages.responses.ResultMessage;
041
042 /**
043 * @author Franck WOLFF
044 */
045 public class AsyncToken extends TimerTask implements ResponseMessageFuture {
046
047 private final RequestMessage request;
048 private final List<ResponseListener> listeners = new ArrayList<ResponseListener>();
049
050 private Event event = null;
051
052 private ResponseListener channelListener = null;
053
054 public AsyncToken(RequestMessage request) {
055 this(request, (ResponseListener[])null);
056 }
057
058 public AsyncToken(RequestMessage request, ResponseListener listener) {
059 this(request, (listener == null ? null : new ResponseListener[]{listener}));
060 }
061
062 public AsyncToken(RequestMessage request, ResponseListener[] listeners) {
063 if (request == null)
064 throw new NullPointerException("request cannot be null");
065 this.request = request;
066
067 if (listeners != null) {
068 for (ResponseListener listener : listeners) {
069 if (listener == null)
070 throw new NullPointerException("listeners cannot contain null values");
071 this.listeners.add(listener);
072 }
073 }
074 }
075
076 public String getId() {
077 return request.getId();
078 }
079
080 public RequestMessage getRequest() {
081 return request;
082 }
083
084 public synchronized Event setChannelListener(ResponseListener channelListener) {
085 if (event == null)
086 this.channelListener = channelListener;
087 return event;
088 }
089
090 @Override
091 public void run() {
092 // Try to dispatch a TimeoutEvent.
093 dispatchTimeout(System.currentTimeMillis());
094 }
095
096 @Override
097 public boolean cancel() {
098 // Try to dispatch a CancelledEvent.
099 return dispatchCancelled();
100 }
101
102 @Override
103 public ResponseMessage get() throws InterruptedException, ExecutionException, TimeoutException {
104 synchronized (this) {
105 if (event == null) {
106 try {
107 wait();
108 }
109 catch (InterruptedException e) {
110 if (dispatchCancelled())
111 throw e;
112 }
113 }
114 }
115
116 return ResponseListenerDispatcher.getResponseMessage(event);
117 }
118
119 @Override
120 public synchronized boolean isCancelled() {
121 return event instanceof CancelledEvent;
122 }
123
124 @Override
125 public synchronized boolean isDone() {
126 return event != null;
127 }
128
129 public boolean dispatchResult(ResultMessage result) {
130 return dispatch(new ResultEvent(request, result));
131 }
132
133 public boolean dispatchFault(FaultMessage fault) {
134 return dispatch(new FaultEvent(request, fault));
135 }
136
137 public boolean dispatchFailure(Exception e) {
138 return dispatch(new FailureEvent(request, e));
139 }
140
141 public boolean dispatchTimeout(long millis) {
142 return dispatch(new TimeoutEvent(request, millis));
143 }
144
145 public boolean dispatchCancelled() {
146 return dispatch(new CancelledEvent(request));
147 }
148
149 private boolean dispatch(Event event) {
150
151 // Cancel this TimerTask.
152 super.cancel();
153
154 synchronized (this) {
155
156 // Make sure we didn't dispatch a previous event.
157 if (this.event != null)
158 return false;
159
160 // Create the corresponding event.
161 this.event = event;
162
163 if (channelListener != null)
164 ResponseListenerDispatcher.dispatch(channelListener, event);
165
166 // Wake up all threads waiting on the get() method.
167 notifyAll();
168 }
169
170 // Call all listeners.
171 for (ResponseListener listener : listeners)
172 ResponseListenerDispatcher.dispatch(listener, event);
173
174 // Release references on listeners to help gc
175 channelListener = null;
176 listeners.clear();
177
178 return true;
179 }
180
181 @Override
182 public boolean equals(Object obj) {
183 if (obj == this)
184 return true;
185 return (obj instanceof AsyncToken) && request.getId().equals(((AsyncToken)obj).request.getId());
186 }
187
188 @Override
189 public int hashCode() {
190 return request.getId().hashCode();
191 }
192
193 @Override
194 public String toString() {
195 return getClass().getName() + " {request=" + request + "}";
196 }
197 }