/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.util;
import com.sun.enterprise.Switch;
import com.sun.enterprise.admin.monitor.callflow.Agent;
import com.sun.enterprise.admin.monitor.callflow.EntityManagerQueryMethod;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.LinkedList;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.FlushModeType;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TransactionRequiredException;
/**
* Wrapper class for javax.persistence.Query objects returned from
* non-transactional access of a container-managed transactional
* EntityManager. Proxying the Query object prevents the
* EntityManagerWrapper from having to keep a physical EntityManager
* open when returning Query objects for non-transactional access.
*
* This results in a cleaner implementation of the non-transactional
* EntityManager behavior and minimizes the amount of time
* non-transactional EntityManager objects are left open. It is likely
* that physical EntityManager objects will have heavy-weight resources
* such as DB connections open even after clear() is called. This is
* one of the main reasons to minimize the number of open non-transactional
* EntityManager objects held internally within injected/looked-up
* container-managed EntityManagers.
*
* The EntityManager and Query delegate objects are provided at
* QueryWrapper creation time. These objects must exist in order
* for the EntityManagerWrapper to provide the correct exception
* behavior to the application when a Query is requested.
* Likewise, the actual delegates must be available
* to handle the majority of the Query API operations such as
* performing validation on the various setter parameters.
*
* The Query/EntityManager delegates
* are closed/discarded after each call to getSingleResult/getResultList.
* A new Query/EntityManager delegate pair is then created lazily
* the next time the Query delegate is needed. The QueryWrapper
* maintains a list of all setter operations invoked by the application.
* These are re-applied in the same order whenever a new Query delegate
* is created to ensure that the state of the Query delegate object matches
* what it would have been if there wasn't any QueryWrapper.
*
*/
public class QueryWrapper implements Query {
// Holds current query/em delegates. These are cleared out after
// query execution to minimize potential entity manager resource leakage.
private Query queryDelegate;
private EntityManager entityManagerDelegate;
// Used if new query/em delegates need to be created.
private EntityManagerFactory entityMgrFactory;
private Map entityMgrProperties;
// State used to construct query delegate object itself.
private QueryType queryType;
private String queryString;
private Class queryResultClass;
private String queryResultSetMapping;
// State used to recreate sequence of setter methods applied to the
// QueryWrapper when a new Query delegate is created.
private List<SetterData> setterInvocations;
public static Query createQueryWrapper(EntityManagerFactory emf,
Map emProperties,
EntityManager emDelegate,
Query queryDelegate,
String ejbqlString) {
return new QueryWrapper(emf, emProperties, emDelegate,
queryDelegate, QueryType.EJBQL,
ejbqlString, null, null);
}
public static Query createNamedQueryWrapper(EntityManagerFactory emf,
Map emProperties,
EntityManager emDelegate,
Query queryDelegate,
String name) {
return new QueryWrapper(emf, emProperties, emDelegate,
queryDelegate, QueryType.NAMED,
name, null, null);
}
public static Query createNativeQueryWrapper(EntityManagerFactory emf,
Map emProperties,
EntityManager emDelegate,
Query queryDelegate,
String sqlString) {
return new QueryWrapper(emf, emProperties, emDelegate,
queryDelegate, QueryType.NATIVE,
sqlString, null, null);
}
public static Query createNativeQueryWrapper(EntityManagerFactory emf,
Map emProperties,
EntityManager emDelegate,
Query queryDelegate,
String sqlString,
Class resultClass) {
return new QueryWrapper(emf, emProperties, emDelegate,
queryDelegate, QueryType.NATIVE,
sqlString, resultClass, null);
}
public static Query createNativeQueryWrapper(EntityManagerFactory emf,
Map emProperties,
EntityManager emDelegate,
Query queryDelegate,
String sqlString,
String resultSetMapping) {
return new QueryWrapper(emf, emProperties, emDelegate,
queryDelegate, QueryType.NATIVE,
sqlString, null, resultSetMapping);
}
private QueryWrapper(EntityManagerFactory emf, Map emProperties,
EntityManager emDelegate, Query qDelegate,
QueryType type, String query,
Class resultClass, String resultSetMapping)
{
entityMgrFactory = emf;
entityMgrProperties = emProperties;
entityManagerDelegate = emDelegate;
queryDelegate = qDelegate;
queryType = type;
queryString = query;
queryResultClass = resultClass;
queryResultSetMapping = resultSetMapping;
setterInvocations = new LinkedList<SetterData>();
}
public List getResultList() {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.GET_RESULT_LIST);
}
Query delegate = getQueryDelegate();
return delegate.getResultList();
} finally {
clearDelegates();
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
}
public Object getSingleResult() {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.GET_SINGLE_RESULT);
}
Query delegate = getQueryDelegate();
return delegate.getSingleResult();
} finally {
clearDelegates();
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
}
public int executeUpdate() {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.EXECUTE_UPDATE);
callFlowAgent.entityManagerQueryEnd();
}
throw new TransactionRequiredException("executeUpdate is not supported for a Query object obtained through non-transactional access of a container-managed transactional EntityManager");
}
public Query setMaxResults(int maxResults) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_MAX_RESULTS);
}
if( maxResults < 0 ) {
throw new IllegalArgumentException("maxResult cannot be negative");
}
Query delegate = getQueryDelegate();
delegate.setMaxResults(maxResults);
SetterData setterData = SetterData.createMaxResults(maxResults);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setFirstResult(int startPosition) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_FIRST_RESULT);
}
if( startPosition < 0 ) {
throw new IllegalArgumentException
("startPosition cannot be negative");
}
Query delegate = getQueryDelegate();
delegate.setFirstResult(startPosition);
SetterData setterData = SetterData.createFirstResult(startPosition);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setHint(String hintName, Object value) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_HINT);
}
Query delegate = getQueryDelegate();
delegate.setHint(hintName, value);
SetterData setterData = SetterData.createHint(hintName, value);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(String name, Object value) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_STRING_OBJECT);
}
Query delegate = getQueryDelegate();
delegate.setParameter(name, value);
SetterData setterData = SetterData.createParameter(name, value);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(String name, Date value,
TemporalType temporalType) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_STRING_DATE_TEMPORAL_TYPE);
}
Query delegate = getQueryDelegate();
delegate.setParameter(name, value, temporalType);
SetterData setterData = SetterData.createParameter(name, value,
temporalType);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(String name, Calendar value,
TemporalType temporalType) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_STRING_CALENDAR_TEMPORAL_TYPE);
}
Query delegate = getQueryDelegate();
delegate.setParameter(name, value, temporalType);
SetterData setterData = SetterData.createParameter(name, value,
temporalType);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(int position, Object value) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_INT_OBJECT);
}
Query delegate = getQueryDelegate();
delegate.setParameter(position, value);
SetterData setterData = SetterData.createParameter(position, value);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(int position, Date value,
TemporalType temporalType) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_INT_DATE_TEMPORAL_TYPE);
}
Query delegate = getQueryDelegate();
delegate.setParameter(position, value, temporalType);
SetterData setterData = SetterData.createParameter(position, value,
temporalType);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setParameter(int position, Calendar value,
TemporalType temporalType) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_PARAMETER_INT_CALENDAR_TEMPORAL_TYPE);
}
Query delegate = getQueryDelegate();
delegate.setParameter(position, value, temporalType);
SetterData setterData = SetterData.createParameter(position, value,
temporalType);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
public Query setFlushMode(FlushModeType flushMode) {
Agent callFlowAgent = Switch.getSwitch().getCallFlowAgent();
try {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryStart(EntityManagerQueryMethod.SET_FLUSH_MODE);
}
Query delegate = getQueryDelegate();
delegate.setFlushMode(flushMode);
SetterData setterData = SetterData.createFlushMode(flushMode);
setterInvocations.add(setterData);
} finally {
if(callFlowAgent.isEnabled()) {
callFlowAgent.entityManagerQueryEnd();
}
}
return this;
}
private void clearDelegates() {
queryDelegate = null;
if( entityManagerDelegate != null ) {
entityManagerDelegate.close();
entityManagerDelegate = null;
}
}
private Query getQueryDelegate() {
if( queryDelegate == null ) {
entityManagerDelegate =
entityMgrFactory.createEntityManager(entityMgrProperties);
switch(queryType) {
case EJBQL :
queryDelegate =
entityManagerDelegate.createQuery(queryString);
break;
case NAMED :
queryDelegate =
entityManagerDelegate.createNamedQuery(queryString);
break;
case NATIVE :
if( queryResultClass != null ) {
queryDelegate = entityManagerDelegate.createNativeQuery
(queryString, queryResultClass);
} else if( queryResultSetMapping != null ) {
queryDelegate = entityManagerDelegate.createNativeQuery
(queryString, queryResultSetMapping);
} else {
queryDelegate = entityManagerDelegate.createNativeQuery
(queryString);
}
break;
}
// Now recreate the sequence of valid setter invocations applied
// to this query.
for(SetterData setterData : setterInvocations) {
setterData.apply(queryDelegate);
}
}
return queryDelegate;
}
private enum QueryType {
EJBQL,
NAMED,
NATIVE
}
private enum SetterType {
MAX_RESULTS,
FIRST_RESULT,
HINT,
PARAM_NAME_OBJECT,
PARAM_NAME_DATE_TEMPORAL,
PARAM_NAME_CAL_TEMPORAL,
PARAM_POSITION_OBJECT,
PARAM_POSITION_DATE_TEMPORAL,
PARAM_POSITION_CAL_TEMPORAL,
FLUSH_MODE
}
private static class SetterData {
SetterType type;
int int1;
String string1;
Object object1;
Date date;
Calendar calendar;
TemporalType temporalType;
FlushModeType flushMode;
static SetterData createMaxResults(int maxResults) {
SetterData data = new SetterData();
data.type = SetterType.MAX_RESULTS;
data.int1 = maxResults;
return data;
}
static SetterData createFirstResult(int firstResult) {
SetterData data = new SetterData();
data.type = SetterType.FIRST_RESULT;
data.int1 = firstResult;
return data;
}
static SetterData createHint(String hintName, Object value) {
SetterData data = new SetterData();
data.type = SetterType.HINT;
data.string1 = hintName;
data.object1 = value;
return data;
}
static SetterData createParameter(String name, Object value) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_NAME_OBJECT;
data.string1 = name;
data.object1 = value;
return data;
}
static SetterData createParameter(String name, Date value,
TemporalType temporalType) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_NAME_DATE_TEMPORAL;
data.string1 = name;
data.date = value;
data.temporalType = temporalType;
return data;
}
static SetterData createParameter(String name, Calendar value,
TemporalType temporalType) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_NAME_CAL_TEMPORAL;
data.string1 = name;
data.calendar = value;
data.temporalType = temporalType;
return data;
}
static SetterData createParameter(int position, Object value) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_POSITION_OBJECT;
data.int1 = position;
data.object1 = value;
return data;
}
static SetterData createParameter(int position, Date value,
TemporalType temporalType) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_POSITION_DATE_TEMPORAL;
data.int1 = position;
data.date = value;
data.temporalType = temporalType;
return data;
}
static SetterData createParameter(int position, Calendar value,
TemporalType temporalType) {
SetterData data = new SetterData();
data.type = SetterType.PARAM_POSITION_CAL_TEMPORAL;
data.int1 = position;
data.calendar = value;
data.temporalType = temporalType;
return data;
}
static SetterData createFlushMode(FlushModeType flushMode) {
SetterData data = new SetterData();
data.type = SetterType.FLUSH_MODE;
data.flushMode = flushMode;
return data;
}
void apply(Query query) {
switch(type) {
case MAX_RESULTS :
query.setMaxResults(int1);
break;
case FIRST_RESULT :
query.setFirstResult(int1);
break;
case HINT :
query.setHint(string1, object1);
break;
case PARAM_NAME_OBJECT :
query.setParameter(string1, object1);
break;
case PARAM_NAME_DATE_TEMPORAL :
query.setParameter(string1, date, temporalType);
break;
case PARAM_NAME_CAL_TEMPORAL :
query.setParameter(string1, calendar, temporalType);
break;
case PARAM_POSITION_OBJECT :
query.setParameter(int1, object1);
break;
case PARAM_POSITION_DATE_TEMPORAL :
query.setParameter(int1, date, temporalType);
break;
case PARAM_POSITION_CAL_TEMPORAL :
query.setParameter(int1, calendar, temporalType);
break;
case FLUSH_MODE :
query.setFlushMode(flushMode);
break;
}
}
}
}
|