001/* 002 * Copyright 2024-2025 Revetware LLC. 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 * http://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 com.soklet.servlet.jakarta; 018 019import javax.annotation.Nonnull; 020import javax.annotation.Nullable; 021import javax.annotation.concurrent.NotThreadSafe; 022import java.io.PrintWriter; 023import java.io.Writer; 024import java.util.Locale; 025import java.util.function.Consumer; 026 027import static java.util.Objects.requireNonNull; 028 029/** 030 * Soklet integration implementation of {@link SokletServletPrintWriter}. 031 * 032 * @author <a href="https://www.revetkn.com">Mark Allen</a> 033 */ 034@NotThreadSafe 035public final class SokletServletPrintWriter extends PrintWriter { 036 @Nonnull 037 private final Consumer<SokletServletPrintWriter> writeOccurredCallback; 038 @Nonnull 039 private final Consumer<SokletServletPrintWriter> writeFinalizedCallback; 040 @Nonnull 041 private Boolean writeFinalized = false; 042 043 @Nonnull 044 public static SokletServletPrintWriter withWriter(@Nonnull Writer writer) { 045 return new Builder(writer).build(); 046 } 047 048 @Nonnull 049 public static Builder builderWithWriter(@Nonnull Writer writer) { 050 return new Builder(writer); 051 } 052 053 private SokletServletPrintWriter(@Nonnull Builder builder) { 054 super(requireNonNull(builder.writer), true); 055 this.writeOccurredCallback = builder.writeOccurredCallback != null ? builder.writeOccurredCallback : (ignored) -> {}; 056 this.writeFinalizedCallback = builder.writeFinalizedCallback != null ? builder.writeFinalizedCallback : (ignored) -> {}; 057 } 058 059 /** 060 * Builder used to construct instances of {@link SokletServletPrintWriter}. 061 * <p> 062 * This class is intended for use by a single thread. 063 * 064 * @author <a href="https://www.revetkn.com">Mark Allen</a> 065 */ 066 @NotThreadSafe 067 public static class Builder { 068 @Nonnull 069 private Writer writer; 070 @Nullable 071 private Consumer<SokletServletPrintWriter> writeOccurredCallback; 072 @Nullable 073 private Consumer<SokletServletPrintWriter> writeFinalizedCallback; 074 075 @Nonnull 076 private Builder(@Nonnull Writer writer) { 077 requireNonNull(writer); 078 this.writer = writer; 079 } 080 081 @Nonnull 082 public Builder writer(@Nonnull Writer writer) { 083 requireNonNull(writer); 084 this.writer = writer; 085 return this; 086 } 087 088 @Nonnull 089 public Builder writeOccurredCallback(@Nullable Consumer<SokletServletPrintWriter> writeOccurredCallback) { 090 this.writeOccurredCallback = writeOccurredCallback; 091 return this; 092 } 093 094 @Nonnull 095 public Builder writeFinalizedCallback(@Nullable Consumer<SokletServletPrintWriter> writeFinalizedCallback) { 096 this.writeFinalizedCallback = writeFinalizedCallback; 097 return this; 098 } 099 100 @Nonnull 101 public SokletServletPrintWriter build() { 102 return new SokletServletPrintWriter(this); 103 } 104 } 105 106 @Nonnull 107 protected Boolean getWriteFinalized() { 108 return this.writeFinalized; 109 } 110 111 protected void setWriteFinalized(@Nonnull Boolean writeFinalized) { 112 requireNonNull(writeFinalized); 113 this.writeFinalized = writeFinalized; 114 } 115 116 @Nonnull 117 protected Consumer<SokletServletPrintWriter> getWriteOccurredCallback() { 118 return this.writeOccurredCallback; 119 } 120 121 @Nonnull 122 protected Consumer<SokletServletPrintWriter> getWriteFinalizedCallback() { 123 return this.writeFinalizedCallback; 124 } 125 126// Implementation of PrintWriter methods below: 127 128 @Override 129 public void write(@Nonnull char[] buf, 130 int off, 131 int len) { 132 requireNonNull(buf); 133 134 super.write(buf, off, len); 135 super.flush(); 136 getWriteOccurredCallback().accept(this); 137 } 138 139 @Override 140 public void write(@Nonnull String s, 141 int off, 142 int len) { 143 requireNonNull(s); 144 145 super.write(s, off, len); 146 super.flush(); 147 getWriteOccurredCallback().accept(this); 148 } 149 150 @Override 151 public void write(int c) { 152 super.write(c); 153 super.flush(); 154 getWriteOccurredCallback().accept(this); 155 } 156 157 @Override 158 public void write(@Nonnull char[] buf) { 159 requireNonNull(buf); 160 161 super.write(buf); 162 super.flush(); 163 getWriteOccurredCallback().accept(this); 164 } 165 166 @Override 167 public void write(@Nonnull String s) { 168 requireNonNull(s); 169 170 super.write(s); 171 super.flush(); 172 getWriteOccurredCallback().accept(this); 173 } 174 175 @Override 176 public void print(boolean b) { 177 super.print(b); 178 super.flush(); 179 getWriteOccurredCallback().accept(this); 180 } 181 182 @Override 183 public void print(char c) { 184 super.print(c); 185 super.flush(); 186 getWriteOccurredCallback().accept(this); 187 } 188 189 @Override 190 public void print(int i) { 191 super.print(i); 192 super.flush(); 193 getWriteOccurredCallback().accept(this); 194 } 195 196 @Override 197 public void print(long l) { 198 super.print(l); 199 super.flush(); 200 getWriteOccurredCallback().accept(this); 201 } 202 203 @Override 204 public void print(float f) { 205 super.print(f); 206 super.flush(); 207 getWriteOccurredCallback().accept(this); 208 } 209 210 @Override 211 public void print(double d) { 212 super.print(d); 213 super.flush(); 214 getWriteOccurredCallback().accept(this); 215 } 216 217 @Override 218 public void print(@Nonnull char[] s) { 219 requireNonNull(s); 220 221 super.print(s); 222 super.flush(); 223 getWriteOccurredCallback().accept(this); 224 } 225 226 @Override 227 public void print(@Nullable String s) { 228 super.print(s); 229 super.flush(); 230 getWriteOccurredCallback().accept(this); 231 } 232 233 @Override 234 public void print(@Nullable Object obj) { 235 super.print(obj); 236 super.flush(); 237 getWriteOccurredCallback().accept(this); 238 } 239 240 @Override 241 public void println() { 242 super.println(); 243 super.flush(); 244 getWriteOccurredCallback().accept(this); 245 } 246 247 @Override 248 public void println(boolean x) { 249 super.println(x); 250 super.flush(); 251 getWriteOccurredCallback().accept(this); 252 } 253 254 @Override 255 public void println(char x) { 256 super.println(x); 257 super.flush(); 258 getWriteOccurredCallback().accept(this); 259 } 260 261 @Override 262 public void println(int x) { 263 super.println(x); 264 super.flush(); 265 getWriteOccurredCallback().accept(this); 266 } 267 268 @Override 269 public void println(long x) { 270 super.println(x); 271 super.flush(); 272 getWriteOccurredCallback().accept(this); 273 } 274 275 @Override 276 public void println(float x) { 277 super.println(x); 278 super.flush(); 279 getWriteOccurredCallback().accept(this); 280 } 281 282 @Override 283 public void println(double x) { 284 super.println(x); 285 super.flush(); 286 getWriteOccurredCallback().accept(this); 287 } 288 289 @Override 290 public void println(char[] x) { 291 requireNonNull(x); 292 293 super.println(x); 294 super.flush(); 295 getWriteOccurredCallback().accept(this); 296 } 297 298 @Override 299 public void println(@Nullable String x) { 300 super.println(x); 301 super.flush(); 302 getWriteOccurredCallback().accept(this); 303 } 304 305 @Override 306 public void println(@Nullable Object x) { 307 super.println(x); 308 super.flush(); 309 getWriteOccurredCallback().accept(this); 310 } 311 312 @Override 313 @Nonnull 314 public PrintWriter printf(@Nonnull String format, 315 @Nullable Object... args) { 316 requireNonNull(format); 317 318 PrintWriter printWriter = super.printf(format, args); 319 super.flush(); 320 getWriteOccurredCallback().accept(this); 321 return printWriter; 322 } 323 324 @Override 325 @Nonnull 326 public PrintWriter printf(@Nullable Locale l, 327 @Nonnull String format, 328 @Nullable Object... args) { 329 requireNonNull(format); 330 331 PrintWriter printWriter = super.printf(l, format, args); 332 super.flush(); 333 getWriteOccurredCallback().accept(this); 334 return printWriter; 335 } 336 337 @Override 338 @Nonnull 339 public PrintWriter format(@Nonnull String format, 340 @Nullable Object... args) { 341 requireNonNull(format); 342 343 PrintWriter printWriter = super.format(format, args); 344 super.flush(); 345 getWriteOccurredCallback().accept(this); 346 return printWriter; 347 } 348 349 @Override 350 @Nonnull 351 public PrintWriter format(@Nullable Locale l, 352 @Nonnull String format, 353 @Nullable Object... args) { 354 requireNonNull(format); 355 356 PrintWriter printWriter = super.format(l, format, args); 357 super.flush(); 358 getWriteOccurredCallback().accept(this); 359 return printWriter; 360 } 361 362 @Override 363 @Nonnull 364 public PrintWriter append(@Nullable CharSequence csq) { 365 PrintWriter printWriter = super.append(csq); 366 super.flush(); 367 getWriteOccurredCallback().accept(this); 368 return printWriter; 369 } 370 371 @Override 372 @Nonnull 373 public PrintWriter append(@Nullable CharSequence csq, 374 int start, 375 int end) { 376 PrintWriter printWriter = super.append(csq, start, end); 377 super.flush(); 378 getWriteOccurredCallback().accept(this); 379 return printWriter; 380 } 381 382 @Override 383 @Nonnull 384 public PrintWriter append(char c) { 385 PrintWriter printWriter = super.append(c); 386 super.flush(); 387 getWriteOccurredCallback().accept(this); 388 return printWriter; 389 } 390 391 @Override 392 public void flush() { 393 super.flush(); 394 395 if (!getWriteFinalized()) { 396 setWriteFinalized(true); 397 getWriteFinalizedCallback().accept(this); 398 } 399 } 400 401 @Override 402 public void close() { 403 super.flush(); 404 super.close(); 405 406 if (!getWriteFinalized()) { 407 setWriteFinalized(true); 408 getWriteFinalizedCallback().accept(this); 409 } 410 } 411}