com.yahoo.processing.Response Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.processing;
import com.yahoo.component.provider.ListenableFreezableClass;
import com.yahoo.concurrent.SystemTimer;
import com.yahoo.processing.execution.ResponseReceiver;
import com.yahoo.processing.impl.ProcessingFuture;
import com.yahoo.processing.request.CompoundName;
import com.yahoo.processing.request.ErrorMessage;
import com.yahoo.processing.response.ArrayDataList;
import com.yahoo.processing.response.Data;
import com.yahoo.processing.response.DataList;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A Response to a Request.
*
* A Response contains a list of Data items, which may (through Data implementations) contain payload data and/or
* further nested data lists.
*
* Frameworks built on top of processing may subclass this to create a stricter definition of a response.
* Processors producing Responses should not create subclasses but should instead
* create additional instances/subclasses of Data. Such Processors should always create Response instances by calling
* execution.process(request), which will return an empty Response if there are no further processors in the chain.
*
* Do not cache this as it may hold references to objects that should be garbage collected.
*
* @author bratseth
*/
public class Response extends ListenableFreezableClass {
private final static CompoundName freezeListenerKey = CompoundName.from("processing.freezeListener");
private final DataList> data;
/** Creates a request containing an empty array data list */
public Response(Request request) {
this(ArrayDataList.create(request));
}
/** Creates a response containing a list of data */
public Response(DataList> data) {
this.data = data;
Runnable freezeListener = null;
Request request = data.request();
if (request != null) // subclasses of DataList may not ensure this
freezeListener = (Runnable)request.properties().get(freezeListenerKey);
if (freezeListener != null) {
if (freezeListener instanceof ResponseReceiver)
((ResponseReceiver)freezeListener).setResponse(this);
data.addFreezeListener(freezeListener, Runnable::run);
}
}
/**
* Convenience constructor which adds the given error message to the given request
*/
public Response(Request request, ErrorMessage errorMessage) {
this(ArrayDataList.create(request));
request.errors().add(errorMessage);
}
/**
* Processors which merges another request into this must call this method to notify the response.
* This does not modify the data of either response.
*/
public void mergeWith(Response other) {
}
/**
* Returns the top level list of data items of this response
*/
public DataList data() {
return data;
}
// ------ static utilities ----------------------------------------------------------------------------
/**
* Returns a future in which the given data list and all lists nested within it are completed.
* The only use of the returned future is to call a get() method on it to complete the given dataList and
* all dataLists nested below it recursively.
*
* Lists are completed in prefix, depth-first order. DataLists added after the point when this method is called
* will not be completed.
*
* @param rootDataList the list to complete recursively
* @return the future in which all data in and below this list is complete, as the given root dataList for convenience
*/
public static CompletableFuture