blob: 81525a7cbde6f0d9bd53cf27a514378cd5f53662 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nifi.web.api.dto.util;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
/**
* Some adapters need to create a Date object from a String that contains only a portion of it, such as the time.
* This is handled by using a DateTimeFormatter with defaulted values for some fields. The defaults are derived from
* the current date/time. Because of this, we can't just create a DateTimeFormatter once and never change it, as doing
* so would result in the wrong date/time after midnight, when the date changes.
* But we don't want to create a new instance of DateTimeFormatter every time, either, because it uses lazy initialization,
* so the first call to parse() is far more expensive than subsequent calls.
* This class allows us to easily create a DateTimeFormatter and cache it, continually reusing it, until the date changes.
*/
public class ParseDefaultingDateTimeFormatter {
private final AtomicReference<Wrapper> wrapperReference = new AtomicReference<>();
private final Function<LocalDateTime, String> applicabilityTransform;
private final Function<LocalDateTime, DateTimeFormatter> formatFactory;
/**
* Default constructor
* @param applicabilityTransform a transform that creates a String that can be used identify whether or not previously created DateTimeFormatter exists. This may be created, for instance,
* by concatenating specific fields from the given LocalDateTime
* @param formatFactory a transform that will give us a DateTimeFormatter that is applicable for the given LocalDateTime
*/
public ParseDefaultingDateTimeFormatter(final Function<LocalDateTime, String> applicabilityTransform, final Function<LocalDateTime, DateTimeFormatter> formatFactory) {
this.applicabilityTransform = applicabilityTransform;
this.formatFactory = formatFactory;
}
public DateTimeFormatter get() {
final LocalDateTime now = LocalDateTime.now();
final String applicabilityValue = applicabilityTransform.apply(now);
final Wrapper wrapper = wrapperReference.get();
if (wrapper != null && wrapper.getApplicabilityValue().equals(applicabilityValue)) {
return wrapper.getFormatter();
}
final DateTimeFormatter formatter = formatFactory.apply(now);
final Wrapper updatedWrapper = new Wrapper(formatter, applicabilityValue);
wrapperReference.compareAndSet(wrapper, updatedWrapper);
return formatter;
}
private static class Wrapper {
private final DateTimeFormatter formatter;
private final String applicabilityValue;
public Wrapper(final DateTimeFormatter formatter, final String applicabilityValue) {
this.formatter = formatter;
this.applicabilityValue = applicabilityValue;
}
public DateTimeFormatter getFormatter() {
return formatter;
}
public String getApplicabilityValue() {
return applicabilityValue;
}
}
}