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 2014-2016 ForgeRock AS. 015*/ 016 017package org.forgerock.json.resource; 018 019import static org.forgerock.http.header.HeaderUtil.quote; 020 021import org.forgerock.util.Reject; 022 023/** 024 * WarningHeader implements RFC 2616 section 14.46 - Warning. 025 * 026 * It implements Advice, which allows it to be used during the routing of CREST requests 027 * such that it can be added into the response in an appropriate location. 028 * 029 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html"> 030 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a> 031 * @since 2.4.0 032 */ 033public final class AdviceWarning { 034 035 /** 036 * 100 Indicates that there is data missing from the request. 037 * 038 * ForgeRock-Specific. 039 */ 040 public final static int NOT_PRESENT = 100; 041 042 /** 043 * 110 Response is stale MUST be included whenever the returned response is stale. 044 */ 045 public static final int RESPONSE_STALE = 110; 046 047 /** 048 * 111 Revalidation failed MUST be included if a cache returns a stale response because 049 * an attempt to revalidate the response failed, due to an inability to reach the server. 050 */ 051 public static final int REVALIDATION_FAILED = 111; 052 053 /** 054 * 112 Disconnected operation SHOULD be included if the cache is intentionally 055 * disconnected from the rest of the network for a period of time. 056 */ 057 public static final int DISCONNECTED_OPERATION = 112; 058 059 /** 060 * 113 Heuristic expiration MUST be included if the cache heuristically chose a 061 * freshness lifetime greater than 24 hours and the response's age is greater than 24 hours. 062 */ 063 public static final int HEURISTIC_EXPIRATION = 113; 064 065 /** 066 * 199 Miscellaneous warning The warning text MAY include arbitrary information to be 067 * presented to a human user, or logged. A system receiving this warning MUST NOT take 068 * any automated action, besides presenting the warning to the user. 069 */ 070 public static final int MISCELLANEOUS_WARNING = 199; 071 072 /** 073 * 214 Transformation applied MUST be added by an intermediate cache or proxy if it applies 074 * any transformation changing the content-coding (as specified in the Content-Encoding header) 075 * or media-type (as specified in the Content-Type header) of the response, or the entity-body 076 * of the response, unless this Warning code already appears in the response. 077 */ 078 public static final int TRANFORMATION_APPLIED = 214; 079 080 /** 081 * 299 Miscellaneous persistent warning The warning text MAY include arbitrary information to 082 * be presented to a human user, or logged. A system receiving this warning MUST NOT take any automated action. 083 */ 084 public static final int MISCELLANEOUS_PERSISTENT_WARNING = 299; 085 086 private final int warningCode; 087 private final String warningAgent; 088 private final String warningText; 089 090 private AdviceWarning(Builder builder) { 091 Reject.ifNull(builder.warningAgent, builder.warningText); 092 warningCode = builder.warningCode; 093 warningAgent = builder.warningAgent; 094 warningText = builder.warningText; 095 } 096 097 @Override 098 public String toString() { 099 return String.valueOf(warningCode) + " " + warningAgent + " " + quote(warningText); 100 } 101 102 /** 103 * Convenience method to quickly generate frequently-used error type: 100. 104 * 105 * @param agentName Name of the component responsible for issuing the warning. 106 * @param missingKey Name of the missing key which must be included. 107 * @return a newly constructed AdviceWarning indicating the expected key was not found in the request. 108 */ 109 public static AdviceWarning getNotPresent(String agentName, String missingKey) { 110 return AdviceWarning.newBuilder() 111 .withWarningAgent(agentName) 112 .withWarningCode(NOT_PRESENT) 113 .withWarningText(missingKey + " should be included in the request.") 114 .build(); 115 } 116 117 /** 118 * Generate a warning using the builder provided. 119 * 120 * @param agentName the agent name 121 * @param fmt The format, which may include embedded %s, etc. 122 * @param args Zero or more args, passed into String.format to generate the warning text 123 * @return a newly built WarningHeader object 124 */ 125 public static AdviceWarning newAdviceWarning(String agentName, String fmt, Object... args) { 126 return AdviceWarning 127 .newBuilder() 128 .withWarningAgent(agentName) 129 .withWarningCode(NOT_PRESENT) 130 .withWarningText(String.format(fmt, args)) 131 .build(); 132 } 133 134 private static Builder newBuilder() { 135 return new Builder(); 136 } 137 138 /** 139 * Accessed via {@link AdviceWarning#newBuilder()}. 140 */ 141 private static final class Builder { 142 143 private int warningCode; 144 private String warningAgent; 145 private String warningText; 146 147 /** 148 * Package private default CTOR to prevent direct instantiation by other than us. 149 */ 150 private Builder() { 151 } 152 153 /** 154 * A three-digit code which can be linked back to the cause of the Warning. 155 * 156 * @param warningCode a three-digit integer. 157 * @return this builder. 158 */ 159 private Builder withWarningCode(int warningCode) { 160 Reject.ifTrue(warningCode < 0); 161 Reject.ifTrue(String.valueOf(warningCode).length() != 3); 162 this.warningCode = warningCode; 163 return this; 164 } 165 166 /** 167 * An identifier, used for debugging so that the receiving agent can 168 * determine from where this Warning originated. 169 * 170 * @param warningAgent a String identifier. 171 * @return this builder. 172 */ 173 private Builder withWarningAgent(String warningAgent) { 174 Reject.ifNull(warningAgent); 175 Reject.ifTrue(warningAgent.isEmpty()); 176 this.warningAgent = warningAgent; 177 return this; 178 } 179 180 /** 181 * A human-readable description of the Warning. 182 * 183 * @param warningText a String description 184 * @return this builder. 185 */ 186 private Builder withWarningText(String warningText) { 187 Reject.ifNull(warningText); 188 Reject.ifTrue(warningText.isEmpty()); 189 this.warningText = warningText; 190 return this; 191 } 192 193 /** 194 * Builds and returns a valid and usable {@code WarningHeader} from this 195 * builder. 196 * 197 * @return The built {@code WarningHeader}. 198 */ 199 private AdviceWarning build() { 200 return new AdviceWarning(this); 201 } 202 } 203}