All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.modeshape.jcr.value.binary.DatabaseBinaryStore Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
/*
 * ModeShape (http://www.modeshape.org)
 * See the COPYRIGHT.txt file distributed with this work for information
 * regarding copyright ownership.  Some portions may be licensed
 * to Red Hat, Inc. under one or more contributor license agreements.
 * See the AUTHORS.txt file in the distribution for a full listing of 
 * individual contributors.
 *
 * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
 * is licensed to you 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.
 * 
 * ModeShape 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 software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.modeshape.jcr.value.binary;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import org.modeshape.common.annotation.ThreadSafe;
import org.modeshape.jcr.JcrI18n;
import org.modeshape.jcr.value.BinaryKey;
import org.modeshape.jcr.value.BinaryValue;

/**
 * A {@link BinaryStore} implementation that uses a database for persisting binary values.
 * 

* This binary store implementation establishes a connection to the specified database and then attempts to determine which type * of database is being used. ModeShape is aware of the following database types: *

    *
  • mysql
  • *
  • postgres
  • *
  • derby
  • *
  • hsql
  • *
  • h2
  • *
  • sqlite
  • *
  • db2
  • *
  • db2_390
  • *
  • informix
  • *
  • interbase
  • *
  • firebird
  • *
  • sqlserver
  • *
  • access
  • *
  • oracle
  • *
  • sybase
  • *
* This binary store implementation then uses DDL and DML statements to create the table(s) if not already existing and to perform * the various operations required of a binary store. ModeShape can use database-specific statements, although a default set of * SQL-99 statements are used as a fallback. *

*

* These statements are read from a property file named "binary_store_{type}_database.properties", where where " * {type}" is one of the above-mentioned database type strings. These properties files are expected to be found on * the classpath directly under "org/modeshape/jcr/database". If the corresponding file is not found on the classpath, then the " * binary_store_default_database.properties" file provided by ModeShape is used. *

*

* ModeShape provides out-of-the-box database-specific files for several of the DBMSes that are popular within the open source * community. The properties files for the other database types are not provided (though the ModeShape community will gladly * incorporate them if you wish to make them available to us); in such cases, simply copy one of the provided properties files * (e.g., "binary_store_default_database.properties" is often a good start) and customize it for your particular * DBMS, naming it according to the pattern described above and including it on the classpath. *

*

* Note that this mechanism can also be used to override the statements that ModeShape does provide out-of-the-box. In such cases, * be sure to place the file on the classpath before the ModeShape JARs so that your file will be discovered first. *

*

* The JDBC driver used needs to be at least JDBC 1.4 (JDK 6) compliant, * because {@link PreparedStatement#setBinaryStream(int parameterIndex, java.io.InputStream x)} is being used. *

*/ @ThreadSafe public class DatabaseBinaryStore extends AbstractBinaryStore { private static final boolean ALIVE = true; private static final boolean UNUSED = false; private FileSystemBinaryStore cache; /** JDBC utility for working with the database. */ private Database database; // JDBC params private final String driverClass; private final String connectionURL; private final String username; private final String password; private final String datasourceJNDILocation; /** * Create new store. * * @param driverClass JDBC driver class name * @param connectionURL database location * @param username database user name * @param password database password */ public DatabaseBinaryStore( String driverClass, String connectionURL, String username, String password ) { this.driverClass = driverClass; this.connectionURL = connectionURL; this.username = username; this.password = password; this.datasourceJNDILocation = null; this.cache = TransientBinaryStore.get(); } /** * Create new store that uses the JDBC DataSource in the given JNDI location. * * @param datasourceJNDILocation the JNDI name of the JDBC Data Source that should be used, or null */ public DatabaseBinaryStore( String datasourceJNDILocation ) { this.driverClass = null; this.connectionURL = null; this.username = null; this.password = null; this.datasourceJNDILocation = datasourceJNDILocation; this.cache = TransientBinaryStore.get(); } @Override public BinaryValue storeValue( InputStream stream ) throws BinaryStoreException { // store into temporary file system store and get SHA-1 BinaryValue temp = cache.storeValue(stream); try { // prepare new binary key based on SHA-1 BinaryKey key = new BinaryKey(temp.getKey().toString()); // check for duplicate content if (this.contentExists(key, ALIVE)) { return new StoredBinaryValue(this, key, temp.getSize()); } // check unused content if (this.contentExists(key, UNUSED)) { PreparedStatement sql = database.restoreContentSQL(key); Database.execute(sql); // doesn't produce a result set return new StoredBinaryValue(this, key, temp.getSize()); } // store content try { PreparedStatement sql = database.insertContentSQL(key, temp.getStream()); Database.execute(sql); // doesn't produce a result set return new StoredBinaryValue(this, key, temp.getSize()); } catch (Exception e) { throw new BinaryStoreException(e); } } finally { // remove content from temp store cache.markAsUnused(temp.getKey()); } } @Override public InputStream getInputStream( BinaryKey key ) throws BinaryStoreException { ResultSet rs = Database.executeQuery(database.retrieveContentSQL(key, true)); InputStream inputStream = Database.asStream(rs); // closes result set if (inputStream == null) { try { throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(key, database.getConnection().getCatalog())); } catch (SQLException e) { logger.debug(e, "Unable to retrieve db information"); } } return inputStream; } @Override public void markAsUnused( Iterable keys ) throws BinaryStoreException { for (BinaryKey key : keys) { PreparedStatement sql = database.markUnusedSQL(key); Database.executeUpdate(sql); // doesn't produce a result set } } @Override public void removeValuesUnusedLongerThan( long minimumAge, TimeUnit unit ) throws BinaryStoreException { // compute usage deadline (in past) long deadline = now() - unit.toMillis(minimumAge); PreparedStatement sql = database.removeExpiredContentSQL(deadline); Database.execute(sql); // doesn't produce a result set } @Override protected String getStoredMimeType( BinaryValue source ) throws BinaryStoreException { checkContentExists(source); ResultSet rs = Database.executeQuery(database.retrieveMimeTypeSQL(source.getKey())); return Database.asString(rs); // closes result set } private void checkContentExists( BinaryValue source ) throws BinaryStoreException { if (!contentExists(source.getKey(), true)) { try { throw new BinaryStoreException(JcrI18n.unableToFindBinaryValue.text(source.getKey(), database.getConnection() .getCatalog())); } catch (SQLException e) { logger.debug("Cannot get catalog information", e); } } } @Override protected void storeMimeType( BinaryValue source, String mimeType ) throws BinaryStoreException { PreparedStatement sql = database.updateMimeTypeSQL(source.getKey(), mimeType); Database.executeUpdate(sql); // doesn't produce a result set } @Override public String getExtractedText( BinaryValue source ) throws BinaryStoreException { checkContentExists(source); ResultSet rs = Database.executeQuery(database.retrieveExtTextSQL(source.getKey())); return Database.asString(rs); // closes result set } @Override public void storeExtractedText( BinaryValue source, String extractedText ) throws BinaryStoreException { PreparedStatement sql = database.updateExtTextSQL(source.getKey(), extractedText); Database.executeUpdate(sql); // doesn't produce a result set } @Override public void start() { super.start(); try { Connection connection = datasourceJNDILocation != null ? DatabaseBinaryStore.connect(datasourceJNDILocation) : DatabaseBinaryStore.connect(driverClass, connectionURL, username, password); // Create the database helper that behaves differently based upon the type of database database = new Database(connection); // Initialize the helper and database, creating the database table if it is missing ... database.initialize(); } catch (Exception e) { throw new RuntimeException(e); } } protected Database doCreateDatabase( Connection connection ) throws BinaryStoreException { return new Database(connection); } @Override public Iterable getAllBinaryKeys() throws BinaryStoreException { Set keys = new HashSet(); try { PreparedStatement sql = database.retrieveBinaryKeys(keys); List keysString = Database.asStringList(Database.executeQuery(sql)); // closes result set Set binaryKeys = new HashSet(keysString.size()); for (String keyString : keysString) { binaryKeys.add(new BinaryKey(keyString)); } return binaryKeys; } catch (Exception e) { throw new BinaryStoreException(e); } } @Override public void shutdown() { super.shutdown(); if (database != null) { database.disconnect(); } } /** * Test content for existence. * * @param key content identifier * @param alive true inside used content and false for checking within content marked as unused. * @return true if content found * @throws BinaryStoreException */ private boolean contentExists( BinaryKey key, boolean alive ) throws BinaryStoreException { ResultSet rs = null; boolean error = false; try { rs = Database.executeQuery(database.retrieveContentSQL(key, alive)); return rs.next(); } catch (SQLException e) { error = true; throw new BinaryStoreException(e); } catch (RuntimeException e) { error = true; throw e; } finally { if (rs != null) { // Always close the result set ... try { rs.close(); } catch (SQLException e) { if (!error) throw new BinaryStoreException(e); } } } } /** * Current time. * * @return current time in milliseconds */ private long now() { return new Date().getTime(); } /** * Creates connection with a database using data source registered via JNDI. * * @param jndiName the JNDI location of the data source * @return connection to database * @throws BinaryStoreException */ private static Connection connect( String jndiName ) throws BinaryStoreException { DataSource dataSource = null; try { InitialContext context = new InitialContext(); dataSource = (DataSource)context.lookup(jndiName); } catch (NamingException e) { throw new BinaryStoreException(e); } if (dataSource == null) { throw new BinaryStoreException("Datasource is not bound: " + jndiName); } try { return dataSource.getConnection(); } catch (SQLException e) { throw new BinaryStoreException(e); } } /** * Creates connection to a database using driver, location and credentials. * * @param driverClass driver's class name * @param connectionURL database location * @param username database user name * @param password user's password. * @return connection to a database. * @throws BinaryStoreException */ private static Connection connect( String driverClass, String connectionURL, String username, String password ) throws BinaryStoreException { try { Class.forName(driverClass); } catch (Exception e) { throw new BinaryStoreException(e); } try { return DriverManager.getConnection(connectionURL, username, password); } catch (Exception e) { throw new BinaryStoreException(e); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy