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.http.header; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022import java.util.function.*; 023 024/** 025 * Represents an HTTP <c>Warning</c> header. 026 * 027 * <p> 028 * A general warning about possible problems with the entity body. 029 * 030 * <h5 class='section'>See Also:</h5><ul> 031 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/NextGenRestClient">Next-Generation REST Client</a> 032 * </ul> 033 * 034 * @since 9.2.1 035 */ 036public class Warning extends HttpStringHeader { 037 038 /** The header name */ 039 public static final String NAME = "Warning"; 040 041 /** 042 * Constructor with an eager value. 043 * 044 * @param value The header value. May be <jk>null</jk>. 045 */ 046 public Warning(String value) { 047 super(NAME, value); 048 } 049 050 /** 051 * Constructor with a lazy value supplier. 052 * 053 * @param valueSupplier Supplier for the header value. Must not be <jk>null</jk>. 054 */ 055 public Warning(Supplier<String> valueSupplier) { 056 super(NAME, valueSupplier); 057 } 058 059 /** 060 * Static factory method with an eager value. 061 * 062 * @param value The header value. May be <jk>null</jk>. 063 * @return A new instance. Never <jk>null</jk>. 064 */ 065 public static Warning of(String value) { 066 return new Warning(value); 067 } 068 069 /** 070 * Static factory method with a lazy value supplier. 071 * 072 * @param valueSupplier Supplier for the header value. Must not be <jk>null</jk>. 073 * @return A new instance. Never <jk>null</jk>. 074 */ 075 public static Warning of(Supplier<String> valueSupplier) { 076 return new Warning(valueSupplier); 077 } 078 079 /** 080 * Creates a new empty {@link Builder}. 081 * 082 * @return A new builder. Never <jk>null</jk>. 083 */ 084 public static Builder create() { 085 return new Builder(); 086 } 087 088 //------------------------------------------------------------------------------------------------------------------ 089 // Inner types 090 //------------------------------------------------------------------------------------------------------------------ 091 092 /** 093 * Fluent builder for assembling an HTTP <c>Warning</c> header value. 094 * 095 * <p> 096 * Composes one or more warning-values defined by 097 * <a class='doclink' href='https://www.rfc-editor.org/rfc/rfc7234#section-5.5'>RFC 7234 ยง5.5</a> into the 098 * <c>warn-code SP warn-agent SP warn-text [SP warn-date]</c> form, with multiple warnings <c>,</c>-separated. The 099 * 3-digit warn-code is zero-padded, the warn-text and optional warn-date are rendered as quoted-strings, and 100 * {@link #add(String)} is a generic escape hatch for a fully pre-rendered warning-value. 101 * 102 * <h5 class='section'>Example:</h5> 103 * <p class='bjava'> 104 * String <jv>value</jv> = Warning.<jsm>create</jsm>() 105 * .warning(110, <js>"anderson/1.3.37"</js>, <js>"Response is stale"</js>) 106 * .build(); 107 * <jc>// => "110 anderson/1.3.37 \"Response is stale\""</jc> 108 * </p> 109 * 110 * <h5 class='section'>See Also:</h5><ul> 111 * <li class='jc'>{@link Warning} 112 * <li class='extlink'><a class='doclink' href='https://www.rfc-editor.org/rfc/rfc7234'>RFC 7234 - HTTP/1.1 Caching</a> 113 * </ul> 114 * 115 * @since 10.0.0 116 */ 117 public static class Builder { 118 119 private final List<String> warnings = l(); 120 121 /** 122 * Adds a warning-value (without a warn-date). 123 * 124 * @param code The 3-digit warn-code. Must be in the range 0-999. 125 * @param agent The warn-agent (host:port or pseudonym, e.g. <js>"-"</js>). Must not be <jk>null</jk> or blank. 126 * @param text The human-readable warn-text. Must not be <jk>null</jk>. 127 * @return This object. 128 * @throws IllegalArgumentException If {@code code} is out of range or {@code agent} is <jk>null</jk> or blank. 129 */ 130 public Builder warning(int code, String agent, String text) { 131 return add(render(code, agent, text, null)); 132 } 133 134 /** 135 * Adds a warning-value with a warn-date. 136 * 137 * @param code The 3-digit warn-code. Must be in the range 0-999. 138 * @param agent The warn-agent (host:port or pseudonym, e.g. <js>"-"</js>). Must not be <jk>null</jk> or blank. 139 * @param text The human-readable warn-text. Must not be <jk>null</jk>. 140 * @param date The warn-date (an HTTP-date). Must not be <jk>null</jk>. 141 * @return This object. 142 * @throws IllegalArgumentException If {@code code} is out of range or {@code agent}/{@code date} is invalid. 143 */ 144 public Builder warning(int code, String agent, String text, String date) { 145 reqnn("date", date); 146 return add(render(code, agent, text, date)); 147 } 148 149 /** 150 * Generic escape hatch for adding a fully pre-rendered warning-value verbatim. 151 * 152 * @param value The pre-rendered warning-value. Must not be <jk>null</jk> or blank. 153 * @return This object. 154 * @throws IllegalArgumentException If {@code value} is <jk>null</jk> or blank. 155 */ 156 public Builder add(String value) { 157 reqnn("value", value); 158 var v = value.trim(); 159 if (v.isEmpty()) 160 throw iaex("warning value must not be blank"); 161 warnings.add(v); 162 return this; 163 } 164 165 private static String render(int code, String agent, String text, String date) { 166 if (code < 0 || code > 999) 167 throw iaex("warn-code must be in the range 0-999: %s", code); 168 reqnn("agent", agent); 169 reqnn("text", text); 170 var a = agent.trim(); 171 if (a.isEmpty()) 172 throw iaex("warn-agent must not be blank"); 173 var sb = new StringBuilder(); 174 sb.append(String.format(Locale.ROOT, "%03d", code)).append(' ').append(a).append(' ').append(q(text)); 175 if (date != null) 176 sb.append(' ').append(q(date)); 177 return sb.toString(); 178 } 179 180 private static String q(String value) { 181 var sb = new StringBuilder(value.length() + 2); 182 sb.append('"'); 183 for (var i = 0; i < value.length(); i++) { 184 var c = value.charAt(i); 185 if (c == '\\' || c == '"') 186 sb.append('\\'); 187 sb.append(c); 188 } 189 sb.append('"'); 190 return sb.toString(); 191 } 192 193 /** 194 * Builds the rendered <c>Warning</c> header value. 195 * 196 * <p> 197 * Returns an empty string when no warnings have been registered. 198 * 199 * @return The header value. Never <jk>null</jk>. 200 */ 201 public String build() { 202 return String.join(", ", warnings); 203 } 204 205 /** 206 * Builds a {@link Warning} header bean directly from this builder. 207 * 208 * @return A {@link Warning} carrying {@link #build()}. Never <jk>null</jk>. 209 */ 210 public Warning toHeader() { 211 return Warning.of(build()); 212 } 213 214 @Override 215 public String toString() { 216 return build(); 217 } 218 } 219}