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    }