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