001 /*
002 * Cumulus4j - Securing your data in the cloud - http://cumulus4j.org
003 * Copyright (C) 2011 NightLabs Consulting GmbH
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program. If not, see <http://www.gnu.org/licenses/>.
017 */
018 package org.cumulus4j.store.query;
019
020 import java.util.ArrayList;
021 import java.util.Collection;
022 import java.util.Collections;
023 import java.util.List;
024 import java.util.Map;
025 import java.util.Set;
026
027 import javax.jdo.PersistenceManager;
028
029 import org.cumulus4j.store.Cumulus4jStoreManager;
030 import org.cumulus4j.store.PersistenceManagerConnection;
031 import org.cumulus4j.store.crypto.CryptoContext;
032 import org.cumulus4j.store.model.ClassMeta;
033 import org.datanucleus.query.evaluator.JPQLEvaluator;
034 import org.datanucleus.query.evaluator.JavaQueryEvaluator;
035 import org.datanucleus.store.ExecutionContext;
036 import org.datanucleus.store.StoreManager;
037 import org.datanucleus.store.connection.ManagedConnection;
038 import org.datanucleus.store.query.AbstractJPQLQuery;
039 import org.datanucleus.util.NucleusLogger;
040
041 /**
042 * JPQL query implementation. Delegates to the query-language-agnostic {@link QueryEvaluator} via
043 * its thin wrapper sub-class {@link JPAQueryEvaluator}.
044 */
045 public class JPQLQuery extends AbstractJPQLQuery {
046
047 private static final long serialVersionUID = 1L;
048
049 // BEGIN DataNucleus 3.0.0-m6 and 3.0.0-release
050 // public JPQLQuery(ExecutionContext ec, AbstractJPQLQuery q) {
051 // super(ec, q);
052 // }
053 //
054 // public JPQLQuery(ExecutionContext ec, String query) {
055 // super(ec, query);
056 // }
057 //
058 // public JPQLQuery(ExecutionContext ec) {
059 // super(ec);
060 // }
061 // END DataNucleus 3.0.0-m6 and 3.0.0-release
062
063 // BEGIN DataNucleus 3.0.1 and newer
064 public JPQLQuery(StoreManager storeMgr, ExecutionContext ec, AbstractJPQLQuery q) {
065 super(storeMgr, ec, q);
066 }
067
068 public JPQLQuery(StoreManager storeMgr, ExecutionContext ec, String query) {
069 super(storeMgr, ec, query);
070 }
071
072 public JPQLQuery(StoreManager storeMgr, ExecutionContext ec) {
073 super(storeMgr, ec);
074 }
075 // END DataNucleus 3.0.1 and newer
076
077 @Override
078 protected Object performExecute(@SuppressWarnings("rawtypes") Map parameters) {
079 ManagedConnection mconn = ec.getStoreManager().getConnection(ec);
080 try {
081 PersistenceManagerConnection pmConn = (PersistenceManagerConnection)mconn.getConnection();
082 PersistenceManager pmData = pmConn.getDataPM();
083
084 Cumulus4jStoreManager storeManager = (Cumulus4jStoreManager) ec.getStoreManager();
085 CryptoContext cryptoContext = new CryptoContext(storeManager.getEncryptionCoordinateSetManager(), storeManager.getKeyStoreRefManager(), ec, pmConn);
086 storeManager.getDatastoreVersionManager().applyOnce(cryptoContext);
087
088 boolean inMemory = evaluateInMemory();
089 boolean inMemory_applyFilter = true;
090 List<Object> candidates = null;
091 if (this.candidateCollection != null) {
092 if (candidateCollection.isEmpty()) {
093 return Collections.EMPTY_LIST;
094 }
095
096 @SuppressWarnings("unchecked")
097 Collection<? extends Object> c = this.candidateCollection;
098 candidates = new ArrayList<Object>(c);
099 }
100 else {
101 // http://sourceforge.net/tracker/?func=detail&aid=3514690&group_id=517465&atid=2102911
102 // Must NOT call this.setCandidateClass(...), because 1st it's already assigned and 2nd it clears the compilation.
103 // Marco :-)
104 // if (candidateExtent != null) {
105 // this.setCandidateClass(candidateExtent.getCandidateClass());
106 // this.setSubclasses(candidateExtent.hasSubclasses());
107 // }
108
109 if (inMemory) {
110 // Retrieve all candidates and perform all evaluation in-memory
111 Set<ClassMeta> classMetas = QueryHelper.getCandidateClassMetas((Cumulus4jStoreManager) ec.getStoreManager(),
112 ec, candidateClass, subclasses);
113 candidates = QueryHelper.getAllPersistentObjectsForCandidateClasses(cryptoContext, pmData, classMetas);
114 }
115 else {
116 try
117 {
118 // Apply filter in datastore
119 @SuppressWarnings("unchecked")
120 Map<String, Object> parameterValues = parameters;
121 JPAQueryEvaluator queryEvaluator = new JPAQueryEvaluator(this, compilation, parameterValues, clr, pmConn, cryptoContext);
122 candidates = queryEvaluator.execute();
123 if (queryEvaluator.isComplete()) {
124 inMemory_applyFilter = false;
125 }
126 else {
127 NucleusLogger.QUERY.debug("Query evaluation of filter in datastore was incomplete so doing further work in-memory");
128 }
129 }
130 catch (UnsupportedOperationException uoe) {
131 // Some part of the filter is not yet supported, so fallback to in-memory evaluation
132 // Retrieve all candidates and perform all evaluation in-memory
133 NucleusLogger.QUERY.info("Query filter is not totally evaluatable in-datastore using Cumulus4j currently, so evaluating in-memory : "+uoe.getMessage());
134 Set<ClassMeta> classMetas = QueryHelper.getCandidateClassMetas((Cumulus4jStoreManager) ec.getStoreManager(),
135 ec, candidateClass, subclasses);
136 candidates = QueryHelper.getAllPersistentObjectsForCandidateClasses(cryptoContext, pmData, classMetas);
137 }
138 }
139 }
140
141 // Evaluate any remaining query components in-memory
142 JavaQueryEvaluator evaluator = new JPQLEvaluator(this, candidates, compilation, parameters, ec.getClassLoaderResolver());
143 return evaluator.execute(inMemory_applyFilter, true, true, true, true);
144 } finally {
145 mconn.release();
146 }
147 }
148 }