001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2015 ForgeRock AS. 015 */ 016 017package org.forgerock.http.header; 018 019import static org.forgerock.http.header.HeaderUtil.*; 020 021import java.util.ArrayList; 022import java.util.List; 023 024import org.forgerock.http.protocol.Header; 025import org.forgerock.http.protocol.Message; 026import org.forgerock.util.Reject; 027 028/** 029 * Processes the <strong>{@code Warning}</strong> message header. For more 030 * information, see <a href="http://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a> 031 * 14.46. 032 */ 033public final class WarningHeader extends Header { 034 035 /** 036 * Constructs a new header, initialized from the specified message. 037 * 038 * @param message The message to initialize the header from. 039 * @return The parsed header. 040 */ 041 public static WarningHeader valueOf(Message message) { 042 return new WarningHeader(toWarnings(parseMultiValuedHeader(message, NAME))); 043 } 044 045 /** 046 * Constructs a new header, initialized from the specified string value. 047 * 048 * @param string The value to initialize the header from. 049 * @return The parsed header. 050 */ 051 public static WarningHeader valueOf(String string) { 052 return new WarningHeader(toWarnings(parseMultiValuedHeader(string))); 053 } 054 055 private static List<Warning> toWarnings(List<String> header) { 056 List<Warning> warnings = new ArrayList<>(); 057 for (String entry : header) { 058 String[] parts = entry.split(" "); 059 if (parts.length >= 3) { 060 warnings.add(new Warning(Integer.parseInt(parts[0]), parts[1], parts[2])); 061 } 062 } 063 return warnings; 064 } 065 066 /** 067 * Constructs a new warning header with the frequently-used error type: 068 * 100. 069 * 070 * @param agentName Name of the component responsible for issuing the 071 * warning. 072 * @param fmt The format, which may include embedded %s, etc. 073 * @param args Zero or more args, passed into String.format to generate the 074 * warning text. 075 * @return A newly constructed {@code WarningHeader} indicating the 076 * expected key was not found in the request. 077 */ 078 public static WarningHeader newWarning(String agentName, String fmt, Object... args) { 079 return new WarningHeader(NOT_PRESENT, agentName, String.format(fmt, args)); 080 } 081 082 /** The name of this header. */ 083 public static final String NAME = "Warning"; 084 085 /** 086 * 100 Indicates that there is data missing from the request. 087 * 088 * <p>ForgeRock-Specific.</p> 089 */ 090 public final static int NOT_PRESENT = 100; 091 092 /** 093 * 110 Response is stale MUST be included whenever the returned response 094 * is stale. 095 */ 096 public static final int RESPONSE_STALE = 110; 097 098 /** 099 * 111 Revalidation failed MUST be included if a cache returns a stale 100 * response because an attempt to revalidate the response failed, due to an 101 * inability to reach the server. 102 */ 103 public static final int REVALIDATION_FAILED = 111; 104 105 /** 106 * 112 Disconnected operation SHOULD be included if the cache is 107 * intentionally disconnected from the rest of the network for a period of 108 * time. 109 */ 110 public static final int DISCONNECTED_OPERATION = 112; 111 112 /** 113 * 113 Heuristic expiration MUST be included if the cache heuristically 114 * chose a freshness lifetime greater than 24 hours and the response's age 115 * is greater than 24 hours. 116 */ 117 public static final int HEURISTIC_EXPIRATION = 113; 118 119 /** 120 * 199 Miscellaneous warning The warning text MAY include arbitrary 121 * information to be presented to a human user, or logged. A system 122 * receiving this warning MUST NOT take any automated action, besides 123 * presenting the warning to the user. 124 */ 125 public static final int MISCELLANEOUS_WARNING = 199; 126 127 /** 128 * 214 Transformation applied MUST be added by an intermediate cache or 129 * proxy if it applies any transformation changing the content-coding (as 130 * specified in the Content-Encoding header) or media-type (as specified in 131 * the Content-Type header) of the response, or the entity-body of the 132 * response, unless this Warning code already appears in the response. 133 */ 134 public static final int TRANFORMATION_APPLIED = 214; 135 136 /** 137 * 299 Miscellaneous persistent warning The warning text MAY include 138 * arbitrary information to be presented to a human user, or logged. A 139 * system receiving this warning MUST NOT take any automated action. 140 */ 141 public static final int MISCELLANEOUS_PERSISTENT_WARNING = 299; 142 143 private final List<Warning> warnings = new ArrayList<>(); 144 145 private static final class Warning { 146 private final int code; 147 private final String agent; 148 private final String text; 149 150 private Warning(int code, String agent, String text) { 151 this.code = code; 152 this.agent = agent; 153 this.text = text; 154 } 155 156 @Override 157 public String toString() { 158 return String.valueOf(code) + " " + agent + " " + text; 159 } 160 } 161 162 private WarningHeader(List<Warning> warnings) { 163 this.warnings.addAll(warnings); 164 } 165 166 /** 167 * Constructs a new header with the provided warning. 168 * 169 * @param code The warning code. 170 * @param agent Name of the component responsible for issuing the warning. 171 * @param text The warning text. 172 */ 173 public WarningHeader(int code, String agent, String text) { 174 Reject.ifNull(agent, text); 175 warnings.add(new Warning(code, agent, text)); 176 } 177 178 @Override 179 public String getName() { 180 return NAME; 181 } 182 183 /** 184 * Constructs a new header with the warnings defined in {@literal this} 185 * {@code WarningHeader} in addition to the provided warning. 186 * 187 * @param code The warning code. 188 * @param agent Name of the component responsible for issuing the warning. 189 * @param text The warning text. 190 * @return A new {@code WarningHeader} instance. 191 */ 192 public WarningHeader add(int code, String agent, String text) { 193 WarningHeader header = new WarningHeader(warnings); 194 header.warnings.add(new Warning(code, agent, text)); 195 return header; 196 } 197 198 @Override 199 public List<String> getValues() { 200 List<String> headerValues = new ArrayList<>(); 201 for (Warning warning : warnings) { 202 headerValues.add(warning.toString().trim()); 203 } 204 return headerValues; 205 } 206 207 static class Factory extends HeaderFactory<WarningHeader> { 208 209 @Override 210 public WarningHeader parse(String value) { 211 return valueOf(value); 212 } 213 214 @Override 215 public WarningHeader parse(List<String> values) { 216 return new WarningHeader(toWarnings(values)); 217 } 218 } 219 220}