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.ExecutionContext;
034    import org.datanucleus.query.evaluator.JPQLEvaluator;
035    import org.datanucleus.query.evaluator.JavaQueryEvaluator;
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    }