001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 (the 006 * "License"); you may not use this file except in compliance with the 007 * License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.http.header; 018 019 020import static java.time.format.DateTimeFormatter.*; 021import static java.time.temporal.ChronoUnit.*; 022import static org.apache.juneau.commons.utils.Shorts.*; 023 024import java.time.*; 025import java.util.*; 026import java.util.function.*; 027 028/** 029 * Base for HTTP-date headers. 030 * 031 * @since 9.2.1 032 */ 033@SuppressWarnings({ 034 "java:S2160", // equals() on HttpHeaderBean uses name + getValue(); typed state is reflected in getValue() 035 "java:S9149", // Public Juneau DSL factory; hiding parent of()/create() is intentional. 036 "unchecked" // Supplier<?> branches cast to typed suppliers after lazy-mode check 037}) 038public class HttpDateHeader extends HttpHeaderBean { 039 040 public static final int LAZY_WIRE_STRING = 0; 041 public static final int LAZY_ZONED_DATE_TIME = 1; 042 043 private final ZonedDateTime cachedZdt; 044 private final Supplier<?> lazySupplier; 045 private final int lazyMode; 046 047 /** 048 * Creates an {@link HttpDateHeader} by parsing the given RFC-1123 wire-format value. 049 * 050 * @param name Header name. Must not be {@code null}. 051 * @param wireValue Wire value (e.g. {@code "Sun, 06 Nov 1994 08:49:37 GMT"}). May be {@code null} or empty. 052 * @return A new instance. Never {@code null}. 053 */ 054 public static HttpDateHeader of(String name, String wireValue) { 055 return new HttpDateHeader(name, wireValue); 056 } 057 058 /** 059 * Creates an {@link HttpDateHeader} with the given typed value. 060 * 061 * @param name Header name. Must not be {@code null}. 062 * @param typedValue The date value. May be {@code null}. 063 * @return A new instance. Never {@code null}. 064 */ 065 public static HttpDateHeader of(String name, ZonedDateTime typedValue) { 066 return new HttpDateHeader(name, typedValue); 067 } 068 069 /** 070 * Constructor. 071 * 072 * @param name Header name. Must not be <jk>null</jk>. 073 * @param value Wire value. Can be <jk>null</jk> or empty, in which case the parsed value is <jk>null</jk>. 074 */ 075 protected HttpDateHeader(String name, String value) { 076 super(name, (String)null); 077 this.cachedZdt = ie(value) ? null : parseHttpDate(value); 078 this.lazySupplier = null; 079 this.lazyMode = -1; 080 } 081 082 /** 083 * Constructor. 084 * 085 * @param name Header name. Must not be <jk>null</jk>. 086 * @param value The date value. Can be <jk>null</jk>, in which case the parsed value is <jk>null</jk>. 087 */ 088 protected HttpDateHeader(String name, ZonedDateTime value) { 089 super(name, (String)null); 090 this.cachedZdt = value; 091 this.lazySupplier = null; 092 this.lazyMode = -1; 093 } 094 095 /** 096 * Constructor with lazy value supplier. 097 * 098 * @param name Header name. Must not be <jk>null</jk>. 099 * @param supplier The lazy value supplier. Must not be <jk>null</jk>. 100 * @param lazyMode Either {@link #LAZY_WIRE_STRING} or {@link #LAZY_ZONED_DATE_TIME}. 101 */ 102 protected HttpDateHeader(String name, Supplier<?> supplier, int lazyMode) { 103 super(name, lazyMode == LAZY_WIRE_STRING 104 ? ((Supplier<String>) supplier)::get 105 : () -> { 106 var z = ((Supplier<ZonedDateTime>) supplier).get(); 107 if (z == null) 108 return null; 109 return RFC_1123_DATE_TIME.format(z); 110 }); 111 this.cachedZdt = null; 112 this.lazySupplier = supplier; 113 this.lazyMode = lazyMode; 114 } 115 116 public Optional<ZonedDateTime> asZonedDateTime() { 117 return o(toZonedDateTime()); 118 } 119 120 /** 121 * Returns the RFC-1123 wire-format value of this header. 122 * 123 * @return The wire value, or <jk>null</jk> if the value is unset. 124 */ 125 @Override 126 public String getValue() { 127 if (lazyMode == LAZY_ZONED_DATE_TIME) { 128 var x = ((Supplier<ZonedDateTime>) lazySupplier).get(); 129 return x == null ? null : RFC_1123_DATE_TIME.format(x); 130 } 131 if (cachedZdt != null) 132 return RFC_1123_DATE_TIME.format(cachedZdt); 133 return super.getValue(); 134 } 135 136 /** 137 * Returns the parsed value of this header, or the specified default if unset. 138 * 139 * @param other The default value. Can be <jk>null</jk> to allow a <jk>null</jk> result when the header is unset. 140 * @return The parsed value, or <c>other</c> if the value is unset. Can be <jk>null</jk> if <c>other</c> is <jk>null</jk>. 141 */ 142 public ZonedDateTime orElse(ZonedDateTime other) { 143 var x = toZonedDateTime(); 144 return nn(x) ? x : other; 145 } 146 147 /** 148 * Returns the parsed value of this header. 149 * 150 * @return The parsed value, or <jk>null</jk> if the value is unset. 151 */ 152 public ZonedDateTime toZonedDateTime() { 153 if (lazyMode == LAZY_ZONED_DATE_TIME) 154 return ((Supplier<ZonedDateTime>) lazySupplier).get(); 155 if (cachedZdt != null) 156 return cachedZdt; 157 var v = super.getValue(); 158 return ie(v) ? null : parseHttpDate(v); 159 } 160 161 private static ZonedDateTime parseHttpDate(String value) { 162 return ZonedDateTime.from(RFC_1123_DATE_TIME.parse(value)).truncatedTo(SECONDS); 163 } 164}