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}