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    }