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 java.time.temporal.ChronoUnit.*; 017import static org.apache.juneau.internal.StringUtils.*; 018 019import java.time.*; 020import java.util.*; 021import java.util.function.*; 022 023import org.apache.juneau.annotation.*; 024import org.apache.juneau.assertions.*; 025import org.apache.juneau.http.*; 026 027/** 028 * Category of headers that consist of a single HTTP-date. 029 * 030 * <p> 031 * <h5 class='figure'>Example</h5> 032 * <p class='bcode w800'> 033 * If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT 034 * </p> 035 * 036 * <ul class='seealso'> 037 * <li class='extlink'>{@doc ExtRFC2616} 038 * </ul> 039 */ 040@BeanIgnore 041public class BasicDateHeader extends BasicHeader { 042 043 private static final long serialVersionUID = 1L; 044 045 /** 046 * Convenience creator. 047 * 048 * @param name The header name. 049 * @param value 050 * The header value. 051 * <br>Can be any of the following: 052 * <ul> 053 * <li><c>String</c> - An RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>). 054 * <li>{@link ZonedDateTime} 055 * <li>{@link Calendar} 056 * <li>Anything else - Converted to <c>String</c>. 057 * </ul> 058 * @return A new {@link BasicDateHeader} object, or <jk>null</jk> if the name or value is <jk>null</jk>. 059 */ 060 public static BasicDateHeader of(String name, Object value) { 061 if (isEmpty(name) || value == null) 062 return null; 063 return new BasicDateHeader(name, value); 064 } 065 066 /** 067 * Convenience creator using supplier. 068 * 069 * <p> 070 * Header value is re-evaluated on each call to {@link #getValue()}. 071 * 072 * @param name The header name. 073 * @param value 074 * The header value supplier. 075 * <br>Can be any of the following: 076 * <ul> 077 * <li><c>String</c> - An RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>). 078 * <li>{@link ZonedDateTime} 079 * <li>{@link Calendar} 080 * <li>Anything else - Converted to <c>String</c>. 081 * </ul> 082 * @return A new {@link BasicDateHeader} object, or <jk>null</jk> if the name or value is <jk>null</jk>. 083 */ 084 public static BasicDateHeader of(String name, Supplier<?> value) { 085 if (isEmpty(name) || value == null) 086 return null; 087 return new BasicDateHeader(name, value); 088 } 089 090 private ZonedDateTime parsed; 091 092 /** 093 * Constructor. 094 * 095 * @param name The header name. 096 * @param value The header value. 097 * <br>Can be any of the following: 098 * <ul> 099 * <li><c>String</c> - An RFC-1123 formated string (e.g. <js>"Sat, 29 Oct 1994 19:43:31 GMT"</js>). 100 * <li>{@link ZonedDateTime} - Will be truncated to seconds. 101 * <li>{@link Calendar} - Will be truncated to seconds. 102 * <li>Anything else - Converted to <c>String</c>. 103 * <li>A {@link Supplier} of anything on this list. 104 * </ul> 105 */ 106 public BasicDateHeader(String name, Object value) { 107 super(name, value); 108 if (! isSupplier(value)) 109 parsed = getParsedValue(); 110 } 111 112 @Override /* Header */ 113 public String getValue() { 114 Object o = getRawValue(); 115 if (o == null) 116 return null; 117 if (o instanceof String) 118 return (String)o; 119 return RFC_1123_DATE_TIME.format(getParsedValue()); 120 } 121 122 /** 123 * Returns this header value as a {@link java.util.Calendar}. 124 * 125 * @return This header value as a {@link java.util.Calendar}, or <jk>null</jk> if the header could not be parsed. 126 */ 127 public Calendar asCalendar() { 128 ZonedDateTime zdt = getParsedValue(); 129 return zdt == null ? null : GregorianCalendar.from(zdt); 130 } 131 132 /** 133 * Returns this header value as a {@link java.util.Date}. 134 * 135 * @return This header value as a {@link java.util.Date}, or <jk>null</jk> if the header could not be parsed. 136 */ 137 public java.util.Date asDate() { 138 Calendar c = asCalendar(); 139 return c == null ? null : c.getTime(); 140 } 141 142 /** 143 * Returns this header value as a {@link ZonedDateTime}. 144 * 145 * @return This header value as a {@link ZonedDateTime}, or <jk>null</jk> if the header could not be parsed. 146 */ 147 public ZonedDateTime asZonedDateTime() { 148 return getParsedValue(); 149 } 150 151 /** 152 * Provides the ability to perform fluent-style assertions on this header. 153 * 154 * <h5 class='section'>Examples:</h5> 155 * <p class='bcode w800'> 156 * <jc>// Validates the response body content is not expired.</jc> 157 * client 158 * .get(<jsf>URL</jsf>) 159 * .run() 160 * .getDateHeader(<js>"Expires"</js>).assertThat().isLessThan(<jk>new</jk> Date()); 161 * </p> 162 * 163 * @return A new fluent assertion object. 164 * @throws AssertionError If assertion failed. 165 */ 166 public FluentZonedDateTimeAssertion<BasicDateHeader> assertZonedDateTime() { 167 return new FluentZonedDateTimeAssertion<>(asZonedDateTime(), this); 168 } 169 170 private ZonedDateTime getParsedValue() { 171 if (parsed != null) 172 return parsed; 173 Object o = getRawValue(); 174 if (o == null) 175 return null; 176 if (o instanceof ZonedDateTime) 177 return ((ZonedDateTime)o).truncatedTo(SECONDS); 178 if (o instanceof GregorianCalendar) 179 return ((GregorianCalendar)o).toZonedDateTime().truncatedTo(SECONDS); 180 return ZonedDateTime.from(RFC_1123_DATE_TIME.parse(o.toString())).truncatedTo(SECONDS); 181 } 182}