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.HashMap;
024    import java.util.HashSet;
025    import java.util.List;
026    import java.util.Map;
027    import java.util.Set;
028    
029    import javax.jdo.PersistenceManager;
030    
031    import org.cumulus4j.store.Cumulus4jStoreManager;
032    import org.cumulus4j.store.crypto.CryptoContext;
033    import org.cumulus4j.store.model.ClassMeta;
034    import org.cumulus4j.store.model.ClassMetaDAO;
035    import org.cumulus4j.store.model.DataEntry;
036    import org.datanucleus.ClassLoaderResolver;
037    import org.datanucleus.identity.IdentityUtils;
038    import org.datanucleus.store.ExecutionContext;
039    
040    /**
041     * Helper methods for querying.
042     */
043    public class QueryHelper {
044    
045            /**
046             * Access the data entry ids for a candidate.
047             * @param cryptoContext the crypto-context (containing the {@link ExecutionContext} and other context data + API).
048             * @param pmData PersistenceManager for the backend data
049             * @param candidateCls Candidate class
050             * @param subclasses Whether to include subclasses
051             * @return The data entry ids
052             */
053            public static Set<Long> getAllDataEntryIdsForCandidate(CryptoContext cryptoContext, PersistenceManager pmData, Class<?> candidateCls, boolean subclasses) {
054                    ExecutionContext ec = cryptoContext.getExecutionContext();
055                    javax.jdo.Query q = pmData.newQuery(DataEntry.class);
056                    q.setResult("this.dataEntryID");
057    
058                    Object queryParam_classMeta;
059                    Set<ClassMeta> classMetas = QueryHelper.getCandidateClassMetas(
060                                    (Cumulus4jStoreManager) ec.getStoreManager(), ec, candidateCls, subclasses
061                    );
062                    StringBuilder filter = new StringBuilder();
063                    filter.append("this.keyStoreRefID == :keyStoreRefID && ");
064                    if (classMetas.size() == 1) {
065                            filter.append("this.classMeta == :classMeta");
066                            queryParam_classMeta = classMetas.iterator().next();
067                    }
068                    else {
069                            filter.append(":classMetas.contains(this.classMeta)");
070                            queryParam_classMeta = classMetas;
071                    }
072                    q.setFilter(filter.toString());
073    
074                    @SuppressWarnings("unchecked")
075                    Collection<Object[]> c = (Collection<Object[]>) q.execute(cryptoContext.getKeyStoreRefID(), queryParam_classMeta);
076                    Set<Long> resultList = new HashSet<Long>(c.size());
077                    for (Object[] oa : c) {
078                            resultList.add((Long)oa[0]);
079                    }
080                    q.closeAll();
081                    return resultList;
082            }
083    
084            /**
085             * Convenience method to return the persistent objects for the classes with the provided ClassMetas.
086             * @param cryptoContext the crypto-context (containing the {@link ExecutionContext} and other context data + API).
087             * @param pmData PersistenceManager for the backend data
088             * @param candidateClassMetas The class metas defining the required classes
089             * @return The persistent objects
090             */
091            public static List<Object> getAllPersistentObjectsForCandidateClasses(CryptoContext cryptoContext, PersistenceManager pmData,
092                            Set<ClassMeta> candidateClassMetas)
093            {
094                    ExecutionContext ec = cryptoContext.getExecutionContext();
095                    Cumulus4jStoreManager storeManager = (Cumulus4jStoreManager) ec.getStoreManager();
096    
097                    javax.jdo.Query q = pmData.newQuery(DataEntry.class);
098                    q.setResult("this.classMeta_classID, this.objectID");
099    
100                    Map<String, Object> queryParams = new HashMap<String, Object>();
101                    StringBuilder filter = new StringBuilder();
102    
103                    filter.append("this.keyStoreRefID == :keyStoreRefID && ");
104                    queryParams.put("keyStoreRefID", cryptoContext.getKeyStoreRefID());
105    
106                    filter.append(ClassMetaDAO.getMultiClassMetaOrFilterPart(queryParams, candidateClassMetas));
107    
108                    q.setFilter(filter.toString());
109    
110                    @SuppressWarnings("unchecked")
111                    Collection<Object[]> c = (Collection<Object[]>) q.executeWithMap(queryParams);
112                    List<Object> resultList = new ArrayList<Object>(c.size());
113                    for (Object[] oa : c) {
114                            Long classID = (Long) oa[0];
115                            if (classID == null)
116                                    throw new IllegalStateException("Query returned classMeta_classID == null!");
117    
118                            ClassMeta classMeta = storeManager.getClassMeta(ec, classID, true);
119                            String objectIDString = (String) oa[1];
120                            Object obj = IdentityUtils.getObjectFromIdString(objectIDString, classMeta.getDataNucleusClassMetaData(ec), ec, true);
121                            resultList.add(obj);
122                    }
123                    q.closeAll();
124                    return resultList;
125            }
126    
127            /**
128             * Convenience method to return the ClassMeta objects for the specified class (and subclasses if required).
129             * @param storeMgr Cumulus4J StoreManager
130             * @param ec ExecutionContext
131             * @param candidateClass The class required
132             * @param withSubclasses Whether to return subclasses too
133             * @return The ClassMeta objects
134             */
135            public static Set<ClassMeta> getCandidateClassMetas(Cumulus4jStoreManager storeMgr, ExecutionContext ec,
136                            Class<?> candidateClass, boolean withSubclasses)
137            {
138                    Set<? extends Class<?>> candidateClasses;
139                    if (withSubclasses) {
140                            ClassLoaderResolver clr = ec.getClassLoaderResolver();
141                            HashSet<String> classNames = storeMgr.getSubClassesForClass(candidateClass.getName(), true, clr);
142                            Set<Class<?>> classes = new HashSet<Class<?>>(classNames.size() + 1);
143                            classes.add(candidateClass);
144                            for (String className : classNames) {
145                                    Class<?> clazz = clr.classForName(className);
146                                    classes.add(clazz);
147                            }
148                            candidateClasses = classes;
149                    }
150                    else
151                            candidateClasses = Collections.singleton(candidateClass);
152    
153                    Set<ClassMeta> candidateClassMetas = new HashSet<ClassMeta>(candidateClasses.size());
154                    for (Class<?> c : candidateClasses) {
155                            ClassMeta cm = storeMgr.getClassMeta(ec, c);
156                            candidateClassMetas.add(cm);
157                    }
158    
159                    return candidateClassMetas;
160            }
161    }