Java tutorial
/*********************************************************************************************************************** * Copyright (c) 2008 empolis GmbH and brox IT Solutions GmbH. All rights reserved. This program and the accompanying * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html * * Contributors: Daniel Stucky (empolis GmbH) - initial creator **********************************************************************************************************************/ package org.eclipse.smila.binarystorage.persistence.jpa; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Properties; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import javax.persistence.Persistence; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.smila.binarystorage.BinaryStorageException; import org.eclipse.smila.binarystorage.config.BinaryStorageConfiguration; import org.eclipse.smila.binarystorage.persistence.BinaryPersistence; import org.eclipse.smila.utils.config.ConfigUtils; import org.eclipse.smila.utils.workspace.WorkspaceHelper; /** * JPA Binary Storage persistence layer. */ public class JPABinaryPersistence extends BinaryPersistence { /** * name of bundle. Used in configuration reading. */ public static final String BUNDLE_NAME = "org.eclipse.smila.binarystorage.persistence.jpa"; /** * Constant for the eclipseLink persistence unit name. */ public static final String PERSISTENCE_UNIT_NAME = "SmilaBinaryObject"; /** * name of configuration file. Hardcoded for now (or fallback), configuration properties should be received from * configuration service later. */ public static final String CONFIGURATION_FILE = "persistence.properties"; /** * local logger. */ private final Log _log = LogFactory.getLog(JPABinaryPersistence.class); /** * service methods use read lock, deactivate needs write lock. */ private ReadWriteLock _lock = new ReentrantReadWriteLock(true); /** * configuration properties. */ private Properties _properties; /** * the EntityManagerFactory. */ private EntityManagerFactory _emf; /** * Basic constructor. * * @param binaryStorageConfig * the BinaryStorageConfiguration * @throws BinaryStorageException * if any error occurs during initialization */ public JPABinaryPersistence(final BinaryStorageConfiguration binaryStorageConfig) throws BinaryStorageException { if (_log.isTraceEnabled()) { _log.trace("creating instance of RecordStorageImpl"); } init(binaryStorageConfig); } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence#storeBinary(java.lang.String, * byte[]) */ @Override public void storeBinary(final String key, final byte[] content) throws BinaryStorageException { if (key == null) { throw new BinaryStorageException("parameter key is null"); } if (content == null) { throw new BinaryStorageException("parameter content is null"); } store(new BinaryStorageDao(key, content)); } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence#storeBinary(java.lang.String, * java.io.InputStream) */ @Override public void storeBinary(final String key, final InputStream stream) throws BinaryStorageException { if (key == null) { throw new BinaryStorageException("parameter key is null"); } if (stream == null) { throw new BinaryStorageException("parameter stream is null"); } try { store(new BinaryStorageDao(key, stream)); } catch (final IOException e) { throw new BinaryStorageException(e); } } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence#deleteBinary(java.lang.String) */ @Override public void deleteBinary(final String key) throws BinaryStorageException { if (key == null) { throw new BinaryStorageException("parameter key is null"); } _lock.readLock().lock(); try { final EntityManager em = createEntityManager(); try { final BinaryStorageDao dao = findBinaryStorageDao(em, key); if (dao != null) { final EntityTransaction transaction = em.getTransaction(); try { transaction.begin(); em.remove(dao); transaction.commit(); } catch (final Exception e) { if (transaction.isActive()) { transaction.rollback(); } throw new BinaryStorageException(e, "error removing record id: " + key); } } else { if (_log.isDebugEnabled()) { _log.debug("could not remove id: " + key + ". no binary object with this id exists."); } } } finally { closeEntityManager(em); } } finally { _lock.readLock().unlock(); } } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence#fetchSize(java.lang.String) */ @Override public long fetchSize(final String key) throws BinaryStorageException { if (key == null) { throw new BinaryStorageException("parameter key is null"); } _lock.readLock().lock(); try { final EntityManager em = createEntityManager(); try { final BinaryStorageDao dao = findBinaryStorageDao(em, key); if (dao != null) { return dao.getBytes().length; } else { throw new BinaryStorageException( "could not fetch size for id: " + key + ". no binary object with this id exists."); } } finally { closeEntityManager(em); } } finally { _lock.readLock().unlock(); } } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence# * loadBinaryAsByteArray(java.lang.String) */ @Override public byte[] loadBinaryAsByteArray(final String key) throws BinaryStorageException { return load(key).getBytes(); } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence# * loadBinaryAsInputStream(java.lang.String) */ @Override public InputStream loadBinaryAsInputStream(final String key) throws BinaryStorageException { return load(key).getBytesAsStream(); } /** * {@inheritDoc} * * @see org.eclipse.smila.binarystorage.internal.impl.persistence.BinaryPersistence#cleanup() */ @Override public void cleanup() throws BinaryStorageException { // close EntityManagerFactory _lock.writeLock().lock(); try { try { if (_emf != null) { _emf.close(); } } catch (final Exception e) { if (_log.isErrorEnabled()) { _log.error("error closing EntityManagerFactory", e); } } _emf = null; // _properties _ if (_properties != null) { _properties.clear(); _properties = null; } if (_log.isTraceEnabled()) { _log.trace("deactivated RecordStorageImpl service"); } } finally { _lock.writeLock().unlock(); } } /** * Initialize JPABinaryPersistence. * * @param binaryStorageConfig * BinaryStorageConfiguration * * @throws BinaryStorageException * if any error occurs */ private void init(final BinaryStorageConfiguration binaryStorageConfig) throws BinaryStorageException { EntityManager em = null; try { readConfiguration(); if (!_properties.containsKey("eclipselink.logging.file")) { final File workingDir = WorkspaceHelper.createWorkingDir(BUNDLE_NAME); final File logfile = new File(workingDir, "jpa.log"); _properties.put("eclipselink.logging.file", logfile.getAbsolutePath()); } // set up eclipseLink _emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, _properties); // create an initial EntityManager to create the database em = _emf.createEntityManager(); } catch (final Exception e) { throw new BinaryStorageException("error activating JPABinaryPersistence", e); } finally { closeEntityManager(em); } if (_log.isTraceEnabled()) { _log.trace("started JPABinaryPersistence"); } } /** * Load the BinaryStorageDao with the given key. * * @param key * the key * @return the BinaryStorageDao * @throws BinaryStorageException * if any error occurs or no BinaryStorageDao was found */ private BinaryStorageDao load(final String key) throws BinaryStorageException { if (key == null) { throw new BinaryStorageException("parameter key is null"); } _lock.readLock().lock(); try { final EntityManager em = createEntityManager(); try { final BinaryStorageDao dao = findBinaryStorageDao(em, key); if (dao != null) { return dao; } throw new BinaryStorageException( "error loading id: " + key + ". no binary object with this id exists."); } finally { closeEntityManager(em); } } finally { _lock.readLock().unlock(); } } /** * Stores the given BinaryStorageDao, updating an existing one or creating a new one. * * @param dao * the BinaryStorageDao to store * @throws BinaryStorageException * if any error occurs */ // TODO: don't know if this synchronize is good, was needed to pass the JUNit test TestConcurrentBSSAccessJPA private synchronized void store(final BinaryStorageDao dao) throws BinaryStorageException { _lock.readLock().lock(); try { final EntityManager em = createEntityManager(); final EntityTransaction transaction = em.getTransaction(); try { transaction.begin(); if (findBinaryStorageDao(em, dao.getId()) == null) { em.persist(dao); } else { em.merge(dao); } transaction.commit(); if (_log.isTraceEnabled()) { _log.trace("stored content of id:" + dao.getId()); } } catch (final Exception e) { if (transaction.isActive()) { transaction.rollback(); } throw new BinaryStorageException(e, "error storing record id: " + dao.getId()); } finally { closeEntityManager(em); } } finally { _lock.readLock().unlock(); } } /** * Internal method to find a BinaryStorageDao object by id. * * @param em * the EntityManager to use * @param id * the id of the BinaryStorageDao * @return the RecordDao object or null */ private BinaryStorageDao findBinaryStorageDao(final EntityManager em, final String id) { return em.find(BinaryStorageDao.class, id); } /** * read configuration property file. * * @throws IOException * error reading configuration file */ private void readConfiguration() throws IOException { _properties = new Properties(); InputStream configurationFileStream = null; try { configurationFileStream = ConfigUtils.getConfigStream(BUNDLE_NAME, CONFIGURATION_FILE); _properties.load(configurationFileStream); } catch (final IOException ex) { throw new IOException( "Could not read configuration property file " + CONFIGURATION_FILE + ": " + ex.toString()); } finally { IOUtils.closeQuietly(configurationFileStream); } } /** * @return new entity manager * @throws BinaryStorageException * service is not active currently (probably deactivated has been called already). */ private EntityManager createEntityManager() throws BinaryStorageException { if (_emf == null) { throw new BinaryStorageException( "BinaryStorage PJA Persistence is not active anymore. Maybe this system is shutting down?"); } return _emf.createEntityManager(); } /** * Closes an EntityManager. * * @param em * the EntityManager */ private void closeEntityManager(final EntityManager em) { try { if (em != null) { em.close(); } } catch (final Exception e) { if (_log.isErrorEnabled()) { _log.error("error closing local EntityManager", e); } } } }