/* * Copyright 2013 Google Inc. * * Licensed 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 com.google.template.soy.data; import com.google.template.soy.jbcsrc.api.RenderResult; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import javax.annotation.Nonnull; /** * Important: Do not use outside of Soy code (treat as superpackage-private). * * <p>SoyValueProvider implementation that represents a wrapped future. * */ public final class SoyFutureValueProvider extends SoyAbstractCachingValueProvider { private static final FutureBlockCallback NOOP = new FutureBlockCallback() { @Override public void beforeBlock() {} }; /** * Allows threads to register a {@link FutureBlockCallback}. * * <p>When calling {@link #resolve()} on this {@link SoyFutureValueProvider}, if the thread needs * to block on the future (because {@link Future#isDone()} is {@code false}), then it will call * the currently registered block callback immediately prior blocking. See {@code * RenderVisitor.exec} for the motivating usecase for this hook. * * <p>Important: Do not use outside of Soy code (treat as superpackage-private). */ public static final ThreadLocal<FutureBlockCallback> futureBlockCallback = new ThreadLocal<FutureBlockCallback>() { @Override protected FutureBlockCallback initialValue() { return NOOP; } }; /** The instance of SoyValueConverter to use for converting the future value (after retrieval). */ private final SoyValueConverter valueConverter; /** The wrapped Future object that will provide the value, if needed. */ private final Future<?> future; /** A callback that gets fired just before this provider will block on a future. */ public interface FutureBlockCallback { void beforeBlock(); } /** * @param valueConverter The instance of SoyValueConverter to use for converting the future value * (after retrieval). * @param future The underlying Future object. */ public SoyFutureValueProvider(SoyValueConverter valueConverter, Future<?> future) { this.valueConverter = valueConverter; this.future = future; } @Override public RenderResult status() { return future.isDone() ? RenderResult.done() : RenderResult.continueAfter(future); } /** * Calls Future.get() and then converts the result to SoyValue. Note that this result can never * return {@code null}, since null converts to {@code NullData.INSTANCE}. */ @Override @Nonnull protected final SoyValue compute() { try { if (!future.isDone()) { futureBlockCallback.get().beforeBlock(); } return valueConverter.convert(future.get()).resolve(); } catch (ExecutionException e) { throw new SoyFutureException(e.getCause()); } catch (Throwable e) { throw new SoyFutureException(e); } } }