1 /* 2 * ==================================================================== 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * ==================================================================== 20 * 21 * This software consists of voluntary contributions made by many 22 * individuals on behalf of the Apache Software Foundation. For more 23 * information on the Apache Software Foundation, please see 24 * <http://www.apache.org/>. 25 * 26 */ 27 28 package org.apache.hc.core5.util; 29 30 import java.text.ParseException; 31 import java.text.SimpleDateFormat; 32 import java.util.Objects; 33 import java.util.concurrent.TimeUnit; 34 35 /** 36 * A deadline based on a UNIX time, the elapsed since 00:00:00 Coordinated Universal Time (UTC), Thursday, 1 January 37 * 1970. 38 * 39 * @since 5.0 40 */ 41 public class Deadline { 42 43 /** 44 * The format used for parsing and formatting dates. 45 */ 46 public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; 47 48 /** 49 * A special internal value that marks a deadline as the longest possible. 50 */ 51 private static final long INTERNAL_MAX_VALUE = Long.MAX_VALUE; 52 53 /** 54 * A special internal value that marks a deadline as the shortest possible. 55 */ 56 private static final long INTERNAL_MIN_VALUE = 0; 57 58 /** 59 * The maximum (longest-lived) deadline. 60 */ 61 public static Deadlineine.html#Deadline">Deadline MAX_VALUE = new Deadline(INTERNAL_MAX_VALUE); 62 63 /** 64 * The minimum (shortest-lived) deadline. 65 */ 66 public static Deadlineine.html#Deadline">Deadline MIN_VALUE = new Deadline(INTERNAL_MIN_VALUE); 67 68 private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DATE_FORMAT); 69 70 /** 71 * Calculates a deadline with a given time in milliseconds plus a given time value. Non-positive time values 72 * represent an indefinite timeout without a deadline. 73 * 74 * @param timeMillis A time in UNIX milliseconds, usually the current time. 75 * @param timeValue time value to add to {@code timeMillis}. 76 * @return a deadline representing the current time plus the given time value. 77 */ 78 public static Deadline calculate(final long timeMillis, final TimeValue timeValue) { 79 if (TimeValue.isPositive(timeValue)) { 80 // TODO handle unlikely overflow 81 final long deadline = timeMillis + timeValue.toMilliseconds(); 82 return deadline < 0 ? Deadline.MAX_VALUE : Deadline.fromUnixMilliseconds(deadline); 83 } 84 return Deadline.MAX_VALUE; 85 } 86 87 /** 88 * Calculates a deadline from now plus a given time value. Non-positive time values 89 * represent an indefinite timeout without a deadline. 90 * 91 * @param timeValue time value to add to {@code timeMillis}. 92 * @return a deadline representing the current time plus the given time value. 93 */ 94 public static Deadline calculate(final TimeValue timeValue) { 95 return calculate(System.currentTimeMillis(), timeValue); 96 } 97 98 /** 99 * Creates a deadline from a UNIX time in milliseconds. 100 * 101 * @param value a UNIX time in milliseconds. 102 * @return a new deadline. 103 */ 104 public static Deadline fromUnixMilliseconds(final long value) { 105 if (value == INTERNAL_MAX_VALUE) { 106 return MAX_VALUE; 107 } 108 if (value == INTERNAL_MIN_VALUE) { 109 return MIN_VALUE; 110 } 111 return new Deadline(value); 112 } 113 114 /** 115 * Creates a deadline from a string in the format {@value #DATE_FORMAT}. 116 * 117 * @param source a string in the format {@value #DATE_FORMAT}. 118 * @return a deadline from a string in the format {@value #DATE_FORMAT}. 119 * @throws ParseException if the specified source string cannot be parsed. 120 */ 121 public static Deadline parse(final String source) throws ParseException { 122 return fromUnixMilliseconds(simpleDateFormat.parse(source).getTime()); 123 } 124 125 private volatile boolean frozen; 126 127 private volatile long lastCheck; 128 129 /* 130 * Internal representation is a UNIX time. 131 */ 132 private final long value; 133 134 /** 135 * Constructs a new instance with the given UNIX time in milliseconds. 136 * 137 * @param deadlineMillis UNIX time in milliseconds. 138 */ 139 private Deadline(final long deadlineMillis) { 140 super(); 141 this.value = deadlineMillis; 142 setLastCheck(); 143 } 144 145 @Override 146 public boolean equals(final Object obj) { 147 // Only take into account the deadline value. 148 if (this == obj) { 149 return true; 150 } 151 if (obj == null) { 152 return false; 153 } 154 if (getClass() != obj.getClass()) { 155 return false; 156 } 157 final Deadline./../../../../org/apache/hc/core5/util/Deadline.html#Deadline">Deadline other = (Deadline) obj; 158 return value == other.value; 159 } 160 161 /** 162 * Formats this deadline. 163 * 164 * @param overdueTimeUnit the time unit to show how much over the deadline we are. 165 * @return a formatted string. 166 */ 167 public String format(final TimeUnit overdueTimeUnit) { 168 return String.format("Deadline: %s, %s overdue", formatTarget(), remainingTimeValue()); 169 } 170 171 /** 172 * Formats the deadline value as a string in the format {@value #DATE_FORMAT}. 173 * 174 * @return a formatted string in the format {@value #DATE_FORMAT}. 175 */ 176 public String formatTarget() { 177 return simpleDateFormat.format(value); 178 } 179 180 public Deadline freeze() { 181 frozen = true; 182 return this; 183 } 184 185 /** 186 * Package private for testing. 187 * 188 * @return the last time we checked the current time. 189 */ 190 long getLastCheck() { 191 return lastCheck; 192 } 193 194 /** 195 * Gets the UNIX time deadline value. 196 * 197 * @return the UNIX time deadline value. 198 */ 199 public long getValue() { 200 return value; 201 } 202 203 @Override 204 public int hashCode() { 205 // Only take into account the deadline value. 206 return Objects.hash(value); 207 } 208 209 /** 210 * Returns whether this deadline occurs before the given time in milliseconds. 211 * 212 * @param millis the time to compare. 213 * @return whether this deadline occurs before the given time in milliseconds. 214 */ 215 public boolean isBefore(final long millis) { 216 return value < millis; 217 } 218 219 /** 220 * Returns whether the deadline has expired. 221 * 222 * @return whether the deadline has expired. 223 */ 224 public boolean isExpired() { 225 setLastCheck(); 226 return value < this.lastCheck; 227 } 228 229 /** 230 * Returns whether this deadline is the maximum deadline. 231 * 232 * @return whether this deadline is the maximum deadline. 233 */ 234 public boolean isMax() { 235 return value == INTERNAL_MAX_VALUE; 236 } 237 238 /** 239 * Returns whether this deadline is the minimum deadline. 240 * 241 * @return whether this deadline is the minimum deadline. 242 */ 243 public boolean isMin() { 244 return value == INTERNAL_MIN_VALUE; 245 } 246 247 /** 248 * Returns whether this deadline has not expired. 249 * 250 * @return whether this deadline has not expired. 251 */ 252 public boolean isNotExpired() { 253 setLastCheck(); 254 return value >= this.lastCheck; 255 } 256 257 /** 258 * Returns the smaller of this and another {@code Deadline}. 259 * 260 * @param other another deadline. 261 * @return the smaller of {@code this} and {@code other}. 262 */ 263 public Deadline/Deadline.html#Deadline">Deadline min(final Deadline other) { 264 return value <= other.value ? this : other; 265 } 266 267 /** 268 * Returns the difference in milliseconds between the deadline and now. 269 * 270 * @return the different in milliseconds between the deadline and now. 271 */ 272 public long remaining() { 273 setLastCheck(); 274 return value - lastCheck; 275 } 276 277 /** 278 * Returns the difference as a TimeValue between the deadline and now. 279 * 280 * @return Returns the different as a TimeValue between the deadline and now. 281 */ 282 public TimeValue remainingTimeValue() { 283 return TimeValue.of(remaining(), TimeUnit.MILLISECONDS); 284 } 285 286 private void setLastCheck() { 287 if (!frozen) { 288 this.lastCheck = System.currentTimeMillis(); 289 }} 290 291 @Override 292 public String toString() { 293 return formatTarget(); 294 } 295 296 }