KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > versant > core > jdo > VersantQueryImp


1
2 /*
3  * Copyright (c) 1998 - 2005 Versant Corporation
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  * Versant Corporation - initial API and implementation
11  */

12 package com.versant.core.jdo;
13
14 import com.versant.core.common.NewObjectOID;
15 import com.versant.core.metadata.ClassMetaData;
16 import com.versant.core.metadata.FetchGroup;
17 import com.versant.core.server.CompiledQuery;
18 import com.versant.core.util.BeanUtils;
19
20 import javax.jdo.Extent;
21 import javax.jdo.PersistenceManager;
22 import javax.jdo.spi.PersistenceCapable;
23 import java.io.Externalizable JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.ObjectInput JavaDoc;
26 import java.io.ObjectOutput JavaDoc;
27 import java.util.*;
28
29 import com.versant.core.common.BindingSupportImpl;
30
31 /**
32  * This is the implementation of a Query.
33  */

34 public final class VersantQueryImp implements VersantQuery, Externalizable JavaDoc {
35
36     private final QueryDetails queryDetails = new QueryDetails();
37     private QueryResult resultList;
38     private PMProxy pmProxy;
39     private CompiledQuery compiledQuery;
40
41     /**
42      * For Serialization.
43      */

44     public VersantQueryImp() {
45     }
46
47     /**
48      * Create a new JDOQL query for pmProxy. The ignoreCache setting is taken
49      * from the curremt setting of pmProxy.
50      */

51     public VersantQueryImp(PMProxy pmProxy) {
52         this(pmProxy, QueryDetails.LANGUAGE_JDOQL);
53     }
54
55     /**
56      * Create a new query for pmProxy. The ignoreCache setting is taken
57      * from the curremt setting of pmProxy.
58      *
59      * @param language Query language
60      * @see QueryDetails#LANGUAGE_JDOQL
61      * @see QueryDetails#LANGUAGE_SQL
62      */

63     public VersantQueryImp(PMProxy pmProxy, int language) {
64         this.pmProxy = pmProxy;
65         queryDetails.setLanguage(language);
66         setIgnoreCache(pmProxy.getIgnoreCache());
67     }
68
69     /**
70      * Create a new query for pmProxy using all the settings of other.
71      */

72     public VersantQueryImp(PMProxy pmProxy, VersantQueryImp other) {
73         this(pmProxy, other.queryDetails);
74     }
75
76     /**
77      * Create a new query for pmProxy using all the settings of params.
78      * This is used to create Query's from named queries in the meta data.
79      */

80     public VersantQueryImp(PMProxy pmProxy, QueryDetails params) {
81         this.pmProxy = pmProxy;
82         queryDetails.fillFrom(params);
83         if (params.isUseIgnoreCacheFromPM()) {
84             setIgnoreCache(pmProxy.getIgnoreCache());
85         }
86     }
87
88     /**
89      * This is a util method that invalidates the preCompiled query because of
90      * change made by the client.
91      */

92     private void changed() {
93         compiledQuery = null;
94     }
95
96     public void setBounded(boolean value) {
97         queryDetails.setBounded(value);
98     }
99
100     public boolean isBounded() {
101         return queryDetails.isBounded();
102     }
103
104     public void setClass(Class JavaDoc cls) {
105         changed();
106         queryDetails.setCandidateClass(cls);
107     }
108
109     public void setCandidates(Extent pcs) {
110         changed();
111         queryDetails.setExtent(pcs);
112     }
113
114     public void setCandidates(Collection pcs) {
115         changed();
116         queryDetails.setCol(pcs);
117     }
118
119     public void setFilter(String JavaDoc filter) {
120         changed();
121         queryDetails.setFilter(filter);
122     }
123
124     public String JavaDoc getFilter() {
125         return queryDetails.getFilter();
126     }
127
128     public void declareImports(String JavaDoc imports) {
129         changed();
130         queryDetails.setImports(imports);
131     }
132
133     public void declareParameters(String JavaDoc params) {
134         changed();
135         queryDetails.declareParameters(params);
136     }
137
138     public void declareVariables(String JavaDoc variables) {
139         changed();
140         queryDetails.setVariables(variables);
141     }
142
143     public void setOrdering(String JavaDoc ordering) {
144         changed();
145         queryDetails.setOrdering(ordering);
146     }
147
148     public void setIgnoreCache(boolean ignoreCache) {
149         changed();
150         queryDetails.setIgnoreCache(ignoreCache);
151     }
152
153     private VersantPersistenceManagerImp getRealPM() {
154         if (pmProxy == null) {
155             throw BindingSupportImpl.getInstance().invalidOperation(
156                     "Query is not associated with a PersistenceManager");
157         }
158         return pmProxy.getRealPM();
159     }
160
161     public void setFetchGroup(String JavaDoc fgName) {
162         if (fgName == null) {
163             queryDetails.setFetchGroupIndex(0);
164             changed();
165             return;
166         } else {
167             if (queryDetails.getCandidateClass() == null) {
168                 throw BindingSupportImpl.getInstance().invalidOperation(
169                         "Please first supply a candidate class");
170             }
171             FetchGroup fg = getRealPM().modelMetaData.getClassMetaData(
172                     queryDetails.getCandidateClass()).getFetchGroup(fgName);
173             if (fg == null) {
174                 throw BindingSupportImpl.getInstance().invalidOperation("No fetchGroup with name "
175                         + fgName
176                         + " for class "
177                         + queryDetails.getCandidateClass().getName());
178             }
179
180             queryDetails.setFetchGroupIndex(fg.index);
181             changed();
182         }
183     }
184
185     public String JavaDoc getFetchGroup() {
186         int i = queryDetails.getFetchGroupIndex();
187         if (i == 0) return null;
188         ClassMetaData cmd = getRealPM().modelMetaData.getClassMetaData(
189                 queryDetails.getCandidateClass());
190         return cmd.fetchGroups[i].name;
191     }
192
193     public void setMaxRows(int amount) {
194         changed();
195         this.queryDetails.setMaxResultCount(amount);
196     }
197
198     public int getMaxRows() {
199         return queryDetails.getMaxResultCount();
200     }
201
202     public void setFetchSize(int value) {
203         changed();
204         this.queryDetails.setResultBatchSize(value);
205     }
206
207     public int getFetchSize() {
208         return queryDetails.getResultBatchSize();
209     }
210
211     public void setRandomAccess(boolean on) {
212         changed();
213         queryDetails.setRandomAccess(on);
214     }
215
216     public boolean isRandomAccess() {
217         return queryDetails.isRandomAccess();
218     }
219
220     public void setCountStarOnSize(boolean on) {
221         changed();
222         queryDetails.setCountOnSize(on);
223     }
224
225     public boolean isCountStarOnSize() {
226         return queryDetails.isCountOnSize();
227     }
228
229     public boolean getIgnoreCache() {
230         return queryDetails.isIgnoreCache();
231     }
232
233     public void setEvictionClasses(Class JavaDoc[] classes, boolean includeSubclasses) {
234         setEvictionClasses(getRealPM().modelMetaData.convertToClassIndexes(
235                 classes,
236                 includeSubclasses));
237     }
238
239     public void setEvictionClasses(int[] classIndexes) {
240         changed();
241         queryDetails.setExtraEvictClasses(classIndexes);
242     }
243
244     public Class JavaDoc[] getEvictionClasses() {
245         int[] a = queryDetails.getExtraEvictClasses();
246         return a == null ? null : getRealPM().modelMetaData.convertFromClassIndexes(
247                 a);
248     }
249
250     public void setResult(String JavaDoc result) {
251         changed();
252         queryDetails.setResult(result);
253     }
254
255     public void setGrouping(String JavaDoc grouping) {
256         changed();
257         queryDetails.setGrouping(grouping);
258     }
259
260     /**
261      * Specify that there is a single result of the query.
262      */

263     public void setUnique(boolean unique) {
264         changed();
265         queryDetails.setUnique(unique);
266     }
267
268     public void compile() {
269         if (compiledQuery == null) {
270             queryDetails.updateCounts();
271             compiledQuery = pmProxy.getRealPM().getStorageManager().compileQuery(
272                     queryDetails);
273         }
274     }
275
276
277     private
278
279
280     void checkParamCount(int n) {
281         int tc = queryDetails.getTotalParamCount();
282         if (tc >= 0 && n != tc) {
283             throw BindingSupportImpl.getInstance().runtime(
284                     "Expected " +
285                     queryDetails.getTotalParamCount() + " parameters, have " + n);
286         }
287     }
288
289     public Object JavaDoc execute() {
290         checkParamCount(0);
291         queryDetails.updateCounts();
292         return executeWithArrayImp(null);
293     }
294
295     public Object JavaDoc execute(Object JavaDoc p1) {
296         checkParamCount(1);
297         queryDetails.updateCounts();
298         if (queryDetails.hasJdoGenieOptions()) {
299             processJdoGenieOptions(p1);
300             return executeWithArrayImp(null);
301         } else {
302             return executeWithArrayImp(new Object JavaDoc[]{p1});
303         }
304     }
305
306     public Object JavaDoc execute(Object JavaDoc p1, Object JavaDoc p2) {
307         checkParamCount(2);
308         queryDetails.updateCounts();
309         switch (queryDetails.getOptionsParamIndex()) {
310             case 0:
311                 processJdoGenieOptions(p1);
312                 return executeWithArrayImp(new Object JavaDoc[]{p2});
313             case 1:
314                 processJdoGenieOptions(p2);
315                 return executeWithArrayImp(new Object JavaDoc[]{p1});
316         }
317         return executeWithArrayImp(new Object JavaDoc[]{p1, p2});
318     }
319
320     public Object JavaDoc execute(Object JavaDoc p1, Object JavaDoc p2, Object JavaDoc p3) {
321         checkParamCount(3);
322         queryDetails.updateCounts();
323         switch (queryDetails.getOptionsParamIndex()) {
324             case 0:
325                 processJdoGenieOptions(p1);
326                 return executeWithArrayImp(new Object JavaDoc[]{p2, p3});
327             case 1:
328                 processJdoGenieOptions(p2);
329                 return executeWithArrayImp(new Object JavaDoc[]{p1, p3});
330             case 2:
331                 processJdoGenieOptions(p2);
332                 return executeWithArrayImp(new Object JavaDoc[]{p1, p2});
333         }
334         return executeWithArrayImp(new Object JavaDoc[]{p1, p2, p3});
335     }
336
337     public final Object JavaDoc executeWithArray(Object JavaDoc[] parameters) {
338         queryDetails.updateCounts();
339         int n = parameters == null ? 0 : parameters.length;
340         checkParamCount(n);
341         int oi = queryDetails.getOptionsParamIndex();
342         if (oi >= 0) {
343             processJdoGenieOptions(parameters[oi]);
344             if (n == 1) {
345                 return executeWithArrayImp(null);
346             }
347             Object JavaDoc[] a = new Object JavaDoc[n - 1];
348             if (oi > 0) System.arraycopy(parameters, 0, a, 0, oi);
349             if (oi < n) System.arraycopy(parameters, oi + 1, a, oi, n - oi - 1);
350             return executeWithArrayImp(a);
351         } else {
352             return executeWithArrayImp(copyParams(parameters));
353         }
354     }
355
356     /**
357      * Get the query plan for this query. This will include the SQL and
358      * possibly also a query plan for the SQL from the database itself.
359      */

360     public VersantQueryPlan getPlan(Object JavaDoc[] parameters) {
361         queryDetails.updateCounts();
362         if (queryDetails.getCol() != null) {
363             throw BindingSupportImpl.getInstance().invalidOperation(
364                     "getPlan is not supported for queries executed against a collection");
365         }
366         int n = parameters == null ? 0 : parameters.length;
367         checkParamCount(n);
368         int oi = queryDetails.getOptionsParamIndex();
369         if (oi >= 0) {
370             processJdoGenieOptions(parameters[oi]);
371             if (n == 1) {
372                 return getPlanImp(null);
373             }
374             Object JavaDoc[] a = new Object JavaDoc[n - 1];
375             if (oi > 0) System.arraycopy(parameters, 0, a, 0, oi);
376             if (oi < n) System.arraycopy(parameters, oi, a, oi - 1, n - oi - 1);
377             return getPlanImp(a);
378         } else {
379             return getPlanImp(parameters);
380         }
381     }
382
383     public Object JavaDoc executeWithMap(Map parameters) {
384         queryDetails.updateCounts();
385         int tp = queryDetails.getTotalParamCount();
386         if (parameters.size() != tp) {
387             throw BindingSupportImpl.getInstance().runtime(
388                     "The number of entries in the map (" + parameters.size() + ") " +
389                     "differs from the number of declared parameters (" + tp + ")");
390         }
391         if (tp == 0) return executeWithArrayImp(null);
392
393         // extract the normal parameters from the map in declaration order
394
Object JavaDoc[] pa;
395         int np = queryDetails.getParamCount();
396         if (np > 0) {
397             pa = new Object JavaDoc[np];
398             String JavaDoc[] names = queryDetails.getParamNames();
399             for (int i = 0; i < np; i++) {
400                 String JavaDoc name = names[i];
401                 if (!parameters.containsKey(name)) {
402                     throw BindingSupportImpl.getInstance().runtime(
403                             "Parameter '" + name + "' not found in map");
404                 }
405                 pa[i] = parameters.get(name);
406             }
407         } else {
408             pa = null;
409         }
410
411         // process the jdoGenieOptions parameter if required
412
if (queryDetails.hasJdoGenieOptions()) {
413             Object JavaDoc o = parameters.get(VERSANT_OPTIONS);
414             if (o == null) o = parameters.get(JDO_GENIE_OPTIONS);
415             processJdoGenieOptions(o);
416         }
417
418         // exec
419
return executeWithArrayImp(pa);
420     }
421
422     private void processJdoGenieOptions(Object JavaDoc o) {
423         // restore default values first
424
setFetchGroup(null);
425         setRandomAccess(false);
426
427         // now set properties
428
if (o == null) return;
429         if (!(o instanceof String JavaDoc)) {
430             throw BindingSupportImpl.getInstance().runtime("Invalid " +
431                     VERSANT_OPTIONS + ": Expected String value: " +
432                     o.getClass());
433         }
434         String JavaDoc props = (String JavaDoc)o;
435         if (props.length() == 0) return;
436         try {
437             BeanUtils.parseProperties(props, this);
438         } catch (Exception JavaDoc e) {
439             throw BindingSupportImpl.getInstance().runtime(
440                     "Invalid " + VERSANT_OPTIONS + ": " + e.getMessage(), e);
441         }
442     }
443
444     /**
445      * The parameters array must NOT contain the jdoGenieOptions.
446      */

447     private final Object JavaDoc executeWithArrayImp(Object JavaDoc[] parameters) {
448         final PMProxy pmProxy = this.pmProxy;
449         pmProxy.getRealPM().convertPcParamsToOID(parameters);
450         if (!pmProxy.isActive() && !pmProxy.getRealPM().isNontransactionalRead()) {
451             throw BindingSupportImpl.getInstance().invalidOperation(
452                     "Must set nonTransactionalRead to true");
453         }
454
455         //Check if this should be a multi-part query
456
Class JavaDoc cls = queryDetails.getCandidateClass();
457         Class JavaDoc[] candidates = null;
458         if (cls != null) {
459             candidates = pmProxy.getRealPM().modelMetaData.getQueryCandidatesFor(cls);
460             if (candidates == null) {
461                 throw BindingSupportImpl.getInstance().unsupported("Queries for class '"
462                         + queryDetails.getCandidateClass() + "' is not supported");
463             }
464         }
465
466         if (candidates != null && candidates.length > 1) {
467             //create subQueries for all the candidates and compile it
468
queryDetails.updateCounts();
469             Set qResults = new HashSet();
470             for (int i = 0; i < candidates.length; i++) {
471                 Class JavaDoc candidate = candidates[i];
472
473                 QueryDetails qd = new QueryDetails(queryDetails);
474                 qd.setCandidateClass(candidate);
475
476                 CompiledQuery cq = this.pmProxy.getRealPM().getStorageManager().compileQuery(qd);
477                 QueryResult qr = getQueryResult(parameters, qd, cq, this.pmProxy);
478
479                 qResults.add(qr);
480             }
481             return new MultiPartQueryResult(qResults);
482         } else {
483             compile();
484
485             //is this a unique query
486
if (compiledQuery.isUnique()) {
487                 //must do immediately
488
return QueryResultBase.resolveRow(pmProxy.getAllQueryResults(
489                         compiledQuery,
490                         parameters).getUnique(),
491                         pmProxy);
492             } else {
493                 return getQueryResult(parameters, queryDetails, compiledQuery, this.pmProxy);
494             }
495         }
496     }
497
498     private static Object JavaDoc[] copyParams(Object JavaDoc[] parameters) {
499         Object JavaDoc[] params = null;
500         if (parameters != null) {
501             params = new Object JavaDoc[parameters.length];
502             for (int i = 0; i < params.length; i++) {
503                 params[i] = parameters[i];
504             }
505         }
506         return params;
507     }
508
509     private QueryResult getQueryResult(Object JavaDoc[] params,
510             QueryDetails queryDetails, CompiledQuery compiledQuery, PMProxy pmProxy) {
511         QueryResult res = null;
512         boolean collectionQuery = queryDetails.getCol() != null;
513         boolean containsNewOID = containsNewOID(params);
514
515         if (collectionQuery) {
516             // query agains a collection in memory
517
res = new MemoryQueryResult(pmProxy,
518                     queryDetails, createSMList(queryDetails.getCol(), pmProxy), params);
519         } else if (containsNewOID && queryDetails.isIgnoreCache()) {
520             // query agains a collection in memory with some new instances
521
res = new MemoryQueryResult();
522         } else if (queryDetails.isRandomAccess()) {
523             // random access query against database
524
res = new RandomAccessQueryResult(pmProxy,
525                     compiledQuery, params);
526         } else {
527             // normal query against database
528
res = new ForwardQueryResult(pmProxy, queryDetails,
529                     compiledQuery, params);
530         }
531         if (pmProxy.getMultithreaded()) {
532             res = new SynchronizedQueryResult(pmProxy, res);
533         }
534
535         addResults(res);
536         return res;
537     }
538
539     /**
540      * Add a set of results to this query.
541      */

542     private void addResults(QueryResult q) {
543         synchronized (pmProxy) {
544             if (resultList == null) {
545                 resultList = q;
546             } else {
547                 q.setNext(resultList);
548                 resultList.setPrev(q);
549                 resultList = q;
550             }
551         }
552     }
553
554     /**
555      * Remove a set of results from this query.
556      */

557     private void removeResults(QueryResult q) {
558         synchronized (pmProxy) {
559             if (resultList == q) { // at tail of list
560
resultList = q.getNext();
561                 if (resultList != null) resultList.setPrev(null);
562                 q.setNext(null);
563             } else {
564                 q.getPrev().setNext(q.getNext());
565                 if (q.getNext() != null) q.getNext().setPrev(q.getPrev());
566                 q.setNext(null);
567                 q.setPrev(null);
568             }
569         }
570     }
571
572     /**
573      * TODO must check not to try and do convertPcParams twice
574      * <p/>
575      * The parameters array must NOT contain the jdoGenieOptions.
576      */

577     private VersantQueryPlan getPlanImp(Object JavaDoc[] parameters) {
578         VersantPersistenceManagerImp realPM = pmProxy.getRealPM();
579         realPM.convertPcParamsToOID(parameters);
580         compile();
581         return realPM.getStorageManager().getQueryPlan(queryDetails,
582                 compiledQuery, parameters);
583     }
584
585     private static List createSMList(Collection col, PMProxy pmProxy) {
586         List tmpList = new ArrayList();
587         for (Iterator iterator = col.iterator(); iterator.hasNext();) {
588             PersistenceCapable persistenceCapable = (PersistenceCapable)iterator.next();
589             tmpList.add(pmProxy.getInternalSM(persistenceCapable));
590         }
591         return tmpList;
592     }
593
594     private static boolean containsNewOID(Object JavaDoc[] params) {
595         if (params != null) {
596             for (int i = 0; i < params.length; i++) {
597                 Object JavaDoc param = params[i];
598                 if (param instanceof NewObjectOID) {
599                     return true;
600                 }
601             }
602         }
603         return false;
604     }
605
606     public PersistenceManager getPersistenceManager() {
607         return pmProxy;
608     }
609
610     public void close(Object JavaDoc queryResult) {
611         QueryResult qr = (QueryResult)queryResult;
612         qr.close();
613         removeResults(qr);
614     }
615
616     public void closeAll() {
617         synchronized (pmProxy) {
618             if (resultList == null) return;
619             resultList.close();
620             for (QueryResult i = resultList.getNext(); i != null; i = i.getNext()) {
621                 i.close();
622                 i.getPrev().setNext(null);
623                 i.setPrev(null);
624             }
625             resultList = null;
626         }
627     }
628
629     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc {
630         queryDetails.writeExternal(out);
631     }
632
633     public void readExternal(ObjectInput JavaDoc in) throws IOException JavaDoc,
634             ClassNotFoundException JavaDoc {
635         QueryDetails qp = new QueryDetails();
636         qp.readExternal(in);
637         this.queryDetails.fillFrom(qp);
638     }
639
640     public void initialiseFrom(VersantQueryImp clientQuery) {
641         this.queryDetails.fillFrom(clientQuery.queryDetails);
642         this.queryDetails.clearExtentAndCol();
643     }
644
645     public void setCacheable(boolean on) {
646         changed();
647         queryDetails.setCacheable(on);
648     }
649
650     public String JavaDoc getImports() {
651         return queryDetails.getImports();
652     }
653
654     public String JavaDoc getParameters() {
655         return queryDetails.getParameters();
656     }
657
658     public String JavaDoc getVariables() {
659         return queryDetails.getVariables();
660     }
661
662     public String JavaDoc getOrdering() {
663         return queryDetails.getOrdering();
664     }
665
666     public String JavaDoc getGrouping() {
667         return queryDetails.getGrouping();
668     }
669
670     public String JavaDoc getResult() {
671         return queryDetails.getResult();
672     }
673
674     public boolean isUnique() {
675         return queryDetails.getUnique() == QueryDetails.TRUE;
676     }
677 }
678
Popular Tags