org.lenskit.LenskitRecommenderEngineLoader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of lenskit-core Show documentation
Show all versions of lenskit-core Show documentation
The core of LensKit, providing basic implementations and algorithm support.
The newest version!
/*
* LensKit, an open source recommender systems toolkit.
* Copyright 2010-2016 LensKit Contributors. See CONTRIBUTORS.md.
* Work on LensKit has been funded by the National Science Foundation under
* grants IIS 05-34939, 08-08692, 08-12148, and 10-17697.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.lenskit;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import org.grouplens.grapht.Component;
import org.grouplens.grapht.Dependency;
import org.grouplens.grapht.ResolutionException;
import org.grouplens.grapht.graph.DAGNode;
import org.grouplens.grapht.solver.DependencySolver;
import org.grouplens.grapht.util.ClassLoaderContext;
import org.grouplens.grapht.util.ClassLoaders;
import org.lenskit.util.io.CompressionMode;
import org.lenskit.util.io.CustomClassLoaderObjectInputStream;
import org.lenskit.util.io.LKFileUtils;
import org.lenskit.inject.GraphtUtils;
import org.lenskit.inject.RecommenderGraphBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.WillClose;
import java.io.*;
import java.util.List;
/**
* Load a pre-built recommender engine from a file.
*
* @since 2.1
* @author GroupLens Research
*/
public class LenskitRecommenderEngineLoader {
private static final Logger logger = LoggerFactory.getLogger(LenskitRecommenderEngineLoader.class);
private ClassLoader classLoader;
private List configurations = Lists.newArrayList();
private EngineValidationMode validationMode = EngineValidationMode.IMMEDIATE;
private CompressionMode compressionMode = CompressionMode.AUTO;
/**
* Get the configured class loader.
* @return The class loader that will be used when loading the engine.
*/
public ClassLoader getClassLoader() {
return classLoader;
}
/**
* Set the class loader to use when reading the engine.
* @param classLoader The class loader to use.
* @return The loader (for chaining).
*/
public LenskitRecommenderEngineLoader setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
return this;
}
/**
* Add a configuration to use when loading the configuration. The loaded graph will be
* post-processed to add in components bound by this configuration. If the engine was saved
* with excluded configurations, then this method should be used to provide configurations to
* reinstate any objects excluded from the saved model. The loader will throw an exception if
* the loaded model has any unresolved placeholders.
*
* @param config The configuration to add.
* @return The loader (for chaining).
*/
public LenskitRecommenderEngineLoader addConfiguration(LenskitConfiguration config) {
configurations.add(config);
return this;
}
/**
* Set the validation mode for loading the recommender engine. The default mode is
* {@link EngineValidationMode#IMMEDIATE}.
* @param mode The validation mode.
* @return The loader (for chaining).
*/
public LenskitRecommenderEngineLoader setValidationMode(EngineValidationMode mode) {
Preconditions.checkNotNull(mode, "validation mode");
validationMode = mode;
return this;
}
/**
* Set the compression mode to use. The default is {@link CompressionMode#AUTO}.
* @param comp The compression mode.
*/
public LenskitRecommenderEngineLoader setCompressionMode(CompressionMode comp) {
compressionMode = comp;
return this;
}
/**
* Load a recommender engine from an input stream.
*
* Note: this method is only capable of auto-detecting gzip-compressed data.
* If the {@linkplain #setCompressionMode(CompressionMode) compression mode} is {@link CompressionMode#AUTO},
* only gzip-compressed streams are supported. Set the compression mode manually if you are
* using XZ compression.
*
*
* @param stream The input stream.
* @return The deserialized recommender.
* @throws IOException if there is an error reading the input data.
* @throws RecommenderConfigurationException
* if there is a configuration error with the deserialized recommender or
* the configurations applied to it.
*/
public LenskitRecommenderEngine load(@WillClose InputStream stream) throws IOException, RecommenderConfigurationException {
InputStream decomp;
if (compressionMode == CompressionMode.AUTO) {
decomp = LKFileUtils.transparentlyDecompress(stream);
} else {
decomp = compressionMode.wrapInput(stream);
}
return loadInternal(decomp);
}
/**
* Load a recommender from a file.
*
* @param file The recommender model file to load.
* @return The recommender engine.
* @throws IOException if there is an error reading the input data.
* @throws RecommenderConfigurationException
* if there is a configuration error with the deserialized recommender or
* the configurations applied to it.
*/
public LenskitRecommenderEngine load(File file) throws IOException, RecommenderConfigurationException {
logger.info("Loading recommender engine from {}", file);
try (FileInputStream input = new FileInputStream(file)) {
CompressionMode effComp = compressionMode.getEffectiveCompressionMode(file.getName());
logger.info("using {} compression", effComp);
return loadInternal(effComp.wrapInput(input));
}
}
/**
* Read a graph from an object input stream. Broken out to localize the necessary warning
* suppression.
*
* @param in The input stream.
* @return the loaded graph.
* @throws IOException If there is an I/O error.
* @throws ClassNotFoundException If there is a class resolution error.
* @see ObjectInput#readObject()
*/
@SuppressWarnings("unchecked")
private DAGNode readGraph(ObjectInput in) throws IOException, ClassNotFoundException {
return (DAGNode) in.readObject();
}
/**
* Load a recommender engine from an input stream. It transparently decompresses the stream
* and handles the classloader nastiness.
*
* @param stream The input stream.
* @return The recommender engine.
* @throws IOException If there is an I/O error reading the engine.
* @throws RecommenderConfigurationException If there is a configuration error (including an
* invalid class reference in the object stream).
*/
private LenskitRecommenderEngine loadInternal(InputStream stream) throws IOException, RecommenderConfigurationException {
logger.debug("using classloader {}", classLoader);
DAGNode graph;
// And load the stream once we've wrapped it appropriately.
ObjectInputStream in = new CustomClassLoaderObjectInputStream(
LKFileUtils.transparentlyDecompress(stream), classLoader);
try {
ClassLoaderContext ctx = null;
if (classLoader != null) {
// Grapht will automatically use the context class loader, set it up
ctx = ClassLoaders.pushContext(classLoader);
}
try {
graph = readGraph(in);
} finally {
if (ctx != null) {
ctx.pop();
}
}
} catch (ClassNotFoundException e) {
throw new RecommenderConfigurationException(e);
} finally {
in.close();
}
if (!configurations.isEmpty()) {
logger.info("rewriting with {} configurations", configurations.size());
RecommenderGraphBuilder rgb = new RecommenderGraphBuilder();
for (LenskitConfiguration config : configurations) {
rgb.addBindings(config.getBindings());
}
DependencySolver solver = rgb.buildDependencySolver();
try {
graph = solver.rewrite(graph);
} catch (ResolutionException e) {
throw new RecommenderConfigurationException("resolution error occured while rewriting recommender", e);
}
}
boolean instantiable = true;
switch (validationMode) {
case IMMEDIATE:
GraphtUtils.checkForPlaceholders(graph, logger);
break;
case DEFERRED:
instantiable = GraphtUtils.getPlaceholderNodes(graph).isEmpty();
break;
case NONE:
break; /* do nothing, mark it as instantiable. */
}
return new LenskitRecommenderEngine(graph, instantiable);
}
}