aboutsummaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/searchchain/FutureResult.java
blob: 64bbcb4780c034b89d2583ae6945033d7d38d3f5 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.searchchain;

import com.yahoo.search.Query;
import com.yahoo.search.Result;
import com.yahoo.search.result.ErrorMessage;
import com.yahoo.yolean.Exceptions;

import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Extends a {@code FutureTask<Result>}, with some added error handling
 * 
 * @author bratseth
 */
public class FutureResult extends FutureTask<Result> {

    private final Query query;

    private final Execution execution;

    private final static Logger log = Logger.getLogger(FutureResult.class.getName());

    public FutureResult(Callable<Result> callable, Execution execution, Query query) {
        super(callable);
        this.query = query;
        this.execution = execution;
    }

    /** 
     * Returns a Result containing the hits returned from this source, or an error otherwise.
     * This will block for however long it takes to get the result: Using this is a bad idea.
     */
    @Override
    public Result get() {
        try {
            return super.get();
        }
        catch (InterruptedException e) {
            return new Result(getQuery(), createInterruptedError(e));
        }
        catch (ExecutionException e) {
            return new Result(getQuery(), createExecutionError(e));
        }
    }

    /** 
     * Returns a Result containing the hits returned from this source, or an error otherwise.
     * This blocks for at most the given timeout and returns a Result containing a timeout error
     * if the result is not available within this time.
     */
    @Override
    public Result get(long timeout, TimeUnit timeunit) {
        return getIfAvailable(timeout, timeunit)
                .orElseGet(() -> new Result(getQuery(), createTimeoutError()));
    }

    /**
     * Same as get(timeout, timeunit) but returns Optional.empty instead of a result with error if the result is 
     * not available in time
     */
    public Optional<Result> getIfAvailable(long timeout, TimeUnit timeunit) {
        try {
            return Optional.of(super.get(timeout, timeunit));
        }
        catch (InterruptedException e) {
            return Optional.of(new Result(getQuery(), createInterruptedError(e)));
        }
        catch (ExecutionException e) {
            // allow searchers to explicitly signal timeout rather than actually time out (useful for testing)
            if (e.getCause() instanceof com.yahoo.search.federation.TimeoutException)
                return Optional.empty();
            return Optional.of(new Result(getQuery(), createExecutionError(e)));
        }
        catch (TimeoutException e) {
            return Optional.empty();
        }
    }

    /** Returns the query used in this execution, never null */
    public Query getQuery() {
        return query;
    }

    /** Returns the execution which creates this */
    public Execution getExecution() { return execution; }

    private ErrorMessage createInterruptedError(Exception e) {
        return ErrorMessage.createUnspecifiedError(execution + " was interrupted while executing: " +
                                                   Exceptions.toMessageString(e));
    }
    
    private ErrorMessage createExecutionError(ExecutionException e) {
        log.log(Level.WARNING,"Exception in " + execution + " of " + query, e.getCause());
        return ErrorMessage.createErrorInPluginSearcher("Error in '" + execution + "': " +
                                                        Exceptions.toMessageString(e.getCause()), e.getCause());
    }

    public ErrorMessage createTimeoutError() {
        return ErrorMessage.createTimeout("Error in " + execution + ": Chain timed out.");
    }

}