/** * Copyright (c) 2010, 2013 Darmstadt University of Technology. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marcel Bruch - initial API and implementation. */ package org.eclipse.recommenders.models; import static com.google.common.base.Optional.*; import static java.util.concurrent.TimeUnit.MINUTES; import java.io.IOException; import java.util.IdentityHashMap; import java.util.Map; import org.apache.commons.pool.BaseKeyedPoolableObjectFactory; import org.apache.commons.pool.impl.GenericKeyedObjectPool; import org.eclipse.recommenders.utils.Nullable; import org.eclipse.recommenders.utils.Throws; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Optional; /** * A model provider implementation that pools recommendation models to further improve performance. Note that models * need to be release by clients. Otherwise the pool may be exhausted quickly. */ public abstract class PoolingModelProvider<K extends IUniqueName<?>, M> extends SimpleModelProvider<K, M> { private final Logger log = LoggerFactory.getLogger(getClass()); // which models are currently borrowed to someone? // we need this mapping for implementing releaseModel properly so that clients don't have to submit their keys too. private final IdentityHashMap<M, K> borrowedModels = new IdentityHashMap<>(); // model pool // REVIEW: we may want to make pool creation configurable later? private GenericKeyedObjectPool<K, M> pool = createModelPool(); public PoolingModelProvider(IModelRepository repository, IModelArchiveCoordinateAdvisor index, String modelType, Map<String, IInputStreamTransformer> transformers) { super(repository, index, modelType, transformers); } private GenericKeyedObjectPool<K, M> createModelPool() { GenericKeyedObjectPool<K, M> pool = new GenericKeyedObjectPool<K, M>(new ModelPoolFactoryMediator()); pool.setMaxTotal(30); pool.setMaxIdle(5); pool.setWhenExhaustedAction(GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL); // run clean up every 5 minutes: pool.setTimeBetweenEvictionRunsMillis(MINUTES.toMillis(5)); // models are evictable after 5 minutes pool.setMinEvictableIdleTimeMillis(MINUTES.toMillis(5)); return pool; } @Override public Optional<M> acquireModel(@Nullable K key) { if (key == null) { return absent(); } try { M model = pool.borrowObject(key); borrowedModels.put(model, key); return of(model); } catch (Exception e) { // Model provider could not find a model for the given key. return absent(); } } @Override public void releaseModel(@Nullable M model) { if (model == null) { return; } try { K key = borrowedModels.remove(model); pool.returnObject(key, model); } catch (Exception e) { log.error("Exception while releasing. Couldn't release model " + model, e); } } /** * Mediates calls from Apache Commons Pool implementation to our {create,destroy,passivate}Model() methods above. */ private final class ModelPoolFactoryMediator extends BaseKeyedPoolableObjectFactory<K, M> { @Override @Nullable public M makeObject(K key) throws Exception { M result = PoolingModelProvider.super.acquireModel(key).orNull(); if (result == null) { Throws.throwCancelationException("Model not found for key '%s'", key); } return result; } @Override public void activateObject(K key, M obj) throws Exception { PoolingModelProvider.this.activateModel(obj); } @Override public void passivateObject(K key, M obj) throws Exception { PoolingModelProvider.this.passivateModel(obj); } @Override public void destroyObject(K key, M obj) throws Exception { PoolingModelProvider.this.destroyModel(obj); } } @Override public void close() throws IOException { try { super.close(); pool.close(); } catch (Exception e) { throw new IOException(e); } } /** * Invoked before the model is returned from the pool. */ protected void activateModel(M model) { } /** * Invoked after the model was released and returned to the pool. */ protected void passivateModel(M model) { } /** * Invoked when the model is removed from the pool. */ protected void destroyModel(M model) { } }