diff src/fschmidt/util/servlet/DbCache.java @ 68:00520880ad02

add fschmidt source
author Franklin Schmidt <fschmidt@gmail.com>
date Sun, 05 Oct 2025 17:24:15 -0600
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/fschmidt/util/servlet/DbCache.java	Sun Oct 05 17:24:15 2025 -0600
@@ -0,0 +1,194 @@
+/*
+Copyright (c) 2008  Franklin Schmidt <fschmidt@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+package fschmidt.util.servlet;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Date;
+import java.util.Map;
+import java.util.HashMap;
+import fschmidt.db.DbDatabase;
+import fschmidt.db.postgres.DbDatabaseImpl;
+
+
+public final class DbCache implements HttpCache {
+	private final DbDatabase db;
+	private long lastClear;
+	private static final String LAST_CLEAR = "_last_clear";
+
+	public DbCache(DbDatabase db) {
+		this.db = db;
+		setLastClear();
+	}
+
+	private void setLastClear() {
+		try {
+			Connection con = db.getConnection();
+			try {
+				PreparedStatement stmt = con.prepareStatement(
+					"select last_modified from http_cache where event=?"
+				);
+				stmt.setString(1,LAST_CLEAR);
+				ResultSet rs = stmt.executeQuery();
+				if( !rs.next() ) {
+					modified(LAST_CLEAR);
+					stmt.setString(1,LAST_CLEAR);
+					rs = stmt.executeQuery();
+					rs.next();
+				}
+				lastClear = getLastModified(rs);
+				stmt.close();
+			} finally {
+				con.close();
+			}
+		} catch(SQLException e) {
+			throw new RuntimeException(e);
+		}
+	}
+
+	private static long getLastModified(ResultSet rs) throws SQLException {
+		return rs.getTimestamp("last_modified").getTime()/1000*1000;
+	}
+
+	public long[] lastModifieds(final String[] modifyingEvents) {
+		switch( modifyingEvents.length ) {
+		case 0:
+			return new long[0];
+		case 1:
+			try {
+				Connection con = db.getConnection();
+				PreparedStatement stmt = con.prepareStatement(
+					"select last_modified from http_cache where event=?"
+				);
+				try {
+					stmt.setString(1,modifyingEvents[0].toString());
+					ResultSet rs = stmt.executeQuery();
+					return new long[]{ rs.next() ? getLastModified(rs) : lastClear };
+				} finally {
+					stmt.close();
+					con.close();
+				}
+			} catch(SQLException e) {
+				throw new RuntimeException(e);
+			}
+		default:
+			StringBuilder select = new StringBuilder();
+			try {
+				final String[] events = new String[modifyingEvents.length];
+				final long[] rtn = new long[modifyingEvents.length];
+				for( int i=0; i<modifyingEvents.length; i++ ) {
+					events[i] = modifyingEvents[i].toString();
+					rtn[i] = lastClear;
+				}
+				Map<String,Long> map = new HashMap<String,Long>();
+				select.append(
+					"select event,last_modified from http_cache where event in ("
+				).append( db.arcana().quote(events[0]) );
+				for( int i=1; i<events.length; i++ ) {
+					select.append( ',' ).append( db.arcana().quote(events[i]) );
+				}
+				select.append( ")" );
+				Connection con = db.getConnection();
+				try {
+					Statement stmt = con.createStatement();
+					ResultSet rs = stmt.executeQuery( select.toString() );
+					while( rs.next() ) {
+						map.put( rs.getString("event"), getLastModified(rs) );
+					}
+					stmt.close();
+				} finally {
+					con.close();
+				}
+				for( int i=0; !map.isEmpty(); i++ ) {
+					Long lm = map.remove(events[i]);
+					if( lm != null )
+						rtn[i] = lm;
+				}
+				return rtn;
+			} catch(SQLException e) {
+				throw new RuntimeException(select.toString(),e);
+			}
+		}
+	}
+
+	public void modified(final String event) {
+		db.runAfterCommit(new Runnable(){public void run(){
+			try {
+				Connection con = db.getConnection();
+				try {
+					boolean didUpdate;
+					{
+						PreparedStatement stmt = con.prepareStatement(
+							"update http_cache set last_modified=now() where event = ?"
+						);
+						stmt.setString(1,event);
+						didUpdate = stmt.executeUpdate() > 0;
+						stmt.close();
+					}
+					if( !didUpdate ) {
+						PreparedStatement stmt = con.prepareStatement(
+							"insert into http_cache (event) values (?)"
+						);
+						stmt.setString(1,event);
+						DbDatabaseImpl.executeUpdateIgnoringDuplicateKeys(stmt);
+						stmt.close();
+					}
+				} finally {
+					con.close();
+				}
+			} catch(SQLException e) {
+				throw new RuntimeException("event = "+event,e);
+			}
+		}});
+	}
+
+	public void clear() {
+		try {
+			Connection con = db.getConnection();
+			try {
+				Statement stmt = con.createStatement();
+				stmt.executeUpdate(
+					"delete from http_cache"
+				);
+				stmt.close();
+			} finally {
+				con.close();
+			}
+		} catch(SQLException e) {
+			throw new RuntimeException(e);
+		}
+		setLastClear();
+	}
+
+/*
+assumes:
+
+create table http_cache (
+	event varchar not null primary key,
+	last_modified timestamp with time zone NOT NULL DEFAULT now()
+);
+*/
+}