openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Kevin Sutter" <kwsut...@gmail.com>
Subject Re: svn commit: r660825 [1/2] - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/ openjpa-kernel/src/main/java/org/apache/openjpa/datacache/ openjpa-kernel/sr
Date Wed, 28 May 2008 17:10:02 GMT
Why did several of the files (QueryCacheStoreQuery.java,
AbstractStoreQuery.java) in this commit get completely replaced?  We had
this problem months (years?) ago, but have not seen it recently.  These type
of commits make it very difficult to determine exactly what was changed.
All commits should be carefully reviewed before actually hitting the
"commit" button.  Can you please check into how or why this happened?
Thanks.

Kevin

On Wed, May 28, 2008 at 1:08 AM, <fancy@apache.org> wrote:

> Author: fancy
> Date: Tue May 27 23:08:41 2008
> New Revision: 660825
>
> URL: http://svn.apache.org/viewvc?rev=660825&view=rev
> Log:
> OPENJPA-612 Add support for calculating update value in
> QueryImpl.updateInMemory
> Help comitting patch provided by Fay Wang
>
> Modified:
>
>  openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
>
>  openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
>
>  openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
>
>  openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
>
>  openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
>
>  openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/StoreQuery.java
>
> Modified:
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
> URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
>
> ==============================================================================
> ---
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
> (original)
> +++
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/JDBCStoreQuery.java
> Tue May 27 23:08:41 2008
> @@ -35,10 +35,12 @@
>  import org.apache.openjpa.jdbc.kernel.exps.JDBCExpressionFactory;
>  import org.apache.openjpa.jdbc.kernel.exps.JDBCStringContains;
>  import org.apache.openjpa.jdbc.kernel.exps.JDBCWildcardMatch;
> +import org.apache.openjpa.jdbc.kernel.exps.PCPath;
>  import org.apache.openjpa.jdbc.kernel.exps.QueryExpressionsState;
>  import org.apache.openjpa.jdbc.kernel.exps.SQLEmbed;
>  import org.apache.openjpa.jdbc.kernel.exps.SQLExpression;
>  import org.apache.openjpa.jdbc.kernel.exps.SQLValue;
> +import org.apache.openjpa.jdbc.kernel.exps.Val;
>  import org.apache.openjpa.jdbc.meta.ClassMapping;
>  import org.apache.openjpa.jdbc.meta.FieldMapping;
>  import org.apache.openjpa.jdbc.meta.strats.VerticalClassStrategy;
> @@ -50,18 +52,24 @@
>  import org.apache.openjpa.jdbc.sql.Select;
>  import org.apache.openjpa.jdbc.sql.Union;
>  import org.apache.openjpa.kernel.ExpressionStoreQuery;
> +import org.apache.openjpa.kernel.Filters;
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
>  import org.apache.openjpa.kernel.OrderingMergedResultObjectProvider;
>  import org.apache.openjpa.kernel.QueryHints;
> +import org.apache.openjpa.kernel.exps.Constant;
>  import org.apache.openjpa.kernel.exps.ExpressionFactory;
>  import org.apache.openjpa.kernel.exps.ExpressionParser;
>  import org.apache.openjpa.kernel.exps.FilterListener;
> +import org.apache.openjpa.kernel.exps.Literal;
>  import org.apache.openjpa.kernel.exps.QueryExpressions;
>  import org.apache.openjpa.lib.rop.MergedResultObjectProvider;
>  import org.apache.openjpa.lib.rop.RangeResultObjectProvider;
>  import org.apache.openjpa.lib.rop.ResultObjectProvider;
>  import org.apache.openjpa.lib.util.Localizer;
>  import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.JavaTypes;
>  import org.apache.openjpa.meta.ValueMetaData;
> +import org.apache.openjpa.util.UnsupportedException;
>  import org.apache.openjpa.util.UserException;
>  import serp.util.Numbers;
>
> @@ -671,4 +679,105 @@
>         throws SQLException {
>         return sql.prepareStatement(conn);
>     }
> +
> +    public Object evaluate(Object value, Object ob, Object[] params,
> +        OpenJPAStateManager sm) {
> +        if (value instanceof org.apache.openjpa.jdbc.kernel.exps.Math) {
> +            org.apache.openjpa.jdbc.kernel.exps.Math mathVal =
> +                (org.apache.openjpa.jdbc.kernel.exps.Math) value;
> +
> +            Val value1 = mathVal.getVal1();
> +            Object val1 = getValue(value1, ob, params, sm);
> +            Class c1 = value1.getType();
> +
> +            Val value2 = mathVal.getVal2();
> +            Object val2 = getValue(value2, ob, params, sm);
> +            Class c2 = value2.getType();
> +
> +            String op = mathVal.getOperation();
> +
> +            if (op.equals(org.apache.openjpa.jdbc.kernel.exps.Math.ADD))
> +                return Filters.add(val1, c1, val2, c2);
> +            else if (op.equals(
> +                    org.apache.openjpa.jdbc.kernel.exps.Math.SUBTRACT))
> +                return Filters.subtract(val1, c1, val2, c2);
> +            else if (op.equals(
> +                    org.apache.openjpa.jdbc.kernel.exps.Math.MULTIPLY))
> +                return Filters.multiply(val1, c1, val2, c2);
> +            else if (op.equals(
> +                    org.apache.openjpa.jdbc.kernel.exps.Math.DIVIDE))
> +                return Filters.divide(val1, c1, val2, c2);
> +            else if
> (op.equals(org.apache.openjpa.jdbc.kernel.exps.Math.MOD))
> +                return Filters.mod(val1, c1, val2, c2);
> +            throw new UnsupportedException();
> +        }
> +        throw new UnsupportedException();
> +    }
> +
> +    private Object getValue(Object ob, FieldMapping fmd,
> +        OpenJPAStateManager sm) {
> +        int i = fmd.getIndex();
> +        switch (fmd.getDeclaredTypeCode()) {
> +            case JavaTypes.BOOLEAN:
> +                return sm.fetchBooleanField(i);
> +            case JavaTypes.BYTE:
> +                return sm.fetchByteField(i);
> +            case JavaTypes.CHAR:
> +                return sm.fetchCharField(i);
> +            case JavaTypes.DOUBLE:
> +                return sm.fetchDoubleField(i);
> +            case JavaTypes.FLOAT:
> +                return sm.fetchFloatField(i);
> +            case JavaTypes.INT:
> +                return sm.fetchIntField(i);
> +            case JavaTypes.LONG:
> +                return sm.fetchLongField(i);
> +            case JavaTypes.SHORT:
> +                return sm.fetchShortField(i);
> +            case JavaTypes.STRING:
> +                return sm.fetchStringField(i);
> +            case JavaTypes.DATE:
> +            case JavaTypes.NUMBER:
> +            case JavaTypes.BOOLEAN_OBJ:
> +            case JavaTypes.BYTE_OBJ:
> +            case JavaTypes.CHAR_OBJ:
> +            case JavaTypes.DOUBLE_OBJ:
> +            case JavaTypes.FLOAT_OBJ:
> +            case JavaTypes.INT_OBJ:
> +            case JavaTypes.LONG_OBJ:
> +            case JavaTypes.SHORT_OBJ:
> +            case JavaTypes.BIGDECIMAL:
> +            case JavaTypes.BIGINTEGER:
> +            case JavaTypes.LOCALE:
> +            case JavaTypes.OBJECT:
> +            case JavaTypes.OID:
> +                return sm.fetchObjectField(i);
> +            default:
> +                throw new UnsupportedException();
> +        }
> +    }
> +
> +    private Object eval(Object ob, Object value, Object[] params,
> +        OpenJPAStateManager sm) {
> +        Object val = null;
> +        if (value instanceof Literal)
> +            val = ((Literal) value).getValue();
> +        else if (value instanceof Constant)
> +            val = ((Constant) value).getValue(params);
> +        else
> +            val = evaluate(value, ob, params, sm);
> +
> +        return val;
> +    }
> +
> +    private Object getValue(Object value, Object ob, Object[] params,
> +        OpenJPAStateManager sm) {
> +        if (value instanceof org.apache.openjpa.jdbc.kernel.exps.Math)
> +            return evaluate(value, ob, params, sm);
> +        else if (value instanceof PCPath) {
> +            FieldMapping fm = (FieldMapping)((PCPath)value).last();
> +            return getValue(ob, fm, sm);
> +        } else
> +            return eval(ob, value, params, sm);
> +    }
>  }
>
> Modified:
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
> URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java?rev=660825&r1=660824&r2=660825&view=diff
>
> ==============================================================================
> ---
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
> (original)
> +++
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/exps/Math.java
> Tue May 27 23:08:41 2008
> @@ -33,7 +33,7 @@
>  *
>  * @author Abe White
>  */
> -class Math
> +public class Math
>     extends AbstractVal {
>
>     public static final String ADD = "+";
> @@ -57,6 +57,18 @@
>         _op = op;
>     }
>
> +    public Val getVal1() {
> +        return _val1;
> +    }
> +
> +    public Val getVal2() {
> +        return _val2;
> +    }
> +
> +    public String getOperation() {
> +        return _op;
> +    }
> +
>     public ClassMetaData getMetaData() {
>         return _meta;
>     }
>
> Modified:
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
> URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
>
> ==============================================================================
> ---
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
> (original)
> +++
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/datacache/QueryCacheStoreQuery.java
> Tue May 27 23:08:41 2008
> @@ -1,660 +1,667 @@
> -/*
> - * Licensed to the Apache Software Foundation (ASF) under one
> - * or more contributor license agreements.  See the NOTICE file
> - * distributed with this work for additional information
> - * regarding copyright ownership.  The ASF licenses this file
> - * to you under the Apache License, Version 2.0 (the
> - * "License"); you may not use this file except in compliance
> - * with the License.  You may obtain a copy of the License at
> - *
> - * http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing,
> - * software distributed under the License is distributed on an
> - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> - * KIND, either express or implied.  See the License for the
> - * specific language governing permissions and limitations
> - * under the License.
> - */
> -package org.apache.openjpa.datacache;
> -
> -import java.io.ObjectStreamException;
> -import java.io.Serializable;
> -import java.util.AbstractList;
> -import java.util.ArrayList;
> -import java.util.BitSet;
> -import java.util.Collections;
> -import java.util.Date;
> -import java.util.List;
> -import java.util.Locale;
> -import java.util.Map;
> -import java.util.TreeMap;
> -
> -import org.apache.commons.collections.map.LinkedMap;
> -import org.apache.openjpa.kernel.FetchConfiguration;
> -import org.apache.openjpa.kernel.LockLevels;
> -import org.apache.openjpa.kernel.QueryContext;
> -import org.apache.openjpa.kernel.StoreContext;
> -import org.apache.openjpa.kernel.StoreQuery;
> -import org.apache.openjpa.kernel.exps.AggregateListener;
> -import org.apache.openjpa.kernel.exps.FilterListener;
> -import org.apache.openjpa.lib.rop.ListResultObjectProvider;
> -import org.apache.openjpa.lib.rop.ResultObjectProvider;
> -import org.apache.openjpa.meta.ClassMetaData;
> -import org.apache.openjpa.meta.JavaTypes;
> -import org.apache.openjpa.meta.MetaDataRepository;
> -import org.apache.openjpa.util.ObjectNotFoundException;
> -import serp.util.Numbers;
> -
> -/**
> - * A {@link StoreQuery} implementation that caches the OIDs involved in
> - * the query, and can determine whether or not the query has been dirtied.
> - *
> - * @author Patrick Linskey
> - * @since 0.2.5.0
> - */
> -public class QueryCacheStoreQuery
> -    implements StoreQuery {
> -
> -    private final StoreQuery _query;
> -    private final QueryCache _cache;
> -    private StoreContext _sctx;
> -    private MetaDataRepository _repos;
> -
> -    /**
> -     * Create a new instance that delegates to <code>query</code> if no
> -     * cached results are available.
> -     */
> -    public QueryCacheStoreQuery(StoreQuery query, QueryCache cache) {
> -        _query = query;
> -        _cache = cache;
> -    }
> -
> -    /**
> -     * Return the {@link QueryCache} that this object is associated with.
> -     */
> -    public QueryCache getCache() {
> -        return _cache;
> -    }
> -
> -    /**
> -     * Delegate.
> -     */
> -    public StoreQuery getDelegate() {
> -        return _query;
> -    }
> -
> -    /**
> -     * Look in the query cache for a result for the given query
> -     * key. Only look if this query is being executed outside a
> -     * transaction or in a transaction with IgnoreChanges set to true
> -     * or in a transaction with IgnoreChanges set to false but in which
> -     * none of the classes involved in this query have been touched.
> -     *  Caching is not used when using object locking.
> -     * This is because we must obtain locks on the
> -     * data, and it is likely that making n trips to the database to
> -     * make the locks will be slower than running the query against
> -     * the database.
> -     *  If the fetch configuration has query caching disabled,
> -     * then this method returns <code>null</code>.
> -     *  Return the list if we meet the above criteria and if a list
> -     * is found for <code>qk</code>. Else, return
> -     * <code>null</code>.
> -     *  This implementation means that queries against the cache
> -     * are of READ_COMMITTED isolation level. It'd be nice to support
> -     * READ_SERIALIZABLE -- to do so, we'd just return false when in
> -     * a transaction.
> -     */
> -    private List checkCache(QueryKey qk) {
> -        if (qk == null)
> -            return null;
> -        FetchConfiguration fetch = getContext().getFetchConfiguration();
> -        if (!fetch.getQueryCacheEnabled())
> -            return null;
> -        if (fetch.getReadLockLevel() > LockLevels.LOCK_NONE)
> -            return null;
> -
> -        // get the cached data
> -        QueryResult res = _cache.get(qk);
> -        if (res == null)
> -            return null;
> -        if (res.isEmpty())
> -            return Collections.EMPTY_LIST;
> -
> -        int projs = getContext().getProjectionAliases().length;
> -        if (projs == 0) {
> -            // make sure the data cache contains the oids for the query
> result;
> -            // if it doesn't, then using the result could be slower than
> not
> -            // using it because of the individual by-oid lookups
> -            ClassMetaData meta = _repos.getMetaData(getContext().
> -                getCandidateType(), _sctx.getClassLoader(), true);
> -            if (meta.getDataCache() == null)
> -                return null;
> -
> -            BitSet idxs = meta.getDataCache().containsAll(res);
> -
> -            // eventually we should optimize this to figure out how many
> objects
> -            // the cache is missing and if only a few do a bulk fetch for
> them
> -            int len = idxs.length();
> -            if (len < res.size())
> -                return null;
> -            for (int i = 0; i < len; i++)
> -                if (!idxs.get(i))
> -                    return null;
> -        }
> -        return new CachedList(res, projs != 0, _sctx);
> -    }
> -
> -    /**
> -     * Wrap the result object provider returned by our delegate in a
> -     * caching provider.
> -     */
> -    private ResultObjectProvider wrapResult(ResultObjectProvider rop,
> -        QueryKey key) {
> -        if (key == null)
> -            return rop;
> -        return new CachingResultObjectProvider(rop, getContext().
> -            getProjectionAliases().length > 0, key);
> -    }
> -
> -    /**
> -     * Copy a projection element for caching / returning.
> -     */
> -    private static Object copyProjection(Object obj, StoreContext ctx) {
> -        if (obj == null)
> -            return null;
> -        switch (JavaTypes.getTypeCode(obj.getClass())) {
> -            case JavaTypes.STRING:
> -            case JavaTypes.BOOLEAN_OBJ:
> -            case JavaTypes.BYTE_OBJ:
> -            case JavaTypes.CHAR_OBJ:
> -            case JavaTypes.DOUBLE_OBJ:
> -            case JavaTypes.FLOAT_OBJ:
> -            case JavaTypes.INT_OBJ:
> -            case JavaTypes.LONG_OBJ:
> -            case JavaTypes.SHORT_OBJ:
> -            case JavaTypes.BIGDECIMAL:
> -            case JavaTypes.BIGINTEGER:
> -            case JavaTypes.OID:
> -                return obj;
> -            case JavaTypes.DATE:
> -                return ((Date) obj).clone();
> -            case JavaTypes.LOCALE:
> -                return ((Locale) obj).clone();
> -            default:
> -                if (obj instanceof CachedObjectId)
> -                    return fromObjectId(((CachedObjectId) obj).oid, ctx);
> -                Object oid = ctx.getObjectId(obj);
> -                if (oid != null)
> -                    return new CachedObjectId(oid);
> -                return obj;
> -        }
> -    }
> -
> -    /**
> -     * Return the result object based on its cached oid.
> -     */
> -    private static Object fromObjectId(Object oid, StoreContext sctx) {
> -        if (oid == null)
> -            return null;
> -
> -        Object obj = sctx.find(oid, null, null, null, 0);
> -        if (obj == null)
> -            throw new ObjectNotFoundException(oid);
> -        return obj;
> -    }
> -
> -    public Object writeReplace()
> -        throws ObjectStreamException {
> -        return _query;
> -    }
> -
> -    public QueryContext getContext() {
> -        return _query.getContext();
> -    }
> -
> -    public void setContext(QueryContext qctx) {
> -        _query.setContext(qctx);
> -        _sctx = qctx.getStoreContext();
> -        _repos = _sctx.getConfiguration().getMetaDataRepositoryInstance();
> -    }
> -
> -    public boolean setQuery(Object query) {
> -        return _query.setQuery(query);
> -    }
> -
> -    public FilterListener getFilterListener(String tag) {
> -        return _query.getFilterListener(tag);
> -    }
> -
> -    public AggregateListener getAggregateListener(String tag) {
> -        return _query.getAggregateListener(tag);
> -    }
> -
> -    public Object newCompilationKey() {
> -        return _query.newCompilationKey();
> -    }
> -
> -    public Object newCompilation() {
> -        return _query.newCompilation();
> -    }
> -
> -    public void populateFromCompilation(Object comp) {
> -        _query.populateFromCompilation(comp);
> -    }
> -
> -    public void invalidateCompilation() {
> -        _query.invalidateCompilation();
> -    }
> -
> -    public boolean supportsDataStoreExecution() {
> -        return _query.supportsDataStoreExecution();
> -    }
> -
> -    public boolean supportsInMemoryExecution() {
> -        return _query.supportsInMemoryExecution();
> -    }
> -
> -    public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs)
> {
> -        return _query.newInMemoryExecutor(meta, subs);
> -    }
> -
> -    public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs)
> {
> -        Executor ex = _query.newDataStoreExecutor(meta, subs);
> -        return new QueryCacheExecutor(ex, meta, subs);
> -    }
> -
> -    public boolean supportsAbstractExecutors() {
> -        return _query.supportsAbstractExecutors();
> -    }
> -
> -    public boolean requiresCandidateType() {
> -        return _query.requiresCandidateType();
> -    }
> -
> -    public boolean requiresParameterDeclarations() {
> -        return _query.requiresParameterDeclarations();
> -    }
> -
> -    public boolean supportsParameterDeclarations() {
> -        return _query.supportsParameterDeclarations();
> -    }
> -
> -    /**
> -     * Caching executor.
> -     */
> -    private static class QueryCacheExecutor
> -        implements Executor {
> -
> -        private final Executor _ex;
> -        private final Class _candidate;
> -        private final boolean _subs;
> -
> -        public QueryCacheExecutor(Executor ex, ClassMetaData meta,
> -            boolean subs) {
> -            _ex = ex;
> -            _candidate = (meta == null) ? null : meta.getDescribedType();
> -            _subs = subs;
> -        }
> -
> -        public ResultObjectProvider executeQuery(StoreQuery q, Object[]
> params,
> -            Range range) {
> -            QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
> -            QueryKey key = QueryKey.newInstance(cq.getContext(),
> -                _ex.isPacking(q), params, _candidate, _subs, range.start,
> -                range.end);
> -            List cached = cq.checkCache(key);
> -            if (cached != null)
> -                return new ListResultObjectProvider(cached);
> -
> -            ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(),
> -                params, range);
> -            return cq.wrapResult(rop, key);
> -        }
> -
> -        /**
> -         * Clear the cached queries associated with the access path
> -         * classes in the query. This is done when bulk operations
> -         * (such as deletes or updates) are performed so that the
> -         * cache remains up-to-date.
> -         */
> -        private void clearAccessPath(StoreQuery q) {
> -            if (q == null)
> -                return;
> -
> -            ClassMetaData[] cmd = getAccessPathMetaDatas(q);
> -            if (cmd == null || cmd.length == 0)
> -                return;
> -
> -            List classes = new ArrayList(cmd.length);
> -            for (int i = 0; i < cmd.length; i++)
> -                classes.add(cmd[i].getDescribedType());
> -
> -            // evict from the query cache
> -            QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
> -            cq.getCache().onTypesChanged(new TypesChangedEvent
> -                (q.getContext(), classes));
> -
> -            // evict from the data cache
> -            for (int i = 0; i < cmd.length; i++) {
> -                if (cmd[i].getDataCache() != null)
> -                    cmd[i].getDataCache().removeAll(
> -                        cmd[i].getDescribedType(), true);
> -            }
> -        }
> -
> -        public Number executeDelete(StoreQuery q, Object[] params) {
> -            try {
> -                return _ex.executeDelete(unwrap(q), params);
> -            } finally {
> -                clearAccessPath(q);
> -            }
> -        }
> -
> -        public Number executeUpdate(StoreQuery q, Object[] params) {
> -            try {
> -                return _ex.executeUpdate(unwrap(q), params);
> -            } finally {
> -                clearAccessPath(q);
> -            }
> -        }
> -
> -        public String[] getDataStoreActions(StoreQuery q, Object[] params,
> -            Range range) {
> -            return EMPTY_STRINGS;
> -        }
> -
> -        public void validate(StoreQuery q) {
> -            _ex.validate(unwrap(q));
> -        }
> -
> -        public void getRange(StoreQuery q, Object[] params, Range range) {
> -            _ex.getRange(q, params, range);
> -        }
> -
> -        public Object getOrderingValue(StoreQuery q, Object[] params,
> -            Object resultObject, int orderIndex) {
> -            return _ex.getOrderingValue(unwrap(q), params, resultObject,
> -                orderIndex);
> -        }
> -
> -        public boolean[] getAscending(StoreQuery q) {
> -            return _ex.getAscending(unwrap(q));
> -        }
> -
> -        public boolean isPacking(StoreQuery q) {
> -            return _ex.isPacking(unwrap(q));
> -        }
> -
> -        public String getAlias(StoreQuery q) {
> -            return _ex.getAlias(unwrap(q));
> -        }
> -
> -        public Class getResultClass(StoreQuery q) {
> -            return _ex.getResultClass(unwrap(q));
> -        }
> -
> -        public String[] getProjectionAliases(StoreQuery q) {
> -            return _ex.getProjectionAliases(unwrap(q));
> -        }
> -
> -        public Class[] getProjectionTypes(StoreQuery q) {
> -            return _ex.getProjectionTypes(unwrap(q));
> -        }
> -
> -        public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
> -            return _ex.getAccessPathMetaDatas(unwrap(q));
> -        }
> -
> -        public int getOperation(StoreQuery q) {
> -            return _ex.getOperation(unwrap(q));
> -        }
> -
> -        public boolean isAggregate(StoreQuery q) {
> -            return _ex.isAggregate(unwrap(q));
> -        }
> -
> -        public boolean hasGrouping(StoreQuery q) {
> -            return _ex.hasGrouping(unwrap(q));
> -        }
> -
> -        public LinkedMap getParameterTypes(StoreQuery q) {
> -            return _ex.getParameterTypes(unwrap(q));
> -        }
> -
> -        public Map getUpdates(StoreQuery q) {
> -            return _ex.getUpdates(unwrap(q));
> -        }
> -
> -        private static StoreQuery unwrap(StoreQuery q) {
> -            return ((QueryCacheStoreQuery) q).getDelegate();
> -        }
> -    }
> -
> -    /**
> -     * Result list implementation for a cached query result.
> Package-protected
> -     * for testing.
> -     */
> -    public static class CachedList
> -        extends AbstractList
> -        implements Serializable {
> -
> -        private final QueryResult _res;
> -        private final boolean _proj;
> -        private final StoreContext _sctx;
> -
> -        public CachedList(QueryResult res, boolean proj, StoreContext ctx)
> {
> -            _res = res;
> -            _proj = proj;
> -            _sctx = ctx;
> -        }
> -
> -        public Object get(int idx) {
> -            if (!_proj)
> -                return fromObjectId(_res.get(idx), _sctx);
> -
> -            Object[] cached = (Object[]) _res.get(idx);
> -            if (cached == null)
> -                return null;
> -            Object[] uncached = new Object[cached.length];
> -            for (int i = 0; i < cached.length; i++)
> -                uncached[i] = copyProjection(cached[i], _sctx);
> -            return uncached;
> -        }
> -
> -        public int size() {
> -            return _res.size();
> -        }
> -
> -        public Object writeReplace()
> -            throws ObjectStreamException {
> -            return new ArrayList(this);
> -        }
> -    }
> -
> -    /**
> -     * A wrapper around a {@link ResultObjectProvider} that builds up a
> list of
> -     * all the OIDs in this list and registers that list with the
> -     * query cache. Abandons monitoring and registering if one of the
> classes
> -     * in the access path is modified while the query results are being
> loaded.
> -     */
> -    private class CachingResultObjectProvider
> -        implements ResultObjectProvider, TypesChangedListener {
> -
> -        private final ResultObjectProvider _rop;
> -        private final boolean _proj;
> -        private final QueryKey _qk;
> -        private final TreeMap _data = new TreeMap();
> -        private boolean _maintainCache = true;
> -        private int _pos = -1;
> -
> -        // used to determine list size without necessarily calling size(),
> -        // which may require a DB trip or return Integer.MAX_VALUE
> -        private int _max = -1;
> -        private int _size = Integer.MAX_VALUE;
> -
> -        /**
> -         * Constructor. Supply delegate result provider and our query key.
> -         */
> -        public CachingResultObjectProvider(ResultObjectProvider rop,
> -            boolean proj, QueryKey key) {
> -            _rop = rop;
> -            _proj = proj;
> -            _qk = key;
> -            _cache.addTypesChangedListener(this);
> -        }
> -
> -        /**
> -         * Stop caching.
> -         */
> -        private void abortCaching() {
> -            if (!_maintainCache)
> -                return;
> -
> -            // this can be called via an event from another thread
> -            synchronized (this) {
> -                // it's important that we set this flag first so that any
> -                // subsequent calls to this object are bypassed.
> -                _maintainCache = false;
> -                _cache.removeTypesChangedListener(this);
> -                _data.clear();
> -            }
> -        }
> -
> -        /**
> -         * Check whether we've buffered all results, while optionally
> adding
> -         * the given result.
> -         */
> -        private void checkFinished(Object obj, boolean result) {
> -            // this can be called at the same time as abortCaching via
> -            // a types changed event
> -            boolean finished = false;
> -            synchronized (this) {
> -                if (_maintainCache) {
> -                    if (result) {
> -                        Integer index = Numbers.valueOf(_pos);
> -                        if (!_data.containsKey(index)) {
> -                            Object cached;
> -                            if (obj == null)
> -                                cached = null;
> -                            else if (!_proj)
> -                                cached = _sctx.getObjectId(obj);
> -                            else {
> -                                Object[] arr = (Object[]) obj;
> -                                Object[] cp = new Object[arr.length];
> -                                for (int i = 0; i < arr.length; i++)
> -                                    cp[i] = copyProjection(arr[i], _sctx);
> -                                cached = cp;
> -                            }
> -                            if (cached != null)
> -                                _data.put(index, cached);
> -                        }
> -                    }
> -                    finished = _size == _data.size();
> -                }
> -            }
> -
> -            if (finished) {
> -                // an abortCaching call can sneak in here via onExpire;
> the
> -                // cache is locked during event firings, so the lock here
> will
> -                // wait for it (or will force the next firing to wait)
> -                _cache.writeLock();
> -                try {
> -                    // make sure we didn't abort
> -                    if (_maintainCache) {
> -                        QueryResult res = new QueryResult(_qk,
> _data.values());
> -                        _cache.put(_qk, res);
> -                        abortCaching();
> -                    }
> -                }
> -                finally {
> -                    _cache.writeUnlock();
> -                }
> -            }
> -        }
> -
> -        public boolean supportsRandomAccess() {
> -            return _rop.supportsRandomAccess();
> -        }
> -
> -        public void open()
> -            throws Exception {
> -            _rop.open();
> -        }
> -
> -        public Object getResultObject()
> -            throws Exception {
> -            Object obj = _rop.getResultObject();
> -            checkFinished(obj, true);
> -            return obj;
> -        }
> -
> -        public boolean next()
> -            throws Exception {
> -            _pos++;
> -            boolean next = _rop.next();
> -            if (!next && _pos == _max + 1) {
> -                _size = _pos;
> -                checkFinished(null, false);
> -            } else if (next && _pos > _max)
> -                _max = _pos;
> -            return next;
> -        }
> -
> -        public boolean absolute(int pos)
> -            throws Exception {
> -            _pos = pos;
> -            boolean valid = _rop.absolute(pos);
> -            if (!valid && _pos == _max + 1) {
> -                _size = _pos;
> -                checkFinished(null, false);
> -            } else if (valid && _pos > _max)
> -                _max = _pos;
> -            return valid;
> -        }
> -
> -        public int size()
> -            throws Exception {
> -            if (_size != Integer.MAX_VALUE)
> -                return _size;
> -            int size = _rop.size();
> -            _size = size;
> -            checkFinished(null, false);
> -            return size;
> -        }
> -
> -        public void reset()
> -            throws Exception {
> -            _rop.reset();
> -            _pos = -1;
> -        }
> -
> -        public void close()
> -            throws Exception {
> -            abortCaching();
> -            _rop.close();
> -        }
> -
> -        public void handleCheckedException(Exception e) {
> -            _rop.handleCheckedException(e);
> -        }
> -
> -        public void onTypesChanged(TypesChangedEvent ev) {
> -            if (_qk.changeInvalidatesQuery(ev.getTypes()))
> -                abortCaching();
> -        }
> -    }
> -
> -    /**
> -     * Struct to recognize cached oids.
> -     */
> -    private static class CachedObjectId {
> -
> -        public final Object oid;
> -
> -        public CachedObjectId (Object oid)
> -               {
> -                       this.oid = oid;
> -               }
> -       }
> -}
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License.  You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied.  See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +package org.apache.openjpa.datacache;
> +
> +import java.io.ObjectStreamException;
> +import java.io.Serializable;
> +import java.util.AbstractList;
> +import java.util.ArrayList;
> +import java.util.BitSet;
> +import java.util.Collections;
> +import java.util.Date;
> +import java.util.List;
> +import java.util.Locale;
> +import java.util.Map;
> +import java.util.TreeMap;
> +
> +import org.apache.commons.collections.map.LinkedMap;
> +import org.apache.openjpa.kernel.FetchConfiguration;
> +import org.apache.openjpa.kernel.LockLevels;
> +import org.apache.openjpa.kernel.OpenJPAStateManager;
> +import org.apache.openjpa.kernel.QueryContext;
> +import org.apache.openjpa.kernel.StoreContext;
> +import org.apache.openjpa.kernel.StoreQuery;
> +import org.apache.openjpa.kernel.exps.AggregateListener;
> +import org.apache.openjpa.kernel.exps.FilterListener;
> +import org.apache.openjpa.lib.rop.ListResultObjectProvider;
> +import org.apache.openjpa.lib.rop.ResultObjectProvider;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.meta.JavaTypes;
> +import org.apache.openjpa.meta.MetaDataRepository;
> +import org.apache.openjpa.util.ObjectNotFoundException;
> +
> +import serp.util.Numbers;
> +
> +/**
> + * A {@link StoreQuery} implementation that caches the OIDs involved in
> + * the query, and can determine whether or not the query has been dirtied.
> + *
> + * @author Patrick Linskey
> + * @since 0.2.5.0
> + */
> +public class QueryCacheStoreQuery
> +    implements StoreQuery {
> +
> +    private final StoreQuery _query;
> +    private final QueryCache _cache;
> +    private StoreContext _sctx;
> +    private MetaDataRepository _repos;
> +
> +    /**
> +     * Create a new instance that delegates to <code>query</code> if no
> +     * cached results are available.
> +     */
> +    public QueryCacheStoreQuery(StoreQuery query, QueryCache cache) {
> +        _query = query;
> +        _cache = cache;
> +    }
> +
> +    /**
> +     * Return the {@link QueryCache} that this object is associated with.
> +     */
> +    public QueryCache getCache() {
> +        return _cache;
> +    }
> +
> +    /**
> +     * Delegate.
> +     */
> +    public StoreQuery getDelegate() {
> +        return _query;
> +    }
> +
> +    /**
> +     * Look in the query cache for a result for the given query
> +     * key. Only look if this query is being executed outside a
> +     * transaction or in a transaction with IgnoreChanges set to true
> +     * or in a transaction with IgnoreChanges set to false but in which
> +     * none of the classes involved in this query have been touched.
> +     *  Caching is not used when using object locking.
> +     * This is because we must obtain locks on the
> +     * data, and it is likely that making n trips to the database to
> +     * make the locks will be slower than running the query against
> +     * the database.
> +     *  If the fetch configuration has query caching disabled,
> +     * then this method returns <code>null</code>.
> +     *  Return the list if we meet the above criteria and if a list
> +     * is found for <code>qk</code>. Else, return
> +     * <code>null</code>.
> +     *  This implementation means that queries against the cache
> +     * are of READ_COMMITTED isolation level. It'd be nice to support
> +     * READ_SERIALIZABLE -- to do so, we'd just return false when in
> +     * a transaction.
> +     */
> +    private List checkCache(QueryKey qk) {
> +        if (qk == null)
> +            return null;
> +        FetchConfiguration fetch = getContext().getFetchConfiguration();
> +        if (!fetch.getQueryCacheEnabled())
> +            return null;
> +        if (fetch.getReadLockLevel() > LockLevels.LOCK_NONE)
> +            return null;
> +
> +        // get the cached data
> +        QueryResult res = _cache.get(qk);
> +        if (res == null)
> +            return null;
> +        if (res.isEmpty())
> +            return Collections.EMPTY_LIST;
> +
> +        int projs = getContext().getProjectionAliases().length;
> +        if (projs == 0) {
> +            // make sure the data cache contains the oids for the query
> result;
> +            // if it doesn't, then using the result could be slower than
> not
> +            // using it because of the individual by-oid lookups
> +            ClassMetaData meta = _repos.getMetaData(getContext().
> +                getCandidateType(), _sctx.getClassLoader(), true);
> +            if (meta.getDataCache() == null)
> +                return null;
> +
> +            BitSet idxs = meta.getDataCache().containsAll(res);
> +
> +            // eventually we should optimize this to figure out how many
> objects
> +            // the cache is missing and if only a few do a bulk fetch for
> them
> +            int len = idxs.length();
> +            if (len < res.size())
> +                return null;
> +            for (int i = 0; i < len; i++)
> +                if (!idxs.get(i))
> +                    return null;
> +        }
> +        return new CachedList(res, projs != 0, _sctx);
> +    }
> +
> +    /**
> +     * Wrap the result object provider returned by our delegate in a
> +     * caching provider.
> +     */
> +    private ResultObjectProvider wrapResult(ResultObjectProvider rop,
> +        QueryKey key) {
> +        if (key == null)
> +            return rop;
> +        return new CachingResultObjectProvider(rop, getContext().
> +            getProjectionAliases().length > 0, key);
> +    }
> +
> +    /**
> +     * Copy a projection element for caching / returning.
> +     */
> +    private static Object copyProjection(Object obj, StoreContext ctx) {
> +        if (obj == null)
> +            return null;
> +        switch (JavaTypes.getTypeCode(obj.getClass())) {
> +            case JavaTypes.STRING:
> +            case JavaTypes.BOOLEAN_OBJ:
> +            case JavaTypes.BYTE_OBJ:
> +            case JavaTypes.CHAR_OBJ:
> +            case JavaTypes.DOUBLE_OBJ:
> +            case JavaTypes.FLOAT_OBJ:
> +            case JavaTypes.INT_OBJ:
> +            case JavaTypes.LONG_OBJ:
> +            case JavaTypes.SHORT_OBJ:
> +            case JavaTypes.BIGDECIMAL:
> +            case JavaTypes.BIGINTEGER:
> +            case JavaTypes.OID:
> +                return obj;
> +            case JavaTypes.DATE:
> +                return ((Date) obj).clone();
> +            case JavaTypes.LOCALE:
> +                return ((Locale) obj).clone();
> +            default:
> +                if (obj instanceof CachedObjectId)
> +                    return fromObjectId(((CachedObjectId) obj).oid, ctx);
> +                Object oid = ctx.getObjectId(obj);
> +                if (oid != null)
> +                    return new CachedObjectId(oid);
> +                return obj;
> +        }
> +    }
> +
> +    /**
> +     * Return the result object based on its cached oid.
> +     */
> +    private static Object fromObjectId(Object oid, StoreContext sctx) {
> +        if (oid == null)
> +            return null;
> +
> +        Object obj = sctx.find(oid, null, null, null, 0);
> +        if (obj == null)
> +            throw new ObjectNotFoundException(oid);
> +        return obj;
> +    }
> +
> +    public Object writeReplace()
> +        throws ObjectStreamException {
> +        return _query;
> +    }
> +
> +    public QueryContext getContext() {
> +        return _query.getContext();
> +    }
> +
> +    public void setContext(QueryContext qctx) {
> +        _query.setContext(qctx);
> +        _sctx = qctx.getStoreContext();
> +        _repos = _sctx.getConfiguration().getMetaDataRepositoryInstance();
> +    }
> +
> +    public boolean setQuery(Object query) {
> +        return _query.setQuery(query);
> +    }
> +
> +    public FilterListener getFilterListener(String tag) {
> +        return _query.getFilterListener(tag);
> +    }
> +
> +    public AggregateListener getAggregateListener(String tag) {
> +        return _query.getAggregateListener(tag);
> +    }
> +
> +    public Object newCompilationKey() {
> +        return _query.newCompilationKey();
> +    }
> +
> +    public Object newCompilation() {
> +        return _query.newCompilation();
> +    }
> +
> +    public void populateFromCompilation(Object comp) {
> +        _query.populateFromCompilation(comp);
> +    }
> +
> +    public void invalidateCompilation() {
> +        _query.invalidateCompilation();
> +    }
> +
> +    public boolean supportsDataStoreExecution() {
> +        return _query.supportsDataStoreExecution();
> +    }
> +
> +    public boolean supportsInMemoryExecution() {
> +        return _query.supportsInMemoryExecution();
> +    }
> +
> +    public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs)
> {
> +        return _query.newInMemoryExecutor(meta, subs);
> +    }
> +
> +    public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs)
> {
> +        Executor ex = _query.newDataStoreExecutor(meta, subs);
> +        return new QueryCacheExecutor(ex, meta, subs);
> +    }
> +
> +    public boolean supportsAbstractExecutors() {
> +        return _query.supportsAbstractExecutors();
> +    }
> +
> +    public boolean requiresCandidateType() {
> +        return _query.requiresCandidateType();
> +    }
> +
> +    public boolean requiresParameterDeclarations() {
> +        return _query.requiresParameterDeclarations();
> +    }
> +
> +    public boolean supportsParameterDeclarations() {
> +        return _query.supportsParameterDeclarations();
> +    }
> +
> +    public Object evaluate(Object value, Object ob, Object[] params,
> +        OpenJPAStateManager sm) {
> +        return _query.evaluate(value, ob, params, sm);
> +    }
> +
> +    /**
> +     * Caching executor.
> +     */
> +    private static class QueryCacheExecutor
> +        implements Executor {
> +
> +        private final Executor _ex;
> +        private final Class _candidate;
> +        private final boolean _subs;
> +
> +        public QueryCacheExecutor(Executor ex, ClassMetaData meta,
> +            boolean subs) {
> +            _ex = ex;
> +            _candidate = (meta == null) ? null : meta.getDescribedType();
> +            _subs = subs;
> +        }
> +
> +        public ResultObjectProvider executeQuery(StoreQuery q, Object[]
> params,
> +            Range range) {
> +            QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
> +            QueryKey key = QueryKey.newInstance(cq.getContext(),
> +                _ex.isPacking(q), params, _candidate, _subs, range.start,
> +                range.end);
> +            List cached = cq.checkCache(key);
> +            if (cached != null)
> +                return new ListResultObjectProvider(cached);
> +
> +            ResultObjectProvider rop = _ex.executeQuery(cq.getDelegate(),
> +                params, range);
> +            return cq.wrapResult(rop, key);
> +        }
> +
> +        /**
> +         * Clear the cached queries associated with the access path
> +         * classes in the query. This is done when bulk operations
> +         * (such as deletes or updates) are performed so that the
> +         * cache remains up-to-date.
> +         */
> +        private void clearAccessPath(StoreQuery q) {
> +            if (q == null)
> +                return;
> +
> +            ClassMetaData[] cmd = getAccessPathMetaDatas(q);
> +            if (cmd == null || cmd.length == 0)
> +                return;
> +
> +            List classes = new ArrayList(cmd.length);
> +            for (int i = 0; i < cmd.length; i++)
> +                classes.add(cmd[i].getDescribedType());
> +
> +            // evict from the query cache
> +            QueryCacheStoreQuery cq = (QueryCacheStoreQuery) q;
> +            cq.getCache().onTypesChanged(new TypesChangedEvent
> +                (q.getContext(), classes));
> +
> +            // evict from the data cache
> +            for (int i = 0; i < cmd.length; i++) {
> +                if (cmd[i].getDataCache() != null)
> +                    cmd[i].getDataCache().removeAll(
> +                        cmd[i].getDescribedType(), true);
> +            }
> +        }
> +
> +        public Number executeDelete(StoreQuery q, Object[] params) {
> +            try {
> +                return _ex.executeDelete(unwrap(q), params);
> +            } finally {
> +                clearAccessPath(q);
> +            }
> +        }
> +
> +        public Number executeUpdate(StoreQuery q, Object[] params) {
> +            try {
> +                return _ex.executeUpdate(unwrap(q), params);
> +            } finally {
> +                clearAccessPath(q);
> +            }
> +        }
> +
> +        public String[] getDataStoreActions(StoreQuery q, Object[] params,
> +            Range range) {
> +            return EMPTY_STRINGS;
> +        }
> +
> +        public void validate(StoreQuery q) {
> +            _ex.validate(unwrap(q));
> +        }
> +
> +        public void getRange(StoreQuery q, Object[] params, Range range) {
> +            _ex.getRange(q, params, range);
> +        }
> +
> +        public Object getOrderingValue(StoreQuery q, Object[] params,
> +            Object resultObject, int orderIndex) {
> +            return _ex.getOrderingValue(unwrap(q), params, resultObject,
> +                orderIndex);
> +        }
> +
> +        public boolean[] getAscending(StoreQuery q) {
> +            return _ex.getAscending(unwrap(q));
> +        }
> +
> +        public boolean isPacking(StoreQuery q) {
> +            return _ex.isPacking(unwrap(q));
> +        }
> +
> +        public String getAlias(StoreQuery q) {
> +            return _ex.getAlias(unwrap(q));
> +        }
> +
> +        public Class getResultClass(StoreQuery q) {
> +            return _ex.getResultClass(unwrap(q));
> +        }
> +
> +        public String[] getProjectionAliases(StoreQuery q) {
> +            return _ex.getProjectionAliases(unwrap(q));
> +        }
> +
> +        public Class[] getProjectionTypes(StoreQuery q) {
> +            return _ex.getProjectionTypes(unwrap(q));
> +        }
> +
> +        public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
> +            return _ex.getAccessPathMetaDatas(unwrap(q));
> +        }
> +
> +        public int getOperation(StoreQuery q) {
> +            return _ex.getOperation(unwrap(q));
> +        }
> +
> +        public boolean isAggregate(StoreQuery q) {
> +            return _ex.isAggregate(unwrap(q));
> +        }
> +
> +        public boolean hasGrouping(StoreQuery q) {
> +            return _ex.hasGrouping(unwrap(q));
> +        }
> +
> +        public LinkedMap getParameterTypes(StoreQuery q) {
> +            return _ex.getParameterTypes(unwrap(q));
> +        }
> +
> +        public Map getUpdates(StoreQuery q) {
> +            return _ex.getUpdates(unwrap(q));
> +        }
> +
> +        private static StoreQuery unwrap(StoreQuery q) {
> +            return ((QueryCacheStoreQuery) q).getDelegate();
> +        }
> +    }
> +
> +    /**
> +     * Result list implementation for a cached query result.
> Package-protected
> +     * for testing.
> +     */
> +    public static class CachedList
> +        extends AbstractList
> +        implements Serializable {
> +
> +        private final QueryResult _res;
> +        private final boolean _proj;
> +        private final StoreContext _sctx;
> +
> +        public CachedList(QueryResult res, boolean proj, StoreContext ctx)
> {
> +            _res = res;
> +            _proj = proj;
> +            _sctx = ctx;
> +        }
> +
> +        public Object get(int idx) {
> +            if (!_proj)
> +                return fromObjectId(_res.get(idx), _sctx);
> +
> +            Object[] cached = (Object[]) _res.get(idx);
> +            if (cached == null)
> +                return null;
> +            Object[] uncached = new Object[cached.length];
> +            for (int i = 0; i < cached.length; i++)
> +                uncached[i] = copyProjection(cached[i], _sctx);
> +            return uncached;
> +        }
> +
> +        public int size() {
> +            return _res.size();
> +        }
> +
> +        public Object writeReplace()
> +            throws ObjectStreamException {
> +            return new ArrayList(this);
> +        }
> +    }
> +
> +    /**
> +     * A wrapper around a {@link ResultObjectProvider} that builds up a
> list of
> +     * all the OIDs in this list and registers that list with the
> +     * query cache. Abandons monitoring and registering if one of the
> classes
> +     * in the access path is modified while the query results are being
> loaded.
> +     */
> +    private class CachingResultObjectProvider
> +        implements ResultObjectProvider, TypesChangedListener {
> +
> +        private final ResultObjectProvider _rop;
> +        private final boolean _proj;
> +        private final QueryKey _qk;
> +        private final TreeMap _data = new TreeMap();
> +        private boolean _maintainCache = true;
> +        private int _pos = -1;
> +
> +        // used to determine list size without necessarily calling size(),
> +        // which may require a DB trip or return Integer.MAX_VALUE
> +        private int _max = -1;
> +        private int _size = Integer.MAX_VALUE;
> +
> +        /**
> +         * Constructor. Supply delegate result provider and our query key.
> +         */
> +        public CachingResultObjectProvider(ResultObjectProvider rop,
> +            boolean proj, QueryKey key) {
> +            _rop = rop;
> +            _proj = proj;
> +            _qk = key;
> +            _cache.addTypesChangedListener(this);
> +        }
> +
> +        /**
> +         * Stop caching.
> +         */
> +        private void abortCaching() {
> +            if (!_maintainCache)
> +                return;
> +
> +            // this can be called via an event from another thread
> +            synchronized (this) {
> +                // it's important that we set this flag first so that any
> +                // subsequent calls to this object are bypassed.
> +                _maintainCache = false;
> +                _cache.removeTypesChangedListener(this);
> +                _data.clear();
> +            }
> +        }
> +
> +        /**
> +         * Check whether we've buffered all results, while optionally
> adding
> +         * the given result.
> +         */
> +        private void checkFinished(Object obj, boolean result) {
> +            // this can be called at the same time as abortCaching via
> +            // a types changed event
> +            boolean finished = false;
> +            synchronized (this) {
> +                if (_maintainCache) {
> +                    if (result) {
> +                        Integer index = Numbers.valueOf(_pos);
> +                        if (!_data.containsKey(index)) {
> +                            Object cached;
> +                            if (obj == null)
> +                                cached = null;
> +                            else if (!_proj)
> +                                cached = _sctx.getObjectId(obj);
> +                            else {
> +                                Object[] arr = (Object[]) obj;
> +                                Object[] cp = new Object[arr.length];
> +                                for (int i = 0; i < arr.length; i++)
> +                                    cp[i] = copyProjection(arr[i], _sctx);
> +                                cached = cp;
> +                            }
> +                            if (cached != null)
> +                                _data.put(index, cached);
> +                        }
> +                    }
> +                    finished = _size == _data.size();
> +                }
> +            }
> +
> +            if (finished) {
> +                // an abortCaching call can sneak in here via onExpire;
> the
> +                // cache is locked during event firings, so the lock here
> will
> +                // wait for it (or will force the next firing to wait)
> +                _cache.writeLock();
> +                try {
> +                    // make sure we didn't abort
> +                    if (_maintainCache) {
> +                        QueryResult res = new QueryResult(_qk,
> _data.values());
> +                        _cache.put(_qk, res);
> +                        abortCaching();
> +                    }
> +                }
> +                finally {
> +                    _cache.writeUnlock();
> +                }
> +            }
> +        }
> +
> +        public boolean supportsRandomAccess() {
> +            return _rop.supportsRandomAccess();
> +        }
> +
> +        public void open()
> +            throws Exception {
> +            _rop.open();
> +        }
> +
> +        public Object getResultObject()
> +            throws Exception {
> +            Object obj = _rop.getResultObject();
> +            checkFinished(obj, true);
> +            return obj;
> +        }
> +
> +        public boolean next()
> +            throws Exception {
> +            _pos++;
> +            boolean next = _rop.next();
> +            if (!next && _pos == _max + 1) {
> +                _size = _pos;
> +                checkFinished(null, false);
> +            } else if (next && _pos > _max)
> +                _max = _pos;
> +            return next;
> +        }
> +
> +        public boolean absolute(int pos)
> +            throws Exception {
> +            _pos = pos;
> +            boolean valid = _rop.absolute(pos);
> +            if (!valid && _pos == _max + 1) {
> +                _size = _pos;
> +                checkFinished(null, false);
> +            } else if (valid && _pos > _max)
> +                _max = _pos;
> +            return valid;
> +        }
> +
> +        public int size()
> +            throws Exception {
> +            if (_size != Integer.MAX_VALUE)
> +                return _size;
> +            int size = _rop.size();
> +            _size = size;
> +            checkFinished(null, false);
> +            return size;
> +        }
> +
> +        public void reset()
> +            throws Exception {
> +            _rop.reset();
> +            _pos = -1;
> +        }
> +
> +        public void close()
> +            throws Exception {
> +            abortCaching();
> +            _rop.close();
> +        }
> +
> +        public void handleCheckedException(Exception e) {
> +            _rop.handleCheckedException(e);
> +        }
> +
> +        public void onTypesChanged(TypesChangedEvent ev) {
> +            if (_qk.changeInvalidatesQuery(ev.getTypes()))
> +                abortCaching();
> +        }
> +    }
> +
> +    /**
> +     * Struct to recognize cached oids.
> +     */
> +    private static class CachedObjectId {
> +
> +        public final Object oid;
> +
> +        public CachedObjectId (Object oid)
> +               {
> +                       this.oid = oid;
> +               }
> +       }
> +}
>
> Modified:
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
> URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java?rev=660825&r1=660824&r2=660825&view=diff
>
> ==============================================================================
> ---
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
> (original)
> +++
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/AbstractStoreQuery.java
> Tue May 27 23:08:41 2008
> @@ -1,184 +1,190 @@
> -/*
> - * Licensed to the Apache Software Foundation (ASF) under one
> - * or more contributor license agreements.  See the NOTICE file
> - * distributed with this work for additional information
> - * regarding copyright ownership.  The ASF licenses this file
> - * to you under the Apache License, Version 2.0 (the
> - * "License"); you may not use this file except in compliance
> - * with the License.  You may obtain a copy of the License at
> - *
> - * http://www.apache.org/licenses/LICENSE-2.0
> - *
> - * Unless required by applicable law or agreed to in writing,
> - * software distributed under the License is distributed on an
> - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> - * KIND, either express or implied.  See the License for the
> - * specific language governing permissions and limitations
> - * under the License.
> - */
> -package org.apache.openjpa.kernel;
> -
> -import java.util.Map;
> -
> -import org.apache.commons.collections.map.LinkedMap;
> -import org.apache.openjpa.kernel.exps.AggregateListener;
> -import org.apache.openjpa.kernel.exps.FilterListener;
> -import org.apache.openjpa.meta.ClassMetaData;
> -import org.apache.openjpa.util.InternalException;
> -
> -/**
> - * Abstract {@link StoreQuery} that implements most methods as no-ops.
> - *
> - * @author Abe White
> - * @since 0.4.0
> - */
> -public abstract class AbstractStoreQuery
> -    implements StoreQuery {
> -
> -    protected QueryContext ctx = null;
> -
> -    public QueryContext getContext() {
> -        return ctx;
> -    }
> -
> -    public void setContext(QueryContext ctx) {
> -        this.ctx = ctx;
> -    }
> -
> -    public boolean setQuery(Object query) {
> -        return false;
> -    }
> -
> -    public FilterListener getFilterListener(String tag) {
> -        return null;
> -    }
> -
> -    public AggregateListener getAggregateListener(String tag) {
> -        return null;
> -    }
> -
> -    public Object newCompilationKey() {
> -        return null;
> -    }
> -
> -    public Object newCompilation() {
> -        return null;
> -    }
> -
> -    public void populateFromCompilation(Object comp) {
> -    }
> -
> -    public void invalidateCompilation() {
> -    }
> -
> -    public boolean supportsDataStoreExecution() {
> -        return false;
> -    }
> -
> -    public boolean supportsInMemoryExecution() {
> -        return false;
> -    }
> -
> -    public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs)
> {
> -        throw new InternalException();
> -    }
> -
> -    public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs)
> {
> -        throw new InternalException();
> -    }
> -
> -    public boolean supportsAbstractExecutors() {
> -        return false;
> -    }
> -
> -    public boolean requiresCandidateType() {
> -        return true;
> -    }
> -
> -    public boolean requiresParameterDeclarations() {
> -        return true;
> -    }
> -
> -    public boolean supportsParameterDeclarations() {
> -        return true;
> -    }
> -
> -    /**
> -     * Abstract {@link Executor} that implements most methods as no-ops.
> -     */
> -    public static abstract class AbstractExecutor
> -        implements Executor {
> -
> -        public Number executeDelete(StoreQuery q, Object[] params) {
> -            return q.getContext().deleteInMemory(q, this, params);
> -        }
> -
> -        public Number executeUpdate(StoreQuery q, Object[] params) {
> -            return q.getContext().updateInMemory(q, this, params);
> -        }
> -
> -        public String[] getDataStoreActions(StoreQuery q, Object[] params,
> -            Range range) {
> -            return EMPTY_STRINGS;
> -        }
> -
> -        public void validate(StoreQuery q) {
> -        }
> -
> -        public void getRange(StoreQuery q, Object[] params, Range range) {
> -        }
> -
> -        public Object getOrderingValue(StoreQuery q, Object[] params,
> -            Object resultObject, int orderIndex) {
> -            return null;
> -        }
> -
> -        public boolean[] getAscending(StoreQuery q) {
> -            return EMPTY_BOOLEANS;
> -        }
> -
> -        public boolean isPacking(StoreQuery q) {
> -            return false;
> -        }
> -
> -        public String getAlias(StoreQuery q) {
> -            return null;
> -        }
> -
> -        public String[] getProjectionAliases(StoreQuery q) {
> -            return EMPTY_STRINGS;
> -        }
> -
> -        public Class[] getProjectionTypes(StoreQuery q) {
> -            return EMPTY_CLASSES;
> -        }
> -
> -        public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
> -            return EMPTY_METAS;
> -        }
> -
> -        public int getOperation(StoreQuery q) {
> -            return OP_SELECT;
> -        }
> -
> -        public boolean isAggregate(StoreQuery q) {
> -            return false;
> -        }
> -
> -        public boolean hasGrouping(StoreQuery q) {
> -            return false;
> -        }
> -
> -        public LinkedMap getParameterTypes(StoreQuery q) {
> -            return EMPTY_PARAMS;
> -        }
> -
> -        public Class getResultClass(StoreQuery q) {
> -            return null;
> -        }
> -
> -        public Map getUpdates(StoreQuery q) {
> -            return null;
> -        }
> -    }
> -}
> +/*
> + * Licensed to the Apache Software Foundation (ASF) under one
> + * or more contributor license agreements.  See the NOTICE file
> + * distributed with this work for additional information
> + * regarding copyright ownership.  The ASF licenses this file
> + * to you under the Apache License, Version 2.0 (the
> + * "License"); you may not use this file except in compliance
> + * with the License.  You may obtain a copy of the License at
> + *
> + * http://www.apache.org/licenses/LICENSE-2.0
> + *
> + * Unless required by applicable law or agreed to in writing,
> + * software distributed under the License is distributed on an
> + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
> + * KIND, either express or implied.  See the License for the
> + * specific language governing permissions and limitations
> + * under the License.
> + */
> +package org.apache.openjpa.kernel;
> +
> +import java.util.Map;
> +
> +import org.apache.commons.collections.map.LinkedMap;
> +import org.apache.openjpa.kernel.exps.AggregateListener;
> +import org.apache.openjpa.kernel.exps.FilterListener;
> +import org.apache.openjpa.meta.ClassMetaData;
> +import org.apache.openjpa.util.InternalException;
> +import org.apache.openjpa.util.UnsupportedException;
> +
> +/**
> + * Abstract {@link StoreQuery} that implements most methods as no-ops.
> + *
> + * @author Abe White
> + * @since 0.4.0
> + */
> +public abstract class AbstractStoreQuery
> +    implements StoreQuery {
> +
> +    protected QueryContext ctx = null;
> +
> +    public QueryContext getContext() {
> +        return ctx;
> +    }
> +
> +    public void setContext(QueryContext ctx) {
> +        this.ctx = ctx;
> +    }
> +
> +    public boolean setQuery(Object query) {
> +        return false;
> +    }
> +
> +    public FilterListener getFilterListener(String tag) {
> +        return null;
> +    }
> +
> +    public AggregateListener getAggregateListener(String tag) {
> +        return null;
> +    }
> +
> +    public Object newCompilationKey() {
> +        return null;
> +    }
> +
> +    public Object newCompilation() {
> +        return null;
> +    }
> +
> +    public void populateFromCompilation(Object comp) {
> +    }
> +
> +    public void invalidateCompilation() {
> +    }
> +
> +    public boolean supportsDataStoreExecution() {
> +        return false;
> +    }
> +
> +    public boolean supportsInMemoryExecution() {
> +        return false;
> +    }
> +
> +    public Executor newInMemoryExecutor(ClassMetaData meta, boolean subs)
> {
> +        throw new InternalException();
> +    }
> +
> +    public Executor newDataStoreExecutor(ClassMetaData meta, boolean subs)
> {
> +        throw new InternalException();
> +    }
> +
> +    public boolean supportsAbstractExecutors() {
> +        return false;
> +    }
> +
> +    public boolean requiresCandidateType() {
> +        return true;
> +    }
> +
> +    public boolean requiresParameterDeclarations() {
> +        return true;
> +    }
> +
> +    public boolean supportsParameterDeclarations() {
> +        return true;
> +    }
> +
> +    public Object evaluate(Object value, Object ob, Object[] params,
> +        OpenJPAStateManager sm) {
> +        throw new UnsupportedException();
> +    }
> +
> +    /**
> +     * Abstract {@link Executor} that implements most methods as no-ops.
> +     */
> +    public static abstract class AbstractExecutor
> +        implements Executor {
> +
> +        public Number executeDelete(StoreQuery q, Object[] params) {
> +            return q.getContext().deleteInMemory(q, this, params);
> +        }
> +
> +        public Number executeUpdate(StoreQuery q, Object[] params) {
> +            return q.getContext().updateInMemory(q, this, params);
> +        }
> +
> +        public String[] getDataStoreActions(StoreQuery q, Object[] params,
> +            Range range) {
> +            return EMPTY_STRINGS;
> +        }
> +
> +        public void validate(StoreQuery q) {
> +        }
> +
> +        public void getRange(StoreQuery q, Object[] params, Range range) {
> +        }
> +
> +        public Object getOrderingValue(StoreQuery q, Object[] params,
> +            Object resultObject, int orderIndex) {
> +            return null;
> +        }
> +
> +        public boolean[] getAscending(StoreQuery q) {
> +            return EMPTY_BOOLEANS;
> +        }
> +
> +        public boolean isPacking(StoreQuery q) {
> +            return false;
> +        }
> +
> +        public String getAlias(StoreQuery q) {
> +            return null;
> +        }
> +
> +        public String[] getProjectionAliases(StoreQuery q) {
> +            return EMPTY_STRINGS;
> +        }
> +
> +        public Class[] getProjectionTypes(StoreQuery q) {
> +            return EMPTY_CLASSES;
> +        }
> +
> +        public ClassMetaData[] getAccessPathMetaDatas(StoreQuery q) {
> +            return EMPTY_METAS;
> +        }
> +
> +        public int getOperation(StoreQuery q) {
> +            return OP_SELECT;
> +        }
> +
> +        public boolean isAggregate(StoreQuery q) {
> +            return false;
> +        }
> +
> +        public boolean hasGrouping(StoreQuery q) {
> +            return false;
> +        }
> +
> +        public LinkedMap getParameterTypes(StoreQuery q) {
> +            return EMPTY_PARAMS;
> +        }
> +
> +        public Class getResultClass(StoreQuery q) {
> +            return null;
> +        }
> +
> +        public Map getUpdates(StoreQuery q) {
> +            return null;
> +        }
> +    }
> +}
>
> Modified:
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
> URL:
> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java?rev=660825&r1=660824&r2=660825&view=diff
>
> ==============================================================================
> ---
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
> (original)
> +++
> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/QueryImpl.java
> Tue May 27 23:08:41 2008
> @@ -1048,7 +1048,7 @@
>
>             int size = 0;
>             for (Iterator i = ((Collection) o).iterator(); i.hasNext();
> size++)
> -                updateInMemory(i.next(), params);
> +                updateInMemory(i.next(), params, q);
>             return Numbers.valueOf(size);
>         } catch (OpenJPAException ke) {
>             throw ke;
> @@ -1063,12 +1063,13 @@
>      * @param ob the persistent instance to change
>      * @param params the parameters passed to the query
>      */
> -    private void updateInMemory(Object ob, Object[] params) {
> +    private void updateInMemory(Object ob, Object[] params, StoreQuery q)
> {
>         for (Iterator it = getUpdates().entrySet().iterator();
>             it.hasNext();) {
>             Map.Entry e = (Map.Entry) it.next();
>             Path path = (Path) e.getKey();
>             FieldMetaData fmd = (FieldMetaData) path.last();
> +            OpenJPAStateManager sm = _broker.getStateManager(ob);
>
>             Object val;
>             Object value = e.getValue();
> @@ -1080,10 +1081,13 @@
>             } else if (value instanceof Constant) {
>                 val = ((Constant) value).getValue(params);
>             } else {
> -                throw new
> UserException(_loc.get("only-update-primitives"));
> +                try {
> +                    val = q.evaluate(value, ob, params, sm);
> +                } catch (UnsupportedException e1) {
> +                    throw new
> UserException(_loc.get("fail-to-get-update-value"));
> +                }
>             }
>
> -            OpenJPAStateManager sm = _broker.getStateManager(ob);
>             int i = fmd.getIndex();
>             PersistenceCapable into = ImplHelper.toPersistenceCapable(ob,
>                 _broker.getConfiguration());
>
>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message