| /** |
| * Licensed to jclouds, Inc. (jclouds) under one or more |
| * contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. jclouds 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.jclouds.util; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.Serializable; |
| import java.util.concurrent.TimeUnit; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Preconditions; |
| import com.google.common.base.Supplier; |
| import com.google.common.io.OutputSupplier; |
| |
| /** |
| * |
| * @author Adrian Cole |
| */ |
| public class Suppliers2 { |
| |
| /** |
| * converts an {@link OutputStream} to an {@link OutputSupplier} |
| * |
| */ |
| public static OutputSupplier<OutputStream> newOutputStreamSupplier(final OutputStream output) { |
| checkNotNull(output, "output"); |
| return new OutputSupplier<OutputStream>() { |
| public OutputStream getOutput() throws IOException { |
| return output; |
| } |
| }; |
| } |
| |
| /** |
| * See Supplier.memoizeWithExpiration. |
| * |
| * Difference between this impl and v11.0 is that we fix http://code.google.com/p/guava-libraries/issues/detail?id=857. |
| */ |
| public static <T> Supplier<T> memoizeWithExpirationOnAbsoluteInterval(Supplier<T> delegate, long duration, TimeUnit unit) { |
| return new ExpiringMemoizingSupplier<T>(delegate, duration, unit); |
| } |
| |
| @VisibleForTesting |
| static class ExpiringMemoizingSupplier<T> implements Supplier<T>, Serializable { |
| final Supplier<T> delegate; |
| final long durationNanos; |
| transient volatile T value; |
| // The special value 0 means "not yet initialized". |
| transient volatile long expirationNanos; |
| |
| ExpiringMemoizingSupplier(Supplier<T> delegate, long duration, TimeUnit unit) { |
| this.delegate = Preconditions.checkNotNull(delegate); |
| this.durationNanos = unit.toNanos(duration); |
| Preconditions.checkArgument(duration > 0); |
| } |
| |
| @Override |
| public T get() { |
| // Another variant of Double Checked Locking. |
| // |
| // We use two volatile reads. We could reduce this to one by |
| // putting our fields into a holder class, but (at least on x86) |
| // the extra memory consumption and indirection are more |
| // expensive than the extra volatile reads. |
| long nanos = expirationNanos; |
| long now = System.nanoTime(); |
| if (nanos == 0 || now - nanos >= 0) { |
| synchronized (this) { |
| if (nanos == expirationNanos) { // recheck for lost race |
| |
| // Set value to null prior to retrieving new val, so old and new are not held in memory simultaneously |
| value = null; |
| |
| T t = delegate.get(); |
| value = t; |
| |
| // Update now so that, if call was expensive, we keep value for the full duration |
| now = System.nanoTime(); |
| |
| nanos = now + durationNanos; |
| // In the very unlikely event that nanos is 0, set it to 1; |
| // no one will notice 1 ns of tardiness. |
| expirationNanos = (nanos == 0) ? 1 : nanos; |
| return t; |
| } |
| } |
| } |
| return value; |
| } |
| |
| private static final long serialVersionUID = 0; |
| } |
| } |