001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.http.header;
014
015import static java.time.format.DateTimeFormatter.*;
016import static org.apache.juneau.common.internal.StringUtils.*;
017import static org.apache.juneau.internal.ClassUtils.*;
018import static org.apache.juneau.internal.CollectionUtils.*;
019
020import java.time.*;
021import java.util.*;
022import java.util.function.*;
023
024import org.apache.juneau.*;
025import org.apache.juneau.http.annotation.*;
026
027/**
028 * Represents a parsed <l>Retry-After</l> HTTP response header.
029 *
030 * <p>
031 * If an entity is temporarily unavailable, this instructs the client to try again later.
032 * Value could be a specified period of time (in seconds) or a HTTP-date.
033 *
034 * <h5 class='figure'>Example</h5>
035 * <p class='bcode'>
036 *    Retry-After: 120
037 *    Retry-After: Fri, 07 Nov 2014 23:59:59 GMT
038 * </p>
039 *
040 * <h5 class='topic'>RFC2616 Specification</h5>
041 *
042 * The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the
043 * service is expected to be unavailable to the requesting client.
044 * This field MAY also be used with any 3xx (Redirection) response to indicate the minimum time the user-agent is asked
045 * wait before issuing the redirected request.
046 * The value of this field can be either an HTTP-date or an integer number of seconds (in decimal) after the time of the
047 * response.
048 *
049 * <p class='bcode'>
050 *    Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
051 * </p>
052 *
053 * <p>
054 * Two examples of its use are
055 * <p class='bcode'>
056 *    Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
057 *    Retry-After: 120
058 * </p>
059 *
060 * <p>
061 * In the latter example, the delay is 2 minutes.
062 *
063 * <h5 class='section'>See Also:</h5><ul>
064 *    <li class='link'><a class="doclink" href="../../../../../index.html#juneau-rest-common">juneau-rest-common</a>
065 *    <li class='extlink'><a class="doclink" href="https://www.w3.org/Protocols/rfc2616/rfc2616.html">Hypertext Transfer Protocol -- HTTP/1.1</a>
066 * </ul>
067 *
068 * @serial exclude
069 */
070@Header("Retry-After")
071public class RetryAfter extends BasicDateHeader {
072
073   //-----------------------------------------------------------------------------------------------------------------
074   // Static
075   //-----------------------------------------------------------------------------------------------------------------
076
077   private static final long serialVersionUID = 1L;
078   private static final String NAME = "Retry-After";
079
080   /**
081    * Static creator.
082    *
083    * @param value
084    *    The header value.
085    *    <br>Must be an RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>) or an integer.
086    *    <br>Can be <jk>null</jk>.
087    * @return A new header bean, or <jk>null</jk> if the name is <jk>null</jk> or empty or the value is <jk>null</jk>.
088    */
089   public static RetryAfter of(String value) {
090      return value == null ? null : new RetryAfter(value);
091   }
092
093   /**
094    * Static creator.
095    *
096    * @param value
097    *    The header value.
098    *    <br>Can be <jk>null</jk>.
099    * @return A new header bean, or <jk>null</jk> if the name is <jk>null</jk> or empty or the value is <jk>null</jk>.
100    */
101   public static RetryAfter of(ZonedDateTime value) {
102      return value == null ? null : new RetryAfter(value);
103   }
104
105   /**
106    * Static creator.
107    *
108    * @param value
109    *    The header value.
110    *    <br>Can be <jk>null</jk>.
111    * @return A new header bean, or <jk>null</jk> if the name is <jk>null</jk> or empty or the value is <jk>null</jk>.
112    */
113   public static RetryAfter of(Integer value) {
114      return value == null ? null : new RetryAfter(value);
115   }
116
117   /**
118    * Static creator with delayed value.
119    *
120    * <p>
121    * Header value is re-evaluated on each call to {@link #getValue()}.
122    *
123    * @param value
124    *    The supplier of the header value.
125    *    <br>Supplier must supply either {@link Integer} or {@link ZonedDateTime} objects.
126    *    <br>Can be <jk>null</jk>.
127    * @return A new header bean, or <jk>null</jk> if the name is <jk>null</jk> or empty or the value is <jk>null</jk>.
128    */
129   public static RetryAfter of(Supplier<?> value) {
130      return value == null ? null : new RetryAfter(value);
131   }
132
133   //-----------------------------------------------------------------------------------------------------------------
134   // Instance
135   //-----------------------------------------------------------------------------------------------------------------
136
137   private final Integer value;
138   private final Supplier<?> supplier;
139
140   /**
141    * Constructor.
142    *
143    * @param value
144    *    The header value.
145    *    <br>Must be an RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>) or an integer.
146    *    <br>Can be <jk>null</jk>.
147    */
148   public RetryAfter(String value) {
149      super(NAME, isNumeric(value) ? null : value);
150      this.value = isNumeric(value) ? Integer.parseInt(value) : null;
151      this.supplier = null;
152   }
153
154   /**
155    * Constructor.
156    *
157    * @param value
158    *    The header value.
159    *    <br>Can be <jk>null</jk>.
160    */
161   public RetryAfter(ZonedDateTime value) {
162      super(NAME, value);
163      this.value = null;
164      this.supplier = null;
165   }
166
167   /**
168    * Constructor.
169    *
170    * @param value
171    *    The header value.
172    *    <br>Can be <jk>null</jk>.
173    */
174   public RetryAfter(Integer value) {
175      super(NAME, (String)null);
176      this.value = value;
177      this.supplier = null;
178   }
179
180   /**
181    * Constructor with delayed value.
182    *
183    * <p>
184    * Header value is re-evaluated on each call to {@link #getValue()}.
185    *
186    * @param value
187    *    The supplier of the header value.
188    *    <br>Supplier must supply either {@link Integer} or {@link ZonedDateTime} objects.
189    *    <br>Can be <jk>null</jk>.
190    */
191   public RetryAfter(Supplier<?> value) {
192      super(NAME, (String)null);
193      this.value = null;
194      this.supplier = value;
195   }
196
197   @Override /* Header */
198   public String getValue() {
199      if (supplier != null) {
200         Object o = supplier.get();
201         if (o == null)
202            return null;
203         if (o instanceof Integer) {
204            return o.toString();
205         } else if (o instanceof ZonedDateTime) {
206            return RFC_1123_DATE_TIME.format((ZonedDateTime)o);
207         }
208         throw new BasicRuntimeException("Invalid object type returned by supplier: {0}", className(o));
209      }
210      if (value != null)
211         return stringify(value);
212      return super.getValue();
213   }
214
215   /**
216    * Returns this header value as an integer.
217    *
218    * @return This header value as a integer, or an empty optional if value was <jk>null</jk> or not an integer.
219    */
220   public Optional<Integer> asInteger() {
221      if (supplier != null) {
222         Object o = supplier.get();
223         return optional(o instanceof Integer ? (Integer)o : null);
224      }
225      return optional(value);
226   }
227}