001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.portal.base;
020
021import java.io.IOException;
022import java.io.OutputStream;
023import java.time.Instant;
024
025import org.jdrupes.httpcodec.protocols.http.HttpConstants.HttpStatus;
026import org.jdrupes.httpcodec.protocols.http.HttpField;
027import org.jdrupes.httpcodec.protocols.http.HttpResponse;
028import org.jdrupes.httpcodec.types.MediaType;
029import org.jgrapes.http.ResponseCreationSupport;
030import org.jgrapes.http.events.Response;
031import org.jgrapes.io.util.ByteBufferOutputStream;
032import org.jgrapes.portal.base.events.ResourceRequest;
033
034// TODO: Auto-generated Javadoc
035/**
036 * Returns a {@link Runnable} that writes to an {@link OutputStream} as result.
037 */
038public class ResourceByGenerator extends ResourceResult {
039
040    private final Generator generator;
041    private final MediaType mediaType;
042    private final Instant lastModifiedAt;
043    private final int maxAge;
044
045    /**
046     * The interface that must be implemented by the content provider.
047     */
048    public interface Generator {
049
050        /**
051         * Write the generated output to the given stream.
052         *
053         * @param stream the output stream
054         * @throws IOException Signals that an I/O exception has occurred.
055         */
056        void write(OutputStream stream) throws IOException;
057    }
058
059    /**
060     * Instantiates a result that is provided by an {@link OutputStream}.
061     *
062     * @param request the request
063     * @param generator the generator
064     * @param mediaType the media type, may be `null`
065     * @param lastModifiedAt the last modified at
066     * @param maxAge the max age
067     */
068    public ResourceByGenerator(ResourceRequest request,
069            Generator generator, MediaType mediaType,
070            Instant lastModifiedAt, int maxAge) {
071        super(request);
072        this.generator = generator;
073        this.mediaType = mediaType;
074        this.lastModifiedAt = lastModifiedAt;
075        this.maxAge = maxAge;
076    }
077
078    /*
079     * (non-Javadoc)
080     * 
081     * @see org.jgrapes.portal.base.base.ResourceResult#process()
082     */
083    @Override
084    public void process() throws IOException, InterruptedException {
085        if (generator == null) {
086            ResponseCreationSupport.sendResponse(request().httpRequest(),
087                request().httpChannel(), HttpStatus.NOT_FOUND);
088            return;
089        }
090
091        HttpResponse response = request().httpRequest().response().get();
092        if (lastModifiedAt != null) {
093            response.setField(HttpField.LAST_MODIFIED, lastModifiedAt);
094        }
095        if (mediaType != null) {
096            response.setContentType(mediaType);
097        } else {
098            // Usually set by setContenType
099            response.setHasPayload(true);
100        }
101        ResponseCreationSupport.setMaxAge(response, maxAge);
102        response.setStatus(HttpStatus.OK);
103        request().httpChannel().respond(new Response(response));
104        // Start sending content
105        try (@SuppressWarnings("resource")
106        OutputStream out = new ByteBufferOutputStream(
107            request().httpChannel()).suppressClose()) {
108            generator.write(out);
109        }
110    }
111
112}