Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]
    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   }

Save This Page
Home » apache-openjpa-1.1.0-source » org.apache.openjpa » kernel » [javadoc | source]