/* * Copyright (c) 2008 Los Alamos National Security, LLC. * * Los Alamos National Laboratory * Research Library * Digital Library Research & Prototyping Team * * This library 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 library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ package gov.lanl.adore.djatoka.openurl.plugin.rftdb; import gov.lanl.adore.djatoka.openurl.DjatokaImageMigrator; import gov.lanl.adore.djatoka.openurl.IReferentMigrator; import gov.lanl.adore.djatoka.openurl.IReferentResolver; import gov.lanl.adore.djatoka.openurl.ResolverException; import gov.lanl.adore.djatoka.util.ImageRecord; import gov.lanl.util.DBCPUtils; import info.openurl.oom.entities.Referent; import java.io.File; import java.net.URI; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; import java.util.Properties; import javax.servlet.http.HttpServletResponse; import javax.sql.DataSource; import org.apache.log4j.Logger; /** * Alternate IReferentResolver implementation using JDBC & DBCP. Define ImageRecordManager.query * to map the unique identifier and file path to 'identifier' and 'imageFile' * * ----- OpenURLJP2KService.properties ----- * OpenURLJP2KService.referentResolverImpl=gov.lanl.adore.djatoka.openurl.plugin.rftdb.DatabaseResolver * DatabaseResolver.url=jdbc:mysql://localhost/djatoka * DatabaseResolver.driver=com.mysql.jdbc.Driver * DatabaseResolver.login=root * DatabaseResolver.pwd= * DatabaseResolver.query=SELECT identifier, imageFile FROM resources WHERE identifier='\\i'; * * ----- DEFAULT SCHEMA ----- * CREATE TABLE `resources` ( * `identifier` varchar(100) NOT NULL, * `imageFile` varchar(255) NOT NULL, * PRIMARY KEY (`identifier`) * ) ENGINE=MyISAM DEFAULT CHARSET=latin1; * * * @author Ryan Chute * */ public class DatabaseResolver implements IReferentResolver { static Logger log = Logger.getLogger(DatabaseResolver.class.getName()); public static final String DEFAULT_DBID = "DatabaseResolver"; public static final String FIELD_IDENTIFIER = "identifier"; public static final String FIELD_IMAGEFILE = "imageFile"; public static final String REPLACE_ID_KEY = "\\i"; private static IReferentMigrator dim = new DjatokaImageMigrator(); private static DataSource dataSource; private static Map<String, ImageRecord> remoteImgs; private static Map<String, ImageRecord> localImgs; private static final String PROP_REMOTE_CACHE = "DatabaseResolver.maxRemoteCacheSize"; private static final int DEFAULT_REMOTE_CACHE_SIZE = 100; private static int maxRemoteCacheSize = DEFAULT_REMOTE_CACHE_SIZE; private static String query = "SELECT identifier, imageFile FROM resources WHERE identifier='\\i';"; /** * Referent Identifier to be resolved from Identifier Resolver. The returned * ImageRecord need only contain the imageId and image file path. * @param rft identifier of the image to be resolved * @return ImageRecord instance containing resolvable metadata * @throws ResolverException */ public ImageRecord getImageRecord(Referent rft) throws ResolverException { String id = ((URI) rft.getDescriptors()[0]).toASCIIString(); return getImageRecord(id); } /** * Referent Identifier to be resolved from Identifier Resolver. The returned * ImageRecord need only contain the imageId and image file path. * @param rftId identifier of the image to be resolved * @return ImageRecord instance containing resolvable metadata * @throws ResolverException */ public ImageRecord getImageRecord(String rftId) throws ResolverException { ImageRecord ir = null; if (isResolvableURI(rftId)) { ir = getRemoteImage(rftId); } else { ir = getLocalImage(rftId); } return ir; } public IReferentMigrator getReferentMigrator() { return dim; } public int getStatus(String rftId) { if (remoteImgs.get(rftId) != null || getLocalImage(rftId) != null) return HttpServletResponse.SC_OK; else if (dim.getProcessingList().contains(rftId)) return HttpServletResponse.SC_ACCEPTED; else return HttpServletResponse.SC_NOT_FOUND; } /** * Sets a Properties object that may be used by underlying implementation * @param props Properties object for use by implementation * @throws ResolverException */ public void setProperties(Properties props) throws ResolverException { localImgs = Collections.synchronizedMap(new LinkedHashMap<String, ImageRecord>(16, 0.75f, true)); // Initialize remote image cache management String mrcs = props.getProperty(PROP_REMOTE_CACHE); if (mrcs != null) maxRemoteCacheSize = Integer.parseInt(mrcs); remoteImgs = Collections.synchronizedMap(new LinkedHashMap<String, ImageRecord>(16, 0.75f, true){ private static final long serialVersionUID = 1; protected boolean removeEldestEntry(Map.Entry<String, ImageRecord> eldest) { log.debug("remoteCacheSize: " + size()); boolean d = size() > maxRemoteCacheSize; if (d) { File f = new File((String) eldest.getValue().getImageFile()); log.debug("deleting: " + eldest.getValue().getImageFile()); if (f.exists()) f.delete(); remove(eldest.getKey()); } return false; }; }); query = props.getProperty(DEFAULT_DBID + ".query"); if (query == null) throw new ResolverException(DEFAULT_DBID + ".query is not defined in properties"); try { dataSource = DBCPUtils.setupDataSource(DEFAULT_DBID, props); } catch (Throwable e) { log.error(e,e); throw new ResolverException("DBCP Libraries are not in the classpath"); } } private static boolean isResolvableURI(String rftId) { return (rftId.startsWith("http") || rftId.startsWith("file") || rftId.startsWith("ftp")); } private ImageRecord getLocalImage(String rftId) { ImageRecord ir = localImgs.get(rftId); if (ir == null) { Connection conn = null; Statement stmt = null; ResultSet rset = null; try { conn = dataSource.getConnection(); stmt = conn.createStatement(); rset = stmt.executeQuery(query.replace("\\i", rftId)); if (rset.next()) { ir = new ImageRecord(); ir.setIdentifier(rset.getString(FIELD_IDENTIFIER)); ir.setImageFile(rset.getString(FIELD_IMAGEFILE)); } } catch (SQLException e) { log.error(e, e); } finally { try { rset.close(); } catch (Exception e) { } try { stmt.close(); } catch (Exception e) { } try { conn.close(); } catch (Exception e) { } } if (ir != null) localImgs.put(rftId, ir); } return ir; } private ImageRecord getRemoteImage(String rftId) throws ResolverException { ImageRecord ir = remoteImgs.get(rftId); if (ir == null || !new File(ir.getImageFile()).exists()) { try { URI uri = new URI(rftId); if (dim.getProcessingList().contains(uri.toString())) { int i = 0; Thread.sleep(1000); while (dim.getProcessingList().contains(uri) && i < (5 * 60)) { Thread.sleep(1000); i++; } if (remoteImgs.containsKey(rftId)) return remoteImgs.get(rftId); } File f = dim.convert(uri); ir = new ImageRecord(rftId, f.getAbsolutePath()); if (f.length() > 0) remoteImgs.put(rftId, ir); else throw new ResolverException( "An error occurred processing file:" + uri.toURL().toString()); } catch (Exception e) { log.error("Unable to access " + rftId); return null; } } return ir; } }