1 /*
2 * The contents of this file are subject to the terms of the Common Development and
3 * Distribution License (the License). You may not use this file except in compliance with the
4 * License.
5 *
6 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7 * specific language governing permission and limitations under the License.
8 *
9 * When distributing Covered Software, include this CDDL Header Notice in each file and include
10 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11 * Header, with the fields enclosed by brackets [] replaced by your own identifying
12 * information: "Portions copyright [year] [name of copyright owner]".
13 *
14 * Copyright 2014-2016 ForgeRock AS.
15 */
16
17 package org.forgerock.json.resource;
18
19 import static org.forgerock.http.header.HeaderUtil.quote;
20
21 import org.forgerock.util.Reject;
22
23 /**
24 * WarningHeader implements RFC 2616 section 14.46 - Warning.
25 *
26 * It implements Advice, which allows it to be used during the routing of CREST requests
27 * such that it can be added into the response in an appropriate location.
28 *
29 * @see <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html">
30 * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html</a>
31 * @since 2.4.0
32 */
33 public final class AdviceWarning {
34
35 /**
36 * 100 Indicates that there is data missing from the request.
37 *
38 * ForgeRock-Specific.
39 */
40 public final static int NOT_PRESENT = 100;
41
42 /**
43 * 110 Response is stale MUST be included whenever the returned response is stale.
44 */
45 public static final int RESPONSE_STALE = 110;
46
47 /**
48 * 111 Revalidation failed MUST be included if a cache returns a stale response because
49 * an attempt to revalidate the response failed, due to an inability to reach the server.
50 */
51 public static final int REVALIDATION_FAILED = 111;
52
53 /**
54 * 112 Disconnected operation SHOULD be included if the cache is intentionally
55 * disconnected from the rest of the network for a period of time.
56 */
57 public static final int DISCONNECTED_OPERATION = 112;
58
59 /**
60 * 113 Heuristic expiration MUST be included if the cache heuristically chose a
61 * freshness lifetime greater than 24 hours and the response's age is greater than 24 hours.
62 */
63 public static final int HEURISTIC_EXPIRATION = 113;
64
65 /**
66 * 199 Miscellaneous warning The warning text MAY include arbitrary information to be
67 * presented to a human user, or logged. A system receiving this warning MUST NOT take
68 * any automated action, besides presenting the warning to the user.
69 */
70 public static final int MISCELLANEOUS_WARNING = 199;
71
72 /**
73 * 214 Transformation applied MUST be added by an intermediate cache or proxy if it applies
74 * any transformation changing the content-coding (as specified in the Content-Encoding header)
75 * or media-type (as specified in the Content-Type header) of the response, or the entity-body
76 * of the response, unless this Warning code already appears in the response.
77 */
78 public static final int TRANFORMATION_APPLIED = 214;
79
80 /**
81 * 299 Miscellaneous persistent warning The warning text MAY include arbitrary information to
82 * be presented to a human user, or logged. A system receiving this warning MUST NOT take any automated action.
83 */
84 public static final int MISCELLANEOUS_PERSISTENT_WARNING = 299;
85
86 private final int warningCode;
87 private final String warningAgent;
88 private final String warningText;
89
90 private AdviceWarning(Builder builder) {
91 Reject.ifNull(builder.warningAgent, builder.warningText);
92 warningCode = builder.warningCode;
93 warningAgent = builder.warningAgent;
94 warningText = builder.warningText;
95 }
96
97 @Override
98 public String toString() {
99 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 }