/** * 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.calls; import static com.google.common.base.Optional.of; import static org.eclipse.recommenders.utils.Constants.*; import static org.eclipse.recommenders.utils.Zips.*; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.eclipse.recommenders.models.IInputStreamTransformer; import org.eclipse.recommenders.models.UniqueTypeName; import org.eclipse.recommenders.utils.IOUtils; import org.eclipse.recommenders.utils.Openable; import org.eclipse.recommenders.utils.Zips; import org.eclipse.recommenders.utils.names.ITypeName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; /** * A model provider that uses a single zip file to resolve and load call models from. * <p> * Note that this provider does not implement any pooling behavior, i.e., calls to {@link #acquireModel(UniqueTypeName)} * may return the <b>same</b> {@link ICallModel} independent of whether {@link #releaseModel(ICallModel)} was called or * not. Thus, these <b>models should not be shared between and used by several recommenders at the same time</b>. */ @Beta public class SingleZipCallModelProvider implements ICallModelProvider, Openable { private static final Logger LOG = LoggerFactory.getLogger(SingleZipCallModelProvider.class); private static final int CACHE_SIZE = 30; private final LoadingCache<ITypeName, ICallModel> cache = CacheBuilder.newBuilder() .expireAfterAccess(3, TimeUnit.MINUTES).maximumSize(CACHE_SIZE).build(new CallNetCacheLoader()); private final File models; private final Map<String, IInputStreamTransformer> transformers; private ZipFile zip; public SingleZipCallModelProvider(File models, Map<String, IInputStreamTransformer> transformers) { this.models = models; this.transformers = transformers; } @Override public void open() throws IOException { readFully(models); zip = new ZipFile(models); } @Override public void close() throws IOException { closeQuietly(zip); } @Override public Optional<ICallModel> acquireModel(UniqueTypeName key) { try { ICallModel net = cache.get(key.getName()); net.reset(); return of(net); } catch (ExecutionException e) { LOG.error("Failed to acquire model for " + key, e); return Optional.absent(); } } public Set<ITypeName> acquireableTypes() { Set<ITypeName> acquireableTypesSet = Zips.types(zip.entries(), DOT_JBIF); for (Entry<String, IInputStreamTransformer> transformer : transformers.entrySet()) { acquireableTypesSet.addAll(Zips.types(zip.entries(), DOT_JBIF + "." + transformer.getKey())); //$NON-NLS-1$ } return acquireableTypesSet; } @Override public void releaseModel(ICallModel value) { } private final class CallNetCacheLoader extends CacheLoader<ITypeName, ICallModel> { @Override public ICallModel load(ITypeName type) throws IOException { InputStream in = null; try { String path = Zips.path(type, DOT_JBIF); in = getInputStream(zip, path).orNull(); ICallModel model = null; if (in != null) { model = JayesCallModel.load(in, type); } if (model == null) { return NullCallModel.INSTANCE; } return model; } finally { IOUtils.closeQuietly(in); } } private Optional<InputStream> getInputStream(ZipFile zip, String path) throws IOException { for (Entry<String, IInputStreamTransformer> transformer : transformers.entrySet()) { ZipEntry toTransform = zip.getEntry(path + "." + transformer.getKey()); //$NON-NLS-1$ if (toTransform == null) { continue; } return Optional.of(transformer.getValue().transform(zip.getInputStream(toTransform))); } ZipEntry entry = zip.getEntry(path); if (entry == null) { return Optional.absent(); } return Optional.of(zip.getInputStream(entry)); } } }