/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.mahout.cf.taste.impl.model.jdbc; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import com.google.common.io.Closeables; import org.apache.mahout.cf.taste.common.TasteException; import org.apache.mahout.cf.taste.impl.common.jdbc.AbstractJDBCComponent; /** * <p> * A generic {@link org.apache.mahout.cf.taste.model.DataModel} designed for use with other JDBC data sources; * one just specifies all necessary SQL queries to the constructor here. Optionally, the queries can be * specified from a {@link Properties} object, {@link File}, or {@link InputStream}. This class is most * appropriate when other existing implementations of {@link AbstractJDBCDataModel} are not suitable. If you * are using this class to support a major database, consider contributing a specialized implementation of * {@link AbstractJDBCDataModel} to the project for this database. * </p> */ public final class GenericJDBCDataModel extends AbstractJDBCDataModel { public static final String DATA_SOURCE_KEY = "dataSource"; public static final String GET_PREFERENCE_SQL_KEY = "getPreferenceSQL"; public static final String GET_PREFERENCE_TIME_SQL_KEY = "getPreferenceTimeSQL"; public static final String GET_USER_SQL_KEY = "getUserSQL"; public static final String GET_ALL_USERS_SQL_KEY = "getAllUsersSQL"; public static final String GET_NUM_USERS_SQL_KEY = "getNumUsersSQL"; public static final String GET_NUM_ITEMS_SQL_KEY = "getNumItemsSQL"; public static final String SET_PREFERENCE_SQL_KEY = "setPreferenceSQL"; public static final String REMOVE_PREFERENCE_SQL_KEY = "removePreferenceSQL"; public static final String GET_USERS_SQL_KEY = "getUsersSQL"; public static final String GET_ITEMS_SQL_KEY = "getItemsSQL"; public static final String GET_PREFS_FOR_ITEM_SQL_KEY = "getPrefsForItemSQL"; public static final String GET_NUM_PREFERENCE_FOR_ITEM_KEY = "getNumPreferenceForItemSQL"; public static final String GET_NUM_PREFERENCE_FOR_ITEMS_KEY = "getNumPreferenceForItemsSQL"; public static final String GET_MAX_PREFERENCE_KEY = "getMaxPreferenceSQL"; public static final String GET_MIN_PREFERENCE_KEY = "getMinPreferenceSQL"; /** * <p> * Specifies all SQL queries in a {@link Properties} object. See the {@code *_KEY} constants in this * class (e.g. {@link #GET_USER_SQL_KEY}) for a list of all keys which must map to a value in this object. * </p> * * @param props * {@link Properties} object containing values * @throws TasteException * if anything goes wrong during initialization */ public GenericJDBCDataModel(Properties props) throws TasteException { super(AbstractJDBCComponent.lookupDataSource(props.getProperty(DATA_SOURCE_KEY)), props.getProperty(GET_PREFERENCE_SQL_KEY), props.getProperty(GET_PREFERENCE_TIME_SQL_KEY), props.getProperty(GET_USER_SQL_KEY), props.getProperty(GET_ALL_USERS_SQL_KEY), props.getProperty(GET_NUM_ITEMS_SQL_KEY), props.getProperty(GET_NUM_USERS_SQL_KEY), props.getProperty(SET_PREFERENCE_SQL_KEY), props.getProperty(REMOVE_PREFERENCE_SQL_KEY), props.getProperty(GET_USERS_SQL_KEY), props.getProperty(GET_ITEMS_SQL_KEY), props.getProperty(GET_PREFS_FOR_ITEM_SQL_KEY), props.getProperty(GET_NUM_PREFERENCE_FOR_ITEM_KEY), props.getProperty(GET_NUM_PREFERENCE_FOR_ITEMS_KEY), props.getProperty(GET_MAX_PREFERENCE_KEY), props.getProperty(GET_MIN_PREFERENCE_KEY)); } /** * <p> * See {@link #GenericJDBCDataModel(Properties)}. This constructor reads values from a file * instead, as if with {@link Properties#load(InputStream)}. So, the file should be in standard Java * properties file format -- containing {@code key=value} pairs, one per line. * </p> * * @param propertiesFile * properties file * @throws TasteException * if anything goes wrong during initialization */ public GenericJDBCDataModel(File propertiesFile) throws TasteException { this(getPropertiesFromFile(propertiesFile)); } /** * <p> * See {@link #GenericJDBCDataModel(Properties)}. This constructor reads values from a resource available in * the classpath, as if with {@link Class#getResourceAsStream(String)} and * {@link Properties#load(InputStream)}. This is useful if your configuration file is, for example, packaged * in a JAR file that is in the classpath. * </p> * * @param resourcePath * path to resource in classpath (e.g. "/com/foo/TasteSQLQueries.properties") * @throws TasteException * if anything goes wrong during initialization */ public GenericJDBCDataModel(String resourcePath) throws TasteException { this(getPropertiesFromStream(GenericJDBCDataModel.class .getResourceAsStream(resourcePath))); } private static Properties getPropertiesFromFile(File file) throws TasteException { try { return getPropertiesFromStream(new FileInputStream(file)); } catch (FileNotFoundException fnfe) { throw new TasteException(fnfe); } } private static Properties getPropertiesFromStream(InputStream is) throws TasteException { try { try { Properties props = new Properties(); props.load(is); return props; } finally { Closeables.closeQuietly(is); } } catch (IOException ioe) { throw new TasteException(ioe); } } }