/*! * Copyright 2010 - 2015 Pentaho Corporation. All rights reserved. * * 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 org.pentaho.di.repository.pur; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import org.pentaho.di.core.util.ExecutorUtil; public class ActiveCache<Key, Value> { private final Map<Key, ActiveCacheResult<Value>> valueMap; private final Map<Key, Future<ActiveCacheResult<Value>>> loadingMap; private final ActiveCacheLoader<Key, Value> loader; private final long timeout; private final ExecutorServiceGetter executorServiceGetter; public static interface ExecutorServiceGetter { ExecutorService getExecutor(); } public ActiveCache( ActiveCacheLoader<Key, Value> loader, long timeout ) { this( loader, timeout, new ExecutorServiceGetter() { @Override public ExecutorService getExecutor() { return ExecutorUtil.getExecutor(); } } ); } public ActiveCache( ActiveCacheLoader<Key, Value> loader, long timeout, ExecutorServiceGetter executorServiceGetter ) { this( loader, new HashMap<Key, ActiveCacheResult<Value>>(), new HashMap<Key, Future<ActiveCacheResult<Value>>>(), timeout, executorServiceGetter ); } public ActiveCache( ActiveCacheLoader<Key, Value> loader, Map<Key, ActiveCacheResult<Value>> valueMap, Map<Key, Future<ActiveCacheResult<Value>>> loadingMap, long timeout, ExecutorServiceGetter executorServiceGetter ) { this.valueMap = valueMap; this.loadingMap = loadingMap; this.loader = loader; this.timeout = timeout; this.executorServiceGetter = executorServiceGetter; } public Value get( Key key ) throws Exception { ActiveCacheResult<Value> result = null; Future<ActiveCacheResult<Value>> futureResult = null; synchronized ( this ) { result = valueMap.get( key ); boolean shouldReload = false; long time = System.currentTimeMillis(); if ( result == null || result.getTimeLoaded() + timeout < time ) { // Expired, we need to wait on reload result = null; shouldReload = true; } else if ( result.getTimeLoaded() + ( timeout / 2.0 ) < time ) { // Preemptively reload shouldReload = true; } if ( shouldReload ) { futureResult = loadingMap.get( key ); if ( futureResult == null ) { futureResult = executorServiceGetter.getExecutor().submit( new ActiveCacheCallable<Key, Value>( this, valueMap, loadingMap, key, loader ) ); loadingMap.put( key, futureResult ); } } } if ( result == null ) { result = futureResult.get(); } Exception exception = result.getException(); if ( exception != null ) { throw exception; } return result.getValue(); } }