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}