/******************************************************************************* * Copyright (c) 2009, 2010 Fraunhofer IWU and others. * 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: * Fraunhofer IWU - initial API and implementation *******************************************************************************/ package net.enilink.komma.em; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.cache.Cache; import com.google.inject.Inject; import com.google.inject.Key; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.multibindings.Multibinder; import com.google.inject.name.Named; import net.enilink.commons.iterator.WrappedIterator; import net.enilink.composition.asm.BehaviourMethodProcessor; import net.enilink.composition.cache.IPropertyCache; import net.enilink.composition.cache.behaviours.CacheBehaviourMethodProcessor; import net.enilink.composition.properties.PropertySet; import net.enilink.komma.core.IEntityManager; import net.enilink.komma.core.URI; import net.enilink.komma.em.internal.CachedEntity; import net.enilink.komma.em.internal.CachingEntityManager; import net.enilink.komma.em.internal.Fqn; public class CachingEntityManagerModule extends DecoratingEntityManagerModule { @Override protected void configure() { super.configure(); requireBinding(new Key<Cache<Object, CachedEntity>>() { }); Multibinder<BehaviourMethodProcessor> multibinder = Multibinder.newSetBinder(binder(), BehaviourMethodProcessor.class); multibinder.addBinding().to(CacheBehaviourMethodProcessor.class); } @Provides @Inject(optional = true) Fqn provideContextKey(@Named("modifyContexts") Set<URI> modifyContexts) { if (modifyContexts != null) { return new Fqn(modifyContexts.toArray()); } return new Fqn(); } /** * The property cache implementation that is used within {@link PropertySet} * . */ static class PropertyCache implements IPropertyCache { protected static Logger log = LoggerFactory.getLogger(PropertyCache.class); final Cache<Object, CachedEntity> cache; final Fqn contextKey; PropertyCache(Cache<Object, CachedEntity> cache, Fqn contextKey) { this.cache = cache; this.contextKey = contextKey; } @SuppressWarnings("serial") class IteratorList extends ArrayList<Object> { }; @Override public Object put(Object entity, Object property, Object[] parameters, Object value) { boolean isIterator = value instanceof Iterator<?>; if (isIterator) { // usually an iterator cannot be cached // -> cache a special list instead and return an // iterator for this list IteratorList itValues = new IteratorList(); while (((Iterator<?>) value).hasNext()) { itValues.add(((Iterator<?>) value).next()); } value = itValues; } try { CachedEntity cached = cache.get(entity, CachedEntity.FACTORY); cached.put(contextKey, new Fqn(property, Arrays.asList(parameters)), value); } catch (ExecutionException e) { log.error("Error while caching property data.", e); } if (isIterator) { return WrappedIterator.create(((List<?>) value).iterator()); } return value; } @Override public Object get(Object entity, Object property, Object[] parameters) { CachedEntity cached = cache.getIfPresent(entity); if (cached != null) { Object value = cached.get(contextKey, new Fqn(property, Arrays.asList(parameters))); boolean isIterator = value instanceof IteratorList; if (isIterator) { return WrappedIterator.create(((List<?>) value).iterator()); } return value; } return null; } } @Singleton @Provides IPropertyCache providePropertyCache(final Cache<Object, CachedEntity> cache, final Fqn contextKey) { return new PropertyCache(cache, contextKey); } @Override protected Class<? extends IEntityManager> getManagerClass() { return CachingEntityManager.class; } }