/**
* Copyright 2012 Anjuke Inc.
*
* 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 com.anjuke.romar.mahout.similarity;
import java.io.File;
import java.util.Collection;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.mahout.cf.taste.common.Refreshable;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.similarity.GenericUserSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.GenericUserSimilarity.UserUserSimilarity;
import org.apache.mahout.cf.taste.impl.similarity.file.FileItemSimilarity;
import org.apache.mahout.cf.taste.similarity.PreferenceInferrer;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.anjuke.romar.mahout.similarity.file.RomarFileSimilarityIterator.IteratorBuiler;
import com.anjuke.romar.mahout.util.Util;
import com.google.common.base.Preconditions;
public class RomarFileUserSimilarity implements UserSimilarity {
private static final Logger LOG = LoggerFactory
.getLogger(RomarFileUserSimilarity.class);
private final IteratorBuiler<UserUserSimilarity> _iteratorBuilder;
private final File _dataFile;
private UserSimilarity _delegate;
private final ReentrantLock _reloadLock;
private long _lastModified;
private final long _minReloadIntervalMS;
/**
* @param dataFile
* file containing the similarity data
*/
public RomarFileUserSimilarity(File dataFile,
IteratorBuiler<UserUserSimilarity> iteratorBuilder) {
this(dataFile, iteratorBuilder, FileItemSimilarity.DEFAULT_MIN_RELOAD_INTERVAL_MS);
}
/**
* @param minReloadIntervalMS
* the minimum interval in milliseconds after which a full reload
* of the original datafile is done when refresh() is called
* @see #FileItemSimilarity(File)
*/
public RomarFileUserSimilarity(File dataFile,
IteratorBuiler<UserUserSimilarity> iteratorBuilder, long minReloadIntervalMS) {
Preconditions.checkArgument(dataFile != null, "dataFile is null");
Preconditions.checkArgument(dataFile.exists() && !dataFile.isDirectory(),
"dataFile is missing or a directory: %s", dataFile);
LOG.info("Creating FileItemSimilarity for file {}", dataFile);
this._lastModified = dataFile.lastModified();
this._dataFile = dataFile;
this._iteratorBuilder = iteratorBuilder;
this._minReloadIntervalMS = minReloadIntervalMS;
this._reloadLock = new ReentrantLock();
reload();
}
@Override
public void setPreferenceInferrer(PreferenceInferrer inferrer) {
_delegate.setPreferenceInferrer(inferrer);
}
@Override
public double userSimilarity(long userID1, long userID2) throws TasteException {
return _delegate.userSimilarity(userID1, userID2);
}
@Override
public void refresh(Collection<Refreshable> alreadyRefreshed) {
if (_dataFile.lastModified() > _lastModified + _minReloadIntervalMS) {
LOG.debug("File has changed; reloading...");
reload();
}
}
protected void reload() {
if (_reloadLock.tryLock()) {
try {
long newLastModified = _dataFile.lastModified();
LOG.info("reading similarity from " + _dataFile.getAbsolutePath());
_delegate = new GenericUserSimilarity(Util.iterable(_iteratorBuilder
.build(_dataFile)));
LOG.info("read similarity finish");
_lastModified = newLastModified;
} finally {
_reloadLock.unlock();
}
}
}
@Override
public String toString() {
return "FileItemSimilarity[dataFile:" + _dataFile + ']';
}
}