001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.marshall.sse; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.io.*; 022 023import org.apache.juneau.commons.collections.*; 024import org.apache.juneau.marshall.parser.*; 025import org.apache.juneau.marshall.stream.*; 026 027/** 028 * Parses <c>text/event-stream</c> input (WHATWG Server-Sent Events) into Java objects. 029 * 030 * <h5 class='topic'>Media types</h5> 031 * <p> 032 * Handles <c>Content-Type</c> types: <bc>text/event-stream</bc>. 033 * 034 * <h5 class='topic'>Description</h5> 035 * <p> 036 * Eager parse path — drains the stream into a {@link java.util.List List<SseEvent>}. 037 * For true line-driven streaming over long-lived connections, use {@link SseEventReader} directly. 038 * 039 * <h5 class='section'>Notes:</h5><ul> 040 * <li class='note'>This class is thread safe and reusable. 041 * </ul> 042 * 043 * <h5 class='section'>See Also:</h5><ul> 044 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Sse">SSE</a> 045 * </ul> 046 */ 047@SuppressWarnings({ 048 "java:S110", // Inheritance depth acceptable for parser hierarchy 049 "java:S9149", // Per-format static factories intentionally shadow the parent's. 050 "resource" // Closeable resources are owned by the caller's parser session; Eclipse JDT @Owning warning is by design. 051}) 052public class SseParser extends ReaderParser implements RecordReadable { 053 054 /** Default parser, all default settings. */ 055 public static final SseParser DEFAULT = new SseParser(create()); 056 057 /** 058 * Creates a new builder for this object. 059 * 060 * @return A new builder. 061 */ 062 public static Builder create() { 063 return new Builder(); 064 } 065 066 /** 067 * Builder class. 068 */ 069 public static class Builder extends ReaderParser.Builder<Builder> { 070 071 private static final Cache<HashKey,SseParser> CACHE = Cache.of(HashKey.class, SseParser.class).build(); 072 073 /** 074 * Constructor, default settings. 075 */ 076 protected Builder() { 077 consumes(SseSerializer.MEDIA_TYPE); 078 type(SseParser.class); 079 } 080 081 /** 082 * Copy constructor. 083 * 084 * @param copyFrom The builder to copy from. Must not be <jk>null</jk>. 085 */ 086 protected Builder(Builder copyFrom) { 087 super(reqnn("copyFrom", copyFrom)); 088 } 089 090 /** 091 * Copy constructor. 092 * 093 * @param copyFrom The parser to copy from. Must not be <jk>null</jk>. 094 */ 095 protected Builder(SseParser copyFrom) { 096 super(reqnn("copyFrom", copyFrom)); 097 } 098 099 @Override /* Overridden from Context.Builder<?> */ 100 public HashKey hashKey() { 101 return HashKey.of(super.hashKey()); 102 } 103 104 @Override /* Overridden from Context.Builder<?> */ 105 public SseParser build() { 106 return cache(CACHE).build(SseParser.class); 107 } 108 109 @Override /* Overridden from Context.Builder<?> */ 110 public Builder copy() { 111 return new Builder(this); 112 } 113 } 114 115 /** 116 * Constructor. 117 * 118 * @param builder The builder for this object. 119 */ 120 public SseParser(Builder builder) { 121 super(builder); 122 } 123 124 @Override /* Overridden from Context */ 125 public Builder copy() { 126 return new Builder(this); 127 } 128 129 @Override /* Overridden from Context */ 130 public SseParserSession.Builder createSession() { 131 return SseParserSession.create(this); 132 } 133 134 @Override /* Overridden from Context */ 135 public SseParserSession getSession() { 136 return createSession().build(); 137 } 138 139 /** 140 * Convenience delegator that opens a {@link RecordReader} over the input using 141 * <b>default session arguments</b> (mirrors {@link #read(Object, Class)}). 142 * 143 * <p> 144 * The real implementation lives on {@link SseParserSession#readRecords(Object)}. Callers 145 * that need request-derived configuration (locale, timezone, schema, swaps) should call 146 * {@link #createSession()} and invoke {@link SseParserSession#readRecords(Object)} on the 147 * built session instead. 148 * 149 * @param input The input. 150 * @return A new {@link RecordReader} cursor. 151 * @throws IOException If a problem occurred opening the underlying input. 152 */ 153 @Override /* RecordReadable */ 154 public RecordReader readRecords(Object input) throws IOException { 155 return getSession().readRecords(input); 156 } 157 158 @Override /* RecordReadable */ 159 public boolean isRecordStreaming() { 160 return false; 161 } 162}