001/*
002 * Ad Hoc Polling Application
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.events;
020
021import java.net.URI;
022import java.net.URL;
023import java.time.Instant;
024import java.util.Optional;
025
026import org.jdrupes.httpcodec.protocols.http.HttpRequest;
027import org.jgrapes.core.Event;
028import org.jgrapes.http.Session;
029import org.jgrapes.http.events.Request.In.Get;
030import org.jgrapes.io.IOSubchannel;
031import org.jgrapes.portal.base.RenderSupport;
032import org.jgrapes.portal.base.ResourceByUrl;
033import org.jgrapes.portal.base.ResourceNotModified;
034import org.jgrapes.portal.base.ResourceProvided;
035import org.jgrapes.portal.base.ResourceResult;
036
037/**
038 * An event that signals the request for a resource by the browser.
039 * 
040 * This event is effectively a "transformed" {@link Get}. It 
041 * simplifies handling of such an event by portal components, because
042 * they can simply set a result of type {@link ResourceResult} and
043 * thus need no knowledge about generating all the events required to 
044 * properly respond to a {@link Get}.
045 * 
046 * The complete sequence of events is shown in the diagram.
047 * 
048 * ![Resource Response Sequence](ResourceResponseSeq.svg)
049 * 
050 * Of course, due to internal buffering, the "Response Header" data
051 * and the "Response body" data may collapse in a single message
052 * that is sent to the browser (in case of a small resource).
053 * 
054 * If a value is provided by {@link #ifModifiedSince()},
055 * and the resource has not changed since the given instant,
056 * a resource provider may set {@link ResourceNotModified} as
057 * result. This information will be forwarded to the browser.
058 * For a result of type {@link ResourceByUrl}, the check
059 * for modification will be made automatically, using information
060 * derived from the {@link URL}.
061 * 
062 * Handlers of {@link ResourceRequest} events use usually only
063 * the information provided by {@link #resourceUri()}. The other
064 * items are needed by the handler of the {@link ResourceRequestCompleted}
065 * event (the portal) to generate the response for the {@link Get}.
066 * 
067 * If none of the provided {@link ResourceResult} type matches the
068 * requirements of the resource provider, it can set
069 * {@link ResourceProvided} as result. This signals that it genertes
070 * the response itself.
071 * 
072 * ![Extended Resource Response Sequence](ResourceResponseSelfSeq.svg)
073 * 
074 * @startuml ResourceResponseSeq.svg
075 * hide footbox
076 * 
077 * activate Portal
078 * actor Framework
079 * Portal -> ResourceProvider: ResourceRequest
080 * activate ResourceProvider
081 * deactivate ResourceProvider
082 * deactivate Portal
083 * Framework -> Portal: ResourceRequestCompleted
084 * activate Portal
085 * Portal -> Browser: "Response Header"
086 * loop until end of data
087 *     Portal -> Browser: Output
088 *     Portal -> Browser: "Response body"
089 *     deactivate Portal
090 * end loop
091 * deactivate ResourceProvider
092 * deactivate Browser
093 * @enduml
094 * 
095 * @startuml ResourceResponseSelfSeq.svg
096 * hide footbox
097 * 
098 * activate Portal
099 * actor Framework
100 * Portal -> ResourceProvider: ResourceRequest
101 * activate ResourceProvider
102 * Framework -> Portal: ResourceRequestCompleted
103 * deactivate Portal
104 * ResourceProvider -> Browser: "Response Header"
105 * loop until end of data
106 *     ResourceProvider -> Browser: Output
107 *     ResourceProvider -> Browser: "Response body"
108 *     deactivate Portal
109 * end loop
110 * deactivate ResourceProvider
111 * deactivate Browser
112 * 
113 * @enduml
114 */
115@SuppressWarnings("PMD.DataClass")
116public class ResourceRequest extends Event<ResourceResult> {
117
118    private final URI resourceUri;
119    private final Instant ifModifiedSince;
120    private final HttpRequest httpRequest;
121    private final IOSubchannel httpChannel;
122    private final RenderSupport renderSupport;
123    private final Session session;
124
125    /**
126     * Creates a new request, including the associated 
127     * {@link ResourceRequestCompleted} event.
128     * 
129     * @param resourceUri the requested resource
130     * @param httpRequest the original HTTP request
131     * @param httpChannel the channel that the HTTP request was received on
132     * @param renderSupport the render support
133     */
134    public ResourceRequest(URI resourceUri, Instant ifModifiedSince,
135            HttpRequest httpRequest, IOSubchannel httpChannel,
136            Session session, RenderSupport renderSupport) {
137        this.resourceUri = resourceUri;
138        this.ifModifiedSince = ifModifiedSince;
139        this.httpRequest = httpRequest;
140        this.httpChannel = httpChannel;
141        this.renderSupport = renderSupport;
142        this.session = session;
143        new ResourceRequestCompleted(this);
144    }
145
146    /**
147     * @return the resourceUri
148     */
149    public URI resourceUri() {
150        return resourceUri;
151    }
152
153    /**
154     * If not null, this value may be used to decide if the
155     * resource must be refreshed.
156     *
157     * @return the instant
158     */
159    public Optional<Instant> ifModifiedSince() {
160        return Optional.ofNullable(ifModifiedSince);
161    }
162
163    /**
164     * Returns the "raw" request as provided by the HTTP decoder.
165     * 
166     * @return the request
167     */
168    public HttpRequest httpRequest() {
169        return httpRequest;
170    }
171
172    /**
173     * @return the httpChannel
174     */
175    public IOSubchannel httpChannel() {
176        return httpChannel;
177    }
178
179    /**
180     * Returns the render support.
181     * 
182     * @return the render support
183     */
184    public RenderSupport renderSupport() {
185        return renderSupport;
186    }
187
188    /**
189     * Returns the (browser) session.
190     *
191     * @return the session
192     */
193    public Session session() {
194        return session;
195    }
196}