org.opencms.db.jpa.CmsSqlManager Maven / Gradle / Ivy
Show all versions of opencms-test Show documentation
/*
* This library is part of OpenCms -
* the Open Source Content Management System
*
* Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
*
* 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.
*
* For further information about Alkacon Software, please see the
* company website: http://www.alkacon.com
*
* For further information about OpenCms, please see the
* project website: http://www.opencms.org
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.opencms.db.jpa;
import org.opencms.configuration.CmsParameterConfiguration;
import org.opencms.configuration.CmsPersistenceUnitConfiguration;
import org.opencms.db.CmsDbContext;
import org.opencms.db.CmsDbException;
import org.opencms.db.CmsDbPool;
import org.opencms.db.generic.Messages;
import org.opencms.file.CmsProject;
import org.opencms.main.CmsLog;
import org.opencms.main.CmsRuntimeException;
import org.opencms.util.CmsCollectionsGenericWrapper;
import org.opencms.util.CmsStringUtil;
import org.opencms.util.CmsUUID;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.pool.ObjectPool;
import org.apache.commons.pool.impl.StackObjectPool;
/**
* JPA database server implementation of the SQL manager interface.
*
* @since 8.0.0
*/
public class CmsSqlManager extends org.opencms.db.CmsSqlManager {
/** Default pool size for EntityManager instances. */
public static final int DEFAULT_ENTITY_MANAGER_POOL_SIZE = 250;
/** The persistence unit name in the persistence.xml file for OpenCms'es persistence classes. */
public static final String JPA_PERSISTENCE_UNIT = "OpenCmsJPAPool";
/** Property name for pool size configuration. */
public static final String JPA_POOL_SIZE_PROPERTY_NAME = "opencms.jpa.EntityManagerPoolSize";
/** The fully qualified Java class name of the JDBC driver to be used. */
public static final String KEY_DRIVER_CLASS_NAME = "driverClassName";
/** The initial number of connections that are created when the pool is started. */
public static final String KEY_INITIAL_SIZE = "initialSize";
/**
* The maximum number of active connections that can be allocated from
* this pool at the same time, or negative for no limit.
*/
public static final String KEY_MAX_ACTIVE = "maxActive";
/**
* The maximum number of connections that can remain idle in the pool,
* without extra ones being released, or negative for no limit.
*/
public static final String KEY_MAX_IDLE = "maxIdle";
/**
* The maximum number of milliseconds that the pool will wait (when there are no available connections)
* for a connection to be returned before throwing an exception, or <= 0 to wait indefinitely.
*/
public static final String KEY_MAX_WAIT = "maxWait";
/**
* The minimum amount of time an object may sit idle in the pool
* before it is eligable for eviction by the idle object evictor (if any).
*/
public static final String KEY_MIN_EVICTABLE_IDLE_TIME = "minEvictableIdleTimeMillis";
/**
* The minimum number of active connections that can remain idle in the pool,
* without extra ones being created, or 0 to create none.
*/
public static final String KEY_MIN_IDLE = "minIdle";
/** The number of objects to examine during each run of the idle object evictor thread (if any). */
public static final String KEY_NUM_TESTS_PER_EVICTION_RUN = "numTestsPerEvictionRun";
/** The connection password to be passed to our JDBC driver to establish a connection. */
public static final String KEY_PASS = "password";
/** Prepared statement pooling for this pool. */
public static final String KEY_PREP_STATEMENTS = "poolPreparedStatements";
/** The indication of whether objects will be validated before being borrowed from the pool. */
public static final String KEY_TEST_ON_BORROW = "testOnBorrow";
/** The indication of whether objects will be validated by the idle object evictor (if any). */
public static final String KEY_TEST_WHILE_IDLE = "testWhileIdle";
/** The number of milliseconds to sleep between runs of the idle object evictor thread. */
public static final String KEY_TIME_BETWEEN_EVICTION_RUNS = "timeBetweenEvictionRunsMillis";
/** The connection URL to be passed to our JDBC driver to establish a connection. */
public static final String KEY_URL = "url";
/** The connection username to be passed to our JDBC driver to establish a connection. */
public static final String KEY_USER = "username";
/** The SQL query that will be used to validate connections from this pool before returning them to the caller. */
public static final String KEY_VALIDATION_QUERY = "validationQuery";
/** Poll of EntityManager instances for OpenCms. */
protected static ObjectPool m_openCmsEmPool;
/** The value to be replaced with for online project. */
protected static final String OFFLINE_PROJECT = "Offline";
/** The value to be replaced with for online project. */
protected static final String ONLINE_PROJECT = "Online";
/** A pattern being replaced in JPQL queries to generate JPQL queries to access online/offline tables. */
protected static final String QUERY_PROJECT_SEARCH_PATTERN = "${PROJECT}";
/** String which indicates project parameter in the queries. */
protected static final String QUERY_PROJECT_STRING = "PROJECT";
/** Contains JPQL placeholder for query parameters. Currently it's question mark.*/
private static final String JPQL_PARAMETER_PLACEHOLDER = "?";
/** Number of characters for JPQL parameter's placeholder. */
private static final int JPQL_PARAMETER_PLACEHOLDER_LENGTH = JPQL_PARAMETER_PLACEHOLDER.length();
/** The log object for this class. */
private static final Log LOG = CmsLog.getLog(CmsSqlManager.class);
/** The hashtable with all factories. You may have additional factories for OpenCms modules. */
private static Hashtable m_factoryTable = new Hashtable();
/** Contains the state of initialization of the static part of the class. */
private static boolean m_isInitialized;
/** EntityManager factory for OpenCms application. */
private static EntityManagerFactory m_persistenceFactory;
/** Contains information about uncleared EntityManager instances. */
private static Hashtable m_trackOn = new Hashtable();
/** The filename/path of the JPQL query properties. */
private static final String QUERY_PROPERTIES = "org/opencms/db/jpa/query.properties";
/** A map to cache queries with replaced search patterns. */
protected Hashtable m_cachedQueries;
/** A map holding all JPQL queries. */
protected Hashtable m_queries;
/** Queries with parameters. */
protected Hashtable m_queriesWithParameters;
/**
* The constructor.
*
* @throws CmsDbException if the manager is not initialized yet
*/
public CmsSqlManager()
throws CmsDbException {
if (!m_isInitialized) {
throw new CmsDbException(Messages.get().container(Messages.ERR_SQLMANAGER_NOT_INITIALIZED_0));
}
m_cachedQueries = new Hashtable();
m_queries = new Hashtable();
m_queriesWithParameters = new Hashtable();
loadQueryProperties(QUERY_PROPERTIES);
}
/**
* Create EntityManager instance for given unit name. If factory
* for this unit is not already created it creates one.
*
* @param unitName - the unit name in the persistence.xml file
* @return EntityManager instance for given unit name
*/
public static EntityManager createEntityManager(String unitName) {
EntityManager em = null;
EntityManagerFactory factory = getFactory(unitName);
if (factory != null) {
em = factory.createEntityManager();
}
return em;
}
/**
* Close all instances of EntityManagerFactory.
*/
public static synchronized void destroy() {
if (CmsLog.INIT.isDebugEnabled()) {
trackOn();
}
try {
m_openCmsEmPool.close();
} catch (Exception e) {
// do nothing
}
if (m_factoryTable != null) {
Set s = m_factoryTable.keySet();
EntityManagerFactory emf;
for (String f : s) {
emf = m_factoryTable.get(f);
if (emf != null) {
emf.close();
m_factoryTable.remove(f);
}
}
}
m_isInitialized = false;
}
/**
* Creates EntityManager from OpenCms's factory.
*
* @return EntityManager created from OpenCms's factory
*/
public static EntityManager getEntityManager() {
EntityManager em = null;
try {
em = (EntityManager)m_openCmsEmPool.borrowObject();
if (CmsLog.INIT.isDebugEnabled()) {
m_trackOn.put(em, Thread.currentThread().getStackTrace());
}
} catch (Exception e) {
LOG.error("Error while getting JPA EntityManager", e);
}
return em;
}
/**
* Returns EntityManagerFactory for given unit name. If the factory does not already exists it creates one.
*
* @param unitName - the unit name in the persistence.xml file
*
* @return EntityManagerFactory for given unit name
*/
public static EntityManagerFactory getFactory(String unitName) {
EntityManagerFactory factory = m_factoryTable.get(unitName);
if (factory == null) {
factory = Persistence.createEntityManagerFactory(unitName);
m_factoryTable.put(unitName, factory);
}
return factory;
}
/**
* Creates a new instance of a SQL manager.
*
* @param classname the classname of the SQL manager
*
* @return a new instance of the SQL manager
*/
public static CmsSqlManager getInstance(String classname) {
CmsSqlManager sqlManager;
try {
sqlManager = new CmsSqlManager();
} catch (Throwable t) {
LOG.error(Messages.get().getBundle().key(Messages.LOG_SQL_MANAGER_INIT_FAILED_1, classname), t);
sqlManager = null;
}
if (CmsLog.INIT.isInfoEnabled()) {
CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_DRIVER_SQL_MANAGER_1, classname));
}
return sqlManager;
}
/**
* Initialize the static part of the class.
*
* @param config the combined configuration of "opencms.properties" and the "persistence.xml"
*/
public static void init(CmsParameterConfiguration config) {
if (!m_isInitialized) {
m_isInitialized = true;
String connProps = buildConnectionPropertiesValue(config, CmsDbPool.OPENCMS_DEFAULT_POOL_NAME);
Properties systemProps = System.getProperties();
systemProps.setProperty(CmsPersistenceUnitConfiguration.ATTR_CONNECTION_PROPERTIES, connProps);
m_persistenceFactory = Persistence.createEntityManagerFactory(JPA_PERSISTENCE_UNIT, systemProps);
m_factoryTable.put(JPA_PERSISTENCE_UNIT, m_persistenceFactory);
CmsPoolEntityManagerFactory entityMan = new CmsPoolEntityManagerFactory(m_persistenceFactory);
int entityManagerPoolSize = config.getInteger(
CmsDbPool.KEY_DATABASE_POOL
+ "."
+ CmsDbPool.OPENCMS_DEFAULT_POOL_NAME
+ "."
+ CmsDbPool.KEY_ENTITY_MANAGER_POOL_SIZE,
DEFAULT_ENTITY_MANAGER_POOL_SIZE);
m_openCmsEmPool = new StackObjectPool(entityMan, entityManagerPoolSize, 0);
}
}
/**
* Returns EntityManager instance from OpenCms, back to pool.
*
* @param em - instance which returns back to pool of OpenCmsJPAPool persistence context.
*/
public static void returnEntityManager(EntityManager em) {
try {
m_openCmsEmPool.returnObject(em);
if (CmsLog.INIT.isDebugEnabled()) {
m_trackOn.remove(em);
}
} catch (Exception e) {
LOG.error(e);
}
}
/**
* Builds the connection property value for JPA.
*
* @param config the opencms properties
* @param key the pool name
*
* @return the connection properties value
*/
private static String buildConnectionPropertiesValue(CmsParameterConfiguration config, String key) {
StringBuffer propValue = new StringBuffer();
// read the values of the pool configuration specified by the given key
String jdbcDriver = config.get(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_JDBC_DRIVER);
String jdbcUrl = config.get(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_JDBC_URL);
String jdbcUrlParams = config.get(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_JDBC_URL_PARAMS);
int maxActive = config.getInteger(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_MAX_ACTIVE, 10);
int maxWait = config.getInteger(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_MAX_WAIT, 2000);
int maxIdle = config.getInteger(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_MAX_IDLE, 5);
int minEvictableIdleTime = config.getInteger(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_MIN_EVICTABLE_IDLE_TIME,
1800000);
int minIdle = config.getInteger(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_MIN_IDLE, 0);
int numTestsPerEvictionRun = config.getInteger(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_NUM_TESTS_PER_EVICTION_RUN,
3);
int timeBetweenEvictionRuns = config.getInteger(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_TIME_BETWEEN_EVICTION_RUNS,
3600000);
String username = config.getString(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_USERNAME, "");
String password = config.getString(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_PASSWORD, "");
boolean testOnBorrow = Boolean.valueOf(
config.getString(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_TEST_ON_BORROW,
"false").trim()).booleanValue();
boolean testWhileIdle = Boolean.valueOf(
config.getString(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_TEST_WHILE_IDLE,
"false").trim()).booleanValue();
String testQuery = config.get(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + CmsDbPool.KEY_TEST_QUERY);
if ("".equals(testQuery)) {
testQuery = null;
}
int initialSize = config.getInteger(CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + KEY_INITIAL_SIZE, 20);
boolean poolPreparedStatements = config.getBoolean(
CmsDbPool.KEY_DATABASE_POOL + '.' + key + '.' + KEY_PREP_STATEMENTS,
true);
propValue.append(KEY_DRIVER_CLASS_NAME);
propValue.append("=");
propValue.append(jdbcDriver);
propValue.append(", ");
propValue.append(KEY_URL);
propValue.append("=");
propValue.append(jdbcUrl);
propValue.append(StringUtils.defaultString(jdbcUrlParams));
propValue.append(", ");
propValue.append(KEY_USER);
propValue.append("=");
propValue.append(username);
propValue.append(", ");
propValue.append(KEY_PASS);
propValue.append("=");
propValue.append(StringUtils.defaultString(password));
propValue.append(", ");
propValue.append(KEY_MAX_ACTIVE);
propValue.append("=");
propValue.append(maxActive);
propValue.append(", ");
propValue.append(KEY_MAX_IDLE);
propValue.append("=");
propValue.append(maxIdle);
propValue.append(", ");
propValue.append(KEY_MAX_WAIT);
propValue.append("=");
propValue.append(maxWait);
propValue.append(", ");
propValue.append(KEY_MIN_IDLE);
propValue.append("=");
propValue.append(minIdle);
propValue.append(", ");
if (testQuery != null) {
propValue.append(KEY_VALIDATION_QUERY);
propValue.append("=");
propValue.append(StringUtils.defaultString(testQuery));
propValue.append(", ");
propValue.append(KEY_TEST_ON_BORROW);
propValue.append("=");
propValue.append(testOnBorrow);
propValue.append(", ");
propValue.append(KEY_TEST_WHILE_IDLE);
propValue.append("=");
propValue.append(testWhileIdle);
propValue.append(", ");
propValue.append(KEY_TIME_BETWEEN_EVICTION_RUNS);
propValue.append("=");
propValue.append(timeBetweenEvictionRuns);
propValue.append(", ");
propValue.append(KEY_NUM_TESTS_PER_EVICTION_RUN);
propValue.append("=");
propValue.append(numTestsPerEvictionRun);
propValue.append(", ");
propValue.append(KEY_MIN_EVICTABLE_IDLE_TIME);
propValue.append("=");
propValue.append(minEvictableIdleTime);
propValue.append(", ");
}
propValue.append(KEY_INITIAL_SIZE);
propValue.append("=");
propValue.append(initialSize);
propValue.append(", ");
propValue.append(KEY_PREP_STATEMENTS);
propValue.append("=");
propValue.append(poolPreparedStatements);
return propValue.toString();
}
/**
* Replaces the project search pattern in JPQL queries by the pattern _ONLINE_ or _OFFLINE_ depending on the
* specified project ID.
*
* @param projectId the ID of the current project
* @param query the JPQL query
* @return String the JPQL query with the table key search pattern replaced
*/
private static String replaceProjectPattern(CmsUUID projectId, String query) {
// make the statement project dependent
String replacePattern = ((projectId == null) || projectId.equals(CmsProject.ONLINE_PROJECT_ID))
? ONLINE_PROJECT
: OFFLINE_PROJECT;
return CmsStringUtil.substitute(query, QUERY_PROJECT_SEARCH_PATTERN, replacePattern);
}
/**
* Write information about uncleared EntityManager instances in the log.
*/
private static void trackOn() {
LOG.debug("#################### Start Tracking on EM instances ");
LOG.debug(" there is " + m_trackOn.keySet().size() + " instances uncleared");
Set set = m_trackOn.keySet();
int i = 0;
for (EntityManager em : set) {
i++;
LOG.debug("--- " + i + " instance tracelog --- ");
StackTraceElement[] el = m_trackOn.get(em);
for (int b = 0; b < el.length; b++) {
LOG.debug(el[b].toString());
}
}
LOG.debug("#################### Stop Tracking on EM instances ");
}
/**
* Returns a Query for a EntityManagerContext specified by the key of a SQL query
* and the project-ID.
*
* @param dbc the the db context
* @param projectId the ID of the specified CmsProject
* @param queryKey the key of the SQL query
*
* @return Query a new Query containing the pre-compiled SQL statement
*/
public Query createNativeQuery(CmsDbContext dbc, CmsUUID projectId, String queryKey) {
org.opencms.db.jpa.CmsDbContext jpaDbc = (org.opencms.db.jpa.CmsDbContext)dbc;
jpaDbc.getEntityManager().flush();
String rawSql = readQuery(projectId, queryKey);
return jpaDbc.getEntityManager().createNativeQuery(prepareQueryParameters(rawSql).toUpperCase());
}
/**
* Returns a Query for a JDBC connection specified by the key of a JPQL query
* and the CmsProject.
*
* @param dbc the db context
* @param project the specified CmsProject
* @param queryKey the key of the JPQL query
*
* @return Query a new Query containing the pre-compiled JPQL statement
*/
public Query createQuery(CmsDbContext dbc, CmsProject project, String queryKey) {
return createQuery(dbc, project.getUuid(), queryKey);
}
/**
* Returns a Query for a EntityManagerContext specified by the key of a JPQL query
* and the project-ID.
*
* @param dbc the dbc context
* @param projectId the ID of the specified CmsProject
* @param queryKey the key of the JPQL query
*
* @return Query a new Query containing the pre-compiled JPQL statement
*
*/
public Query createQuery(CmsDbContext dbc, CmsUUID projectId, String queryKey) {
org.opencms.db.jpa.CmsDbContext jpaDbc = (org.opencms.db.jpa.CmsDbContext)dbc;
jpaDbc.getEntityManager().flush();
String rawJpql = readQuery(projectId, queryKey);
return jpaDbc.getEntityManager().createQuery(prepareQueryParameters(rawJpql));
}
/**
* Returns a Query for a EntityManagerContext specified by the key of a JPQL query.
*
* @param dbc the db context
* @param queryKey the key of the JPQL query
* @return Query a new Query containing the pre-compiled JPQL statement
*/
public Query createQuery(CmsDbContext dbc, String queryKey) {
org.opencms.db.jpa.CmsDbContext jpaDbc = (org.opencms.db.jpa.CmsDbContext)dbc;
jpaDbc.getEntityManager().flush();
String rawJpql = readQuery(CmsUUID.getNullUUID(), queryKey);
return jpaDbc.getEntityManager().createQuery(prepareQueryParameters(rawJpql));
}
/**
* Returns a Query for a JDBC connection specified by the JPQL query.
*
* @param dbc the db context object
* @param query the JPQL query
* @return Query a new Query containing the pre-compiled JPQL statement
*/
public Query createQueryFromJPQL(CmsDbContext dbc, String query) {
org.opencms.db.jpa.CmsDbContext jpaDbc = (org.opencms.db.jpa.CmsDbContext)dbc;
jpaDbc.getEntityManager().flush();
return jpaDbc.getEntityManager().createQuery(prepareQueryParameters(query));
}
/**
* Returns a Query for a JDBC connection specified by the JPQL query.
*
* @param dbc the db context object
* @param query the JPQL query
* @param params the parameters to insert into the query
*
* @return Query a new Query containing the pre-compiled JPQL statement
*/
public Query createQueryWithParametersFromJPQL(CmsDbContext dbc, String query, List