001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.http.codec;
018
019import java.time.Duration;
020
021import org.springframework.lang.Nullable;
022
023/**
024 * Representation for a Server-Sent Event for use with Spring's reactive Web support.
025 * {@code Flux<ServerSentEvent>} or {@code Observable<ServerSentEvent>} is the
026 * reactive equivalent to Spring MVC's {@code SseEmitter}.
027 *
028 * @author Sebastien Deleuze
029 * @author Arjen Poutsma
030 * @since 5.0
031 * @param <T> the type of data that this event contains
032 * @see ServerSentEventHttpMessageWriter
033 * @see <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events W3C recommendation</a>
034 */
035public final class ServerSentEvent<T> {
036
037        @Nullable
038        private final String id;
039
040        @Nullable
041        private final String event;
042
043        @Nullable
044        private final Duration retry;
045
046        @Nullable
047        private final String comment;
048
049        @Nullable
050        private final T data;
051
052
053        private ServerSentEvent(@Nullable String id, @Nullable String event, @Nullable Duration retry,
054                        @Nullable String comment, @Nullable T data) {
055
056                this.id = id;
057                this.event = event;
058                this.retry = retry;
059                this.comment = comment;
060                this.data = data;
061        }
062
063
064        /**
065         * Return the {@code id} field of this event, if available.
066         */
067        @Nullable
068        public String id() {
069                return this.id;
070        }
071
072        /**
073         * Return the {@code event} field of this event, if available.
074         */
075        @Nullable
076        public String event() {
077                return this.event;
078        }
079
080        /**
081         * Return the {@code retry} field of this event, if available.
082         */
083        @Nullable
084        public Duration retry() {
085                return this.retry;
086        }
087
088        /**
089         * Return the comment of this event, if available.
090         */
091        @Nullable
092        public String comment() {
093                return this.comment;
094        }
095
096        /**
097         * Return the {@code data} field of this event, if available.
098         */
099        @Nullable
100        public T data() {
101                return this.data;
102        }
103
104
105        @Override
106        public String toString() {
107                return ("ServerSentEvent [id = '" + this.id + "\', event='" + this.event + "\', retry=" +
108                                this.retry + ", comment='" + this.comment + "', data=" + this.data + ']');
109        }
110
111
112        /**
113         * Return a builder for a {@code SseEvent}.
114         * @param <T> the type of data that this event contains
115         * @return the builder
116         */
117        public static <T> Builder<T> builder() {
118                return new BuilderImpl<>();
119        }
120
121        /**
122         * Return a builder for a {@code SseEvent}, populated with the give {@linkplain #data() data}.
123         * @param <T> the type of data that this event contains
124         * @return the builder
125         */
126        public static <T> Builder<T> builder(T data) {
127                return new BuilderImpl<>(data);
128        }
129
130
131        /**
132         * A mutable builder for a {@code SseEvent}.
133         *
134         * @param <T> the type of data that this event contains
135         */
136        public interface Builder<T> {
137
138                /**
139                 * Set the value of the {@code id} field.
140                 * @param id the value of the id field
141                 * @return {@code this} builder
142                 */
143                Builder<T> id(String id);
144
145                /**
146                 * Set the value of the {@code event} field.
147                 * @param event the value of the event field
148                 * @return {@code this} builder
149                 */
150                Builder<T> event(String event);
151
152                /**
153                 * Set the value of the {@code retry} field.
154                 * @param retry the value of the retry field
155                 * @return {@code this} builder
156                 */
157                Builder<T> retry(Duration retry);
158
159                /**
160                 * Set SSE comment. If a multi-line comment is provided, it will be turned into multiple
161                 * SSE comment lines as defined in Server-Sent Events W3C recommendation.
162                 * @param comment the comment to set
163                 * @return {@code this} builder
164                 */
165                Builder<T> comment(String comment);
166
167                /**
168                 * Set the value of the {@code data} field. If the {@code data} argument is a multi-line
169                 * {@code String}, it will be turned into multiple {@code data} field lines as defined
170                 * in the Server-Sent Events W3C recommendation. If {@code data} is not a String, it will
171                 * be {@linkplain org.springframework.http.codec.json.Jackson2JsonEncoder encoded} into JSON.
172                 * @param data the value of the data field
173                 * @return {@code this} builder
174                 */
175                Builder<T> data(@Nullable T data);
176
177                /**
178                 * Builds the event.
179                 * @return the built event
180                 */
181                ServerSentEvent<T> build();
182        }
183
184
185        private static class BuilderImpl<T> implements Builder<T> {
186
187                @Nullable
188                private String id;
189
190                @Nullable
191                private String event;
192
193                @Nullable
194                private Duration retry;
195
196                @Nullable
197                private String comment;
198
199                @Nullable
200                private T data;
201
202                public BuilderImpl() {
203                }
204
205                public BuilderImpl(T data) {
206                        this.data = data;
207                }
208
209                @Override
210                public Builder<T> id(String id) {
211                        this.id = id;
212                        return this;
213                }
214
215                @Override
216                public Builder<T> event(String event) {
217                        this.event = event;
218                        return this;
219                }
220
221                @Override
222                public Builder<T> retry(Duration retry) {
223                        this.retry = retry;
224                        return this;
225                }
226
227                @Override
228                public Builder<T> comment(String comment) {
229                        this.comment = comment;
230                        return this;
231                }
232
233                @Override
234                public Builder<T> data(@Nullable T data) {
235                        this.data = data;
236                        return this;
237                }
238
239                @Override
240                public ServerSentEvent<T> build() {
241                        return new ServerSentEvent<>(this.id, this.event, this.retry, this.comment, this.data);
242                }
243        }
244
245}