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 org.apache.juneau.internal.StringUtils.*;
016
017import java.time.*;
018import java.util.*;
019import java.util.function.*;
020
021import org.apache.juneau.http.annotation.*;
022
023/**
024 * Represents a parsed <l>Retry-After</l> HTTP response header.
025 *
026 * <p>
027 * If an entity is temporarily unavailable, this instructs the client to try again later.
028 * Value could be a specified period of time (in seconds) or a HTTP-date.
029 *
030 * <h5 class='figure'>Example</h5>
031 * <p class='bcode w800'>
032 *    Retry-After: 120
033 *    Retry-After: Fri, 07 Nov 2014 23:59:59 GMT
034 * </p>
035 *
036 * <h5 class='topic'>RFC2616 Specification</h5>
037 *
038 * The Retry-After response-header field can be used with a 503 (Service Unavailable) response to indicate how long the
039 * service is expected to be unavailable to the requesting client.
040 * This field MAY also be used with any 3xx (Redirection) response to indicate the minimum time the user-agent is asked
041 * wait before issuing the redirected request.
042 * The value of this field can be either an HTTP-date or an integer number of seconds (in decimal) after the time of the
043 * response.
044 *
045 * <p class='bcode w800'>
046 *    Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
047 * </p>
048 *
049 * <p>
050 * Two examples of its use are
051 * <p class='bcode w800'>
052 *    Retry-After: Fri, 31 Dec 1999 23:59:59 GMT
053 *    Retry-After: 120
054 * </p>
055 *
056 * <p>
057 * In the latter example, the delay is 2 minutes.
058 *
059 * <ul class='seealso'>
060 *    <li class='extlink'>{@doc ExtRFC2616}
061 * </ul>
062 */
063@Header("Retry-After")
064public class RetryAfter extends BasicDateHeader {
065
066   private static final long serialVersionUID = 1L;
067
068   private final Object value;  // Only set if value is an integer.
069
070   /**
071    * Convenience creator.
072    *
073    * @param value
074    *    The header value.
075    *    <br>Can be any of the following:
076    *    <ul>
077    *       <li>{@link String}
078    *       <li>Anything else - Converted to <c>String</c> then parsed.
079    *    </ul>
080    * @return A new {@link RetryAfter} object.
081    */
082   public static RetryAfter of(Object value) {
083      if (value == null)
084         return null;
085      return new RetryAfter(value);
086   }
087
088   /**
089    * Convenience creator using supplier.
090    *
091    * <p>
092    * Header value is re-evaluated on each call to {@link #getValue()}.
093    *
094    * @param value
095    *    The header value supplier.
096    *    <br>Can be any of the following:
097    *    <ul>
098    *       <li>{@link String}
099    *       <li>Anything else - Converted to <c>String</c> then parsed.
100    *    </ul>
101    * @return A new {@link RetryAfter} object.
102    */
103   public static RetryAfter of(Supplier<?> value) {
104      if (value == null)
105         return null;
106      return new RetryAfter(value);
107   }
108
109   /**
110    * Constructor.
111    *
112    * @param value
113    *    The header value.
114    *    <br>Can be any of the following:
115    *    <ul>
116    *       <li>{@link String}
117    *       <li>{@link ZonedDateTime}
118    *       <li>{@link Calendar}
119    *       <li>{@link Number}
120    *       <li>Anything else - Converted to <c>String</c> then parsed.
121    *       <li>A {@link Supplier} of anything on this list.
122    *    </ul>
123    */
124   public RetryAfter(Object value) {
125      super("Retry-After", dateValue(value));
126      this.value = intValue(value);
127   }
128
129   /**
130    * Constructor
131    *
132    * @param value
133    *    The header value.
134    */
135   public RetryAfter(String value) {
136      this((Object)value);
137   }
138
139   private static Object dateValue(Object o) {
140      Object o2 = unwrap(o);
141      if (o2 == null || isInt(o2))
142         return null;
143      return o;
144   }
145
146   private static Object intValue(Object o) {
147      Object o2 = unwrap(o);
148      if (o2 == null || isInt(o2))
149         return o;
150      return null;
151   }
152
153   private static boolean isInt(Object o) {
154      if (o instanceof Number)
155         return true;
156      String s = o.toString();
157      char c0 = charAt(s, 0);
158      return Character.isDigit(c0);
159   }
160
161   @Override /* Header */
162   public String getValue() {
163      if (value == null)
164         return super.getValue();
165      Object o = unwrap(value);
166      return (o == null ? null : o.toString());
167   }
168
169   /**
170    * Returns this header value as an integer.
171    *
172    * @return This header value as a integer, or <c>-1</c> if the value is not an integer.
173    */
174   public int asInt() {
175      if (value != null) {
176         Object o = unwrap(value);
177         return o == null ? -1 : Integer.parseInt(o.toString());
178      }
179      return -1;
180   }
181}