All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.yahoo.processing.handler.ProcessingResponse Maven / Gradle / Ivy

There is a newer version: 8.458.13
Show newest version
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.processing.handler;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

import com.google.common.collect.ImmutableList;
import com.yahoo.container.jdisc.AsyncHttpResponse;
import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.container.jdisc.VespaHeaders;
import com.yahoo.container.logging.AccessLogEntry;
import com.yahoo.jdisc.handler.CompletionHandler;
import com.yahoo.jdisc.handler.ContentChannel;
import com.yahoo.processing.Request;
import com.yahoo.processing.Response;
import com.yahoo.processing.execution.Execution;
import com.yahoo.processing.execution.Execution.Trace.LogValue;
import com.yahoo.processing.rendering.AsynchronousRenderer;
import com.yahoo.processing.rendering.Renderer;
import com.yahoo.processing.request.ErrorMessage;
import com.yahoo.processing.response.Data;
import com.yahoo.processing.response.DataList;

/**
 * A response from running a request through processing. This response is just a
 * wrapper of the knowhow needed to render the Response from processing.
 *
 * @author bratseth
 * @author Steinar Knutsen
 * @since 5.1.12
 */
public class ProcessingResponse extends AsyncHttpResponse {

    private final com.yahoo.processing.Request processingRequest;
    private final com.yahoo.processing.Response processingResponse;
    private final Execution execution;
    private final Renderer renderer;

    /** True if the return status has been set explicitly and should not be further changed */
    private boolean explicitStatusSet = false;

    @SuppressWarnings("unchecked")
    public ProcessingResponse(int status, com.yahoo.processing.Request processingRequest,
                              com.yahoo.processing.Response processingResponse,
                              Renderer renderer,
                              Executor renderingExecutor, Execution execution) {
        super(status);
        this.processingRequest = processingRequest;
        this.processingResponse = processingResponse;
        this.execution = execution;
        this.renderer = renderer;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void render(OutputStream stream, ContentChannel channel, 
                       CompletionHandler completionHandler) throws IOException {
        if (renderer instanceof AsynchronousRenderer) {
            AsynchronousRenderer asyncRenderer = (AsynchronousRenderer)renderer;
            asyncRenderer.setNetworkWiring(channel, completionHandler);
        }
        renderer.render(stream, processingResponse, execution, processingRequest);
        // the stream is closed in AsynchronousSectionedRenderer, after all data
        // has arrived
    }

    @Override
    public String getContentType() {
        return renderer.getMimeType();
    }

    @Override
    public String getCharacterEncoding() {
        return renderer.getEncoding();
    }

    @Override
    public void complete() {
        // Add headers
        addHeadersAndStatusFrom(processingResponse.data());

        if ( ! explicitStatusSet) {
            // Set status from errors TODO: This could be decomplicated a bit
            List errors = flattenErrors(processingResponse);
            boolean isSuccess = !(processingResponse.data().asList().isEmpty() && !errors.isEmpty()); // NOT success if ( no data AND are errors )
            setStatus(getHttpResponseStatus(isSuccess, processingRequest, errors.size() == 0 ? null : errors.get(0), errors));
        }
    }

    /**
     * This sets header and status from special Data items used for the purpose.
     * Do both at once to avoid traversing the data tree twice.
     */
    @SuppressWarnings("unchecked")
    private void addHeadersAndStatusFrom(DataList dataList) {
        for (Data data : dataList.asList()) {
            if (data instanceof ResponseHeaders) {
                headers().addAll(((ResponseHeaders) data).headers());
            }
            else if ( ! explicitStatusSet && (data instanceof ResponseStatus)) {
                setStatus(((ResponseStatus)data).code());
                explicitStatusSet = true;
            }
            else if (data instanceof DataList) {
                addHeadersAndStatusFrom((DataList) data);
            }
        }
    }

    private List flattenErrors(Response processingResponse) {
        Set errors = flattenErrors(null, processingResponse.data());
        if (errors == null) return Collections.emptyList();
        return ImmutableList.copyOf(errors);
    }

    @SuppressWarnings("unchecked")
    private Set flattenErrors(Set errors, Data data) {
        if (data.request() == null) return Collections.EMPTY_SET; // Not allowed, but handle anyway
        errors = addTo(errors, data.request().errors());

        if (data instanceof DataList) {
            for (Data item : ((DataList) data).asList())
                errors = flattenErrors(errors, item);
        }

        return errors;
    }

    private Set addTo(Set allErrors, List errors) {
        if (errors.isEmpty()) return allErrors;

        if (allErrors == null)
            allErrors = new LinkedHashSet<>();
        allErrors.addAll(errors);
        return allErrors;
    }

    private int getHttpResponseStatus(boolean isSuccess, Request request,
                                      ErrorMessage mainError, List errors) {
        if (isBenchmarking(request)) return VespaHeaders.getEagerErrorStatus(mainError,errors.iterator());
        return VespaHeaders.getStatus(isSuccess, mainError, errors.iterator());
    }

    private boolean isBenchmarking(Request request) {
        com.yahoo.container.jdisc.HttpRequest httpRequest = (com.yahoo.container.jdisc.HttpRequest)request.properties().get(Request.JDISC_REQUEST);
        if (httpRequest == null) return false;
        return VespaHeaders.benchmarkOutput(httpRequest);
    }

    @Override
    public Iterable getLogValues() {
        return execution.trace()::logValueIterator;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy