001// Copyright 2013 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http:#www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.internal.util; 016 017import org.apache.tapestry5.ioc.Invokable; 018import org.apache.tapestry5.services.InvalidationEventHub; 019 020/** 021 * A utility class for managing a cacheable result that can be recomputed when needed. 022 */ 023public class RecomputableSupport 024{ 025 private volatile int masterVersion = 1; 026 027 /** 028 * Invalidates any existing {@link #create(org.apache.tapestry5.ioc.Invokable)} wrappers} such that they will 029 * re-perform the computation when next invoked. 030 */ 031 public void invalidate() 032 { 033 masterVersion++; 034 } 035 036 /** 037 * Forces {@link #invalidate()} to be invoked when the hub emits an {@linkplain InvalidationEventHub#addInvalidationCallback(Runnable) invalidation callback}. 038 * 039 * @param hub 040 */ 041 public void initialize(InvalidationEventHub hub) 042 { 043 hub.addInvalidationCallback(new Runnable() 044 { 045 public void run() 046 { 047 invalidate(); 048 } 049 }); 050 } 051 052 /** 053 * Wraps a computation with caching logic; once computed, the Invokable will return the same value, until 054 * {@link #invalidate()} is invoked. 055 * 056 * @param invokable 057 * a computation to perform, whose results are cacheable until invalidated 058 * @param <T> 059 * type of result 060 * @return caching-enabled wrapper around invokable 061 */ 062 public <T> Invokable<T> create(final Invokable<T> invokable) 063 { 064 return new Invokable<T>() 065 { 066 private volatile int localVersion = masterVersion; 067 068 private volatile T cachedResult; 069 070 public T invoke() 071 { 072 // Has the master version changed since the computation was last executed? 073 if (localVersion != masterVersion) 074 { 075 cachedResult = null; 076 localVersion = masterVersion; 077 } 078 079 if (cachedResult == null) 080 { 081 cachedResult = invokable.invoke(); 082 } 083 084 085 return cachedResult; 086 } 087 }; 088 } 089}