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}