aboutsummaryrefslogtreecommitdiffstats
path: root/documentapi/src/main/java/com/yahoo/documentapi/VisitorControlHandler.java
blob: 7435e48d6a3d6b9593362aa1380dd16e9d857221 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.documentapi;

import com.yahoo.vdslib.VisitorStatistics;

import java.time.Duration;

/**
 * A class for controlling a visitor supplied through visitor parameters when
 * creating the visitor session. The class defines callbacks for reporting
 * progress and that the visitor is done. If you want to reimplement the
 * default behavior of those callbacks, you can write your own subclass.
 *
 * @author Håkon Humberset
 */
public class VisitorControlHandler {
    /** Possible completion codes for visiting. */
    public enum CompletionCode {
        /** Visited all specified data successfully. */
        SUCCESS,
        /** Aborted by user. */
        ABORTED,
        /** Fatal failure. */
        FAILURE,
        /** Create visitor reply did not return within the specified timeframe, or the session timed out. */
        TIMEOUT
    };

    /**
     * The result of the visitor, containing a completion code and an optional
     * error message.
     */
    public class Result {
        public CompletionCode code;
        public String message;

        public String toString() {
            switch(code) {
            case SUCCESS:
                return "OK: " + message;
            case ABORTED:
                return "ABORTED: " + message;
            case FAILURE:
                return "FAILURE: " + message;
            case TIMEOUT:
                return "TIMEOUT: " + message;
            }

            return "Unknown error";
        }

        public CompletionCode getCode() {
            return code;
        }

        public String getMessage() {
            return message;
        }
    };

    private VisitorControlSession session;
    private ProgressToken currentProgress;
    private boolean completed = false;
    private Result result;
    private VisitorStatistics currentStatistics;

    /**
     * Called before the visitor starts. Override this method if you need
     * to reset local data. Remember to call the superclass' method as well.
     */
    public void reset() {
        synchronized (this) {
            session = null;
            currentProgress = null;
            completed = false;
            result = null;
        }
    }

    /**
     * Callback called when progress has changed.
     *
     * @param token the most recent progress token for this visitor
     */
    public void onProgress(ProgressToken token) {
        currentProgress = token;
    }

    /**
     * Callback for visitor error messages.
     *
     * @param message the error message
     */
    public void onVisitorError(String message) {
    }

    /**
     * Callback for visitor statistics updates.
     *
     * @param vs The current statistics for this visitor.
     */
    public void onVisitorStatistics(VisitorStatistics vs) {
        currentStatistics = vs;
    }

    /**
     * Returns true iff the statistics reported by the visiting session indicates at least one
     * bucket has been completely visited.
     *
     * Not thread safe, so should only be called on a quiescent session after waitUntilDone
     * has completed successfully.
     */
    public boolean hasVisitedAnyBuckets() {
        return ((currentStatistics != null) && (currentStatistics.getBucketsVisited() > 0));
    }

    /**
     * Callback called when the visitor is done.
     *
     * @param code the completion code
     * @param message an optional error message
     */
    public void onDone(CompletionCode code, String message) {
        synchronized (this) {
            completed = true;
            result = new Result();
            result.code = code;
            result.message = message;
            notifyAll();
        }
    }

    /** @param session the visitor session used for this visitor */
    public void setSession(VisitorControlSession session) {
        this.session = session;
    }

    /** @return Retrieves the last progress token gotten for this visitor. If visitor has not been started, returns null.*/
    public ProgressToken getProgress() { return currentProgress; }

    public VisitorStatistics getVisitorStatistics() { return currentStatistics; }

    /** @return True if the visiting is done (either by error or success). */
    public boolean isDone() {
        synchronized (this) {
            return completed;
        }
    }

    /**
     * Waits until visiting is done, or the given timeout (in ms) expires.
     * Will wait forever if timeout is 0.
     *
     * @param timeout Maximum time duration to wait before returning.
     * @return True if visiting is done (either by error or success), false if session has timed out.
     * @throws InterruptedException If an interrupt signal was received while waiting.
     */
    public boolean waitUntilDone(Duration timeout) throws InterruptedException {
        synchronized (this) {
            if (completed) {
                return true;
            }
            if (timeout.isZero()) {
                while (!completed) {
                    wait();
                }
            } else {
                wait(timeout.toMillis());
            }
            return completed;
        }
    }

    /**
     * Waits until visiting is done, or the given timeout (in ms) expires.
     * Will wait forever if timeout is 0.
     *
     * @param timeoutMs The maximum amount of milliseconds to wait.
     * @return True if visiting is done (either by error or success), false if session has timed out.
     * @throws InterruptedException If an interrupt signal was received while waiting.
     *
     * TODO deprecate this in favor of waitUntilDone(Duration)
     */
    public boolean waitUntilDone(long timeoutMs) throws InterruptedException {
        return waitUntilDone(Duration.ofMillis(timeoutMs));
    }

    /**
     * Waits until visiting is done. Session timeout implicitly completes
     * the visitor session, but will set an unsuccessful result code.
     *
     * @throws InterruptedException If an interrupt signal was received while waiting.
     */
    public void waitUntilDone() throws InterruptedException {
        final boolean done = waitUntilDone(Duration.ZERO);
        assert done : "Infinite waitUntilDone timeout should always complete";
    }

    /**
     * Abort this visitor
     */
    public void abort() { session.abort(); }

    /**
       @return The result of the visiting, if done. If not done, returns null.
    */
    public Result getResult() { return result; };
}