/*
* Copyright (c) 2010, the Last.fm Java Project and Committers
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* - Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package de.umass.lastfm.cache;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
/**
* Generic class for caching into a database. It's constructor takes a {@link Connection} instance, which must
* be opened and closed by the client. SQL code used in this class should work with all common databases
* (which support varchar, timestamp and longvarchar datatypes).<br/>
* For more specialized versions of this class for different databases one may extend this class and
* override methods as needed.
*
* @author Janni Kovacs
*/
public class DatabaseCache extends Cache {
protected static final String TABLE_NAME = "LASTFM_CACHE";
protected Connection conn;
public DatabaseCache(Connection connection) throws SQLException {
this.conn = connection;
ResultSet tables = conn.getMetaData().getTables(null, null, TABLE_NAME, null);
if (!tables.next()) {
createTable();
}
}
protected void createTable() throws SQLException {
PreparedStatement stmt = conn
.prepareStatement(
"CREATE TABLE " + TABLE_NAME + " (key varchar, expiration_date timestamp, response longvarchar);");
stmt.execute();
stmt.close();
}
public boolean contains(String cacheEntryName) {
try {
PreparedStatement stmt = conn.prepareStatement("SELECT key FROM " + TABLE_NAME + " WHERE key = ?;");
stmt.setString(1, cacheEntryName);
ResultSet result = stmt.executeQuery();
boolean b = result.next();
stmt.close();
return b;
} catch (SQLException e) {
return false;
}
}
public InputStream load(String cacheEntryName) {
try {
PreparedStatement stmt = conn.prepareStatement("SELECT response FROM " + TABLE_NAME + " WHERE key = ?;");
stmt.setString(1, cacheEntryName);
ResultSet result = stmt.executeQuery();
if (result.next()) {
String s = result.getString("response");
stmt.close();
return new ByteArrayInputStream(s.getBytes("UTF-8"));
}
stmt.close();
} catch (SQLException e) {
// ignore
} catch (UnsupportedEncodingException e) {
// won't happen
}
return null;
}
public void remove(String cacheEntryName) {
try {
PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + TABLE_NAME + " WHERE key = ?;");
stmt.setString(1, cacheEntryName);
stmt.execute();
stmt.close();
} catch (SQLException e) {
// ignore
}
}
public void store(String cacheEntryName, InputStream inputStream, long expirationDate) {
try {
InputStreamReader reader = new InputStreamReader(inputStream);
StringBuilder sb = new StringBuilder(inputStream.available());
char[] buf = new char[2048];
int read;
while ((read = reader.read(buf, 0, buf.length)) != -1) {
sb.append(buf, 0, read);
}
PreparedStatement stmt = conn
.prepareStatement(
"INSERT INTO " + TABLE_NAME + " (key, expiration_date, response) VALUES(?, ?, ?);");
stmt.setString(1, cacheEntryName);
stmt.setTimestamp(2, new Timestamp(expirationDate));
stmt.setString(3, sb.toString());
stmt.execute();
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
// ignore
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
// won't happen
} catch (IOException e) {
e.printStackTrace();
// better won't happen
}
}
public boolean isExpired(String cacheEntryName) {
try {
PreparedStatement stmt = conn
.prepareStatement("SELECT expiration_date FROM " + TABLE_NAME + " WHERE key = ?;");
stmt.setString(1, cacheEntryName);
ResultSet result = stmt.executeQuery();
if (result.next()) {
Timestamp timestamp = result.getTimestamp("expiration_date");
long expirationDate = timestamp.getTime();
stmt.close();
return expirationDate < System.currentTimeMillis();
}
} catch (SQLException e) {
// ignore
}
return false;
}
public void clear() {
try {
PreparedStatement stmt = conn.prepareStatement("DELETE FROM " + TABLE_NAME + ";");
stmt.execute();
stmt.close();
} catch (SQLException e) {
// ignore
}
}
}