1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.openjpa.kernel;
20
21 import java.io.Serializable;
22 import java.security.AccessController;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.ListIterator;
31 import java.util.Map;
32
33 import org.apache.commons.collections.map.LinkedMap;
34 import org.apache.commons.lang.ObjectUtils;
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.openjpa.conf.OpenJPAConfiguration;
37 import org.apache.openjpa.enhance.PersistenceCapable;
38 import org.apache.openjpa.kernel.exps.AggregateListener;
39 import org.apache.openjpa.kernel.exps.FilterListener;
40 import org.apache.openjpa.kernel.exps.Constant;
41 import org.apache.openjpa.kernel.exps.Literal;
42 import org.apache.openjpa.kernel.exps.Val;
43 import org.apache.openjpa.lib.log.Log;
44 import org.apache.openjpa.lib.rop.EagerResultList;
45 import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
46 import org.apache.openjpa.lib.rop.RangeResultObjectProvider;
47 import org.apache.openjpa.lib.rop.ResultList;
48 import org.apache.openjpa.lib.rop.ResultObjectProvider;
49 import org.apache.openjpa.lib.util.J2DoPrivHelper;
50 import org.apache.openjpa.lib.util.Localizer;
51 import org.apache.openjpa.lib.util.ReferenceHashSet;
52 import java.util.concurrent.locks.ReentrantLock;
53 import org.apache.openjpa.meta.ClassMetaData;
54 import org.apache.openjpa.meta.FieldMetaData;
55 import org.apache.openjpa.meta.JavaTypes;
56 import org.apache.openjpa.meta.MetaDataRepository;
57 import org.apache.openjpa.util.GeneralException;
58 import org.apache.openjpa.util.InvalidStateException;
59 import org.apache.openjpa.util.NonUniqueResultException;
60 import org.apache.openjpa.util.NoResultException;
61 import org.apache.openjpa.util.OpenJPAException;
62 import org.apache.openjpa.util.UnsupportedException;
63 import org.apache.openjpa.util.UserException;
64 import org.apache.openjpa.util.ImplHelper;
65 import serp.util.Numbers;
66 import serp.util.Strings;
67
68 /**
69 * Implementation of the {@link Query} interface.
70 *
71 * @author Abe White
72 * @nojavadoc
73 */
74 public class QueryImpl
75 implements Query {
76
77 private static final Localizer _loc = Localizer.forPackage(QueryImpl.class);
78
79 private final String _language;
80 private final StoreQuery _storeQuery;
81 private transient final BrokerImpl _broker;
82 private transient final Log _log;
83 private transient ClassLoader _loader = null;
84
85 // query has its own internal lock
86 private final ReentrantLock _lock;
87
88 // unparsed state
89 private Class _class = null;
90 private boolean _subclasses = true;
91 private boolean _readOnly = false;
92 private String _query = null;
93 private String _params = null;
94
95 // parsed state
96 private transient Compilation _compiled = null;
97 private transient boolean _compiling = false;
98 private transient ResultPacker _packer = null;
99
100 // candidates
101 private transient Collection _collection = null;
102 private transient Extent _extent = null;
103
104 // listeners
105 private Map _filtListeners = null;
106 private Map _aggListeners = null;
107
108 // configuration for loading objects
109 private FetchConfiguration _fc = null;
110 private boolean _ignoreChanges = false;
111 private Class _resultMappingScope = null;
112 private String _resultMappingName = null;
113
114 // these fields should only be used directly after we have a compilation,
115 // because their values may be encoded in the query string
116 private Boolean _unique = null;
117 private Class _resultClass = null;
118 private transient long _startIdx = 0;
119 private transient long _endIdx = Long.MAX_VALUE;
120 private transient boolean _rangeSet = false;
121
122 // remember the list of all the results we have returned so we
123 // can free their resources when close or closeAll is called
124 private transient final Collection _resultLists = new ReferenceHashSet
125 (ReferenceHashSet.WEAK);
126
127 /**
128 * Construct a query managed by the given broker.
129 */
130 public QueryImpl(Broker broker, String language, StoreQuery storeQuery) {
131 _broker = (BrokerImpl) broker;
132 _language = language;
133 _storeQuery = storeQuery;
134 _fc = (FetchConfiguration) broker.getFetchConfiguration().clone();
135 _log = broker.getConfiguration().getLog(OpenJPAConfiguration.LOG_QUERY);
136 _storeQuery.setContext(this);
137
138 if (_broker != null && _broker.getMultithreaded())
139 _lock = new ReentrantLock();
140 else
141 _lock = null;
142 }
143
144 /**
145 * Internal store query.
146 */
147 public StoreQuery getStoreQuery() {
148 return _storeQuery;
149 }
150
151 public Broker getBroker() {
152 return _broker;
153 }
154
155 public Query getQuery() {
156 return this;
157 }
158
159 public StoreContext getStoreContext() {
160 return _broker;
161 }
162
163 public String getLanguage() {
164 return _language;
165 }
166
167 public FetchConfiguration getFetchConfiguration() {
168 return _fc;
169 }
170
171 public String getQueryString() {
172 return _query;
173 }
174
175 public boolean getIgnoreChanges() {
176 assertOpen();
177 return _ignoreChanges;
178 }
179
180 public void setIgnoreChanges(boolean flag) {
181 lock();
182 try {
183 assertOpen();
184 // allowed modification: no read-only check
185 _ignoreChanges = flag;
186 } finally {
187 unlock();
188 }
189 }
190
191 public boolean isReadOnly() {
192 assertOpen();
193 return _readOnly;
194 }
195
196 public void setReadOnly(boolean flag) {
197 lock();
198 try {
199 assertOpen();
200 _readOnly = flag;
201 } finally {
202 unlock();
203 }
204 }
205
206 public void addFilterListener(FilterListener listener) {
207 lock();
208 try {
209 assertOpen();
210 assertNotReadOnly();
211 if (_filtListeners == null)
212 _filtListeners = new HashMap(5);
213 _filtListeners.put(listener.getTag(), listener);
214 } finally {
215 unlock();
216 }
217 }
218
219 public void removeFilterListener(FilterListener listener) {
220 lock();
221 try {
222 assertOpen();
223 assertNotReadOnly();
224 if (_filtListeners != null)
225 _filtListeners.remove(listener.getTag());
226 } finally {
227 unlock();
228 }
229 }
230
231 public Collection getFilterListeners() {
232 return (_filtListeners == null) ? Collections.EMPTY_LIST
233 : _filtListeners.values();
234 }
235
236 public FilterListener getFilterListener(String tag) {
237 // first check listeners for this query
238 if (_filtListeners != null) {
239 FilterListener listen = (FilterListener) _filtListeners.get(tag);
240 if (listen != null)
241 return listen;
242 }
243
244 // check user-defined listeners from configuration
245 FilterListener[] confListeners = _broker.getConfiguration().
246 getFilterListenerInstances();
247 for (int i = 0; i < confListeners.length; i++)
248 if (confListeners[i].getTag().equals(tag))
249 return confListeners[i];
250
251 // check store listeners
252 return _storeQuery.getFilterListener(tag);
253 }
254
255 public void addAggregateListener(AggregateListener listener) {
256 lock();
257 try {
258 assertOpen();
259 assertNotReadOnly();
260 if (_aggListeners == null)
261 _aggListeners = new HashMap(5);
262 _aggListeners.put(listener.getTag(), listener);
263 } finally {
264 unlock();
265 }
266 }
267
268 public void removeAggregateListener(AggregateListener listener) {
269 lock();
270 try {
271 assertOpen();
272 assertNotReadOnly();
273 if (_aggListeners != null)
274 _aggListeners.remove(listener.getTag());
275 } finally {
276 unlock();
277 }
278 }
279
280 public Collection getAggregateListeners() {
281 return (_aggListeners == null) ? Collections.EMPTY_LIST
282 : _aggListeners.values();
283 }
284
285 public AggregateListener getAggregateListener(String tag) {
286 // first check listeners for this query
287 if (_aggListeners != null) {
288 AggregateListener listen = (AggregateListener) _aggListeners.
289 get(tag);
290 if (listen != null)
291 return listen;
292 }
293
294 // check user-defined listeners from configuration
295 AggregateListener[] confListeners = _broker.getConfiguration().
296 getAggregateListenerInstances();
297 for (int i = 0; i < confListeners.length; i++)
298 if (confListeners[i].getTag().equals(tag))
299 return confListeners[i];
300
301 // check store listeners
302 return _storeQuery.getAggregateListener(tag);
303 }
304
305 public Extent getCandidateExtent() {
306 // if just the class is set, fetch the corresponding extent; if the
307 // extent is already set but its ignore cache setting is wrong,
308 // get a new extent with the correct setting (don't modify orig extent
309 // in case the user has a reference to it and might use it)
310 lock();
311 try {
312 Class cls = getCandidateType();
313 if (_extent == null && _collection == null && _broker != null
314 && cls != null) {
315 _extent = _broker.newExtent(cls, _subclasses);
316 _extent.setIgnoreChanges(_ignoreChanges);
317 } else if (_extent != null
318 && _extent.getIgnoreChanges() != _ignoreChanges && cls != null){
319 _extent = _broker.newExtent(cls, _extent.hasSubclasses());
320 _extent.setIgnoreChanges(_ignoreChanges);
321 }
322 return _extent;
323 } finally {
324 unlock();
325 }
326 }
327
328 public void setCandidateExtent(Extent candidateExtent) {
329 lock();
330 try {
331 assertOpen();
332 assertNotReadOnly();
333
334 if (candidateExtent == _extent)
335 return;
336 if (candidateExtent == null) {
337 _extent = null;
338 return;
339 }
340
341 // if extent then not collection
342 _extent = candidateExtent;
343 _collection = null;
344
345 boolean invalidate = false;
346 if (_extent.getElementType() != _class) {
347 _class = _extent.getElementType();
348 _loader = null;
349 invalidate = true;
350 }
351 if (_extent.hasSubclasses() != _subclasses) {
352 _subclasses = _extent.hasSubclasses();
353 invalidate = true;
354 }
355 if (invalidate)
356 invalidateCompilation();
357 } finally {
358 unlock();
359 }
360 }
361
362 public Collection getCandidateCollection() {
363 assertOpen();
364 return _collection;
365 }
366
367 public void setCandidateCollection(Collection candidateCollection) {
368 if (!_storeQuery.supportsInMemoryExecution())
369 throw new UnsupportedException(_loc.get("query-nosupport",
370 _language));
371
372 lock();
373 try {
374 assertOpen();
375
376 // if collection then not extent
377 _collection = candidateCollection;
378 if (_collection != null)
379 _extent = null;
380 } finally {
381 unlock();
382 }
383 }
384
385 public Class getCandidateType() {
386 lock();
387 try {
388 assertOpen();
389 if (_class != null || _compiled != null || _query == null
390 || _broker == null)
391 return _class;
392
393 // check again after compilation; maybe encoded in string
394 compileForCompilation();
395 return _class;
396 } finally {
397 unlock();
398 }
399 }
400
401 public void setCandidateType(Class candidateClass, boolean subs) {
402 lock();
403 try {
404 assertOpen();
405 assertNotReadOnly();
406 _class = candidateClass;
407 _subclasses = subs;
408 _loader = null;
409 invalidateCompilation();
410 } finally {
411 unlock();
412 }
413 }
414
415 public boolean hasSubclasses() {
416 return _subclasses;
417 }
418
419 public String getResultMappingName() {
420 assertOpen();
421 return _resultMappingName;
422 }
423
424 public Class getResultMappingScope() {
425 assertOpen();
426 return _resultMappingScope;
427 }
428
429 public void setResultMapping(Class scope, String name) {
430 lock();
431 try {
432 assertOpen();
433 _resultMappingScope = scope;
434 _resultMappingName = name;
435 _packer = null;
436 } finally {
437 unlock();
438 }
439 }
440
441 public boolean isUnique() {
442 lock();
443 try {
444 assertOpen();
445 if (_unique != null)
446 return _unique.booleanValue();
447 if (_query == null || _compiling || _broker == null)
448 return false;
449
450 // check again after compilation; maybe encoded in string
451 if (_compiled == null) {
452 compileForCompilation();
453 if (_unique != null)
454 return _unique.booleanValue();
455 }
456
457 // no explicit setting; default
458 StoreQuery.Executor ex = compileForExecutor();
459 if (!ex.isAggregate(_storeQuery))
460 return false;
461 return !ex.hasGrouping(_storeQuery);
462 } finally {
463 unlock();
464 }
465 }
466
467 public void setUnique(boolean unique) {
468 lock();
469 try {
470 assertOpen();
471 assertNotReadOnly();
472 _unique = (unique) ? Boolean.TRUE : Boolean.FALSE;
473 } finally {
474 unlock();
475 }
476 }
477
478 public Class getResultType() {
479 lock();
480 try {
481 assertOpen();
482 if (_resultClass != null || _compiled != null || _query == null
483 || _broker == null)
484 return _resultClass;
485
486 // check again after compilation; maybe encoded in string
487 compileForCompilation();
488 return _resultClass;
489 } finally {
490 unlock();
491 }
492 }
493
494 public void setResultType(Class cls) {
495 lock();
496 try {
497 assertOpen();
498 // allowed modification: no read-only check
499 _resultClass = cls;
500 _packer = null;
501 } finally {
502 unlock();
503 }
504 }
505
506 public long getStartRange() {
507 assertOpen();
508 return _startIdx;
509 }
510
511 public long getEndRange() {
512 assertOpen();
513 return _endIdx;
514 }
515
516 public void setRange(long start, long end) {
517 if (start < 0 || end < 0)
518 throw new UserException(_loc.get("invalid-range",
519 String.valueOf(start), String.valueOf(end)));
520
521 if (end - start > Integer.MAX_VALUE && end != Long.MAX_VALUE)
522 throw new UserException(_loc.get("range-too-big",
523 String.valueOf(start), String.valueOf(end)));
524
525 lock();
526 try {
527 assertOpen();
528 // allowed modification: no read-only check
529 _startIdx = start;
530 _endIdx = end;
531 _rangeSet = true;
532 } finally {
533 unlock();
534 }
535 }
536
537 public String getParameterDeclaration() {
538 lock();
539 try {
540 assertOpen();
541 if (_params != null || _compiled != null || _compiling
542 || _broker == null)
543 return _params;
544
545 // check again after compilation; maybe encoded in string
546 compileForCompilation();
547 return _params;
548 } finally {
549 unlock();
550 }
551 }
552
553 public void declareParameters(String params) {
554 if (!_storeQuery.supportsParameterDeclarations())
555 throw new UnsupportedException(_loc.get("query-nosupport",
556 _language));
557
558 lock();
559 try {
560 assertOpen();
561 assertNotReadOnly();
562 _params = StringUtils.trimToNull(params);
563 invalidateCompilation();
564 } finally {
565 unlock();
566 }
567 }
568
569 public void compile() {
570 lock();
571 try {
572 assertOpen();
573 StoreQuery.Executor ex = compileForExecutor();
574 getResultPacker(_storeQuery, ex);
575 ex.validate(_storeQuery);
576 } finally {
577 unlock();
578 }
579 }
580
581 public Object getCompilation() {
582 lock();
583 try {
584 return compileForCompilation().storeData;
585 } finally {
586 unlock();
587 }
588 }
589
590 /**
591 * Compile query properties.
592 */
593 private Compilation compileForCompilation() {
594 if (_compiled != null || _compiling)
595 return _compiled;
596
597 assertNotSerialized();
598 assertOpen();
599
600 boolean readOnly = _readOnly;
601 _readOnly = false;
602 _compiling = true;
603 try {
604 _compiled = compilationFromCache();
605 return _compiled;
606 } catch (OpenJPAException ke) {
607 throw ke;
608 } catch (RuntimeException re) {
609 throw new GeneralException(re);
610 } finally {
611 _compiling = false;
612 _readOnly = readOnly;
613 }
614 }
615
616 /**
617 * Find the cached compilation for the current query, creating one if it
618 * does not exist.
619 */
620 protected Compilation compilationFromCache() {
621 Map compCache =
622 _broker.getConfiguration().getQueryCompilationCacheInstance();
623 if (compCache == null) {
624 return newCompilation();
625 } else {
626 CompilationKey key = new CompilationKey();
627 key.queryType = _storeQuery.getClass();
628 key.candidateType = getCandidateType();
629 key.subclasses = hasSubclasses();
630 key.query = getQueryString();
631 key.language = getLanguage();
632 key.storeKey = _storeQuery.newCompilationKey();
633 Compilation comp = (Compilation) compCache.get(key);
634
635 // parse declarations if needed
636 boolean cache = false;
637 if (comp == null) {
638 comp = newCompilation();
639 // only cache those queries that can be compiled
640 cache = comp.storeData != null;
641 } else
642 _storeQuery.populateFromCompilation(comp.storeData);
643
644 // cache parsed state if needed
645 if (cache)
646 compCache.put(key, comp);
647 return comp;
648 }
649 }
650
651 /**
652 * Create and populate a new compilation.
653 */
654 private Compilation newCompilation() {
655 Compilation comp = new Compilation();
656 comp.storeData = _storeQuery.newCompilation();
657 _storeQuery.populateFromCompilation(comp.storeData);
658 return comp;
659 }
660
661 /**
662 * Compile for execution, choosing between datastore and in-mem
663 * compilation based on what we support and our settings.
664 */
665 private StoreQuery.Executor compileForExecutor() {
666 Compilation comp = compileForCompilation();
667 if (_collection == null) {
668 if (comp.datastore != null)
669 return comp.datastore;
670 if (comp.memory != null)
671 return comp.memory;
672 if (_storeQuery.supportsDataStoreExecution())
673 return compileForDataStore(comp);
674 return compileForInMemory(comp);
675 }
676
677 if (comp.memory != null)
678 return comp.memory;
679 if (comp.datastore != null)
680 return comp.datastore;
681 if (_storeQuery.supportsInMemoryExecution())
682 return compileForInMemory(comp);
683 return compileForDataStore(comp);
684 }
685
686 /**
687 * Create an expression tree for datastore execution.
688 */
689 private StoreQuery.Executor compileForDataStore(Compilation comp) {
690 if (comp.datastore == null)
691 comp.datastore = createExecutor(false);
692 return comp.datastore;
693 }
694
695 /**
696 * Create an expression tree for in-memory execution.
697 */
698 private StoreQuery.Executor compileForInMemory(Compilation comp) {
699 if (comp.memory == null)
700 comp.memory = createExecutor(true);
701 return comp.memory;
702 }
703
704 /**
705 * Return a query executor of the proper type.
706 */
707 private StoreQuery.Executor createExecutor(boolean inMem) {
708 assertCandidateType();
709
710 MetaDataRepository repos = _broker.getConfiguration().
711 getMetaDataRepositoryInstance();
712 ClassMetaData meta = repos.getMetaData(_class,
713 _broker.getClassLoader(), false);
714
715 ClassMetaData[] metas;
716 if (_class == null || _storeQuery.supportsAbstractExecutors())
717 metas = new ClassMetaData[]{ meta };
718 else if (_subclasses && (meta == null || meta.isManagedInterface()))
719 metas = repos.getImplementorMetaDatas(_class,
720 _broker.getClassLoader(), true);
721 else if (meta != null && (_subclasses || meta.isMapped()))
722 metas = new ClassMetaData[]{ meta };
723 else
724 metas = StoreQuery.EMPTY_METAS;
725
726 if (metas.length == 0)
727 throw new UserException(_loc.get("no-impls", _class));
728 try {
729 if (metas.length == 1) {
730 if (inMem)
731 return _storeQuery.newInMemoryExecutor(metas[0],
732 _subclasses);
733 return _storeQuery.newDataStoreExecutor(metas[0], _subclasses);
734 }
735
736 // multiple implementors
737 StoreQuery.Executor[] es = new StoreQuery.Executor[metas.length];
738 for (int i = 0; i < es.length; i++) {
739 if (inMem)
740 es[i] = _storeQuery.newInMemoryExecutor(metas[i], true);
741 else
742 es[i] = _storeQuery.newDataStoreExecutor(metas[i], true);
743 }
744 return new MergedExecutor(es);
745 } catch (OpenJPAException ke) {
746 throw ke;
747 } catch (RuntimeException re) {
748 throw new GeneralException(re);
749 }
750 }
751
752 /**
753 * Clear any compilation, forcing this query to be recompiled
754 * next time it's executed. This should be invoked whenever any
755 * state changes that would cause the underlying query
756 * representation to change.
757 *
758 * @since 0.3.0
759 */
760 private boolean invalidateCompilation() {
761 if (_compiling)
762 return false;
763 _storeQuery.invalidateCompilation();
764 _compiled = null;
765 _packer = null;
766 return true;
767 }
768
769 public Object execute() {
770 return execute((Object[]) null);
771 }
772
773 public Object execute(Object[] params) {
774 return execute(OP_SELECT, params);
775 }
776
777 public Object execute(Map params) {
778 return execute(OP_SELECT, params);
779 }
780
781 private Object execute(int operation, Object[] params) {
782 if (params == null)
783 params = StoreQuery.EMPTY_OBJECTS;
784
785 lock();
786 try {
787 assertNotSerialized();
788 _broker.beginOperation(true);
789 try {
790 assertOpen();
791 _broker.assertNontransactionalRead();
792
793 // get executor
794 Compilation comp = compileForCompilation();
795 StoreQuery.Executor ex = (isInMemory(operation))
796 ? compileForInMemory(comp) : compileForDataStore(comp);
797
798 assertParameters(_storeQuery, ex, params);
799 if (_log.isTraceEnabled())
800 logExecution(operation, ex.getParameterTypes(_storeQuery),
801 params);
802
803 if (operation == OP_SELECT)
804 return execute(_storeQuery, ex, params);
805 if (operation == OP_DELETE)
806 return delete(_storeQuery, ex, params);
807 if (operation == OP_UPDATE)
808 return update(_storeQuery, ex, params);
809 throw new UnsupportedException();
810 } catch (OpenJPAException ke) {
811 throw ke;
812 } catch (Exception e) {
813 throw new UserException(e);
814 } finally {
815 _broker.endOperation();
816 }
817 }
818 finally {
819 unlock();
820 }
821 }
822
823 private Object execute(int operation, Map params) {
824 if (params == null)
825 params = Collections.EMPTY_MAP;
826
827 lock();
828 try {
829 _broker.beginOperation(true);
830 try {
831 assertNotSerialized();
832 assertOpen();
833 _broker.assertNontransactionalRead();
834
835 // get executor
836 Compilation comp = compileForCompilation();
837 StoreQuery.Executor ex = (isInMemory(operation))
838 ? compileForInMemory(comp) : compileForDataStore(comp);
839
840 Object[] arr = (params.isEmpty()) ? StoreQuery.EMPTY_OBJECTS :
841 toParameterArray(ex.getParameterTypes(_storeQuery), params);
842 assertParameters(_storeQuery, ex, arr);
843 if (_log.isTraceEnabled())
844 logExecution(operation, params);
845
846 if (operation == OP_SELECT)
847 return execute(_storeQuery, ex, arr);
848 if (operation == OP_DELETE)
849 return delete(_storeQuery, ex, arr);
850 if (operation == OP_UPDATE)
851 return update(_storeQuery, ex, arr);
852 throw new UnsupportedException();
853 } catch (OpenJPAException ke) {
854 throw ke;
855 } catch (Exception e) {
856 throw new UserException(e);
857 } finally {
858 _broker.endOperation();
859 }
860 }
861 finally {
862 unlock();
863 }
864 }
865
866 public long deleteAll() {
867 return deleteAll((Object[]) null);
868 }
869
870 public long deleteAll(Object[] params) {
871 return ((Number) execute(OP_DELETE, params)).longValue();
872 }
873
874 public long deleteAll(Map params) {
875 return ((Number) execute(OP_DELETE, params)).longValue();
876 }
877
878 public long updateAll() {
879 return updateAll((Object[]) null);
880 }
881
882 public long updateAll(Object[] params) {
883 return ((Number) execute(OP_UPDATE, params)).longValue();
884 }
885
886 public long updateAll(Map params) {
887 return ((Number) execute(OP_UPDATE, params)).longValue();
888 }
889
890 private Object[] toParameterArray(LinkedMap paramTypes, Map params) {
891 if (params == null || params.isEmpty())
892 return StoreQuery.EMPTY_OBJECTS;
893
894 Object[] arr = new Object[params.size()];
895 Map.Entry entry;
896 Object key;
897 int idx;
898 int base = -1;
899 for (Iterator itr = params.entrySet().iterator(); itr.hasNext();) {
900 entry = (Map.Entry) itr.next();
901 key = entry.getKey();
902 idx = (paramTypes == null) ? -1 : paramTypes.indexOf(key);
903
904 // allow positional parameters and natural order parameters
905 if (idx != -1)
906 arr[idx] = entry.getValue();
907 else if (key instanceof Number) {
908 if (base == -1)
909 base = positionalParameterBase(params.keySet());
910 arr[((Number) key).intValue() - base] = entry.getValue();
911 } else
912 throw new UserException(_loc.get("bad-param-name", key));
913 }
914 return arr;
915 }
916
917 /**
918 * Return the base (generally 0 or 1) to use for positional parameters.
919 */
920 private static int positionalParameterBase(Collection params) {
921 int low = Integer.MAX_VALUE;
922 Object obj;
923 int val;
924 for (Iterator itr = params.iterator(); itr.hasNext();) {
925 obj = itr.next();
926 if (!(obj instanceof Number))
927 return 0; // use 0 base when params are mixed types
928
929 val = ((Number) obj).intValue();
930 if (val == 0)
931 return val;
932 if (val < low)
933 low = val;
934 }
935 return low;
936 }
937
938 /**
939 * Return whether we should execute this query in memory.
940 */
941 private boolean isInMemory(int operation) {
942 // if there are any dirty instances in the current trans that are
943 // involved in this query, we have to execute in memory or flush
944 boolean inMem = !_storeQuery.supportsDataStoreExecution()
945 || _collection != null;
946 if (!inMem && (!_ignoreChanges || operation != OP_SELECT)
947 && _broker.isActive() && isAccessPathDirty()) {
948 int flush = _fc.getFlushBeforeQueries();
949 if ((flush == FLUSH_TRUE
950 || (flush == FLUSH_WITH_CONNECTION && _broker.hasConnection())
951 || operation != OP_SELECT
952 || !_storeQuery.supportsInMemoryExecution())
953 && _broker.getConfiguration().supportedOptions().
954 contains(OpenJPAConfiguration.OPTION_INC_FLUSH)) {
955 _broker.flush();
956 } else {
957 if (_log.isInfoEnabled())
958 _log.info(_loc.get("force-in-mem", _class));
959 inMem = true;
960 }
961 }
962
963 if (inMem && !_storeQuery.supportsInMemoryExecution())
964 throw new InvalidStateException(_loc.get("cant-exec-inmem",
965 _language));
966 return inMem;
967 }
968
969 /**
970 * Execute the query using the given compilation, executor, and parameter
971 * values. All other execute methods delegate to this one or to
972 * {@link #execute(StoreQuery.Executor,Map)} after validation and locking.
973 */
974 private Object execute(StoreQuery q, StoreQuery.Executor ex,
975 Object[] params)
976 throws Exception {
977 // if this is an impossible result range, return null / empty list
978 StoreQuery.Range range = new StoreQuery.Range(_startIdx, _endIdx);
979 if (!_rangeSet)
980 ex.getRange(q, params, range);
981 if (range.start >= range.end)
982 return emptyResult(q, ex);
983
984 // execute; if we have a result class or we have only one result
985 // and so need to remove it from its array, wrap in a packing rop
986 range.lrs = isLRS(range.start, range.end);
987 ResultObjectProvider rop = ex.executeQuery(q, params, range);
988 try {
989 return toResult(q, ex, rop, range);
990 } catch (Exception e) {
991 if (rop != null)
992 try { rop.close(); } catch (Exception e2) {}
993 throw e;
994 }
995 }
996
997 /**
998 * Delete the query using the given executor, and parameter
999 * values. All other execute methods delegate to this one or to
1000 * {@link #delete(StoreQuery.Executor,Map)} after validation and locking.
1001 * The return value will be a Number indicating the number of
1002 * instances deleted.
1003 */
1004 private Number delete(StoreQuery q, StoreQuery.Executor ex, Object[] params)
1005 throws Exception {
1006 assertBulkModify(q, ex, params);
1007 return ex.executeDelete(q, params);
1008 }
1009
1010 public Number deleteInMemory(StoreQuery q, StoreQuery.Executor executor,
1011 Object[] params) {
1012 try {
1013 Object o = execute(q, executor, params);
1014 if (!(o instanceof Collection))
1015 o = Collections.singleton(o);
1016
1017 int size = 0;
1018 for (Iterator i = ((Collection) o).iterator(); i.hasNext(); size++)
1019 _broker.delete(i.next(), null);
1020 return Numbers.valueOf(size);
1021 } catch (OpenJPAException ke) {
1022 throw ke;
1023 } catch (Exception e) {
1024 throw new UserException(e);
1025 }
1026 }
1027
1028 /**
1029 * Update the query using the given compilation, executor, and parameter
1030 * values. All other execute methods delegate to this one or to
1031 * {@link #update(StoreQuery.Executor,Map)} after validation and locking.
1032 * The return value will be a Number indicating the number of
1033 * instances updated.
1034 */
1035 private Number update(StoreQuery q, StoreQuery.Executor ex, Object[] params)
1036 throws Exception {
1037 assertBulkModify(q, ex, params);
1038 return ex.executeUpdate(q, params);
1039 }
1040
1041 public Number updateInMemory(StoreQuery q, StoreQuery.Executor executor,
1042 Object[] params) {
1043 try {
1044 Object o = execute(q, executor, params);
1045 if (!(o instanceof Collection))
1046 o = Collections.singleton(o);
1047
1048 int size = 0;
1049 for (Iterator i = ((Collection) o).iterator(); i.hasNext(); size++)
1050 updateInMemory(i.next(), params);
1051 return Numbers.valueOf(size);
1052 } catch (OpenJPAException ke) {
1053 throw ke;
1054 } catch (Exception e) {
1055 throw new UserException(e);
1056 }
1057 }
1058
1059 /**
1060 * Set the values for the updates in memory.
1061 *
1062 * @param ob the persistent instance to change
1063 * @param params the parameters passed to the query
1064 */
1065 private void updateInMemory(Object ob, Object[] params) {
1066 for (Iterator it = getUpdates().entrySet().iterator();
1067 it.hasNext();) {
1068 Map.Entry e = (Map.Entry) it.next();
1069 FieldMetaData fmd = (FieldMetaData) e.getKey();
1070
1071 Object val;
1072 Object value = e.getValue();
1073 if (value instanceof Val) {
1074 val = ((Val) value).
1075 evaluate(ob, null, getStoreContext(), params);
1076 } else if (value instanceof Literal) {
1077 val = ((Literal) value).getValue();
1078 } else if (value instanceof Constant) {
1079 val = ((Constant) value).getValue(params);
1080 } else {
1081 throw new UserException(_loc.get("only-update-primitives"));
1082 }
1083
1084 OpenJPAStateManager sm = _broker.getStateManager(ob);
1085 int i = fmd.getIndex();
1086 PersistenceCapable into = ImplHelper.toPersistenceCapable(ob,
1087 _broker.getConfiguration());
1088
1089 // set the actual field in the instance
1090 int set = OpenJPAStateManager.SET_USER;
1091 switch (fmd.getDeclaredTypeCode()) {
1092 case JavaTypes.BOOLEAN:
1093 sm.settingBooleanField(into, i, sm.fetchBooleanField(i),
1094 val == null ? false : ((Boolean) val).booleanValue(),
1095 set);
1096 break;
1097 case JavaTypes.BYTE:
1098 sm.settingByteField(into, i, sm.fetchByteField(i),
1099 val == null ? 0 : ((Number) val).byteValue(), set);
1100 break;
1101 case JavaTypes.CHAR:
1102 sm.settingCharField(into, i, sm.fetchCharField(i),
1103 val == null ? 0 : val.toString().charAt(0), set);
1104 break;
1105 case JavaTypes.DOUBLE:
1106 sm.settingDoubleField(into, i, sm.fetchDoubleField(i),
1107 val == null ? 0 : ((Number) val).doubleValue(), set);
1108 break;
1109 case JavaTypes.FLOAT:
1110 sm.settingFloatField(into, i, sm.fetchFloatField(i),
1111 val == null ? 0 : ((Number) val).floatValue(), set);
1112 break;
1113 case JavaTypes.INT:
1114 sm.settingIntField(into, i, sm.fetchIntField(i),
1115 val == null ? 0 : ((Number) val).intValue(), set);
1116 break;
1117 case JavaTypes.LONG:
1118 sm.settingLongField(into, i, sm.fetchLongField(i),
1119 val == null ? 0 : ((Number) val).longValue(), set);
1120 break;
1121 case JavaTypes.SHORT:
1122 sm.settingShortField(into, i, sm.fetchShortField(i),
1123 val == null ? 0 : ((Number) val).shortValue(), set);
1124 break;
1125 case JavaTypes.STRING:
1126 sm.settingStringField(into, i, sm.fetchStringField(i),
1127 val == null ? null : val.toString(), set);
1128 break;
1129 case JavaTypes.DATE:
1130 case JavaTypes.NUMBER:
1131 case JavaTypes.BOOLEAN_OBJ:
1132 case JavaTypes.BYTE_OBJ:
1133 case JavaTypes.CHAR_OBJ:
1134 case JavaTypes.DOUBLE_OBJ:
1135 case JavaTypes.FLOAT_OBJ:
1136 case JavaTypes.INT_OBJ:
1137 case JavaTypes.LONG_OBJ:
1138 case JavaTypes.SHORT_OBJ:
1139 case JavaTypes.BIGDECIMAL:
1140 case JavaTypes.BIGINTEGER:
1141 case JavaTypes.LOCALE:
1142 case JavaTypes.OBJECT:
1143 case JavaTypes.OID:
1144 sm.settingObjectField(into, i, sm.fetchObjectField(i), val,
1145 set);
1146 break;
1147 default:
1148 throw new UserException(_loc.get("only-update-primitives"));
1149 }
1150 }
1151 }
1152
1153 /**
1154 * Trace log that the query is executing.
1155 */
1156 private void logExecution(int op, LinkedMap types, Object[] params) {
1157 Map pmap = Collections.EMPTY_MAP;
1158 if (params.length > 0) {
1159 pmap = new HashMap((int) (params.length * 1.33 + 1));
1160 if (types != null && types.size() == params.length) {
1161 int i = 0;
1162 for (Iterator itr = types.keySet().iterator(); itr.hasNext();)
1163 pmap.put(itr.next(), params[i++]);
1164 } else {
1165 for (int i = 0; i < params.length; i++)
1166 pmap.put(String.valueOf(i), params[i]);
1167 }
1168 }
1169 logExecution(op, pmap);
1170 }
1171
1172 /**
1173 * Trace log that the query is executing.
1174 */
1175 private void logExecution(int op, Map params) {
1176 String s = _query;
1177 if (StringUtils.isEmpty(s))
1178 s = toString();
1179
1180 String msg = "executing-query";
1181 if (!params.isEmpty())
1182 msg += "-with-params";
1183
1184 _log.trace(_loc.get(msg, s, params));
1185 }
1186
1187 /**
1188 * Return whether this should be treated as a potential large result set.
1189 */
1190 private boolean isLRS(long start, long end) {
1191 long range = end - start;
1192 return _fc.getFetchBatchSize() >= 0
1193 && !(range <= _fc.getFetchBatchSize()
1194 || (_fc.getFetchBatchSize() == 0 && range <= 50));
1195 }
1196
1197 /**
1198 * Return the query result for the given result object provider.
1199 */
1200 protected Object toResult(StoreQuery q, StoreQuery.Executor ex,
1201 ResultObjectProvider rop, StoreQuery.Range range)
1202 throws Exception {
1203 // pack projections if necessary
1204 String[] aliases = ex.getProjectionAliases(q);
1205 if (!ex.isPacking(q)) {
1206 ResultPacker packer = getResultPacker(q, ex);
1207 if (packer != null || aliases.length == 1)
1208 rop = new PackingResultObjectProvider(rop, packer,
1209 aliases.length);
1210 }
1211
1212 // if single result, extract it
1213 if (_unique == Boolean.TRUE || (aliases.length > 0
1214 && !ex.hasGrouping(q) && ex.isAggregate(q)))
1215 return singleResult(rop, range);
1216
1217 // now that we've executed the query, we can call isAggregate and
1218 // hasGrouping efficiently
1219 boolean detach = (_broker.getAutoDetach() &
1220 AutoDetach.DETACH_NONTXREAD) > 0 && !_broker.isActive();
1221 boolean lrs = range.lrs && !ex.isAggregate(q) && !ex.hasGrouping(q);
1222 ResultList res = (!detach && lrs) ? _fc.newResultList(rop)
1223 : new EagerResultList(rop);
1224
1225 _resultLists.add(decorateResultList(res));
1226 return res;
1227 }
1228
1229 /**
1230 * Optionally decorate the native result.
1231 */
1232 protected ResultList decorateResultList(ResultList res) {
1233 return new RemoveOnCloseResultList(res);
1234 }
1235
1236 /**
1237 * Return a result packer for this projection, or null.
1238 */
1239 private ResultPacker getResultPacker(StoreQuery q, StoreQuery.Executor ex) {
1240 if (_packer != null)
1241 return _packer;
1242
1243 Class resultClass = (_resultClass != null) ? _resultClass
1244 : ex.getResultClass(q);
1245 if (resultClass == null)
1246 return null;
1247
1248 String[] aliases = ex.getProjectionAliases(q);
1249 if (aliases.length == 0) {
1250 // result class but no result; means candidate is being set
1251 // into some result class
1252 _packer = new ResultPacker(_class, getAlias(), resultClass);
1253 } else if (resultClass != null) {
1254 // projection
1255 Class[] types = ex.getProjectionTypes(q);
1256 _packer = new ResultPacker(types, aliases, resultClass);
1257 }
1258 return _packer;
1259 }
1260
1261 /**
1262 * Create an empty result for this query.
1263 */
1264 private Object emptyResult(StoreQuery q, StoreQuery.Executor ex) {
1265 if (_unique == Boolean.TRUE || (_unique == null
1266 && !ex.hasGrouping(q) && ex.isAggregate(q)))
1267 return null;
1268 return Collections.EMPTY_LIST;
1269 }
1270
1271 /**
1272 * Extract an expected single result from the given provider. Used when
1273 * the result is an ungrouped aggregate or the unique flag is set to true.
1274 */
1275 private Object singleResult(ResultObjectProvider rop,
1276 StoreQuery.Range range)
1277 throws Exception {
1278 rop.open();
1279 try {
1280 // move to expected result
1281 boolean next = rop.next();
1282
1283 // extract single result; throw an exception if multiple results
1284 // match and not constrainted by range, or if a unique query with
1285 // no results
1286 Object single = null;
1287 if (next) {
1288 single = rop.getResultObject();
1289 if (range.end != range.start + 1 && rop.next())
1290 throw new NonUniqueResultException(_loc.get("not-unique",
1291 _class, _query));
1292 } else if (_unique == Boolean.TRUE)
1293 throw new NoResultException(_loc.get("no-result",
1294 _class, _query));
1295
1296 // if unique set to false, use collection
1297 if (_unique == Boolean.FALSE) {
1298 if (!next)
1299 return Collections.EMPTY_LIST;
1300 // Collections.singletonList is JDK 1.3, so...
1301 return Arrays.asList(new Object[]{ single });
1302 }
1303
1304 // return single result
1305 return single;
1306 } finally {
1307 rop.close();
1308 }
1309 }
1310
1311 /**
1312 * Calculates whether the access path of this query intersects with
1313 * any dirty objects in the transaction.
1314 */
1315 private boolean isAccessPathDirty() {
1316 return isAccessPathDirty(_broker, getAccessPathMetaDatas());
1317 }
1318
1319 public static boolean isAccessPathDirty(Broker broker,
1320 ClassMetaData[] accessMetas) {
1321 Collection persisted = broker.getPersistedTypes();
1322 Collection updated = broker.getUpdatedTypes();
1323 Collection deleted = broker.getDeletedTypes();
1324 if (persisted.isEmpty() && updated.isEmpty() && deleted.isEmpty())
1325 return false;
1326
1327 // if no access metas, assume every dirty object affects path just
1328 // to be safe
1329 if (accessMetas.length == 0)
1330 return true;
1331
1332 // compare dirty classes to the access path classes
1333 Class accClass;
1334 for (int i = 0; i < accessMetas.length; i++) {
1335 // shortcut if actual class is dirty
1336 accClass = accessMetas[i].getDescribedType();
1337 if (persisted.contains(accClass) || updated.contains(accClass)
1338 || deleted.contains(accClass))
1339 return true;
1340
1341 // check for dirty subclass
1342 for (Iterator dirty = persisted.iterator(); dirty.hasNext();)
1343 if (accClass.isAssignableFrom((Class) dirty.next()))
1344 return true;
1345 for (Iterator dirty = updated.iterator(); dirty.hasNext();)
1346 if (accClass.isAssignableFrom((Class) dirty.next()))
1347 return true;
1348 for (Iterator dirty = deleted.iterator(); dirty.hasNext();)
1349 if (accClass.isAssignableFrom((Class) dirty.next()))
1350 return true;
1351 }
1352
1353 // no intersection
1354 return false;
1355 }
1356
1357 public void closeAll() {
1358 closeResults(true);
1359 }
1360
1361 public void closeResources() {
1362 closeResults(false);
1363 }
1364
1365 /**
1366 * Close open results.
1367 */
1368 private void closeResults(boolean force) {
1369 lock();
1370 try {
1371 assertOpen();
1372
1373 RemoveOnCloseResultList res;
1374 for (Iterator itr = _resultLists.iterator(); itr.hasNext();) {
1375 res = (RemoveOnCloseResultList) itr.next();
1376 if (force || res.isProviderOpen())
1377 res.close(false);
1378 }
1379 _resultLists.clear();
1380 } finally {
1381 unlock();
1382 }
1383 }
1384
1385 public String[] getDataStoreActions(Map params) {
1386 if (params == null)
1387 params = Collections.EMPTY_MAP;
1388
1389 lock();
1390 try {
1391 assertNotSerialized();
1392 assertOpen();
1393
1394 StoreQuery.Executor ex = compileForExecutor();
1395 Object[] arr = toParameterArray(ex.getParameterTypes(_storeQuery),
1396 params);
1397 assertParameters(_storeQuery, ex, arr);
1398 StoreQuery.Range range = new StoreQuery.Range(_startIdx, _endIdx);
1399 if (!_rangeSet)
1400 ex.getRange(_storeQuery, arr, range);
1401 return ex.getDataStoreActions(_storeQuery, arr, range);
1402 } catch (OpenJPAException ke) {
1403 throw ke;
1404 } catch (Exception e) {
1405 throw new UserException(e);
1406 } finally {
1407 unlock();
1408 }
1409 }
1410
1411 public boolean setQuery(Object query) {
1412 lock();
1413 try {
1414 assertOpen();
1415 assertNotReadOnly();
1416
1417 if (query == null || query instanceof String) {
1418 invalidateCompilation();
1419 _query = (String) query;
1420 if (_query != null)
1421 _query = _query.trim();
1422 return true;
1423 }
1424 if (!(query instanceof QueryImpl))
1425 return _storeQuery.setQuery(query);
1426
1427 // copy all non-transient state from the given query
1428 invalidateCompilation();
1429 QueryImpl q = (QueryImpl) query;
1430 _class = q._class;
1431 _subclasses = q._subclasses;
1432 _query = q._query;
1433 _ignoreChanges = q._ignoreChanges;
1434 _unique = q._unique;
1435 _resultClass = q._resultClass;
1436 _params = q._params;
1437 _resultMappingScope = q._resultMappingScope;
1438 _resultMappingName = q._resultMappingName;
1439 _readOnly = q._readOnly;
1440
1441 // don't share mutable objects
1442 _fc.copy(q._fc);
1443 if (q._filtListeners != null)
1444 _filtListeners = new HashMap(q._filtListeners);
1445 if (q._aggListeners != null)
1446 _aggListeners = new HashMap(q._aggListeners);
1447 return true;
1448 } finally {
1449 unlock();
1450 }
1451 }
1452
1453 public String getAlias() {
1454 lock();
1455 try {
1456 String alias = compileForExecutor().getAlias(_storeQuery);
1457 if (alias == null)
1458 alias = Strings.getClassName(_class);
1459 return alias;
1460 } finally {
1461 unlock();
1462 }
1463 }
1464
1465 public String[] getProjectionAliases() {
1466 lock();
1467 try {
1468 return compileForExecutor().getProjectionAliases(_storeQuery);
1469 } finally {
1470 unlock();
1471 }
1472 }
1473
1474 public Class[] getProjectionTypes() {
1475 lock();
1476 try {
1477 return compileForExecutor().getProjectionTypes(_storeQuery);
1478 } finally {
1479 unlock();
1480 }
1481 }
1482
1483 public int getOperation() {
1484 lock();
1485 try {
1486 return compileForExecutor().getOperation(_storeQuery);
1487 } finally {
1488 unlock();
1489 }
1490 }
1491
1492 public boolean isAggregate() {
1493 lock();
1494 try {
1495 return compileForExecutor().isAggregate(_storeQuery);
1496 } finally {
1497 unlock();
1498 }
1499 }
1500
1501 public boolean hasGrouping() {
1502 lock();
1503 try {
1504 return compileForExecutor().hasGrouping(_storeQuery);
1505 } finally {
1506 unlock();
1507 }
1508 }
1509
1510 public ClassMetaData[] getAccessPathMetaDatas() {
1511 lock();
1512 try {
1513 ClassMetaData[] metas = compileForExecutor().
1514 getAccessPathMetaDatas(_storeQuery);
1515 return (metas == null) ? StoreQuery.EMPTY_METAS : metas;
1516 } finally {
1517 unlock();
1518 }
1519 }
1520
1521 public LinkedMap getParameterTypes() {
1522 lock();
1523 try {
1524 return compileForExecutor().getParameterTypes(_storeQuery);
1525 } finally {
1526 unlock();
1527 }
1528 }
1529
1530 public Map getUpdates() {
1531 lock();
1532 try {
1533 return compileForExecutor().getUpdates(_storeQuery);
1534 } finally {
1535 unlock();
1536 }
1537 }
1538
1539 public void lock() {
1540 if (_lock != null)
1541 _lock.lock();
1542 }
1543
1544 public void unlock() {
1545 if (_lock != null && _lock.isLocked())
1546 _lock.unlock();
1547 }
1548
1549 /////////
1550 // Utils
1551 /////////
1552
1553 public Class classForName(String name, String[] imports) {
1554 // full class name or primitive type?
1555 Class type = toClass(name);
1556 if (type != null)
1557 return type;
1558
1559 // first check the aliases map in the MetaDataRepository
1560 ClassLoader loader = (_class == null) ? _loader
1561 : (ClassLoader) AccessController.doPrivileged(
1562 J2DoPrivHelper.getClassLoaderAction(_class));
1563 ClassMetaData meta = _broker.getConfiguration().
1564 getMetaDataRepositoryInstance().getMetaData(name, loader, false);
1565 if (meta != null)
1566 return meta.getDescribedType();
1567
1568 // try the name in the package of the candidate class
1569 if (_class != null) {
1570 String fullName = _class.getName().substring
1571 (0, _class.getName().lastIndexOf('.') + 1) + name;
1572 type = toClass(fullName);
1573 if (type != null)
1574 return type;
1575 }
1576
1577 // try java.lang
1578 type = toClass("java.lang." + name);
1579 if (type != null)
1580 return type;
1581
1582 // try each import
1583 if (imports != null && imports.length > 0) {
1584 String dotName = "." + name;
1585 String importName;
1586 for (int i = 0; i < imports.length; i++) {
1587 importName = imports[i];
1588
1589 // full class name import
1590 if (importName.endsWith(dotName))
1591 type = toClass(importName);
1592 // wildcard; strip to package
1593 else if (importName.endsWith(".*")) {
1594 importName = importName.substring
1595 (0, importName.length() - 1);
1596 type = toClass(importName + name);
1597 }
1598 if (type != null)
1599 return type;
1600 }
1601 }
1602 return null;
1603 }
1604
1605 /**
1606 * Return the {@link Class} for the given name, or null if name not valid.
1607 */
1608 private Class toClass(String name) {
1609 if (_loader == null)
1610 _loader = _broker.getConfiguration().getClassResolverInstance().
1611 getClassLoader(_class, _broker.getClassLoader());
1612 try {
1613 return Strings.toClass(name, _loader);
1614 } catch (RuntimeException re) {
1615 } catch (NoClassDefFoundError ncdfe) {
1616 }
1617 return null;
1618 }
1619
1620 public void assertOpen() {
1621 if (_broker != null)
1622 _broker.assertOpen();
1623 }
1624
1625 public void assertNotReadOnly() {
1626 if (_readOnly)
1627 throw new InvalidStateException(_loc.get("read-only"));
1628 }
1629
1630 public void assertNotSerialized() {
1631 if (_broker == null)
1632 throw new InvalidStateException(_loc.get("serialized"));
1633 }
1634
1635 /**
1636 * Check that a candidate class has been set for the query.
1637 */
1638 private void assertCandidateType() {
1639 if (_class == null && _storeQuery.requiresCandidateType())
1640 throw new InvalidStateException(_loc.get("no-class"));
1641 }
1642
1643 /**
1644 * Check that we are in a state to be able to perform a bulk operation;
1645 * also flush the current modfications if any elements are currently dirty.
1646 */
1647 private void assertBulkModify(StoreQuery q, StoreQuery.Executor ex,
1648 Object[] params) {
1649 _broker.assertActiveTransaction();
1650 if (_startIdx != 0 || _endIdx != Long.MAX_VALUE)
1651 throw new UserException(_loc.get("no-modify-range"));
1652 if (_resultClass != null)
1653 throw new UserException(_loc.get("no-modify-resultclass"));
1654 StoreQuery.Range range = new StoreQuery.Range();
1655 ex.getRange(q, params, range);
1656 if (range.start != 0 || range.end != Long.MAX_VALUE)
1657 throw new UserException(_loc.get("no-modify-range"));
1658 }
1659
1660 /**
1661 * Checks that the passed parameters match the declarations.
1662 */
1663 protected void assertParameters(StoreQuery q, StoreQuery.Executor ex,
1664 Object[] params) {
1665 if (!q.requiresParameterDeclarations())
1666 return;
1667
1668 LinkedMap paramTypes = ex.getParameterTypes(q);
1669 int typeCount = paramTypes.size();
1670 if (typeCount > params.length)
1671 throw new UserException(_loc.get("unbound-params",
1672 paramTypes.keySet()));
1673
1674 Iterator itr = paramTypes.entrySet().iterator();
1675 Map.Entry entry;
1676 for (int i = 0; itr.hasNext(); i++) {
1677 entry = (Map.Entry) itr.next();
1678 if (((Class) entry.getValue()).isPrimitive() && params[i] == null)
1679 throw new UserException(_loc.get("null-primitive-param",
1680 entry.getKey()));
1681 }
1682 }
1683
1684 public String toString() {
1685 StringBuffer buf = new StringBuffer(64);
1686 buf.append("Query: ").append(super.toString());
1687 buf.append("; candidate class: ").append(_class);
1688 buf.append("; query: ").append(_query);
1689 return buf.toString();
1690 }
1691
1692 /**
1693 * Struct of compiled query properties.
1694 */
1695 protected static class Compilation
1696 implements Serializable {
1697
1698 public StoreQuery.Executor memory = null;
1699 public StoreQuery.Executor datastore = null;
1700 public Object storeData = null;
1701 }
1702
1703 /**
1704 * Struct to hold the unparsed properties associated with a query.
1705 */
1706 private static class CompilationKey
1707 implements Serializable {
1708
1709 public Class queryType = null;
1710 public Class candidateType = null;
1711 public boolean subclasses = true;
1712 public String query = null;
1713 public String language = null;
1714 public Object storeKey = null;
1715
1716 public int hashCode() {
1717 int rs = 17;
1718 rs = 37 * rs + ((queryType == null) ? 0 : queryType.hashCode());
1719 rs = 37 * rs + ((query == null) ? 0 : query.hashCode());
1720 rs = 37 * rs + ((language == null) ? 0 : language.hashCode());
1721 rs = 37 * rs + ((storeKey == null) ? 0 : storeKey.hashCode());
1722 if (subclasses)
1723 rs++;
1724 return rs;
1725 }
1726
1727 public boolean equals(Object other) {
1728 if (other == this)
1729 return true;
1730 if (other == null || other.getClass() != getClass())
1731 return false;
1732
1733 CompilationKey key = (CompilationKey) other;
1734 if (key.queryType != queryType
1735 || !StringUtils.equals(key.query, query)
1736 || !StringUtils.equals(key.language, language))
1737 return false;
1738 if (key.subclasses != subclasses)
1739 return false;
1740 if (!ObjectUtils.equals(key.storeKey, storeKey))
1741 return false;
1742
1743 // allow either candidate type to be null because it might be
1744 // encoded in the query string, but if both are set then they
1745 // must be equal
1746 return key.candidateType == null || candidateType == null
1747 || key.candidateType == candidateType;
1748 }
1749 }
1750
1751 /**
1752 * A merged executor executes multiple Queries and returns
1753 * a merged result list with the appropriate ordering (if more than
1754 * one query needs to be executed). This executor has the following
1755 * limitations:
1756 * <ul>
1757 * <li>It cannot combine aggregates.
1758 * <li>It cannot collate the result lists if ordering is specified and
1759 * a result string is given, but does not include the ordering
1760 * criteria.</li>
1761 * <li>It cannot filter duplicate results from different result lists if
1762 * the result is marked distinct. This would require tracking all
1763 * previous results, which would interfere with large result set
1764 * handling.</li>
1765 * </ul>
1766 *
1767 * @author Marc Prud'hommeaux
1768 * @nojavadoc
1769 */
1770 private static class MergedExecutor
1771 implements StoreQuery.Executor {
1772
1773 private final StoreQuery.Executor[] _executors;
1774
1775 public MergedExecutor(StoreQuery.Executor[] executors) {
1776 _executors = executors;
1777 }
1778
1779 public ResultObjectProvider executeQuery(StoreQuery q,
1780 Object[] params, StoreQuery.Range range) {
1781 if (_executors.length == 1)
1782 return _executors[0].executeQuery(q, params, range);
1783
1784 // use lrs settings if we couldn't take advantage of the start index
1785 // so that hopefully the skip to the start will be efficient
1786 StoreQuery.Range ropRange = new StoreQuery.Range(0, range.end);
1787 ropRange.lrs = range.lrs || (range.start > 0 && q.getContext().
1788 getFetchConfiguration().getFetchBatchSize() >= 0);
1789
1790 // execute the query; we cannot use the lower bound of the result
1791 // range, but we can take advantage of the upper bound
1792 ResultObjectProvider[] rops =
1793 new ResultObjectProvider[_executors.length];
1794 for (int i = 0; i < _executors.length; i++)
1795 rops[i] = _executors[i].executeQuery(q, params, ropRange);
1796
1797 boolean[] asc = _executors[0].getAscending(q);
1798 ResultObjectProvider rop;
1799 if (asc.length == 0)
1800 rop = new MergedResultObjectProvider(rops);
1801 else
1802 rop = new OrderingMergedResultObjectProvider(rops, asc,
1803 _executors, q, params);
1804
1805 // if there is a lower bound, wrap in range rop
1806 if (range.start != 0)
1807 rop = new RangeResultObjectProvider(rop, range.start,
1808 range.end);
1809 return rop;
1810 }
1811
1812 public Number executeDelete(StoreQuery q, Object[] params) {
1813 long num = 0;
1814 for (int i = 0; i < _executors.length; i++)
1815 num += _executors[i].executeDelete(q, params).longValue();
1816 return Numbers.valueOf(num);
1817 }
1818
1819 public Number executeUpdate(StoreQuery q, Object[] params) {
1820 long num = 0;
1821 for (int i = 0; i < _executors.length; i++)
1822 num += _executors[i].executeUpdate(q, params).longValue();
1823 return Numbers.valueOf(num);
1824 }
1825
1826 public String[] getDataStoreActions(StoreQuery q, Object[] params,
1827 StoreQuery.Range range) {
1828 if (_executors.length == 1)
1829 return _executors[0].getDataStoreActions(q, params, range);
1830
1831 List results = new ArrayList(_executors.length);
1832 StoreQuery.Range ropRange = new StoreQuery.Range(0L, range.end);
1833 String[] actions;
1834 for (int i = 0; i < _executors.length; i++) {
1835 actions = _executors[i].getDataStoreActions(q, params,ropRange);
1836 if (actions != null && actions.length > 0)
1837 results.addAll(Arrays.asList(actions));
1838 }
1839 return (String[]) results.toArray(new String[results.size()]);
1840 }
1841
1842 public void validate(StoreQuery q) {
1843 _executors[0].validate(q);
1844 }
1845
1846 public void getRange(StoreQuery q, Object[] params,
1847 StoreQuery.Range range) {
1848 _executors[0].getRange(q, params, range);
1849 }
1850
1851 public Object getOrderingValue(StoreQuery q, Object[] params,
1852 Object resultObject, int idx) {
1853 // unfortunately, at this point (must be a merged rop containing
1854 // other merged rops) we have no idea which executor to extract
1855 // the value from
1856 return _executors[0].getOrderingValue(q, params, resultObject, idx);
1857 }
1858
1859 public boolean[] getAscending(StoreQuery q) {
1860 return _executors[0].getAscending(q);
1861 }
1862
1863 public String getAlias(StoreQuery q) {
1864 return _executors[0].getAlias(q);
1865 }
1866
1867 public String[] getProjectionAliases(StoreQuery q) {
1868 return _executors[0].getProjectionAliases(q);
1869 }
1870
1871 public Class getResultClass(StoreQuery q) {
1872 return _executors[0].getResultClass(q);
1873 }
1874
1875 public Class[] getProjectionTypes(StoreQuery q) {
1876 return _executors[0].getProjectionTypes(q);
1877 }
1878
1879 public boolean isPacking(StoreQuery q) {
1880 return _executors[0].isPacking(q);
1881 }
1882
1883 public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
1884 if (_executors.length == 1)
1885 return _executors[0].getAccessPathMetaDatas(q);
1886
1887 // create set of base class metadatas in access path
1888 List metas = null;
1889 for (int i = 0; i < _executors.length; i++)
1890 metas = Filters.addAccessPathMetaDatas(metas, _executors[i].
1891 getAccessPathMetaDatas(q));
1892 if (metas == null)
1893 return StoreQuery.EMPTY_METAS;
1894 return (ClassMetaData[]) metas.toArray
1895 (new ClassMetaData[metas.size()]);
1896 }
1897
1898 public boolean isAggregate(StoreQuery q) {
1899 if (!_executors[0].isAggregate(q))
1900 return false;
1901
1902 // we can't merge aggregates
1903 throw new UnsupportedException(_loc.get("merged-aggregate",
1904 q.getContext().getCandidateType(),
1905 q.getContext().getQueryString()));
1906 }
1907
1908 public int getOperation(StoreQuery q) {
1909 return _executors[0].getOperation(q);
1910 }
1911
1912 public boolean hasGrouping(StoreQuery q) {
1913 return _executors[0].hasGrouping(q);
1914 }
1915
1916 public LinkedMap getParameterTypes(StoreQuery q) {
1917 return _executors[0].getParameterTypes(q);
1918 }
1919
1920 public Map getUpdates(StoreQuery q) {
1921 return _executors[0].getUpdates(q);
1922 }
1923 }
1924
1925 /**
1926 * Result object provider that packs results before returning them.
1927 */
1928 private static class PackingResultObjectProvider
1929 implements ResultObjectProvider {
1930
1931 private final ResultObjectProvider _delegate;
1932 private final ResultPacker _packer;
1933 private final int _len;
1934
1935 public PackingResultObjectProvider(ResultObjectProvider delegate,
1936 ResultPacker packer, int resultLength) {
1937 _delegate = delegate;
1938 _packer = packer;
1939 _len = resultLength;
1940 }
1941
1942 public boolean supportsRandomAccess() {
1943 return _delegate.supportsRandomAccess();
1944 }
1945
1946 public void open()
1947 throws Exception {
1948 _delegate.open();
1949 }
1950
1951 public Object getResultObject()
1952 throws Exception {
1953 Object ob = _delegate.getResultObject();
1954 if (_packer == null && _len == 1)
1955 return ((Object[]) ob)[0];
1956 if (_packer == null)
1957 return ob;
1958 if (_len == 0)
1959 return _packer.pack(ob);
1960 return _packer.pack((Object[]) ob);
1961 }
1962
1963 public boolean next()
1964 throws Exception {
1965 return _delegate.next();
1966 }
1967
1968 public boolean absolute(int pos)
1969 throws Exception {
1970 return _delegate.absolute(pos);
1971 }
1972
1973 public int size()
1974 throws Exception {
1975 return _delegate.size();
1976 }
1977
1978 public void reset()
1979 throws Exception {
1980 _delegate.reset();
1981 }
1982
1983 public void close()
1984 throws Exception {
1985 _delegate.close();
1986 }
1987
1988 public void handleCheckedException(Exception e) {
1989 _delegate.handleCheckedException(e);
1990 }
1991 }
1992
1993 /**
1994 * Result list that removes itself from the query's open result list
1995 * when it is closed. Public for testing.
1996 */
1997 public class RemoveOnCloseResultList
1998 implements ResultList {
1999
2000 private final ResultList _res;
2001
2002 public RemoveOnCloseResultList(ResultList res) {
2003 _res = res;
2004 }
2005
2006 public ResultList getDelegate() {
2007 return _res;
2008 }
2009
2010 public boolean isProviderOpen() {
2011 return _res.isProviderOpen();
2012 }
2013
2014 public boolean isClosed() {
2015 return _res.isClosed();
2016 }
2017
2018 public void close() {
2019 close(true);
2020 }
2021
2022 public void close(boolean remove) {
2023 if (isClosed())
2024 return;
2025
2026 _res.close();
2027 if (!remove)
2028 return;
2029
2030 lock();
2031 try {
2032 // don't use standard _resultLists.remove method b/c relies on
2033 // collection equality, which relies on element equality, which
2034 // means we end up traversing entire result lists!
2035 for (Iterator itr = _resultLists.iterator(); itr.hasNext();) {
2036 if (itr.next() == this) {
2037 itr.remove();
2038 break;
2039 }
2040 }
2041 } finally {
2042 unlock();
2043 }
2044 }
2045
2046 public int size() {
2047 return _res.size();
2048 }
2049
2050 public boolean isEmpty() {
2051 return _res.isEmpty();
2052 }
2053
2054 public boolean contains(Object o) {
2055 return _res.contains(o);
2056 }
2057
2058 public Iterator iterator() {
2059 return _res.iterator();
2060 }
2061
2062 public Object[] toArray() {
2063 return _res.toArray();
2064 }
2065
2066 public Object[] toArray(Object[] a) {
2067 return _res.toArray(a);
2068 }
2069
2070 public boolean add(Object o) {
2071 return _res.add(o);
2072 }
2073
2074 public boolean remove(Object o) {
2075 return _res.remove(o);
2076 }
2077
2078 public boolean containsAll(Collection c) {
2079 return _res.containsAll(c);
2080 }
2081
2082 public boolean addAll(Collection c) {
2083 return _res.addAll(c);
2084 }
2085
2086 public boolean addAll(int idx, Collection c) {
2087 return _res.addAll(idx, c);
2088 }
2089
2090 public boolean removeAll(Collection c) {
2091 return _res.removeAll(c);
2092 }
2093
2094 public boolean retainAll(Collection c) {
2095 return _res.retainAll(c);
2096 }
2097
2098 public void clear() {
2099 _res.clear();
2100 }
2101
2102 public Object get(int idx) {
2103 return _res.get(idx);
2104 }
2105
2106 public Object set(int idx, Object o) {
2107 return _res.set(idx, o);
2108 }
2109
2110 public void add(int idx, Object o) {
2111 _res.add(idx, o);
2112 }
2113
2114 public Object remove(int idx) {
2115 return _res.remove(idx);
2116 }
2117
2118 public int indexOf(Object o) {
2119 return _res.indexOf(o);
2120 }
2121
2122 public int lastIndexOf(Object o) {
2123 return _res.lastIndexOf(o);
2124 }
2125
2126 public ListIterator listIterator() {
2127 return _res.listIterator();
2128 }
2129
2130 public ListIterator listIterator(int idx) {
2131 return _res.listIterator(idx);
2132 }
2133
2134 public List subList(int start, int end) {
2135 return _res.subList(start, end);
2136 }
2137
2138 public boolean equals(Object o) {
2139 return _res.equals(o);
2140 }
2141
2142 public int hashCode() {
2143 return _res.hashCode();
2144 }
2145
2146 public String toString ()
2147 {
2148 return _res.toString ();
2149 }
2150
2151 public Object writeReplace ()
2152 {
2153 return _res;
2154 }
2155 }
2156 }