001/* Copyright 2011-2012 the original author or authors:
002 *
003 *    Marc Palmer (marc@grailsrocks.com)
004 *    Stéphane Maldini (smaldini@vmware.com)
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *      http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.grails.plugin.platform.events;
019
020import grails.events.EventException;
021import groovy.lang.Closure;
022
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.Collection;
026import java.util.List;
027import java.util.concurrent.ExecutionException;
028import java.util.concurrent.Future;
029import java.util.concurrent.TimeUnit;
030import java.util.concurrent.TimeoutException;
031
032/**
033 * @author Stephane Maldini <smaldini@vmware.com>
034 * @version 1.0
035 * @file
036 * @date 30/12/11
037 * @section DESCRIPTION
038 * <p/>
039 * [Does stuff]
040 */
041public class EventReply implements Serializable, Future<Object> {
042
043    private Future<?> futureReply;
044    private List<Object> values;
045    private Object value;
046    private int receivers;
047    private boolean futureReplyLoaded = false;
048    private Closure onError = null;
049
050    public void setOnError(Closure onError) {
051        this.onError = onError;
052    }
053
054    public Closure getOnError() {
055        return onError;
056    }
057
058    public EventReply(Object val, int receivers) {
059        this.receivers = receivers;
060        initValues(val);
061    }
062
063    @SuppressWarnings("unchecked")
064    protected void initValues(Object val) {
065        this.values = new ArrayList<Object>();
066
067        if (receivers > 1 && val instanceof Collection) {
068            this.values.addAll((Collection) val);
069            this.value = values.get(0);
070        } else if(receivers != 0 || val != null) {
071            this.value = val;
072            this.values.add(this.value);
073        }
074        this.futureReplyLoaded = true;
075    }
076
077    public EventReply(Future<?> future, int receivers) {
078        this.receivers = receivers;
079        this.futureReply = future;
080    }
081
082    protected void addValue(Object v) {
083        values.add(v);
084    }
085
086    public List<Object> getValues() throws Throwable {
087        if (!futureReplyLoaded) {
088            get();
089        }
090        throwError();
091        return values;
092    }
093
094    public Object getValue() throws Throwable{
095        if (!futureReplyLoaded) {
096            get();
097        }
098        throwError();
099        return value;
100    }
101
102    public boolean cancel(){
103        return cancel(true);
104    }
105
106    public boolean cancel(boolean b) {
107        return futureReply == null || futureReply.cancel(b);
108    }
109
110    public boolean isCancelled() {
111        return futureReply != null && futureReply.isCancelled();
112    }
113
114    public boolean isDone() {
115        return futureReply == null || futureReply.isDone();
116    }
117
118    public boolean isSuccess() {
119        return isDone() && !hasErrors();
120    }
121
122    public List<Throwable> getErrors() {
123        List<Throwable> ex = new ArrayList<Throwable>();
124        if (values != null) {
125            for (Object v : values) {
126                if (v != null && Throwable.class.isAssignableFrom(v.getClass())) {
127                    ex.add((Throwable) v);
128                }
129            }
130        }
131
132        return ex;
133    }
134
135    public boolean hasErrors() {
136        for (Object v : values) {
137            if (v != null && Throwable.class.isAssignableFrom(v.getClass())) {
138                return true;
139            }
140        }
141        return false;
142    }
143
144    public void throwError() throws Throwable{
145        if(hasErrors()){
146            if(onError != null){
147                onError.call(this);
148            }else{
149                throw new EventException(getErrors().get(0));
150            }
151        }
152    }
153
154    public int size() throws Throwable {
155        get();
156        throwError();
157        return receivers;
158    }
159
160    protected void setReceivers(int receivers) {
161        this.receivers = receivers;
162    }
163
164    public EventReply waitFor() throws Throwable {
165        get();
166        throwError();
167        return this;
168    }
169
170    public EventReply waitFor(long l) throws Throwable {
171        get(l, TimeUnit.MILLISECONDS);
172        throwError();
173        return this;
174    }
175
176    public Object get() throws InterruptedException, ExecutionException {
177        Object val = futureReply == null ? value : futureReply.get();
178        if (!futureReplyLoaded) {
179            initValues(val);
180        }
181        return val;
182    }
183
184    public Object get(long l, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
185        Object val = futureReply == null ? value : futureReply.get(l, timeUnit);
186        if (!futureReplyLoaded) {
187            initValues(val);
188        }
189        return val;
190    }
191
192}