You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openjpa.apache.org by Donald Woods <dw...@apache.org> on 2009/02/11 18:52:31 UTC

Re: svn commit: r743396 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-lib/src/main/java/org/apache/open...

In 
openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java

Is this right?
+    public void testJPAHintSetsFetchPlan() {
+        String jpaKey = "javax.persistence.query.timeout";
+        query.setHint(jpaKey, 5671);
+        assertEquals(5671, query.getFetchPlan().getLockTimeout());
+    }
+

Shouldn't it be fetching the QueryTimeout instead?
+    public void testJPAHintSetsFetchPlan() {
+        String jpaKey = "javax.persistence.query.timeout";
+        query.setHint(jpaKey, 5671);
+        assertEquals(5671, query.getFetchPlan().getQueryTimeout());
+    }
+


-Donald

ppoddar@apache.org wrote:
> Author: ppoddar
> Date: Wed Feb 11 16:57:26 2009
> New Revision: 743396
> 
> URL: http://svn.apache.org/viewvc?rev=743396&view=rev
> Log:
> OPENJPA-900: Consolidate Query Hint related processing. As a byproduct, support new JPA 2.0 getHins() and getSupportedHints().
> 
> Added:
>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>     openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
> Modified:
>     openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>     openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>     openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>     openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>     openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>     openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>     openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
> 
> Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java Wed Feb 11 16:57:26 2009
> @@ -18,11 +18,14 @@
>   */
>  package org.apache.openjpa.jdbc.conf;
>  
> +import java.util.Collections;
>  import java.util.Map;
> +import java.util.Set;
>  
>  import org.apache.openjpa.conf.BrokerFactoryValue;
>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>  import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>  import org.apache.openjpa.lib.conf.ConfigurationProvider;
>  
> @@ -48,4 +51,9 @@
>          }
>          return false;
>      }
> +    
> +    @Override
> +    public Set<String> getSupportedQueryHints() {
> +        return Collections.singleton(OracleDictionary.SELECT_HINT);
> +    }
>  }
> 
> Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java (original)
> +++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java Wed Feb 11 16:57:26 2009
> @@ -21,13 +21,18 @@
>  import java.lang.reflect.AccessibleObject;
>  import java.lang.reflect.Field;
>  import java.lang.reflect.InvocationTargetException;
> +import java.lang.reflect.Member;
>  import java.lang.reflect.Method;
>  import java.lang.reflect.Modifier;
>  import java.security.AccessController;
> +import java.util.Collections;
> +import java.util.Set;
> +import java.util.TreeSet;
>  
>  import org.apache.commons.lang.StringUtils;
>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>  import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.lib.util.Reflectable;
>  import org.apache.openjpa.util.GeneralException; 
>  import org.apache.openjpa.util.UserException; 
>  
> @@ -754,4 +759,166 @@
>      public static void set(Object target, Method setter, short value) {
>          set(target, setter, new Short(value));
>      }
> +    
> +    /**
> +     * Gets all bean-style property names of the given Class or its superclass.
> +     * A bean-style property 'abc' exists in Class C iff C has declared 
> +     * following pair of methods:
> +     *   public void setAbc(Y y) or public C setAbc(Y y)
> +     *   public Y getAbc();
> +     *   
> +     * If a getter property is annotated with {@link Reflectable}, then
> +     * it is ignored.
> +     *   
> +     */
> +    public static Set<String> getBeanStylePropertyNames(Class c) {
> +        if (c == null)
> +            return Collections.EMPTY_SET;
> +        Method[] methods = c.getMethods();
> +        if (methods == null || methods.length < 2)
> +            return Collections.EMPTY_SET;
> +        Set<String> result = new TreeSet<String>();
> +        for (Method m : methods) {
> +            if (m.getName().startsWith("get")) {
> +                if (!canReflect(m))
> +                    continue;
> +                String prop = StringUtils.capitalize(m.getName()
> +                    .substring("get".length()));
> +                Class rtype = m.getReturnType();
> +                try {
> +                  Method setter = c.getMethod("set"+prop, new Class[]{rtype});
> +                  if (setter.getReturnType() == void.class || 
> +                      setter.getReturnType().isAssignableFrom(c))
> +                  result.add(prop);
> +                } catch (NoSuchMethodException e) {
> +                    
> +                }
> +            }
> +        }
> +        return result;
> +    }
> +    
> +    /**
> +     * Gets all public field names of the given Class.
> +     *   
> +     */
> +    public static Set<String> getPublicFieldNames(Class c) {
> +        if (c == null)
> +            return Collections.EMPTY_SET;
> +        Field[] fields = c.getFields();
> +        if (fields == null || fields.length == 0)
> +            return Collections.EMPTY_SET;
> +        Set<String> result = new TreeSet<String>();
> +        for (Field f : fields) {
> +            if (canReflect(f))
> +                result.add(f.getName());
> +        }
> +        return result;
> +    }
> +    
> +    /**
> +     * Gets values of all field f the given class such that f exactly 
> +     * match the given modifiers and are of given type (Object implies any type)
> +     * unless f is annotated as {@link Reflectable}. 
> +     *   
> +     */
> +    public static <T> Set<T> getFieldValues(Class c, int mods, Class<T> t){
> +        if (c == null)
> +            return Collections.EMPTY_SET;
> +        Field[] fields = c.getFields();
> +        if (fields == null || fields.length == 0)
> +            return Collections.EMPTY_SET;
> +        Set<T> result = new TreeSet<T>();
> +        for (Field f : fields) {
> +            if (mods == f.getModifiers() 
> +            && (t == Object.class || t.isAssignableFrom(f.getType()))
> +            && canReflect(f)) {
> +                try {
> +                    result.add((T)f.get(null));
> +                } catch (IllegalArgumentException e) {
> +                } catch (IllegalAccessException e) {
> +                }
> +            }
> +        }
> +        return result;
> +    }
> +
> +    /**
> +     * Affirms if the given member is selected for reflection. The decision is
> +     * based on the following truth table on both the class-level and 
> +     * member-level annotation (null annotation represents MAYBE) 
> +     * 
> +     * Class   member  
> +     * MAYBE   MAYBE   YES
> +     * MAYBE   YES     YES
> +     * MAYBE   NO      NO
> +     *
> +     * YES     MAYBE   YES
> +     * YES     YES     YES
> +     * YES     NO      NO
> +     *
> +     * NO      YES     YES
> +     * NO      MAYBE   NO
> +     * NO      NO      NO 
> +     * 
> +    */   
> +    static boolean canReflect(Reflectable cls, Reflectable member) {
> +        if (cls == null || cls.value()) {
> +            return member == null || member.value() == true;
> +        } else {
> +            return member != null && member.value() == true;
> +        }
> +    }
> +    
> +    /**
> +     * Affirms if the original declaration the given field is annotated
> +     * for reflection. 
> +     */
> +    static boolean canReflect(Field field) {
> +        Class cls = field.getDeclaringClass();
> +        return canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
> +            field.getAnnotation(Reflectable.class));
> +    }
> +    
> +    /**
> +     * Affirms if the original declaration the given method is annotated
> +     * for reflection. 
> +     */
> +    static boolean canReflect(Method method) {
> +        Class cls = getDeclaringClass(method);
> +        if (cls != method.getDeclaringClass())
> +            method = getDeclaringMethod(cls, method);
> +        return canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
> +            method.getAnnotation(Reflectable.class));
> +    }
> +    
> +    /**
> +     * Gets the declaring class of the given method signature but also checks
> +     * if the method is declared in an interface. If yes, then returns the
> +     * interface. 
> +     */
> +    public static Class getDeclaringClass(Method m) {
> +        if (m == null)
> +            return null;
> +        Class cls = m.getDeclaringClass();
> +        Class[] intfs =  cls.getInterfaces();
> +        for (Class intf : intfs) {
> +            if (getDeclaringMethod(intf, m) != null)
> +                cls = intf;
> +        }
> +        return cls;
> +    }
> +    
> +    /**
> +     * Gets the method in the given class that has the same signature of the
> +     * given method, if exists. Otherwise, null.
> +     */
> +    public static Method getDeclaringMethod(Class c, Method m) {
> +        try {
> +            Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
> +            return m0;
> +        } catch (Exception e) {
> +            return null;
> +        }
> +    }    
>  }
> 
> Modified: openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java (original)
> +++ openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java Wed Feb 11 16:57:26 2009
> @@ -247,12 +247,23 @@
>          if (type.isAssignableFrom(o.getClass()))
>              return o;
>  
> -        // the only non-numeric conversions we do are to string, or from
> +        // the non-numeric conversions we do are to string, or from
>          // string/char to number, or calendar/date
> +        // String to Boolean
> +        // String to Integer
>          boolean num = o instanceof Number;
>          if (!num) {
>              if (type == String.class)
>                  return o.toString();
> +            else if (type == Boolean.class && o instanceof String) 
> +                return Boolean.valueOf(o.toString());
> +            else if (type == Integer.class && o instanceof String)
> +                try {
> +                    return new Integer(o.toString());
> +                } catch (NumberFormatException e) {
> +                    throw new ClassCastException(_loc.get("cant-convert", o,
> +                        o.getClass(), type).getMessage());
> +                }
>              else if (type == Character.class) {
>                  String str = o.toString();
>                  if (str != null && str.length() == 1)
> 
> Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java Wed Feb 11 16:57:26 2009
> @@ -19,7 +19,9 @@
>  package org.apache.openjpa.lib.conf;
>  
>  import java.io.File;
> +import java.util.Collections;
>  import java.util.List;
> +import java.util.Set;
>  
>  /**
>   * Abstract no-op product derivation for easy extension.
> @@ -85,4 +87,8 @@
>      
>      public void beforeConfigurationClose(Configuration conf) {
>      }
> +        
> +    public Set<String> getSupportedQueryHints() {
> +        return Collections.EMPTY_SET;
> +    }
>  }
> 
> Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java Wed Feb 11 16:57:26 2009
> @@ -21,6 +21,7 @@
>  import java.io.File;
>  import java.io.IOException;
>  import java.util.List;
> +import java.util.Set;
>  
>  /**
>   * Hooks for deriving products with additional functionality.
> @@ -157,4 +158,13 @@
>       * @since 0.9.7
>       */
>      public void beforeConfigurationClose(Configuration conf);
> +    
> +       
> +    /**
> +     * Return set of Query hint keys recognized by this receiver. 
> +     * 
> +     * @since 2.0.0
> +     */
> +    public Set<String> getSupportedQueryHints();
> +
>  }
> 
> Modified: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java (original)
> +++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java Wed Feb 11 16:57:26 2009
> @@ -29,6 +29,8 @@
>  import java.util.List;
>  import java.util.Map;
>  import java.util.MissingResourceException;
> +import java.util.Set;
> +import java.util.TreeSet;
>  
>  import org.apache.commons.lang.StringUtils;
>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
> @@ -434,6 +436,19 @@
>                  collection.add(fqLoc);
>          }
>      }
> +    
> +    
> +    public static Set<String> getSupportedQueryHints() {
> +        Set<String> result = new TreeSet<String>();
> +        // most specific to least
> +        for (int i = _derivations.length - 1; i >= 0; i--) {
> +            Set<String> members = _derivations[i].getSupportedQueryHints();
> +            if (members != null || !members.isEmpty())
> +                result.addAll(members);
> +        }
> +        return result;
> +    }
> +
>  
>      /**
>       * Compare {@link ProductDerivation}s.
> 
> Added: openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java (added)
> +++ openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java Wed Feb 11 16:57:26 2009
> @@ -0,0 +1,25 @@
> +package org.apache.openjpa.lib.util;
> +
> +import static java.lang.annotation.ElementType.FIELD;
> +import static java.lang.annotation.ElementType.METHOD;
> +import static java.lang.annotation.ElementType.TYPE;
> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
> +
> +import java.lang.annotation.Retention;
> +import java.lang.annotation.Target;
> +
> +/**
> + * Annotates a getter method or field so {@link Reflection reflection 
> + * utility} to control whether the annotated member is recorded during scanning 
> + * for bean-style method or field.
> + * 
> + * @author Pinaki Poddar
> + * 
> + * @since 2.0.0
> + *
> + */
> +@Target({TYPE, METHOD, FIELD})
> +@Retention(RUNTIME)
> +public @interface Reflectable {
> +    boolean value() default true;
> +}
> 
> Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java (added)
> +++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java Wed Feb 11 16:57:26 2009
> @@ -0,0 +1,167 @@
> +/*
> + * 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.conf;
> +
> +import javax.persistence.EntityManager;
> +import javax.persistence.Query;
> +
> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
> +import org.apache.openjpa.kernel.QueryHints;
> +import org.apache.openjpa.persistence.HintHandler;
> +import org.apache.openjpa.persistence.OpenJPAPersistence;
> +import org.apache.openjpa.persistence.OpenJPAQuery;
> +import org.apache.openjpa.persistence.test.SingleEMFTestCase;
> +
> +/**
> + * Tests JPA 2.0 API methods {@link Query#getSupportedHints()} and 
> + * {@link Query#getHints()}. 
> + * 
> + * @author Pinaki Poddar
> + *
> + */
> +public class TestQueryHints extends SingleEMFTestCase {
> +    EntityManager em;
> +    OpenJPAQuery query;
> +    
> +    public void setUp() {
> +       super.setUp((Object[])null);
> +       em = emf.createEntityManager();
> +       String sql = "select * from Person";
> +       query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
> +    }
> +    
> +    public void testSupportedHintsContainProductDerivationHints() {
> +        assertSupportedHint(OracleDictionary.SELECT_HINT, true);
> +    }
> +    
> +    public void testSupportedHintsContainFetchPlanHints() {
> +        assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
> +    }
> +
> +    public void testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
> +        assertSupportedHint("openjpa.FetchPlan.QueryResultCache", false);
> +    }
> +    
> +    public void testSupportedHintsContainQueryProperty() {
> +        assertSupportedHint("openjpa.Subclasses", true);
> +    }
> +    
> +    public void testSupportedHintsContainKernelQueryHints() {
> +        assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, true);
> +    }
> +    
> +    public void testSupportedHintsContainJPAQueryHints() {
> +        assertSupportedHint("javax.persistence.query.timeout", true);
> +    }
> +    
> +    public void testUnrecognizedKeyIsIgnored() {
> +        String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
> +        query.setHint(unrecognizedKey, "xyz");
> +        assertFalse(query.getHints().containsKey(unrecognizedKey));
> +        assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
> +     }
> +    
> +    public void testRecognizedKeyIsNotRecordedButAvailable() {
> +        String recognizedKey = "openjpa.some.derivation.hint";
> +        query.setHint(recognizedKey, "abc");
> +        assertFalse(query.getHints().containsKey(recognizedKey));
> +        assertEquals("abc", query.getFetchPlan().getDelegate().getHint(recognizedKey));
> +    }
> +
> +    public void testSupportedKeyIsRecordedAndAvailable() {
> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
> +        query.setHint(supportedKey, 42);
> +        assertTrue(query.getHints().containsKey(supportedKey));
> +        assertEquals(42, query.getFetchPlan().getFetchBatchSize());
> +    }
> +    
> +    public void testSupportedKeyWrongValue() {
> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
> +        short goodValue = (short)42;
> +        float badValue = 57.9f;
> +        query.setHint(supportedKey, goodValue);
> +        assertTrue(query.getHints().containsKey(supportedKey));
> +        assertEquals(goodValue, query.getFetchPlan().getFetchBatchSize());
> +        
> +        try {
> +            query.setHint(supportedKey, badValue);
> +            fail("Expected to fail to set " + supportedKey + " hint to " + badValue);
> +        } catch (IllegalArgumentException e) {
> +            // good
> +        }
> +    }
> +    
> +    public void testSupportedKeyIntegerValueConversion() {
> +        String supportedKey = "openjpa.hint.OptimizeResultCount";
> +        String goodValue = "57";
> +        int badValue = -3;
> +        query.setHint(supportedKey, goodValue);
> +        assertTrue(query.getHints().containsKey(supportedKey));
> +        assertEquals(57, query.getFetchPlan().getDelegate().getHint(supportedKey));
> +        
> +        try {
> +            query.setHint(supportedKey, badValue);
> +            fail("Expected to fail to set " + supportedKey + " hint to " + badValue);
> +        } catch (IllegalArgumentException e) {
> +            // good
> +        }
> +    }
> +
> +    public void testSupportedKeyBooleanValueConversion() {
> +        String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
> +        String goodValue = "true";
> +        query.setHint(supportedKey, goodValue);
> +        assertTrue(query.getHints().containsKey(supportedKey));
> +        assertEquals(true, query.getFetchPlan().getDelegate().getHint(supportedKey));
> +        
> +        goodValue = "false";
> +        query.setHint(supportedKey, goodValue);
> +        assertTrue(query.getHints().containsKey(supportedKey));
> +        assertEquals(false, query.getFetchPlan().getDelegate().getHint(supportedKey));
> +    }
> +    
> +    public void testJPAHintSetsFetchPlan() {
> +        String jpaKey = "javax.persistence.query.timeout";
> +        query.setHint(jpaKey, 5671);
> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
> +    }
> +    
> +    public void testParts() {
> +        HintHandler.HintKeyComparator test = new HintHandler.HintKeyComparator();
> +        assertEquals(0, test.countDots("a"));
> +        assertEquals(1, test.countDots("a.b"));
> +        assertEquals(2, test.countDots("a.b.c"));
> +        assertEquals(3, test.countDots("a.b.c.d"));
> +        assertEquals(4, test.countDots("a.b.c.d."));
> +        assertEquals(1, test.countDots("."));
> +        assertEquals(0, test.countDots(""));
> +        assertEquals(0, test.countDots(null));
> +    }
> +    
> +    void assertSupportedHint(String hint, boolean contains) {
> +        if (contains)
> +            assertTrue("Expeceted suppoerted hint [" + hint + "]",
> +                query.getSupportedHints().contains(hint));
> +        else
> +            assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
> +                query.getSupportedHints().contains(hint));
> +    }
> +    
> +    
> +}
> 
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java Wed Feb 11 16:57:26 2009
> @@ -22,6 +22,7 @@
>  import javax.persistence.LockModeType;
>  
>  import org.apache.openjpa.kernel.FetchConfiguration;
> +import org.apache.openjpa.lib.util.Reflectable;
>  import org.apache.openjpa.meta.FetchGroup;
>  
>  /**
> @@ -88,6 +89,7 @@
>       *
>       * @since 1.0.0
>       */
> +    @Reflectable(false)
>      public boolean getQueryResultCacheEnabled();
>  
>      /**
> @@ -102,6 +104,7 @@
>      /**
>       * @deprecated use {@link #getQueryResultCacheEnabled()} instead.
>       */
> +    @Reflectable(false)
>      public boolean getQueryResultCache();
>  
>      /**
> @@ -293,5 +296,6 @@
>       * @deprecated cast to {@link FetchPlanImpl} instead. This
>       * method pierces the published-API boundary, as does the SPI cast.
>       */
> +    @Reflectable(false)
>      public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
>  }
> 
> Added: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java (added)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java Wed Feb 11 16:57:26 2009
> @@ -0,0 +1,365 @@
> +package org.apache.openjpa.persistence;
> +
> +import static org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
> +import static org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
> +import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
> +
> +import java.lang.reflect.Method;
> +import java.lang.reflect.Modifier;
> +import java.util.Collections;
> +import java.util.Comparator;
> +import java.util.Map;
> +import java.util.Set;
> +import java.util.TreeMap;
> +import java.util.TreeSet;
> +
> +import org.apache.openjpa.conf.OpenJPAConfiguration;
> +import org.apache.openjpa.enhance.Reflection;
> +import org.apache.openjpa.kernel.FetchConfiguration;
> +import org.apache.openjpa.kernel.Filters;
> +import org.apache.openjpa.kernel.exps.AggregateListener;
> +import org.apache.openjpa.kernel.exps.FilterListener;
> +import org.apache.openjpa.lib.conf.ProductDerivation;
> +import org.apache.openjpa.lib.conf.ProductDerivations;
> +import org.apache.openjpa.lib.log.Log;
> +import org.apache.openjpa.lib.util.Localizer;
> +import org.apache.openjpa.lib.util.StringDistance;
> +
> +
> +/**
> + * Manages query hint keys and handles their values on behalf of a owning
> + * {@link QueryImpl}. Uses specific knowledge of hint keys declared in
> + * different parts of the system.
> + * 
> + * This receiver collects hint keys from different parts of the system. The
> + * keys are implicitly or explicitly declared by several different mechanics.
> + * This receiver sets the values on behalf of a owning {@link QueryImpl}
> + * based on the its specific knowledge of these keys.
> + * 
> + * The hint keys from following sources are collected and handled: 
> + * 
> + * 1. {@link org.apache.openjpa.kernel.QueryHints} interface declares hint keys
> + *    as public static final fields. These fields are collected by reflection.
> + *    The values are handled by invoking methods on the owning {@link QueryImpl}
> + *    
> + * 2. Some hint keys are collected from bean-style property names of {@link 
> + *    JDBCFetchPlan} by {@link Reflection#getBeanStylePropertyNames(Class) 
> + *    reflection} and prefixed with <code>openjpa.FetchPlan</code>. 
> + *    Their values are used to set the corresponding property of {@link 
> + *    FetchPlan} via {@link #hintToSetter(FetchPlan, String, Object) reflection}
> + *      
> + * 3. Currently defined <code>javax.persistence.*</code> hint keys have  
> + *    a equivalent counterpart to one of these FetchPlan keys. 
> + *    The JPA keys are mapped to equivalent FetchPlan hint keys.
> + *    
> + * 4. Some keys directly invoke setters or add listeners to the owning 
> + *    {@link QueryImpl}. These hint keys are statically declared in 
> + *    this receiver itself. 
> + *    
> + * 5. ProductDerivation may introduce their own query hint keys via {@link 
> + *    ProductDerivation#getSupportedQueryHints()}. Their values are set in the 
> + *    {@link FetchConfiguration#setHint(String, Object)}
> + *     
> + *  A hint key is classified into one of the following three categories:
> + *  
> + *  1. Supported: A key is known to this receiver as collected from different 
> + *     parts of the system. The value of a supported key is recorded and 
> + *     available via {@link #getHints()} method. 
> + *  2. Recognized: A key is not known to this receiver but has a prefix which
> + *     is known to this receiver. The value of a recognized key is not recorded 
> + *     but its value is available via {@link FetchConfiguration#getHint(String)}
> + *  3. Unrecognized: A key is neither supported nor recognized. The value of a 
> + *     unrecognized key is neither recorded nor set anywhere.
> + *  
> + *  If an incompatible value is supplied for a supported key, a non-fatal
> + *  {@link ArgumentException} is raised.
> + *  
> + * @author Pinaki Poddar
> + *
> + * @since 2.0.0
> + * 
> + * @nojavadoc
> + */
> +public class HintHandler {
> +    private final QueryImpl owner;
> +    private Map<String, Object> _hints;
> +    private Set<String> _supportedKeys;
> +    private Set<String> _supportedPrefixes;
> +    
> +    static final String PREFIX_JPA = "javax.persistence.";
> +    static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
> +    
> +    // These keys are directly handled in {@link QueryImpl} class.
> +    // Declaring a public static final String variable in this class will 
> +    // make it register as a supported hint key
> +    // if you do not want that then annotate as {@link Reflectable(false)}.
> +    public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
> +    public static final String HINT_FILTER_LISTENER = "openjpa.FilterListener";
> +    public static final String HINT_FILTER_LISTENERS = 
> +        "openjpa.FilterListeners";
> +    public static final String HINT_AGGREGATE_LISTENER = 
> +        "openjpa.AggregateListener";
> +    public static final String HINT_AGGREGATE_LISTENERS = 
> +        "openjpa.AggregateListeners";
> +    
> +    // JPA Specification 2.0 keys are mapped to equivalent FetchPlan keys
> +    public static Map<String,String> _jpaKeys = new TreeMap<String, String>();
> +    static {
> +        _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"), 
> +            addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
> +    }
> +    
> +    private static final String DOT = ".";
> +    private static final String BLANK = "";
> +    private static final Localizer _loc = Localizer.forPackage(
> +        HintHandler.class);
> +    
> +    public HintHandler(QueryImpl impl) {
> +        owner = impl;
> +    }
> +    
> +    /**
> +     * Gets all the recorded hint keys and their values.
> +     */
> +    @SuppressWarnings("unchecked")
> +    public Map<String, Object> getHints() {
> +        return _hints == null ? Collections.EMPTY_MAP 
> +            : Collections.unmodifiableMap(_hints);
> +    }
> +    
> +    /**
> +     * Record a key-value pair only only if the given key is supported.
> +     * 
> +     * @return FALSE if the key is unrecognized. 
> +     *         null (i.e. MAY BE) if the key is recognized, but not supported.
> +     *         TRUE if the key is supported.
> +     */
> +    public Boolean record(String hint, Object value) {
> +        if (hint == null)
> +            return Boolean.FALSE;
> +        if (isSupported(hint)) {
> +            if (_hints == null)
> +                _hints = new TreeMap<String, Object>();
> +            _hints.put(hint, value);
> +            return Boolean.TRUE;
> +        }
> +        
> +        Log log = owner.getDelegate().getBroker().getConfiguration()
> +            .getLog(OpenJPAConfiguration.LOG_RUNTIME);
> +        String possible = StringDistance.getClosestLevenshteinDistance(hint, 
> +            getSupportedHints());
> +        if (log.isWarnEnabled()) {
> +            log.warn(_loc.get("bad-query-hint", hint, possible));
> +        }
> +        return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
> +    }
> +    
> +    /**
> +     * Gets all the supported hint keys. The set of supported hint keys is
> +     * statically determined by collecting hint keys from the ProductDerivations
> +     * and reflecting upon some of the known classes.
> +     */
> +    public Set<String> getSupportedHints() {
> +        if (_supportedKeys == null) {
> +            _supportedKeys = new TreeSet<String>(new HintKeyComparator());
> +            _supportedPrefixes = new TreeSet<String>();
> +            
> +            _supportedKeys.addAll(Reflection.getFieldValues(
> +                org.apache.openjpa.kernel.QueryHints.class, 
> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
> +                String.class));
> +
> +            _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN, 
> +                Reflection.getBeanStylePropertyNames(
> +                    owner.getFetchPlan().getClass())));
> +
> +            _supportedKeys.addAll(_jpaKeys.keySet());
> +
> +            _supportedKeys.addAll(Reflection.getFieldValues(
> +                HintHandler.class, 
> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
> +                String.class));
> +
> +            _supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());    
> +            
> +            for (String key : _supportedKeys) {
> +                _supportedPrefixes.add(getPrefixOf(key));
> +            }
> +        }
> +        return _supportedKeys;
> +    }
> +    
> +    /**
> +     * Add a hint key to the set of supported hint keys.
> +     */
> +    public void addHintKey(String key) {
> +        getSupportedHints().add(key);
> +        _supportedPrefixes.add(getPrefixOf(key));
> +    }
> +    
> +    public Set<String> getKnownPrefixes() {
> +        getSupportedHints();
> +        return _supportedPrefixes;
> +    }
> +    
> +    /**
> +     * Affirms the given key matches one of the supported keys.
> +     */
> +    public boolean isSupported(String key) {
> +        return getSupportedHints().contains(key);
> +    }
> +    
> +    /**
> +     * Affirms the given key has a prefix that matches with any of the 
> +     * supported prefixes.
> +     */
> +    public boolean isSupportedPrefix(String key) {
> +        return getKnownPrefixes().contains(getPrefixOf(key));
> +    }
> +    
> +    static Set<String> addPrefix(String prefix, Set<String> original) {
> +        Set<String> result = new TreeSet<String>();
> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
> +        for (String o : original)
> +            result.add(prefix + join + o);
> +        return result;
> +    }
> +    
> +    static String addPrefix(String prefix, String original) {
> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
> +        return prefix + join + original;
> +    }
> +    
> +    public static String removePrefix(String key, String prefix) {
> +        if (prefix == null)
> +            return key;
> +        if (!prefix.endsWith(DOT))
> +            prefix = prefix + DOT;
> +        if (key != null && key.startsWith(prefix))
> +            return key.substring(prefix.length());
> +        return key;
> +    }
> +    
> +    static String getPrefixOf(String key) {
> +        int index = key == null ? -1 : key.indexOf(DOT);
> +        return (index != -1) ? key.substring(0,index) : key;
> +    }
> +    
> +    boolean isKnownHintPrefix(String key) {
> +        String prefix = getPrefixOf(key);
> +        return getKnownPrefixes().contains(prefix);
> +    }
> +    
> +    public static boolean hasPrefix(String key, String prefix) {
> +        if (key == null || prefix == null)
> +            return false;
> +        if (!prefix.endsWith(DOT))
> +            prefix = prefix + DOT;
> +        return key.startsWith(prefix);
> +    }
> +    
> +    public void setHint(String key, Object value) {
> +        Boolean record = record(key, value);
> +        FetchConfiguration fetch = owner.getDelegate().getFetchConfiguration();
> +        ClassLoader loader = owner.getDelegate().getBroker().getClassLoader();
> +        if (record == Boolean.FALSE)
> +            return;
> +        if (record == null) {
> +            fetch.setHint(key, value);
> +            return;
> +        }
> +        try {
> +            if (HINT_SUBCLASSES.equals(key)) {
> +                if (value instanceof String)
> +                    value = Boolean.valueOf((String) value);
> +                owner.setSubclasses(((Boolean) value).booleanValue());
> +            } else if (HINT_FILTER_LISTENER.equals(key))
> +                owner.addFilterListener(Filters.hintToFilterListener(value, 
> +                    loader));
> +            else if (HINT_FILTER_LISTENERS.equals(key)) {
> +                FilterListener[] arr = Filters.hintToFilterListeners(value, 
> +                    loader);
> +                for (int i = 0; i < arr.length; i++)
> +                    owner.addFilterListener(arr[i]);
> +            } else if (HINT_AGGREGATE_LISTENER.equals(key))
> +                owner.addAggregateListener(Filters.hintToAggregateListener(
> +                    value, loader));
> +            else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
> +                AggregateListener[] arr = Filters.hintToAggregateListeners(
> +                        value, loader);
> +                for (int i = 0; i < arr.length; i++)
> +                    owner.addAggregateListener(arr[i]);
> +            } else if (isFetchPlanHint(key)) {
> +                hintToSetter(owner.getFetchPlan(), 
> +                    getFetchPlanProperty(key), value);
> +            } else if (HINT_RESULT_COUNT.equals(key)) {
> +                int v = (Integer)Filters.convert(value, Integer.class);
> +                if (v < 0)
> +                    throw new ArgumentException(_loc.get("bad-query-hint-value", 
> +                        key, value), null,  null, false);
> +                    fetch.setHint(key, v);
> +            }  else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
> +                fetch.setHint(key, Filters.convert(value, Boolean.class));
> +                owner.invalidatePreparedQuery();
> +            } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
> +                fetch.setHint(key, Filters.convert(value, Boolean.class));
> +                owner.ignorePreparedQuery();
> +            } else { // default 
> +                fetch.setHint(key, value);
> +            }
> +            return;
> +        } catch (IllegalArgumentException iae) {
> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
> +                key, value), null,  null, false);
> +        } catch (ClassCastException ce) {
> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
> +                key, ce.getMessage()), null,  null, false);
> +        } catch (Exception e) {
> +            throw PersistenceExceptions.toPersistenceException(e);
> +        }
> +    }
> +    
> +    private boolean isFetchPlanHint(String key) {
> +        return key.startsWith(PREFIX_FETCHPLAN) 
> +           || (_jpaKeys.containsKey(key) && isFetchPlanHint(_jpaKeys.get(key)));
> +    }
> +    
> +    private String getFetchPlanProperty(String key) {
> +        if (key.startsWith(PREFIX_FETCHPLAN))
> +            return removePrefix(key, PREFIX_FETCHPLAN);
> +        else if (_jpaKeys.containsKey(key))
> +            return getFetchPlanProperty(_jpaKeys.get(key));
> +        else
> +            return key;
> +    }
> +    
> +    private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
> +        if (fetchPlan == null || k == null)
> +            return;
> +
> +        Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
> +        Class paramType = setter.getParameterTypes()[0];
> +        if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
> +            value = Enum.valueOf(paramType, (String) value);
> +
> +        Filters.hintToSetter(fetchPlan, k, value);
> +    }
> +    
> +    public static class HintKeyComparator implements Comparator<String> {
> +        public int compare(String s1, String s2) {
> +            if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
> +                int n1 = countDots(s1);
> +                int n2 = countDots(s2);
> +                return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
> +            } else
> +                return s1.compareTo(s2);
> +        }
> +        
> +        public int countDots(String s) {
> +            if (s == null || s.length() == 0)
> +                return 0;
> +            int index = s.indexOf(DOT);
> +            return (index == -1) ? 0 : countDots(s.substring(index+1)) + 1;
> +        }
> +    }
> +}
> 
> Modified: openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java Wed Feb 11 16:57:26 2009
> @@ -18,8 +18,9 @@
>   */
>  package org.apache.openjpa.persistence;
>  
> +import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
> +
>  import java.io.Serializable;
> -import java.lang.reflect.Method;
>  import java.sql.Time;
>  import java.sql.Timestamp;
>  import java.util.ArrayList;
> @@ -41,15 +42,12 @@
>  import javax.persistence.TemporalType;
>  
>  import org.apache.openjpa.conf.OpenJPAConfiguration;
> -import org.apache.openjpa.enhance.Reflection;
>  import org.apache.openjpa.kernel.Broker;
>  import org.apache.openjpa.kernel.DelegatingQuery;
>  import org.apache.openjpa.kernel.DelegatingResultList;
>  import org.apache.openjpa.kernel.FetchConfiguration;
> -import org.apache.openjpa.kernel.Filters;
>  import org.apache.openjpa.kernel.PreparedQuery;
>  import org.apache.openjpa.kernel.PreparedQueryCache;
> -import org.apache.openjpa.kernel.QueryHints;
>  import org.apache.openjpa.kernel.QueryLanguages;
>  import org.apache.openjpa.kernel.QueryOperations;
>  import org.apache.openjpa.kernel.QueryStatistics;
> @@ -63,8 +61,6 @@
>  import org.apache.openjpa.util.RuntimeExceptionTranslator;
>  import org.apache.openjpa.util.UserException;
>  
> -import static org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
> -
>  /**
>   * Implementation of {@link Query} interface.
>   * 
> @@ -85,6 +81,8 @@
>  	private Map<String, Object> _named;
>  	private Map<Integer, Object> _positional;
>  	private String _id;
> +	
> +	private final HintHandler _hintHandler;
>  
>  	/**
>  	 * Constructor; supply factory exception translator and delegate.
> @@ -97,6 +95,7 @@
>  			org.apache.openjpa.kernel.Query query) {
>  		_em = em;
>  		_query = new DelegatingQuery(query, ret);
> +		_hintHandler = new HintHandler(this);
>  	}
>  
>  	/**
> @@ -254,7 +253,7 @@
>  	}
>  	
>  	private Object execute() {
> -		if (_query.getOperation() != QueryOperations.OP_SELECT)
> +		if (!isNative() && _query.getOperation() != QueryOperations.OP_SELECT)
>  			throw new InvalidStateException(_loc.get("not-select-query", _query
>  					.getQueryString()), null, null, false);
>  		
> @@ -351,81 +350,8 @@
>  
>  	public OpenJPAQuery setHint(String key, Object value) {
>  		_em.assertNotCloseInvoked();
> -		if (key == null)
> -			return this;
> -		if (!key.startsWith("openjpa.")) {
> -			_query.getFetchConfiguration().setHint(key, value);
> -			return this;
> -		}
> -		String k = key.substring("openjpa.".length());
> -
> -		try {
> -			if ("Subclasses".equals(k)) {
> -				if (value instanceof String)
> -					value = Boolean.valueOf((String) value);
> -				setSubclasses(((Boolean) value).booleanValue());
> -			} else if ("FilterListener".equals(k))
> -				addFilterListener(Filters.hintToFilterListener(value, _query
> -						.getBroker().getClassLoader()));
> -			else if ("FilterListeners".equals(k)) {
> -				FilterListener[] arr = Filters.hintToFilterListeners(value,
> -						_query.getBroker().getClassLoader());
> -				for (int i = 0; i < arr.length; i++)
> -					addFilterListener(arr[i]);
> -			} else if ("AggregateListener".equals(k))
> -				addAggregateListener(Filters.hintToAggregateListener(value,
> -						_query.getBroker().getClassLoader()));
> -			else if ("FilterListeners".equals(k)) {
> -				AggregateListener[] arr = Filters.hintToAggregateListeners(
> -						value, _query.getBroker().getClassLoader());
> -				for (int i = 0; i < arr.length; i++)
> -					addAggregateListener(arr[i]);
> -			} else if (k.startsWith("FetchPlan.")) {
> -				k = k.substring("FetchPlan.".length());
> -				hintToSetter(getFetchPlan(), k, value);
> -			} else if (k.startsWith("hint.")) {
> -				if ("hint.OptimizeResultCount".equals(k)) {
> -					if (value instanceof String) {
> -						try {
> -							value = new Integer((String) value);
> -						} catch (NumberFormatException nfe) {
> -						}
> -					}
> -					if (!(value instanceof Number)
> -							|| ((Number) value).intValue() < 0)
> -						throw new ArgumentException(_loc.get(
> -								"bad-query-hint-value", key, value), null,
> -								null, false);
> -                    _query.getFetchConfiguration().setHint(key, value);
> -				}  else if (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
> -                    (key)) {
> -                    _query.getFetchConfiguration().setHint(key, (Boolean)value);
> -                    invalidatePreparedQuery();
> -                } else if (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
> -                    _query.getFetchConfiguration().setHint(key, (Boolean)value);
> -                    ignorePreparedQuery();
> -                } else {
> -                    _query.getFetchConfiguration().setHint(key, value);
> -                }
> -            } else
> -				throw new ArgumentException(_loc.get("bad-query-hint", key),
> -						null, null, false);
> -			return this;
> -		} catch (Exception e) {
> -			throw PersistenceExceptions.toPersistenceException(e);
> -		}
> -	}
> -
> -	private void hintToSetter(FetchPlan fetchPlan, String k, Object value) {
> -		if (fetchPlan == null || k == null)
> -			return;
> -
> -		Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
> -		Class paramType = setter.getParameterTypes()[0];
> -		if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
> -			value = Enum.valueOf(paramType, (String) value);
> -
> -		Filters.hintToSetter(fetchPlan, k, value);
> +		_hintHandler.setHint(key, value);
> +		return this;
>  	}
>  
>  	public OpenJPAQuery setParameter(int position, Calendar value,
> @@ -613,7 +539,7 @@
>  	 */
>      //TODO: JPA 2.0 Hints that are not set to FetchConfiguration 
>      public Map<String, Object> getHints() {
> -        return _query.getFetchConfiguration().getHints();
> +        return _hintHandler.getHints();
>      }
>  
>      public LockModeType getLockMode() {
> @@ -622,8 +548,7 @@
>      }
>  
>      public Set<String> getSupportedHints() {
> -        throw new UnsupportedOperationException(
> -            "JPA 2.0 - Method not yet implemented");
> +        return _hintHandler.getSupportedHints();
>      }
>  
>      public Query setLockMode(LockModeType lockMode) {
> @@ -704,7 +629,7 @@
>      /**
>       * Remove this query from PreparedQueryCache. 
>       */
> -    private boolean invalidatePreparedQuery() {
> +    boolean invalidatePreparedQuery() {
>          PreparedQueryCache cache = _em.getPreparedQueryCache();
>          if (cache == null)
>              return false;
> @@ -716,7 +641,7 @@
>       * Ignores this query from PreparedQueryCache by recreating the original
>       * query if it has been cached. 
>       */
> -    private void ignorePreparedQuery() {
> +    void ignorePreparedQuery() {
>          PreparedQuery cached = _em.getPreparedQuery(_id);
>          if (cached == null)
>              return;
> 
> Modified: openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties (original)
> +++ openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties Wed Feb 11 16:57:26 2009
> @@ -70,7 +70,7 @@
>  mult-results: Query returned multiple results: "{0}".
>  no-pos-named-params-mix: Cannot mix named and positional parameters in query \
>  	"{0}".
> -bad-query-hint: "{0}" is not a recognized query hint.
> +bad-query-hint: "{0}" is not a supported query hint. May be you meant "{1}"?
>  bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
>  detached: Cannot perform this operation on detached entity "{0}".
>  removed: Cannot perform this operation on removed entity "{0}".
> 
> Modified: openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
> URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
> ==============================================================================
> --- openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java (original)
> +++ openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java Wed Feb 11 16:57:26 2009
> @@ -18,7 +18,9 @@
>   */
>  package org.apache.openjpa.slice;
>  
> +import java.util.Collections;
>  import java.util.Map;
> +import java.util.Set;
>  
>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
> @@ -98,4 +100,8 @@
>          	log.warn(_loc.get("forced-set-config", 
>          		prefix+"."+v.getProperty(), forced));
>      }
> +    
> +    public Set<String> getSupportedQueryHints() {
> +        return Collections.singleton(HINT_TARGET);
> +    }
>  }
> 
> 
> 

Re: svn commit: r743396 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-lib/src/main/java/org/apache/open...

Posted by Pinaki Poddar <pp...@apache.org>.
> Also, SelectImpl is using it, even though I question the behavior -
The point of distinction between a lock timeout and query timeout is valid.
But that discussion requires a different scope as well as change in the way
current implementation interprets these values. This current issue/commit
does not change existing usage/interpretation/implementation of hint values,
it retains the same behavior but consolidates collection of different query
hints from different parts of the system to present a single point of entry
(or failure:).

> Isn't it being stored in FetchConfigurationImpl.ConfigurationState.Hints?
The FetchConfigurationImpl.ConfigurationState.Hints holds a generic
name-value map and hence *can* hold the query timeout value as well. But
then the internal usage needs to be modified as well.

> How does a FetchPlan relate to the Delegating*Statement classes?
As a carrier of execution context information - some of the context
parameters (e.g. getLockTimeout()) appear as concrete APIs while a generic
map (FetchConfigurationImpl.ConfigurationState.Hints) keeps other
possibilities open.


-- 
View this message in context: http://n2.nabble.com/Re%3A-svn-commit%3A-r743396---in--openjpa-trunk%3A-openjpa-jdbc-src-main-java-org-apache-openjpa-jdbc-conf--openjpa-kernel-src-main-java-org-apache-openjpa-enhance--openjpa-kernel-src-main-java-org-ap-tp2310190p2311475.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.


Re: svn commit: r743396 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-lib/src/main/java/org/apache/open...

Posted by Donald Woods <dw...@apache.org>.
Isn't it being stored in FetchConfigurationImpl.ConfigurationState.Hints?

Also, SelectImpl is using it, even though I question the behavior -
     // if this is a locking select and the lock timeout is greater than
     // the configured query timeout, use the lock timeout
     if (forUpdate && _dict.supportsQueryTimeout && fetch != null
         && fetch.getLockTimeout() > stmnt.getQueryTimeout() * 1000) {

How does a FetchPlan relate to the Delegating*Statement classes?


-Donald


Pinaki Poddar wrote:
> Yes, ideally. But query fetch plan's lock timeout is currently interpreted as
> JDBC statement's query timeout during select operation.
> FetchPlan/FetchConfiguration currently is not holding two separate timeout
> values for locking and selection operation.
> May be OPENJPA-878 should consider such a change.
> 
> 
> Donald Woods wrote:
>> In 
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>
>> Is this right?
>> +    public void testJPAHintSetsFetchPlan() {
>> +        String jpaKey = "javax.persistence.query.timeout";
>> +        query.setHint(jpaKey, 5671);
>> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
>> +    }
>> +
>>
>> Shouldn't it be fetching the QueryTimeout instead?
>> +    public void testJPAHintSetsFetchPlan() {
>> +        String jpaKey = "javax.persistence.query.timeout";
>> +        query.setHint(jpaKey, 5671);
>> +        assertEquals(5671, query.getFetchPlan().getQueryTimeout());
>> +    }
>> +
>>
>>
>> -Donald
>>
>> ppoddar@apache.org wrote:
>>> Author: ppoddar
>>> Date: Wed Feb 11 16:57:26 2009
>>> New Revision: 743396
>>>
>>> URL: http://svn.apache.org/viewvc?rev=743396&view=rev
>>> Log:
>>> OPENJPA-900: Consolidate Query Hint related processing. As a byproduct,
>>> support new JPA 2.0 getHins() and getSupportedHints().
>>>
>>> Added:
>>>    
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>>>    
>>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>>    
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>>> Modified:
>>>    
>>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>>>    
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>>>    
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>>>    
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>>>    
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>>>    
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>>>    
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>>>    
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>>>    
>>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>>>    
>>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -18,11 +18,14 @@
>>>   */
>>>  package org.apache.openjpa.jdbc.conf;
>>>  
>>> +import java.util.Collections;
>>>  import java.util.Map;
>>> +import java.util.Set;
>>>  
>>>  import org.apache.openjpa.conf.BrokerFactoryValue;
>>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>>  import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
>>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>>>  import org.apache.openjpa.lib.conf.ConfigurationProvider;
>>>  
>>> @@ -48,4 +51,9 @@
>>>          }
>>>          return false;
>>>      }
>>> +    
>>> +    @Override
>>> +    public Set<String> getSupportedQueryHints() {
>>> +        return Collections.singleton(OracleDictionary.SELECT_HINT);
>>> +    }
>>>  }
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -21,13 +21,18 @@
>>>  import java.lang.reflect.AccessibleObject;
>>>  import java.lang.reflect.Field;
>>>  import java.lang.reflect.InvocationTargetException;
>>> +import java.lang.reflect.Member;
>>>  import java.lang.reflect.Method;
>>>  import java.lang.reflect.Modifier;
>>>  import java.security.AccessController;
>>> +import java.util.Collections;
>>> +import java.util.Set;
>>> +import java.util.TreeSet;
>>>  
>>>  import org.apache.commons.lang.StringUtils;
>>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>>>  import org.apache.openjpa.lib.util.Localizer;
>>> +import org.apache.openjpa.lib.util.Reflectable;
>>>  import org.apache.openjpa.util.GeneralException; 
>>>  import org.apache.openjpa.util.UserException; 
>>>  
>>> @@ -754,4 +759,166 @@
>>>      public static void set(Object target, Method setter, short value) {
>>>          set(target, setter, new Short(value));
>>>      }
>>> +    
>>> +    /**
>>> +     * Gets all bean-style property names of the given Class or its
>>> superclass.
>>> +     * A bean-style property 'abc' exists in Class C iff C has declared 
>>> +     * following pair of methods:
>>> +     *   public void setAbc(Y y) or public C setAbc(Y y)
>>> +     *   public Y getAbc();
>>> +     *   
>>> +     * If a getter property is annotated with {@link Reflectable}, then
>>> +     * it is ignored.
>>> +     *   
>>> +     */
>>> +    public static Set<String> getBeanStylePropertyNames(Class c) {
>>> +        if (c == null)
>>> +            return Collections.EMPTY_SET;
>>> +        Method[] methods = c.getMethods();
>>> +        if (methods == null || methods.length < 2)
>>> +            return Collections.EMPTY_SET;
>>> +        Set<String> result = new TreeSet<String>();
>>> +        for (Method m : methods) {
>>> +            if (m.getName().startsWith("get")) {
>>> +                if (!canReflect(m))
>>> +                    continue;
>>> +                String prop = StringUtils.capitalize(m.getName()
>>> +                    .substring("get".length()));
>>> +                Class rtype = m.getReturnType();
>>> +                try {
>>> +                  Method setter = c.getMethod("set"+prop, new
>>> Class[]{rtype});
>>> +                  if (setter.getReturnType() == void.class || 
>>> +                      setter.getReturnType().isAssignableFrom(c))
>>> +                  result.add(prop);
>>> +                } catch (NoSuchMethodException e) {
>>> +                    
>>> +                }
>>> +            }
>>> +        }
>>> +        return result;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets all public field names of the given Class.
>>> +     *   
>>> +     */
>>> +    public static Set<String> getPublicFieldNames(Class c) {
>>> +        if (c == null)
>>> +            return Collections.EMPTY_SET;
>>> +        Field[] fields = c.getFields();
>>> +        if (fields == null || fields.length == 0)
>>> +            return Collections.EMPTY_SET;
>>> +        Set<String> result = new TreeSet<String>();
>>> +        for (Field f : fields) {
>>> +            if (canReflect(f))
>>> +                result.add(f.getName());
>>> +        }
>>> +        return result;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets values of all field f the given class such that f exactly 
>>> +     * match the given modifiers and are of given type (Object implies
>>> any type)
>>> +     * unless f is annotated as {@link Reflectable}. 
>>> +     *   
>>> +     */
>>> +    public static <T> Set<T> getFieldValues(Class c, int mods, Class<T>
>>> t){
>>> +        if (c == null)
>>> +            return Collections.EMPTY_SET;
>>> +        Field[] fields = c.getFields();
>>> +        if (fields == null || fields.length == 0)
>>> +            return Collections.EMPTY_SET;
>>> +        Set<T> result = new TreeSet<T>();
>>> +        for (Field f : fields) {
>>> +            if (mods == f.getModifiers() 
>>> +            && (t == Object.class || t.isAssignableFrom(f.getType()))
>>> +            && canReflect(f)) {
>>> +                try {
>>> +                    result.add((T)f.get(null));
>>> +                } catch (IllegalArgumentException e) {
>>> +                } catch (IllegalAccessException e) {
>>> +                }
>>> +            }
>>> +        }
>>> +        return result;
>>> +    }
>>> +
>>> +    /**
>>> +     * Affirms if the given member is selected for reflection. The
>>> decision is
>>> +     * based on the following truth table on both the class-level and 
>>> +     * member-level annotation (null annotation represents MAYBE) 
>>> +     * 
>>> +     * Class   member  
>>> +     * MAYBE   MAYBE   YES
>>> +     * MAYBE   YES     YES
>>> +     * MAYBE   NO      NO
>>> +     *
>>> +     * YES     MAYBE   YES
>>> +     * YES     YES     YES
>>> +     * YES     NO      NO
>>> +     *
>>> +     * NO      YES     YES
>>> +     * NO      MAYBE   NO
>>> +     * NO      NO      NO 
>>> +     * 
>>> +    */   
>>> +    static boolean canReflect(Reflectable cls, Reflectable member) {
>>> +        if (cls == null || cls.value()) {
>>> +            return member == null || member.value() == true;
>>> +        } else {
>>> +            return member != null && member.value() == true;
>>> +        }
>>> +    }
>>> +    
>>> +    /**
>>> +     * Affirms if the original declaration the given field is annotated
>>> +     * for reflection. 
>>> +     */
>>> +    static boolean canReflect(Field field) {
>>> +        Class cls = field.getDeclaringClass();
>>> +        return
>>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>>> +            field.getAnnotation(Reflectable.class));
>>> +    }
>>> +    
>>> +    /**
>>> +     * Affirms if the original declaration the given method is annotated
>>> +     * for reflection. 
>>> +     */
>>> +    static boolean canReflect(Method method) {
>>> +        Class cls = getDeclaringClass(method);
>>> +        if (cls != method.getDeclaringClass())
>>> +            method = getDeclaringMethod(cls, method);
>>> +        return
>>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>>> +            method.getAnnotation(Reflectable.class));
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets the declaring class of the given method signature but also
>>> checks
>>> +     * if the method is declared in an interface. If yes, then returns
>>> the
>>> +     * interface. 
>>> +     */
>>> +    public static Class getDeclaringClass(Method m) {
>>> +        if (m == null)
>>> +            return null;
>>> +        Class cls = m.getDeclaringClass();
>>> +        Class[] intfs =  cls.getInterfaces();
>>> +        for (Class intf : intfs) {
>>> +            if (getDeclaringMethod(intf, m) != null)
>>> +                cls = intf;
>>> +        }
>>> +        return cls;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets the method in the given class that has the same signature of
>>> the
>>> +     * given method, if exists. Otherwise, null.
>>> +     */
>>> +    public static Method getDeclaringMethod(Class c, Method m) {
>>> +        try {
>>> +            Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
>>> +            return m0;
>>> +        } catch (Exception e) {
>>> +            return null;
>>> +        }
>>> +    }    
>>>  }
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -247,12 +247,23 @@
>>>          if (type.isAssignableFrom(o.getClass()))
>>>              return o;
>>>  
>>> -        // the only non-numeric conversions we do are to string, or from
>>> +        // the non-numeric conversions we do are to string, or from
>>>          // string/char to number, or calendar/date
>>> +        // String to Boolean
>>> +        // String to Integer
>>>          boolean num = o instanceof Number;
>>>          if (!num) {
>>>              if (type == String.class)
>>>                  return o.toString();
>>> +            else if (type == Boolean.class && o instanceof String) 
>>> +                return Boolean.valueOf(o.toString());
>>> +            else if (type == Integer.class && o instanceof String)
>>> +                try {
>>> +                    return new Integer(o.toString());
>>> +                } catch (NumberFormatException e) {
>>> +                    throw new
>>> ClassCastException(_loc.get("cant-convert", o,
>>> +                        o.getClass(), type).getMessage());
>>> +                }
>>>              else if (type == Character.class) {
>>>                  String str = o.toString();
>>>                  if (str != null && str.length() == 1)
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -19,7 +19,9 @@
>>>  package org.apache.openjpa.lib.conf;
>>>  
>>>  import java.io.File;
>>> +import java.util.Collections;
>>>  import java.util.List;
>>> +import java.util.Set;
>>>  
>>>  /**
>>>   * Abstract no-op product derivation for easy extension.
>>> @@ -85,4 +87,8 @@
>>>      
>>>      public void beforeConfigurationClose(Configuration conf) {
>>>      }
>>> +        
>>> +    public Set<String> getSupportedQueryHints() {
>>> +        return Collections.EMPTY_SET;
>>> +    }
>>>  }
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -21,6 +21,7 @@
>>>  import java.io.File;
>>>  import java.io.IOException;
>>>  import java.util.List;
>>> +import java.util.Set;
>>>  
>>>  /**
>>>   * Hooks for deriving products with additional functionality.
>>> @@ -157,4 +158,13 @@
>>>       * @since 0.9.7
>>>       */
>>>      public void beforeConfigurationClose(Configuration conf);
>>> +    
>>> +       
>>> +    /**
>>> +     * Return set of Query hint keys recognized by this receiver. 
>>> +     * 
>>> +     * @since 2.0.0
>>> +     */
>>> +    public Set<String> getSupportedQueryHints();
>>> +
>>>  }
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -29,6 +29,8 @@
>>>  import java.util.List;
>>>  import java.util.Map;
>>>  import java.util.MissingResourceException;
>>> +import java.util.Set;
>>> +import java.util.TreeSet;
>>>  
>>>  import org.apache.commons.lang.StringUtils;
>>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>>> @@ -434,6 +436,19 @@
>>>                  collection.add(fqLoc);
>>>          }
>>>      }
>>> +    
>>> +    
>>> +    public static Set<String> getSupportedQueryHints() {
>>> +        Set<String> result = new TreeSet<String>();
>>> +        // most specific to least
>>> +        for (int i = _derivations.length - 1; i >= 0; i--) {
>>> +            Set<String> members =
>>> _derivations[i].getSupportedQueryHints();
>>> +            if (members != null || !members.isEmpty())
>>> +                result.addAll(members);
>>> +        }
>>> +        return result;
>>> +    }
>>> +
>>>  
>>>      /**
>>>       * Compare {@link ProductDerivation}s.
>>>
>>> Added:
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>>> (added)
>>> +++
>>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -0,0 +1,25 @@
>>> +package org.apache.openjpa.lib.util;
>>> +
>>> +import static java.lang.annotation.ElementType.FIELD;
>>> +import static java.lang.annotation.ElementType.METHOD;
>>> +import static java.lang.annotation.ElementType.TYPE;
>>> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
>>> +
>>> +import java.lang.annotation.Retention;
>>> +import java.lang.annotation.Target;
>>> +
>>> +/**
>>> + * Annotates a getter method or field so {@link Reflection reflection 
>>> + * utility} to control whether the annotated member is recorded during
>>> scanning 
>>> + * for bean-style method or field.
>>> + * 
>>> + * @author Pinaki Poddar
>>> + * 
>>> + * @since 2.0.0
>>> + *
>>> + */
>>> +@Target({TYPE, METHOD, FIELD})
>>> +@Retention(RUNTIME)
>>> +public @interface Reflectable {
>>> +    boolean value() default true;
>>> +}
>>>
>>> Added:
>>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>> (added)
>>> +++
>>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -0,0 +1,167 @@
>>> +/*
>>> + * 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.conf;
>>> +
>>> +import javax.persistence.EntityManager;
>>> +import javax.persistence.Query;
>>> +
>>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>>> +import org.apache.openjpa.kernel.QueryHints;
>>> +import org.apache.openjpa.persistence.HintHandler;
>>> +import org.apache.openjpa.persistence.OpenJPAPersistence;
>>> +import org.apache.openjpa.persistence.OpenJPAQuery;
>>> +import org.apache.openjpa.persistence.test.SingleEMFTestCase;
>>> +
>>> +/**
>>> + * Tests JPA 2.0 API methods {@link Query#getSupportedHints()} and 
>>> + * {@link Query#getHints()}. 
>>> + * 
>>> + * @author Pinaki Poddar
>>> + *
>>> + */
>>> +public class TestQueryHints extends SingleEMFTestCase {
>>> +    EntityManager em;
>>> +    OpenJPAQuery query;
>>> +    
>>> +    public void setUp() {
>>> +       super.setUp((Object[])null);
>>> +       em = emf.createEntityManager();
>>> +       String sql = "select * from Person";
>>> +       query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
>>> +    }
>>> +    
>>> +    public void testSupportedHintsContainProductDerivationHints() {
>>> +        assertSupportedHint(OracleDictionary.SELECT_HINT, true);
>>> +    }
>>> +    
>>> +    public void testSupportedHintsContainFetchPlanHints() {
>>> +        assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
>>> +    }
>>> +
>>> +    public void
>>> testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
>>> +        assertSupportedHint("openjpa.FetchPlan.QueryResultCache",
>>> false);
>>> +    }
>>> +    
>>> +    public void testSupportedHintsContainQueryProperty() {
>>> +        assertSupportedHint("openjpa.Subclasses", true);
>>> +    }
>>> +    
>>> +    public void testSupportedHintsContainKernelQueryHints() {
>>> +        assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY,
>>> true);
>>> +    }
>>> +    
>>> +    public void testSupportedHintsContainJPAQueryHints() {
>>> +        assertSupportedHint("javax.persistence.query.timeout", true);
>>> +    }
>>> +    
>>> +    public void testUnrecognizedKeyIsIgnored() {
>>> +        String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
>>> +        query.setHint(unrecognizedKey, "xyz");
>>> +        assertFalse(query.getHints().containsKey(unrecognizedKey));
>>> +       
>>> assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
>>> +     }
>>> +    
>>> +    public void testRecognizedKeyIsNotRecordedButAvailable() {
>>> +        String recognizedKey = "openjpa.some.derivation.hint";
>>> +        query.setHint(recognizedKey, "abc");
>>> +        assertFalse(query.getHints().containsKey(recognizedKey));
>>> +        assertEquals("abc",
>>> query.getFetchPlan().getDelegate().getHint(recognizedKey));
>>> +    }
>>> +
>>> +    public void testSupportedKeyIsRecordedAndAvailable() {
>>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>>> +        query.setHint(supportedKey, 42);
>>> +        assertTrue(query.getHints().containsKey(supportedKey));
>>> +        assertEquals(42, query.getFetchPlan().getFetchBatchSize());
>>> +    }
>>> +    
>>> +    public void testSupportedKeyWrongValue() {
>>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>>> +        short goodValue = (short)42;
>>> +        float badValue = 57.9f;
>>> +        query.setHint(supportedKey, goodValue);
>>> +        assertTrue(query.getHints().containsKey(supportedKey));
>>> +        assertEquals(goodValue,
>>> query.getFetchPlan().getFetchBatchSize());
>>> +        
>>> +        try {
>>> +            query.setHint(supportedKey, badValue);
>>> +            fail("Expected to fail to set " + supportedKey + " hint to "
>>> + badValue);
>>> +        } catch (IllegalArgumentException e) {
>>> +            // good
>>> +        }
>>> +    }
>>> +    
>>> +    public void testSupportedKeyIntegerValueConversion() {
>>> +        String supportedKey = "openjpa.hint.OptimizeResultCount";
>>> +        String goodValue = "57";
>>> +        int badValue = -3;
>>> +        query.setHint(supportedKey, goodValue);
>>> +        assertTrue(query.getHints().containsKey(supportedKey));
>>> +        assertEquals(57,
>>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>>> +        
>>> +        try {
>>> +            query.setHint(supportedKey, badValue);
>>> +            fail("Expected to fail to set " + supportedKey + " hint to "
>>> + badValue);
>>> +        } catch (IllegalArgumentException e) {
>>> +            // good
>>> +        }
>>> +    }
>>> +
>>> +    public void testSupportedKeyBooleanValueConversion() {
>>> +        String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
>>> +        String goodValue = "true";
>>> +        query.setHint(supportedKey, goodValue);
>>> +        assertTrue(query.getHints().containsKey(supportedKey));
>>> +        assertEquals(true,
>>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>>> +        
>>> +        goodValue = "false";
>>> +        query.setHint(supportedKey, goodValue);
>>> +        assertTrue(query.getHints().containsKey(supportedKey));
>>> +        assertEquals(false,
>>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>>> +    }
>>> +    
>>> +    public void testJPAHintSetsFetchPlan() {
>>> +        String jpaKey = "javax.persistence.query.timeout";
>>> +        query.setHint(jpaKey, 5671);
>>> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
>>> +    }
>>> +    
>>> +    public void testParts() {
>>> +        HintHandler.HintKeyComparator test = new
>>> HintHandler.HintKeyComparator();
>>> +        assertEquals(0, test.countDots("a"));
>>> +        assertEquals(1, test.countDots("a.b"));
>>> +        assertEquals(2, test.countDots("a.b.c"));
>>> +        assertEquals(3, test.countDots("a.b.c.d"));
>>> +        assertEquals(4, test.countDots("a.b.c.d."));
>>> +        assertEquals(1, test.countDots("."));
>>> +        assertEquals(0, test.countDots(""));
>>> +        assertEquals(0, test.countDots(null));
>>> +    }
>>> +    
>>> +    void assertSupportedHint(String hint, boolean contains) {
>>> +        if (contains)
>>> +            assertTrue("Expeceted suppoerted hint [" + hint + "]",
>>> +                query.getSupportedHints().contains(hint));
>>> +        else
>>> +            assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
>>> +                query.getSupportedHints().contains(hint));
>>> +    }
>>> +    
>>> +    
>>> +}
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -22,6 +22,7 @@
>>>  import javax.persistence.LockModeType;
>>>  
>>>  import org.apache.openjpa.kernel.FetchConfiguration;
>>> +import org.apache.openjpa.lib.util.Reflectable;
>>>  import org.apache.openjpa.meta.FetchGroup;
>>>  
>>>  /**
>>> @@ -88,6 +89,7 @@
>>>       *
>>>       * @since 1.0.0
>>>       */
>>> +    @Reflectable(false)
>>>      public boolean getQueryResultCacheEnabled();
>>>  
>>>      /**
>>> @@ -102,6 +104,7 @@
>>>      /**
>>>       * @deprecated use {@link #getQueryResultCacheEnabled()} instead.
>>>       */
>>> +    @Reflectable(false)
>>>      public boolean getQueryResultCache();
>>>  
>>>      /**
>>> @@ -293,5 +296,6 @@
>>>       * @deprecated cast to {@link FetchPlanImpl} instead. This
>>>       * method pierces the published-API boundary, as does the SPI cast.
>>>       */
>>> +    @Reflectable(false)
>>>      public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
>>>  }
>>>
>>> Added:
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>>> (added)
>>> +++
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -0,0 +1,365 @@
>>> +package org.apache.openjpa.persistence;
>>> +
>>> +import static
>>> org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
>>> +import static
>>> org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
>>> +import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
>>> +
>>> +import java.lang.reflect.Method;
>>> +import java.lang.reflect.Modifier;
>>> +import java.util.Collections;
>>> +import java.util.Comparator;
>>> +import java.util.Map;
>>> +import java.util.Set;
>>> +import java.util.TreeMap;
>>> +import java.util.TreeSet;
>>> +
>>> +import org.apache.openjpa.conf.OpenJPAConfiguration;
>>> +import org.apache.openjpa.enhance.Reflection;
>>> +import org.apache.openjpa.kernel.FetchConfiguration;
>>> +import org.apache.openjpa.kernel.Filters;
>>> +import org.apache.openjpa.kernel.exps.AggregateListener;
>>> +import org.apache.openjpa.kernel.exps.FilterListener;
>>> +import org.apache.openjpa.lib.conf.ProductDerivation;
>>> +import org.apache.openjpa.lib.conf.ProductDerivations;
>>> +import org.apache.openjpa.lib.log.Log;
>>> +import org.apache.openjpa.lib.util.Localizer;
>>> +import org.apache.openjpa.lib.util.StringDistance;
>>> +
>>> +
>>> +/**
>>> + * Manages query hint keys and handles their values on behalf of a
>>> owning
>>> + * {@link QueryImpl}. Uses specific knowledge of hint keys declared in
>>> + * different parts of the system.
>>> + * 
>>> + * This receiver collects hint keys from different parts of the system.
>>> The
>>> + * keys are implicitly or explicitly declared by several different
>>> mechanics.
>>> + * This receiver sets the values on behalf of a owning {@link QueryImpl}
>>> + * based on the its specific knowledge of these keys.
>>> + * 
>>> + * The hint keys from following sources are collected and handled: 
>>> + * 
>>> + * 1. {@link org.apache.openjpa.kernel.QueryHints} interface declares
>>> hint keys
>>> + *    as public static final fields. These fields are collected by
>>> reflection.
>>> + *    The values are handled by invoking methods on the owning {@link
>>> QueryImpl}
>>> + *    
>>> + * 2. Some hint keys are collected from bean-style property names of
>>> {@link 
>>> + *    JDBCFetchPlan} by {@link
>>> Reflection#getBeanStylePropertyNames(Class) 
>>> + *    reflection} and prefixed with <code>openjpa.FetchPlan</code>. 
>>> + *    Their values are used to set the corresponding property of {@link 
>>> + *    FetchPlan} via {@link #hintToSetter(FetchPlan, String, Object)
>>> reflection}
>>> + *      
>>> + * 3. Currently defined <code>javax.persistence.*</code> hint keys have  
>>> + *    a equivalent counterpart to one of these FetchPlan keys. 
>>> + *    The JPA keys are mapped to equivalent FetchPlan hint keys.
>>> + *    
>>> + * 4. Some keys directly invoke setters or add listeners to the owning 
>>> + *    {@link QueryImpl}. These hint keys are statically declared in 
>>> + *    this receiver itself. 
>>> + *    
>>> + * 5. ProductDerivation may introduce their own query hint keys via
>>> {@link 
>>> + *    ProductDerivation#getSupportedQueryHints()}. Their values are set
>>> in the 
>>> + *    {@link FetchConfiguration#setHint(String, Object)}
>>> + *     
>>> + *  A hint key is classified into one of the following three categories:
>>> + *  
>>> + *  1. Supported: A key is known to this receiver as collected from
>>> different 
>>> + *     parts of the system. The value of a supported key is recorded and 
>>> + *     available via {@link #getHints()} method. 
>>> + *  2. Recognized: A key is not known to this receiver but has a prefix
>>> which
>>> + *     is known to this receiver. The value of a recognized key is not
>>> recorded 
>>> + *     but its value is available via {@link
>>> FetchConfiguration#getHint(String)}
>>> + *  3. Unrecognized: A key is neither supported nor recognized. The
>>> value of a 
>>> + *     unrecognized key is neither recorded nor set anywhere.
>>> + *  
>>> + *  If an incompatible value is supplied for a supported key, a
>>> non-fatal
>>> + *  {@link ArgumentException} is raised.
>>> + *  
>>> + * @author Pinaki Poddar
>>> + *
>>> + * @since 2.0.0
>>> + * 
>>> + * @nojavadoc
>>> + */
>>> +public class HintHandler {
>>> +    private final QueryImpl owner;
>>> +    private Map<String, Object> _hints;
>>> +    private Set<String> _supportedKeys;
>>> +    private Set<String> _supportedPrefixes;
>>> +    
>>> +    static final String PREFIX_JPA = "javax.persistence.";
>>> +    static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
>>> +    
>>> +    // These keys are directly handled in {@link QueryImpl} class.
>>> +    // Declaring a public static final String variable in this class
>>> will 
>>> +    // make it register as a supported hint key
>>> +    // if you do not want that then annotate as {@link
>>> Reflectable(false)}.
>>> +    public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
>>> +    public static final String HINT_FILTER_LISTENER =
>>> "openjpa.FilterListener";
>>> +    public static final String HINT_FILTER_LISTENERS = 
>>> +        "openjpa.FilterListeners";
>>> +    public static final String HINT_AGGREGATE_LISTENER = 
>>> +        "openjpa.AggregateListener";
>>> +    public static final String HINT_AGGREGATE_LISTENERS = 
>>> +        "openjpa.AggregateListeners";
>>> +    
>>> +    // JPA Specification 2.0 keys are mapped to equivalent FetchPlan
>>> keys
>>> +    public static Map<String,String> _jpaKeys = new TreeMap<String,
>>> String>();
>>> +    static {
>>> +        _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"), 
>>> +            addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
>>> +    }
>>> +    
>>> +    private static final String DOT = ".";
>>> +    private static final String BLANK = "";
>>> +    private static final Localizer _loc = Localizer.forPackage(
>>> +        HintHandler.class);
>>> +    
>>> +    public HintHandler(QueryImpl impl) {
>>> +        owner = impl;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets all the recorded hint keys and their values.
>>> +     */
>>> +    @SuppressWarnings("unchecked")
>>> +    public Map<String, Object> getHints() {
>>> +        return _hints == null ? Collections.EMPTY_MAP 
>>> +            : Collections.unmodifiableMap(_hints);
>>> +    }
>>> +    
>>> +    /**
>>> +     * Record a key-value pair only only if the given key is supported.
>>> +     * 
>>> +     * @return FALSE if the key is unrecognized. 
>>> +     *         null (i.e. MAY BE) if the key is recognized, but not
>>> supported.
>>> +     *         TRUE if the key is supported.
>>> +     */
>>> +    public Boolean record(String hint, Object value) {
>>> +        if (hint == null)
>>> +            return Boolean.FALSE;
>>> +        if (isSupported(hint)) {
>>> +            if (_hints == null)
>>> +                _hints = new TreeMap<String, Object>();
>>> +            _hints.put(hint, value);
>>> +            return Boolean.TRUE;
>>> +        }
>>> +        
>>> +        Log log = owner.getDelegate().getBroker().getConfiguration()
>>> +            .getLog(OpenJPAConfiguration.LOG_RUNTIME);
>>> +        String possible =
>>> StringDistance.getClosestLevenshteinDistance(hint, 
>>> +            getSupportedHints());
>>> +        if (log.isWarnEnabled()) {
>>> +            log.warn(_loc.get("bad-query-hint", hint, possible));
>>> +        }
>>> +        return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Gets all the supported hint keys. The set of supported hint keys
>>> is
>>> +     * statically determined by collecting hint keys from the
>>> ProductDerivations
>>> +     * and reflecting upon some of the known classes.
>>> +     */
>>> +    public Set<String> getSupportedHints() {
>>> +        if (_supportedKeys == null) {
>>> +            _supportedKeys = new TreeSet<String>(new
>>> HintKeyComparator());
>>> +            _supportedPrefixes = new TreeSet<String>();
>>> +            
>>> +            _supportedKeys.addAll(Reflection.getFieldValues(
>>> +                org.apache.openjpa.kernel.QueryHints.class, 
>>> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
>>> +                String.class));
>>> +
>>> +            _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN, 
>>> +                Reflection.getBeanStylePropertyNames(
>>> +                    owner.getFetchPlan().getClass())));
>>> +
>>> +            _supportedKeys.addAll(_jpaKeys.keySet());
>>> +
>>> +            _supportedKeys.addAll(Reflection.getFieldValues(
>>> +                HintHandler.class, 
>>> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
>>> +                String.class));
>>> +
>>> +           
>>> _supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());    
>>> +            
>>> +            for (String key : _supportedKeys) {
>>> +                _supportedPrefixes.add(getPrefixOf(key));
>>> +            }
>>> +        }
>>> +        return _supportedKeys;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Add a hint key to the set of supported hint keys.
>>> +     */
>>> +    public void addHintKey(String key) {
>>> +        getSupportedHints().add(key);
>>> +        _supportedPrefixes.add(getPrefixOf(key));
>>> +    }
>>> +    
>>> +    public Set<String> getKnownPrefixes() {
>>> +        getSupportedHints();
>>> +        return _supportedPrefixes;
>>> +    }
>>> +    
>>> +    /**
>>> +     * Affirms the given key matches one of the supported keys.
>>> +     */
>>> +    public boolean isSupported(String key) {
>>> +        return getSupportedHints().contains(key);
>>> +    }
>>> +    
>>> +    /**
>>> +     * Affirms the given key has a prefix that matches with any of the 
>>> +     * supported prefixes.
>>> +     */
>>> +    public boolean isSupportedPrefix(String key) {
>>> +        return getKnownPrefixes().contains(getPrefixOf(key));
>>> +    }
>>> +    
>>> +    static Set<String> addPrefix(String prefix, Set<String> original) {
>>> +        Set<String> result = new TreeSet<String>();
>>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>>> +        for (String o : original)
>>> +            result.add(prefix + join + o);
>>> +        return result;
>>> +    }
>>> +    
>>> +    static String addPrefix(String prefix, String original) {
>>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>>> +        return prefix + join + original;
>>> +    }
>>> +    
>>> +    public static String removePrefix(String key, String prefix) {
>>> +        if (prefix == null)
>>> +            return key;
>>> +        if (!prefix.endsWith(DOT))
>>> +            prefix = prefix + DOT;
>>> +        if (key != null && key.startsWith(prefix))
>>> +            return key.substring(prefix.length());
>>> +        return key;
>>> +    }
>>> +    
>>> +    static String getPrefixOf(String key) {
>>> +        int index = key == null ? -1 : key.indexOf(DOT);
>>> +        return (index != -1) ? key.substring(0,index) : key;
>>> +    }
>>> +    
>>> +    boolean isKnownHintPrefix(String key) {
>>> +        String prefix = getPrefixOf(key);
>>> +        return getKnownPrefixes().contains(prefix);
>>> +    }
>>> +    
>>> +    public static boolean hasPrefix(String key, String prefix) {
>>> +        if (key == null || prefix == null)
>>> +            return false;
>>> +        if (!prefix.endsWith(DOT))
>>> +            prefix = prefix + DOT;
>>> +        return key.startsWith(prefix);
>>> +    }
>>> +    
>>> +    public void setHint(String key, Object value) {
>>> +        Boolean record = record(key, value);
>>> +        FetchConfiguration fetch =
>>> owner.getDelegate().getFetchConfiguration();
>>> +        ClassLoader loader =
>>> owner.getDelegate().getBroker().getClassLoader();
>>> +        if (record == Boolean.FALSE)
>>> +            return;
>>> +        if (record == null) {
>>> +            fetch.setHint(key, value);
>>> +            return;
>>> +        }
>>> +        try {
>>> +            if (HINT_SUBCLASSES.equals(key)) {
>>> +                if (value instanceof String)
>>> +                    value = Boolean.valueOf((String) value);
>>> +                owner.setSubclasses(((Boolean) value).booleanValue());
>>> +            } else if (HINT_FILTER_LISTENER.equals(key))
>>> +               
>>> owner.addFilterListener(Filters.hintToFilterListener(value, 
>>> +                    loader));
>>> +            else if (HINT_FILTER_LISTENERS.equals(key)) {
>>> +                FilterListener[] arr =
>>> Filters.hintToFilterListeners(value, 
>>> +                    loader);
>>> +                for (int i = 0; i < arr.length; i++)
>>> +                    owner.addFilterListener(arr[i]);
>>> +            } else if (HINT_AGGREGATE_LISTENER.equals(key))
>>> +               
>>> owner.addAggregateListener(Filters.hintToAggregateListener(
>>> +                    value, loader));
>>> +            else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
>>> +                AggregateListener[] arr =
>>> Filters.hintToAggregateListeners(
>>> +                        value, loader);
>>> +                for (int i = 0; i < arr.length; i++)
>>> +                    owner.addAggregateListener(arr[i]);
>>> +            } else if (isFetchPlanHint(key)) {
>>> +                hintToSetter(owner.getFetchPlan(), 
>>> +                    getFetchPlanProperty(key), value);
>>> +            } else if (HINT_RESULT_COUNT.equals(key)) {
>>> +                int v = (Integer)Filters.convert(value, Integer.class);
>>> +                if (v < 0)
>>> +                    throw new
>>> ArgumentException(_loc.get("bad-query-hint-value", 
>>> +                        key, value), null,  null, false);
>>> +                    fetch.setHint(key, v);
>>> +            }  else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
>>> +                fetch.setHint(key, Filters.convert(value,
>>> Boolean.class));
>>> +                owner.invalidatePreparedQuery();
>>> +            } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>>> +                fetch.setHint(key, Filters.convert(value,
>>> Boolean.class));
>>> +                owner.ignorePreparedQuery();
>>> +            } else { // default 
>>> +                fetch.setHint(key, value);
>>> +            }
>>> +            return;
>>> +        } catch (IllegalArgumentException iae) {
>>> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
>>> +                key, value), null,  null, false);
>>> +        } catch (ClassCastException ce) {
>>> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
>>> +                key, ce.getMessage()), null,  null, false);
>>> +        } catch (Exception e) {
>>> +            throw PersistenceExceptions.toPersistenceException(e);
>>> +        }
>>> +    }
>>> +    
>>> +    private boolean isFetchPlanHint(String key) {
>>> +        return key.startsWith(PREFIX_FETCHPLAN) 
>>> +           || (_jpaKeys.containsKey(key) &&
>>> isFetchPlanHint(_jpaKeys.get(key)));
>>> +    }
>>> +    
>>> +    private String getFetchPlanProperty(String key) {
>>> +        if (key.startsWith(PREFIX_FETCHPLAN))
>>> +            return removePrefix(key, PREFIX_FETCHPLAN);
>>> +        else if (_jpaKeys.containsKey(key))
>>> +            return getFetchPlanProperty(_jpaKeys.get(key));
>>> +        else
>>> +            return key;
>>> +    }
>>> +    
>>> +    private void hintToSetter(FetchPlan fetchPlan, String k, Object
>>> value) {
>>> +        if (fetchPlan == null || k == null)
>>> +            return;
>>> +
>>> +        Method setter = Reflection.findSetter(fetchPlan.getClass(), k,
>>> true);
>>> +        Class paramType = setter.getParameterTypes()[0];
>>> +        if (Enum.class.isAssignableFrom(paramType) && value instanceof
>>> String)
>>> +            value = Enum.valueOf(paramType, (String) value);
>>> +
>>> +        Filters.hintToSetter(fetchPlan, k, value);
>>> +    }
>>> +    
>>> +    public static class HintKeyComparator implements Comparator<String>
>>> {
>>> +        public int compare(String s1, String s2) {
>>> +            if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
>>> +                int n1 = countDots(s1);
>>> +                int n2 = countDots(s2);
>>> +                return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
>>> +            } else
>>> +                return s1.compareTo(s2);
>>> +        }
>>> +        
>>> +        public int countDots(String s) {
>>> +            if (s == null || s.length() == 0)
>>> +                return 0;
>>> +            int index = s.indexOf(DOT);
>>> +            return (index == -1) ? 0 : countDots(s.substring(index+1)) +
>>> 1;
>>> +        }
>>> +    }
>>> +}
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -18,8 +18,9 @@
>>>   */
>>>  package org.apache.openjpa.persistence;
>>>  
>>> +import static
>>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>>> +
>>>  import java.io.Serializable;
>>> -import java.lang.reflect.Method;
>>>  import java.sql.Time;
>>>  import java.sql.Timestamp;
>>>  import java.util.ArrayList;
>>> @@ -41,15 +42,12 @@
>>>  import javax.persistence.TemporalType;
>>>  
>>>  import org.apache.openjpa.conf.OpenJPAConfiguration;
>>> -import org.apache.openjpa.enhance.Reflection;
>>>  import org.apache.openjpa.kernel.Broker;
>>>  import org.apache.openjpa.kernel.DelegatingQuery;
>>>  import org.apache.openjpa.kernel.DelegatingResultList;
>>>  import org.apache.openjpa.kernel.FetchConfiguration;
>>> -import org.apache.openjpa.kernel.Filters;
>>>  import org.apache.openjpa.kernel.PreparedQuery;
>>>  import org.apache.openjpa.kernel.PreparedQueryCache;
>>> -import org.apache.openjpa.kernel.QueryHints;
>>>  import org.apache.openjpa.kernel.QueryLanguages;
>>>  import org.apache.openjpa.kernel.QueryOperations;
>>>  import org.apache.openjpa.kernel.QueryStatistics;
>>> @@ -63,8 +61,6 @@
>>>  import org.apache.openjpa.util.RuntimeExceptionTranslator;
>>>  import org.apache.openjpa.util.UserException;
>>>  
>>> -import static
>>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>>> -
>>>  /**
>>>   * Implementation of {@link Query} interface.
>>>   * 
>>> @@ -85,6 +81,8 @@
>>>  	private Map<String, Object> _named;
>>>  	private Map<Integer, Object> _positional;
>>>  	private String _id;
>>> +	
>>> +	private final HintHandler _hintHandler;
>>>  
>>>  	/**
>>>  	 * Constructor; supply factory exception translator and delegate.
>>> @@ -97,6 +95,7 @@
>>>  			org.apache.openjpa.kernel.Query query) {
>>>  		_em = em;
>>>  		_query = new DelegatingQuery(query, ret);
>>> +		_hintHandler = new HintHandler(this);
>>>  	}
>>>  
>>>  	/**
>>> @@ -254,7 +253,7 @@
>>>  	}
>>>  	
>>>  	private Object execute() {
>>> -		if (_query.getOperation() != QueryOperations.OP_SELECT)
>>> +		if (!isNative() && _query.getOperation() != QueryOperations.OP_SELECT)
>>>  			throw new InvalidStateException(_loc.get("not-select-query", _query
>>>  					.getQueryString()), null, null, false);
>>>  		
>>> @@ -351,81 +350,8 @@
>>>  
>>>  	public OpenJPAQuery setHint(String key, Object value) {
>>>  		_em.assertNotCloseInvoked();
>>> -		if (key == null)
>>> -			return this;
>>> -		if (!key.startsWith("openjpa.")) {
>>> -			_query.getFetchConfiguration().setHint(key, value);
>>> -			return this;
>>> -		}
>>> -		String k = key.substring("openjpa.".length());
>>> -
>>> -		try {
>>> -			if ("Subclasses".equals(k)) {
>>> -				if (value instanceof String)
>>> -					value = Boolean.valueOf((String) value);
>>> -				setSubclasses(((Boolean) value).booleanValue());
>>> -			} else if ("FilterListener".equals(k))
>>> -				addFilterListener(Filters.hintToFilterListener(value, _query
>>> -						.getBroker().getClassLoader()));
>>> -			else if ("FilterListeners".equals(k)) {
>>> -				FilterListener[] arr = Filters.hintToFilterListeners(value,
>>> -						_query.getBroker().getClassLoader());
>>> -				for (int i = 0; i < arr.length; i++)
>>> -					addFilterListener(arr[i]);
>>> -			} else if ("AggregateListener".equals(k))
>>> -				addAggregateListener(Filters.hintToAggregateListener(value,
>>> -						_query.getBroker().getClassLoader()));
>>> -			else if ("FilterListeners".equals(k)) {
>>> -				AggregateListener[] arr = Filters.hintToAggregateListeners(
>>> -						value, _query.getBroker().getClassLoader());
>>> -				for (int i = 0; i < arr.length; i++)
>>> -					addAggregateListener(arr[i]);
>>> -			} else if (k.startsWith("FetchPlan.")) {
>>> -				k = k.substring("FetchPlan.".length());
>>> -				hintToSetter(getFetchPlan(), k, value);
>>> -			} else if (k.startsWith("hint.")) {
>>> -				if ("hint.OptimizeResultCount".equals(k)) {
>>> -					if (value instanceof String) {
>>> -						try {
>>> -							value = new Integer((String) value);
>>> -						} catch (NumberFormatException nfe) {
>>> -						}
>>> -					}
>>> -					if (!(value instanceof Number)
>>> -							|| ((Number) value).intValue() < 0)
>>> -						throw new ArgumentException(_loc.get(
>>> -								"bad-query-hint-value", key, value), null,
>>> -								null, false);
>>> -                    _query.getFetchConfiguration().setHint(key, value);
>>> -				}  else if (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
>>> -                    (key)) {
>>> -                    _query.getFetchConfiguration().setHint(key,
>>> (Boolean)value);
>>> -                    invalidatePreparedQuery();
>>> -                } else if
>>> (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>>> -                    _query.getFetchConfiguration().setHint(key,
>>> (Boolean)value);
>>> -                    ignorePreparedQuery();
>>> -                } else {
>>> -                    _query.getFetchConfiguration().setHint(key, value);
>>> -                }
>>> -            } else
>>> -				throw new ArgumentException(_loc.get("bad-query-hint", key),
>>> -						null, null, false);
>>> -			return this;
>>> -		} catch (Exception e) {
>>> -			throw PersistenceExceptions.toPersistenceException(e);
>>> -		}
>>> -	}
>>> -
>>> -	private void hintToSetter(FetchPlan fetchPlan, String k, Object value)
>>> {
>>> -		if (fetchPlan == null || k == null)
>>> -			return;
>>> -
>>> -		Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
>>> -		Class paramType = setter.getParameterTypes()[0];
>>> -		if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
>>> -			value = Enum.valueOf(paramType, (String) value);
>>> -
>>> -		Filters.hintToSetter(fetchPlan, k, value);
>>> +		_hintHandler.setHint(key, value);
>>> +		return this;
>>>  	}
>>>  
>>>  	public OpenJPAQuery setParameter(int position, Calendar value,
>>> @@ -613,7 +539,7 @@
>>>  	 */
>>>      //TODO: JPA 2.0 Hints that are not set to FetchConfiguration 
>>>      public Map<String, Object> getHints() {
>>> -        return _query.getFetchConfiguration().getHints();
>>> +        return _hintHandler.getHints();
>>>      }
>>>  
>>>      public LockModeType getLockMode() {
>>> @@ -622,8 +548,7 @@
>>>      }
>>>  
>>>      public Set<String> getSupportedHints() {
>>> -        throw new UnsupportedOperationException(
>>> -            "JPA 2.0 - Method not yet implemented");
>>> +        return _hintHandler.getSupportedHints();
>>>      }
>>>  
>>>      public Query setLockMode(LockModeType lockMode) {
>>> @@ -704,7 +629,7 @@
>>>      /**
>>>       * Remove this query from PreparedQueryCache. 
>>>       */
>>> -    private boolean invalidatePreparedQuery() {
>>> +    boolean invalidatePreparedQuery() {
>>>          PreparedQueryCache cache = _em.getPreparedQueryCache();
>>>          if (cache == null)
>>>              return false;
>>> @@ -716,7 +641,7 @@
>>>       * Ignores this query from PreparedQueryCache by recreating the
>>> original
>>>       * query if it has been cached. 
>>>       */
>>> -    private void ignorePreparedQuery() {
>>> +    void ignorePreparedQuery() {
>>>          PreparedQuery cached = _em.getPreparedQuery(_id);
>>>          if (cached == null)
>>>              return;
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>>> Wed Feb 11 16:57:26 2009
>>> @@ -70,7 +70,7 @@
>>>  mult-results: Query returned multiple results: "{0}".
>>>  no-pos-named-params-mix: Cannot mix named and positional parameters in
>>> query \
>>>  	"{0}".
>>> -bad-query-hint: "{0}" is not a recognized query hint.
>>> +bad-query-hint: "{0}" is not a supported query hint. May be you meant
>>> "{1}"?
>>>  bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
>>>  detached: Cannot perform this operation on detached entity "{0}".
>>>  removed: Cannot perform this operation on removed entity "{0}".
>>>
>>> Modified:
>>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>>> URL:
>>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>>> ==============================================================================
>>> ---
>>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>>> (original)
>>> +++
>>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>>> Wed Feb 11 16:57:26 2009
>>> @@ -18,7 +18,9 @@
>>>   */
>>>  package org.apache.openjpa.slice;
>>>  
>>> +import java.util.Collections;
>>>  import java.util.Map;
>>> +import java.util.Set;
>>>  
>>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>>> @@ -98,4 +100,8 @@
>>>          	log.warn(_loc.get("forced-set-config", 
>>>          		prefix+"."+v.getProperty(), forced));
>>>      }
>>> +    
>>> +    public Set<String> getSupportedQueryHints() {
>>> +        return Collections.singleton(HINT_TARGET);
>>> +    }
>>>  }
>>>
>>>
>>>
>>
> 

Re: svn commit: r743396 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/ openjpa-kernel/src/main/java/org/apache/openjpa/enhance/ openjpa-kernel/src/main/java/org/apache/openjpa/kernel/ openjpa-lib/src/main/java/org/apache/open...

Posted by Pinaki Poddar <pp...@apache.org>.
Yes, ideally. But query fetch plan's lock timeout is currently interpreted as
JDBC statement's query timeout during select operation.
FetchPlan/FetchConfiguration currently is not holding two separate timeout
values for locking and selection operation.
May be OPENJPA-878 should consider such a change.


Donald Woods wrote:
> 
> In 
> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
> 
> Is this right?
> +    public void testJPAHintSetsFetchPlan() {
> +        String jpaKey = "javax.persistence.query.timeout";
> +        query.setHint(jpaKey, 5671);
> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
> +    }
> +
> 
> Shouldn't it be fetching the QueryTimeout instead?
> +    public void testJPAHintSetsFetchPlan() {
> +        String jpaKey = "javax.persistence.query.timeout";
> +        query.setHint(jpaKey, 5671);
> +        assertEquals(5671, query.getFetchPlan().getQueryTimeout());
> +    }
> +
> 
> 
> -Donald
> 
> ppoddar@apache.org wrote:
>> Author: ppoddar
>> Date: Wed Feb 11 16:57:26 2009
>> New Revision: 743396
>> 
>> URL: http://svn.apache.org/viewvc?rev=743396&view=rev
>> Log:
>> OPENJPA-900: Consolidate Query Hint related processing. As a byproduct,
>> support new JPA 2.0 getHins() and getSupportedHints().
>> 
>> Added:
>>    
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>>    
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>>    
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>> Modified:
>>    
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>>    
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>>    
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>>    
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>>    
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>>    
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>>    
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>>    
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>>    
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>>    
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>> 
>> Modified:
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
>> Wed Feb 11 16:57:26 2009
>> @@ -18,11 +18,14 @@
>>   */
>>  package org.apache.openjpa.jdbc.conf;
>>  
>> +import java.util.Collections;
>>  import java.util.Map;
>> +import java.util.Set;
>>  
>>  import org.apache.openjpa.conf.BrokerFactoryValue;
>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>  import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>>  import org.apache.openjpa.lib.conf.ConfigurationProvider;
>>  
>> @@ -48,4 +51,9 @@
>>          }
>>          return false;
>>      }
>> +    
>> +    @Override
>> +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.singleton(OracleDictionary.SELECT_HINT);
>> +    }
>>  }
>> 
>> Modified:
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java
>> Wed Feb 11 16:57:26 2009
>> @@ -21,13 +21,18 @@
>>  import java.lang.reflect.AccessibleObject;
>>  import java.lang.reflect.Field;
>>  import java.lang.reflect.InvocationTargetException;
>> +import java.lang.reflect.Member;
>>  import java.lang.reflect.Method;
>>  import java.lang.reflect.Modifier;
>>  import java.security.AccessController;
>> +import java.util.Collections;
>> +import java.util.Set;
>> +import java.util.TreeSet;
>>  
>>  import org.apache.commons.lang.StringUtils;
>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>>  import org.apache.openjpa.lib.util.Localizer;
>> +import org.apache.openjpa.lib.util.Reflectable;
>>  import org.apache.openjpa.util.GeneralException; 
>>  import org.apache.openjpa.util.UserException; 
>>  
>> @@ -754,4 +759,166 @@
>>      public static void set(Object target, Method setter, short value) {
>>          set(target, setter, new Short(value));
>>      }
>> +    
>> +    /**
>> +     * Gets all bean-style property names of the given Class or its
>> superclass.
>> +     * A bean-style property 'abc' exists in Class C iff C has declared 
>> +     * following pair of methods:
>> +     *   public void setAbc(Y y) or public C setAbc(Y y)
>> +     *   public Y getAbc();
>> +     *   
>> +     * If a getter property is annotated with {@link Reflectable}, then
>> +     * it is ignored.
>> +     *   
>> +     */
>> +    public static Set<String> getBeanStylePropertyNames(Class c) {
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Method[] methods = c.getMethods();
>> +        if (methods == null || methods.length < 2)
>> +            return Collections.EMPTY_SET;
>> +        Set<String> result = new TreeSet<String>();
>> +        for (Method m : methods) {
>> +            if (m.getName().startsWith("get")) {
>> +                if (!canReflect(m))
>> +                    continue;
>> +                String prop = StringUtils.capitalize(m.getName()
>> +                    .substring("get".length()));
>> +                Class rtype = m.getReturnType();
>> +                try {
>> +                  Method setter = c.getMethod("set"+prop, new
>> Class[]{rtype});
>> +                  if (setter.getReturnType() == void.class || 
>> +                      setter.getReturnType().isAssignableFrom(c))
>> +                  result.add(prop);
>> +                } catch (NoSuchMethodException e) {
>> +                    
>> +                }
>> +            }
>> +        }
>> +        return result;
>> +    }
>> +    
>> +    /**
>> +     * Gets all public field names of the given Class.
>> +     *   
>> +     */
>> +    public static Set<String> getPublicFieldNames(Class c) {
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Field[] fields = c.getFields();
>> +        if (fields == null || fields.length == 0)
>> +            return Collections.EMPTY_SET;
>> +        Set<String> result = new TreeSet<String>();
>> +        for (Field f : fields) {
>> +            if (canReflect(f))
>> +                result.add(f.getName());
>> +        }
>> +        return result;
>> +    }
>> +    
>> +    /**
>> +     * Gets values of all field f the given class such that f exactly 
>> +     * match the given modifiers and are of given type (Object implies
>> any type)
>> +     * unless f is annotated as {@link Reflectable}. 
>> +     *   
>> +     */
>> +    public static <T> Set<T> getFieldValues(Class c, int mods, Class<T>
>> t){
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Field[] fields = c.getFields();
>> +        if (fields == null || fields.length == 0)
>> +            return Collections.EMPTY_SET;
>> +        Set<T> result = new TreeSet<T>();
>> +        for (Field f : fields) {
>> +            if (mods == f.getModifiers() 
>> +            && (t == Object.class || t.isAssignableFrom(f.getType()))
>> +            && canReflect(f)) {
>> +                try {
>> +                    result.add((T)f.get(null));
>> +                } catch (IllegalArgumentException e) {
>> +                } catch (IllegalAccessException e) {
>> +                }
>> +            }
>> +        }
>> +        return result;
>> +    }
>> +
>> +    /**
>> +     * Affirms if the given member is selected for reflection. The
>> decision is
>> +     * based on the following truth table on both the class-level and 
>> +     * member-level annotation (null annotation represents MAYBE) 
>> +     * 
>> +     * Class   member  
>> +     * MAYBE   MAYBE   YES
>> +     * MAYBE   YES     YES
>> +     * MAYBE   NO      NO
>> +     *
>> +     * YES     MAYBE   YES
>> +     * YES     YES     YES
>> +     * YES     NO      NO
>> +     *
>> +     * NO      YES     YES
>> +     * NO      MAYBE   NO
>> +     * NO      NO      NO 
>> +     * 
>> +    */   
>> +    static boolean canReflect(Reflectable cls, Reflectable member) {
>> +        if (cls == null || cls.value()) {
>> +            return member == null || member.value() == true;
>> +        } else {
>> +            return member != null && member.value() == true;
>> +        }
>> +    }
>> +    
>> +    /**
>> +     * Affirms if the original declaration the given field is annotated
>> +     * for reflection. 
>> +     */
>> +    static boolean canReflect(Field field) {
>> +        Class cls = field.getDeclaringClass();
>> +        return
>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>> +            field.getAnnotation(Reflectable.class));
>> +    }
>> +    
>> +    /**
>> +     * Affirms if the original declaration the given method is annotated
>> +     * for reflection. 
>> +     */
>> +    static boolean canReflect(Method method) {
>> +        Class cls = getDeclaringClass(method);
>> +        if (cls != method.getDeclaringClass())
>> +            method = getDeclaringMethod(cls, method);
>> +        return
>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>> +            method.getAnnotation(Reflectable.class));
>> +    }
>> +    
>> +    /**
>> +     * Gets the declaring class of the given method signature but also
>> checks
>> +     * if the method is declared in an interface. If yes, then returns
>> the
>> +     * interface. 
>> +     */
>> +    public static Class getDeclaringClass(Method m) {
>> +        if (m == null)
>> +            return null;
>> +        Class cls = m.getDeclaringClass();
>> +        Class[] intfs =  cls.getInterfaces();
>> +        for (Class intf : intfs) {
>> +            if (getDeclaringMethod(intf, m) != null)
>> +                cls = intf;
>> +        }
>> +        return cls;
>> +    }
>> +    
>> +    /**
>> +     * Gets the method in the given class that has the same signature of
>> the
>> +     * given method, if exists. Otherwise, null.
>> +     */
>> +    public static Method getDeclaringMethod(Class c, Method m) {
>> +        try {
>> +            Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
>> +            return m0;
>> +        } catch (Exception e) {
>> +            return null;
>> +        }
>> +    }    
>>  }
>> 
>> Modified:
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java
>> Wed Feb 11 16:57:26 2009
>> @@ -247,12 +247,23 @@
>>          if (type.isAssignableFrom(o.getClass()))
>>              return o;
>>  
>> -        // the only non-numeric conversions we do are to string, or from
>> +        // the non-numeric conversions we do are to string, or from
>>          // string/char to number, or calendar/date
>> +        // String to Boolean
>> +        // String to Integer
>>          boolean num = o instanceof Number;
>>          if (!num) {
>>              if (type == String.class)
>>                  return o.toString();
>> +            else if (type == Boolean.class && o instanceof String) 
>> +                return Boolean.valueOf(o.toString());
>> +            else if (type == Integer.class && o instanceof String)
>> +                try {
>> +                    return new Integer(o.toString());
>> +                } catch (NumberFormatException e) {
>> +                    throw new
>> ClassCastException(_loc.get("cant-convert", o,
>> +                        o.getClass(), type).getMessage());
>> +                }
>>              else if (type == Character.class) {
>>                  String str = o.toString();
>>                  if (str != null && str.length() == 1)
>> 
>> Modified:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java
>> Wed Feb 11 16:57:26 2009
>> @@ -19,7 +19,9 @@
>>  package org.apache.openjpa.lib.conf;
>>  
>>  import java.io.File;
>> +import java.util.Collections;
>>  import java.util.List;
>> +import java.util.Set;
>>  
>>  /**
>>   * Abstract no-op product derivation for easy extension.
>> @@ -85,4 +87,8 @@
>>      
>>      public void beforeConfigurationClose(Configuration conf) {
>>      }
>> +        
>> +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.EMPTY_SET;
>> +    }
>>  }
>> 
>> Modified:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java
>> Wed Feb 11 16:57:26 2009
>> @@ -21,6 +21,7 @@
>>  import java.io.File;
>>  import java.io.IOException;
>>  import java.util.List;
>> +import java.util.Set;
>>  
>>  /**
>>   * Hooks for deriving products with additional functionality.
>> @@ -157,4 +158,13 @@
>>       * @since 0.9.7
>>       */
>>      public void beforeConfigurationClose(Configuration conf);
>> +    
>> +       
>> +    /**
>> +     * Return set of Query hint keys recognized by this receiver. 
>> +     * 
>> +     * @since 2.0.0
>> +     */
>> +    public Set<String> getSupportedQueryHints();
>> +
>>  }
>> 
>> Modified:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java
>> Wed Feb 11 16:57:26 2009
>> @@ -29,6 +29,8 @@
>>  import java.util.List;
>>  import java.util.Map;
>>  import java.util.MissingResourceException;
>> +import java.util.Set;
>> +import java.util.TreeSet;
>>  
>>  import org.apache.commons.lang.StringUtils;
>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>> @@ -434,6 +436,19 @@
>>                  collection.add(fqLoc);
>>          }
>>      }
>> +    
>> +    
>> +    public static Set<String> getSupportedQueryHints() {
>> +        Set<String> result = new TreeSet<String>();
>> +        // most specific to least
>> +        for (int i = _derivations.length - 1; i >= 0; i--) {
>> +            Set<String> members =
>> _derivations[i].getSupportedQueryHints();
>> +            if (members != null || !members.isEmpty())
>> +                result.addAll(members);
>> +        }
>> +        return result;
>> +    }
>> +
>>  
>>      /**
>>       * Compare {@link ProductDerivation}s.
>> 
>> Added:
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>> (added)
>> +++
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,25 @@
>> +package org.apache.openjpa.lib.util;
>> +
>> +import static java.lang.annotation.ElementType.FIELD;
>> +import static java.lang.annotation.ElementType.METHOD;
>> +import static java.lang.annotation.ElementType.TYPE;
>> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
>> +
>> +import java.lang.annotation.Retention;
>> +import java.lang.annotation.Target;
>> +
>> +/**
>> + * Annotates a getter method or field so {@link Reflection reflection 
>> + * utility} to control whether the annotated member is recorded during
>> scanning 
>> + * for bean-style method or field.
>> + * 
>> + * @author Pinaki Poddar
>> + * 
>> + * @since 2.0.0
>> + *
>> + */
>> +@Target({TYPE, METHOD, FIELD})
>> +@Retention(RUNTIME)
>> +public @interface Reflectable {
>> +    boolean value() default true;
>> +}
>> 
>> Added:
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>> (added)
>> +++
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,167 @@
>> +/*
>> + * 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.conf;
>> +
>> +import javax.persistence.EntityManager;
>> +import javax.persistence.Query;
>> +
>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>> +import org.apache.openjpa.kernel.QueryHints;
>> +import org.apache.openjpa.persistence.HintHandler;
>> +import org.apache.openjpa.persistence.OpenJPAPersistence;
>> +import org.apache.openjpa.persistence.OpenJPAQuery;
>> +import org.apache.openjpa.persistence.test.SingleEMFTestCase;
>> +
>> +/**
>> + * Tests JPA 2.0 API methods {@link Query#getSupportedHints()} and 
>> + * {@link Query#getHints()}. 
>> + * 
>> + * @author Pinaki Poddar
>> + *
>> + */
>> +public class TestQueryHints extends SingleEMFTestCase {
>> +    EntityManager em;
>> +    OpenJPAQuery query;
>> +    
>> +    public void setUp() {
>> +       super.setUp((Object[])null);
>> +       em = emf.createEntityManager();
>> +       String sql = "select * from Person";
>> +       query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
>> +    }
>> +    
>> +    public void testSupportedHintsContainProductDerivationHints() {
>> +        assertSupportedHint(OracleDictionary.SELECT_HINT, true);
>> +    }
>> +    
>> +    public void testSupportedHintsContainFetchPlanHints() {
>> +        assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
>> +    }
>> +
>> +    public void
>> testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
>> +        assertSupportedHint("openjpa.FetchPlan.QueryResultCache",
>> false);
>> +    }
>> +    
>> +    public void testSupportedHintsContainQueryProperty() {
>> +        assertSupportedHint("openjpa.Subclasses", true);
>> +    }
>> +    
>> +    public void testSupportedHintsContainKernelQueryHints() {
>> +        assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY,
>> true);
>> +    }
>> +    
>> +    public void testSupportedHintsContainJPAQueryHints() {
>> +        assertSupportedHint("javax.persistence.query.timeout", true);
>> +    }
>> +    
>> +    public void testUnrecognizedKeyIsIgnored() {
>> +        String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
>> +        query.setHint(unrecognizedKey, "xyz");
>> +        assertFalse(query.getHints().containsKey(unrecognizedKey));
>> +       
>> assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
>> +     }
>> +    
>> +    public void testRecognizedKeyIsNotRecordedButAvailable() {
>> +        String recognizedKey = "openjpa.some.derivation.hint";
>> +        query.setHint(recognizedKey, "abc");
>> +        assertFalse(query.getHints().containsKey(recognizedKey));
>> +        assertEquals("abc",
>> query.getFetchPlan().getDelegate().getHint(recognizedKey));
>> +    }
>> +
>> +    public void testSupportedKeyIsRecordedAndAvailable() {
>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>> +        query.setHint(supportedKey, 42);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(42, query.getFetchPlan().getFetchBatchSize());
>> +    }
>> +    
>> +    public void testSupportedKeyWrongValue() {
>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>> +        short goodValue = (short)42;
>> +        float badValue = 57.9f;
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(goodValue,
>> query.getFetchPlan().getFetchBatchSize());
>> +        
>> +        try {
>> +            query.setHint(supportedKey, badValue);
>> +            fail("Expected to fail to set " + supportedKey + " hint to "
>> + badValue);
>> +        } catch (IllegalArgumentException e) {
>> +            // good
>> +        }
>> +    }
>> +    
>> +    public void testSupportedKeyIntegerValueConversion() {
>> +        String supportedKey = "openjpa.hint.OptimizeResultCount";
>> +        String goodValue = "57";
>> +        int badValue = -3;
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(57,
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +        
>> +        try {
>> +            query.setHint(supportedKey, badValue);
>> +            fail("Expected to fail to set " + supportedKey + " hint to "
>> + badValue);
>> +        } catch (IllegalArgumentException e) {
>> +            // good
>> +        }
>> +    }
>> +
>> +    public void testSupportedKeyBooleanValueConversion() {
>> +        String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
>> +        String goodValue = "true";
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(true,
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +        
>> +        goodValue = "false";
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(false,
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +    }
>> +    
>> +    public void testJPAHintSetsFetchPlan() {
>> +        String jpaKey = "javax.persistence.query.timeout";
>> +        query.setHint(jpaKey, 5671);
>> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
>> +    }
>> +    
>> +    public void testParts() {
>> +        HintHandler.HintKeyComparator test = new
>> HintHandler.HintKeyComparator();
>> +        assertEquals(0, test.countDots("a"));
>> +        assertEquals(1, test.countDots("a.b"));
>> +        assertEquals(2, test.countDots("a.b.c"));
>> +        assertEquals(3, test.countDots("a.b.c.d"));
>> +        assertEquals(4, test.countDots("a.b.c.d."));
>> +        assertEquals(1, test.countDots("."));
>> +        assertEquals(0, test.countDots(""));
>> +        assertEquals(0, test.countDots(null));
>> +    }
>> +    
>> +    void assertSupportedHint(String hint, boolean contains) {
>> +        if (contains)
>> +            assertTrue("Expeceted suppoerted hint [" + hint + "]",
>> +                query.getSupportedHints().contains(hint));
>> +        else
>> +            assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
>> +                query.getSupportedHints().contains(hint));
>> +    }
>> +    
>> +    
>> +}
>> 
>> Modified:
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java
>> Wed Feb 11 16:57:26 2009
>> @@ -22,6 +22,7 @@
>>  import javax.persistence.LockModeType;
>>  
>>  import org.apache.openjpa.kernel.FetchConfiguration;
>> +import org.apache.openjpa.lib.util.Reflectable;
>>  import org.apache.openjpa.meta.FetchGroup;
>>  
>>  /**
>> @@ -88,6 +89,7 @@
>>       *
>>       * @since 1.0.0
>>       */
>> +    @Reflectable(false)
>>      public boolean getQueryResultCacheEnabled();
>>  
>>      /**
>> @@ -102,6 +104,7 @@
>>      /**
>>       * @deprecated use {@link #getQueryResultCacheEnabled()} instead.
>>       */
>> +    @Reflectable(false)
>>      public boolean getQueryResultCache();
>>  
>>      /**
>> @@ -293,5 +296,6 @@
>>       * @deprecated cast to {@link FetchPlanImpl} instead. This
>>       * method pierces the published-API boundary, as does the SPI cast.
>>       */
>> +    @Reflectable(false)
>>      public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
>>  }
>> 
>> Added:
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>> (added)
>> +++
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,365 @@
>> +package org.apache.openjpa.persistence;
>> +
>> +import static
>> org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
>> +import static
>> org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
>> +import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
>> +
>> +import java.lang.reflect.Method;
>> +import java.lang.reflect.Modifier;
>> +import java.util.Collections;
>> +import java.util.Comparator;
>> +import java.util.Map;
>> +import java.util.Set;
>> +import java.util.TreeMap;
>> +import java.util.TreeSet;
>> +
>> +import org.apache.openjpa.conf.OpenJPAConfiguration;
>> +import org.apache.openjpa.enhance.Reflection;
>> +import org.apache.openjpa.kernel.FetchConfiguration;
>> +import org.apache.openjpa.kernel.Filters;
>> +import org.apache.openjpa.kernel.exps.AggregateListener;
>> +import org.apache.openjpa.kernel.exps.FilterListener;
>> +import org.apache.openjpa.lib.conf.ProductDerivation;
>> +import org.apache.openjpa.lib.conf.ProductDerivations;
>> +import org.apache.openjpa.lib.log.Log;
>> +import org.apache.openjpa.lib.util.Localizer;
>> +import org.apache.openjpa.lib.util.StringDistance;
>> +
>> +
>> +/**
>> + * Manages query hint keys and handles their values on behalf of a
>> owning
>> + * {@link QueryImpl}. Uses specific knowledge of hint keys declared in
>> + * different parts of the system.
>> + * 
>> + * This receiver collects hint keys from different parts of the system.
>> The
>> + * keys are implicitly or explicitly declared by several different
>> mechanics.
>> + * This receiver sets the values on behalf of a owning {@link QueryImpl}
>> + * based on the its specific knowledge of these keys.
>> + * 
>> + * The hint keys from following sources are collected and handled: 
>> + * 
>> + * 1. {@link org.apache.openjpa.kernel.QueryHints} interface declares
>> hint keys
>> + *    as public static final fields. These fields are collected by
>> reflection.
>> + *    The values are handled by invoking methods on the owning {@link
>> QueryImpl}
>> + *    
>> + * 2. Some hint keys are collected from bean-style property names of
>> {@link 
>> + *    JDBCFetchPlan} by {@link
>> Reflection#getBeanStylePropertyNames(Class) 
>> + *    reflection} and prefixed with <code>openjpa.FetchPlan</code>. 
>> + *    Their values are used to set the corresponding property of {@link 
>> + *    FetchPlan} via {@link #hintToSetter(FetchPlan, String, Object)
>> reflection}
>> + *      
>> + * 3. Currently defined <code>javax.persistence.*</code> hint keys have  
>> + *    a equivalent counterpart to one of these FetchPlan keys. 
>> + *    The JPA keys are mapped to equivalent FetchPlan hint keys.
>> + *    
>> + * 4. Some keys directly invoke setters or add listeners to the owning 
>> + *    {@link QueryImpl}. These hint keys are statically declared in 
>> + *    this receiver itself. 
>> + *    
>> + * 5. ProductDerivation may introduce their own query hint keys via
>> {@link 
>> + *    ProductDerivation#getSupportedQueryHints()}. Their values are set
>> in the 
>> + *    {@link FetchConfiguration#setHint(String, Object)}
>> + *     
>> + *  A hint key is classified into one of the following three categories:
>> + *  
>> + *  1. Supported: A key is known to this receiver as collected from
>> different 
>> + *     parts of the system. The value of a supported key is recorded and 
>> + *     available via {@link #getHints()} method. 
>> + *  2. Recognized: A key is not known to this receiver but has a prefix
>> which
>> + *     is known to this receiver. The value of a recognized key is not
>> recorded 
>> + *     but its value is available via {@link
>> FetchConfiguration#getHint(String)}
>> + *  3. Unrecognized: A key is neither supported nor recognized. The
>> value of a 
>> + *     unrecognized key is neither recorded nor set anywhere.
>> + *  
>> + *  If an incompatible value is supplied for a supported key, a
>> non-fatal
>> + *  {@link ArgumentException} is raised.
>> + *  
>> + * @author Pinaki Poddar
>> + *
>> + * @since 2.0.0
>> + * 
>> + * @nojavadoc
>> + */
>> +public class HintHandler {
>> +    private final QueryImpl owner;
>> +    private Map<String, Object> _hints;
>> +    private Set<String> _supportedKeys;
>> +    private Set<String> _supportedPrefixes;
>> +    
>> +    static final String PREFIX_JPA = "javax.persistence.";
>> +    static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
>> +    
>> +    // These keys are directly handled in {@link QueryImpl} class.
>> +    // Declaring a public static final String variable in this class
>> will 
>> +    // make it register as a supported hint key
>> +    // if you do not want that then annotate as {@link
>> Reflectable(false)}.
>> +    public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
>> +    public static final String HINT_FILTER_LISTENER =
>> "openjpa.FilterListener";
>> +    public static final String HINT_FILTER_LISTENERS = 
>> +        "openjpa.FilterListeners";
>> +    public static final String HINT_AGGREGATE_LISTENER = 
>> +        "openjpa.AggregateListener";
>> +    public static final String HINT_AGGREGATE_LISTENERS = 
>> +        "openjpa.AggregateListeners";
>> +    
>> +    // JPA Specification 2.0 keys are mapped to equivalent FetchPlan
>> keys
>> +    public static Map<String,String> _jpaKeys = new TreeMap<String,
>> String>();
>> +    static {
>> +        _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"), 
>> +            addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
>> +    }
>> +    
>> +    private static final String DOT = ".";
>> +    private static final String BLANK = "";
>> +    private static final Localizer _loc = Localizer.forPackage(
>> +        HintHandler.class);
>> +    
>> +    public HintHandler(QueryImpl impl) {
>> +        owner = impl;
>> +    }
>> +    
>> +    /**
>> +     * Gets all the recorded hint keys and their values.
>> +     */
>> +    @SuppressWarnings("unchecked")
>> +    public Map<String, Object> getHints() {
>> +        return _hints == null ? Collections.EMPTY_MAP 
>> +            : Collections.unmodifiableMap(_hints);
>> +    }
>> +    
>> +    /**
>> +     * Record a key-value pair only only if the given key is supported.
>> +     * 
>> +     * @return FALSE if the key is unrecognized. 
>> +     *         null (i.e. MAY BE) if the key is recognized, but not
>> supported.
>> +     *         TRUE if the key is supported.
>> +     */
>> +    public Boolean record(String hint, Object value) {
>> +        if (hint == null)
>> +            return Boolean.FALSE;
>> +        if (isSupported(hint)) {
>> +            if (_hints == null)
>> +                _hints = new TreeMap<String, Object>();
>> +            _hints.put(hint, value);
>> +            return Boolean.TRUE;
>> +        }
>> +        
>> +        Log log = owner.getDelegate().getBroker().getConfiguration()
>> +            .getLog(OpenJPAConfiguration.LOG_RUNTIME);
>> +        String possible =
>> StringDistance.getClosestLevenshteinDistance(hint, 
>> +            getSupportedHints());
>> +        if (log.isWarnEnabled()) {
>> +            log.warn(_loc.get("bad-query-hint", hint, possible));
>> +        }
>> +        return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
>> +    }
>> +    
>> +    /**
>> +     * Gets all the supported hint keys. The set of supported hint keys
>> is
>> +     * statically determined by collecting hint keys from the
>> ProductDerivations
>> +     * and reflecting upon some of the known classes.
>> +     */
>> +    public Set<String> getSupportedHints() {
>> +        if (_supportedKeys == null) {
>> +            _supportedKeys = new TreeSet<String>(new
>> HintKeyComparator());
>> +            _supportedPrefixes = new TreeSet<String>();
>> +            
>> +            _supportedKeys.addAll(Reflection.getFieldValues(
>> +                org.apache.openjpa.kernel.QueryHints.class, 
>> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
>> +                String.class));
>> +
>> +            _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN, 
>> +                Reflection.getBeanStylePropertyNames(
>> +                    owner.getFetchPlan().getClass())));
>> +
>> +            _supportedKeys.addAll(_jpaKeys.keySet());
>> +
>> +            _supportedKeys.addAll(Reflection.getFieldValues(
>> +                HintHandler.class, 
>> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
>> +                String.class));
>> +
>> +           
>> _supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());    
>> +            
>> +            for (String key : _supportedKeys) {
>> +                _supportedPrefixes.add(getPrefixOf(key));
>> +            }
>> +        }
>> +        return _supportedKeys;
>> +    }
>> +    
>> +    /**
>> +     * Add a hint key to the set of supported hint keys.
>> +     */
>> +    public void addHintKey(String key) {
>> +        getSupportedHints().add(key);
>> +        _supportedPrefixes.add(getPrefixOf(key));
>> +    }
>> +    
>> +    public Set<String> getKnownPrefixes() {
>> +        getSupportedHints();
>> +        return _supportedPrefixes;
>> +    }
>> +    
>> +    /**
>> +     * Affirms the given key matches one of the supported keys.
>> +     */
>> +    public boolean isSupported(String key) {
>> +        return getSupportedHints().contains(key);
>> +    }
>> +    
>> +    /**
>> +     * Affirms the given key has a prefix that matches with any of the 
>> +     * supported prefixes.
>> +     */
>> +    public boolean isSupportedPrefix(String key) {
>> +        return getKnownPrefixes().contains(getPrefixOf(key));
>> +    }
>> +    
>> +    static Set<String> addPrefix(String prefix, Set<String> original) {
>> +        Set<String> result = new TreeSet<String>();
>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>> +        for (String o : original)
>> +            result.add(prefix + join + o);
>> +        return result;
>> +    }
>> +    
>> +    static String addPrefix(String prefix, String original) {
>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>> +        return prefix + join + original;
>> +    }
>> +    
>> +    public static String removePrefix(String key, String prefix) {
>> +        if (prefix == null)
>> +            return key;
>> +        if (!prefix.endsWith(DOT))
>> +            prefix = prefix + DOT;
>> +        if (key != null && key.startsWith(prefix))
>> +            return key.substring(prefix.length());
>> +        return key;
>> +    }
>> +    
>> +    static String getPrefixOf(String key) {
>> +        int index = key == null ? -1 : key.indexOf(DOT);
>> +        return (index != -1) ? key.substring(0,index) : key;
>> +    }
>> +    
>> +    boolean isKnownHintPrefix(String key) {
>> +        String prefix = getPrefixOf(key);
>> +        return getKnownPrefixes().contains(prefix);
>> +    }
>> +    
>> +    public static boolean hasPrefix(String key, String prefix) {
>> +        if (key == null || prefix == null)
>> +            return false;
>> +        if (!prefix.endsWith(DOT))
>> +            prefix = prefix + DOT;
>> +        return key.startsWith(prefix);
>> +    }
>> +    
>> +    public void setHint(String key, Object value) {
>> +        Boolean record = record(key, value);
>> +        FetchConfiguration fetch =
>> owner.getDelegate().getFetchConfiguration();
>> +        ClassLoader loader =
>> owner.getDelegate().getBroker().getClassLoader();
>> +        if (record == Boolean.FALSE)
>> +            return;
>> +        if (record == null) {
>> +            fetch.setHint(key, value);
>> +            return;
>> +        }
>> +        try {
>> +            if (HINT_SUBCLASSES.equals(key)) {
>> +                if (value instanceof String)
>> +                    value = Boolean.valueOf((String) value);
>> +                owner.setSubclasses(((Boolean) value).booleanValue());
>> +            } else if (HINT_FILTER_LISTENER.equals(key))
>> +               
>> owner.addFilterListener(Filters.hintToFilterListener(value, 
>> +                    loader));
>> +            else if (HINT_FILTER_LISTENERS.equals(key)) {
>> +                FilterListener[] arr =
>> Filters.hintToFilterListeners(value, 
>> +                    loader);
>> +                for (int i = 0; i < arr.length; i++)
>> +                    owner.addFilterListener(arr[i]);
>> +            } else if (HINT_AGGREGATE_LISTENER.equals(key))
>> +               
>> owner.addAggregateListener(Filters.hintToAggregateListener(
>> +                    value, loader));
>> +            else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
>> +                AggregateListener[] arr =
>> Filters.hintToAggregateListeners(
>> +                        value, loader);
>> +                for (int i = 0; i < arr.length; i++)
>> +                    owner.addAggregateListener(arr[i]);
>> +            } else if (isFetchPlanHint(key)) {
>> +                hintToSetter(owner.getFetchPlan(), 
>> +                    getFetchPlanProperty(key), value);
>> +            } else if (HINT_RESULT_COUNT.equals(key)) {
>> +                int v = (Integer)Filters.convert(value, Integer.class);
>> +                if (v < 0)
>> +                    throw new
>> ArgumentException(_loc.get("bad-query-hint-value", 
>> +                        key, value), null,  null, false);
>> +                    fetch.setHint(key, v);
>> +            }  else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
>> +                fetch.setHint(key, Filters.convert(value,
>> Boolean.class));
>> +                owner.invalidatePreparedQuery();
>> +            } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>> +                fetch.setHint(key, Filters.convert(value,
>> Boolean.class));
>> +                owner.ignorePreparedQuery();
>> +            } else { // default 
>> +                fetch.setHint(key, value);
>> +            }
>> +            return;
>> +        } catch (IllegalArgumentException iae) {
>> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
>> +                key, value), null,  null, false);
>> +        } catch (ClassCastException ce) {
>> +            throw new ArgumentException(_loc.get("bad-query-hint-value", 
>> +                key, ce.getMessage()), null,  null, false);
>> +        } catch (Exception e) {
>> +            throw PersistenceExceptions.toPersistenceException(e);
>> +        }
>> +    }
>> +    
>> +    private boolean isFetchPlanHint(String key) {
>> +        return key.startsWith(PREFIX_FETCHPLAN) 
>> +           || (_jpaKeys.containsKey(key) &&
>> isFetchPlanHint(_jpaKeys.get(key)));
>> +    }
>> +    
>> +    private String getFetchPlanProperty(String key) {
>> +        if (key.startsWith(PREFIX_FETCHPLAN))
>> +            return removePrefix(key, PREFIX_FETCHPLAN);
>> +        else if (_jpaKeys.containsKey(key))
>> +            return getFetchPlanProperty(_jpaKeys.get(key));
>> +        else
>> +            return key;
>> +    }
>> +    
>> +    private void hintToSetter(FetchPlan fetchPlan, String k, Object
>> value) {
>> +        if (fetchPlan == null || k == null)
>> +            return;
>> +
>> +        Method setter = Reflection.findSetter(fetchPlan.getClass(), k,
>> true);
>> +        Class paramType = setter.getParameterTypes()[0];
>> +        if (Enum.class.isAssignableFrom(paramType) && value instanceof
>> String)
>> +            value = Enum.valueOf(paramType, (String) value);
>> +
>> +        Filters.hintToSetter(fetchPlan, k, value);
>> +    }
>> +    
>> +    public static class HintKeyComparator implements Comparator<String>
>> {
>> +        public int compare(String s1, String s2) {
>> +            if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
>> +                int n1 = countDots(s1);
>> +                int n2 = countDots(s2);
>> +                return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
>> +            } else
>> +                return s1.compareTo(s2);
>> +        }
>> +        
>> +        public int countDots(String s) {
>> +            if (s == null || s.length() == 0)
>> +                return 0;
>> +            int index = s.indexOf(DOT);
>> +            return (index == -1) ? 0 : countDots(s.substring(index+1)) +
>> 1;
>> +        }
>> +    }
>> +}
>> 
>> Modified:
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java
>> Wed Feb 11 16:57:26 2009
>> @@ -18,8 +18,9 @@
>>   */
>>  package org.apache.openjpa.persistence;
>>  
>> +import static
>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>> +
>>  import java.io.Serializable;
>> -import java.lang.reflect.Method;
>>  import java.sql.Time;
>>  import java.sql.Timestamp;
>>  import java.util.ArrayList;
>> @@ -41,15 +42,12 @@
>>  import javax.persistence.TemporalType;
>>  
>>  import org.apache.openjpa.conf.OpenJPAConfiguration;
>> -import org.apache.openjpa.enhance.Reflection;
>>  import org.apache.openjpa.kernel.Broker;
>>  import org.apache.openjpa.kernel.DelegatingQuery;
>>  import org.apache.openjpa.kernel.DelegatingResultList;
>>  import org.apache.openjpa.kernel.FetchConfiguration;
>> -import org.apache.openjpa.kernel.Filters;
>>  import org.apache.openjpa.kernel.PreparedQuery;
>>  import org.apache.openjpa.kernel.PreparedQueryCache;
>> -import org.apache.openjpa.kernel.QueryHints;
>>  import org.apache.openjpa.kernel.QueryLanguages;
>>  import org.apache.openjpa.kernel.QueryOperations;
>>  import org.apache.openjpa.kernel.QueryStatistics;
>> @@ -63,8 +61,6 @@
>>  import org.apache.openjpa.util.RuntimeExceptionTranslator;
>>  import org.apache.openjpa.util.UserException;
>>  
>> -import static
>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>> -
>>  /**
>>   * Implementation of {@link Query} interface.
>>   * 
>> @@ -85,6 +81,8 @@
>>  	private Map<String, Object> _named;
>>  	private Map<Integer, Object> _positional;
>>  	private String _id;
>> +	
>> +	private final HintHandler _hintHandler;
>>  
>>  	/**
>>  	 * Constructor; supply factory exception translator and delegate.
>> @@ -97,6 +95,7 @@
>>  			org.apache.openjpa.kernel.Query query) {
>>  		_em = em;
>>  		_query = new DelegatingQuery(query, ret);
>> +		_hintHandler = new HintHandler(this);
>>  	}
>>  
>>  	/**
>> @@ -254,7 +253,7 @@
>>  	}
>>  	
>>  	private Object execute() {
>> -		if (_query.getOperation() != QueryOperations.OP_SELECT)
>> +		if (!isNative() && _query.getOperation() != QueryOperations.OP_SELECT)
>>  			throw new InvalidStateException(_loc.get("not-select-query", _query
>>  					.getQueryString()), null, null, false);
>>  		
>> @@ -351,81 +350,8 @@
>>  
>>  	public OpenJPAQuery setHint(String key, Object value) {
>>  		_em.assertNotCloseInvoked();
>> -		if (key == null)
>> -			return this;
>> -		if (!key.startsWith("openjpa.")) {
>> -			_query.getFetchConfiguration().setHint(key, value);
>> -			return this;
>> -		}
>> -		String k = key.substring("openjpa.".length());
>> -
>> -		try {
>> -			if ("Subclasses".equals(k)) {
>> -				if (value instanceof String)
>> -					value = Boolean.valueOf((String) value);
>> -				setSubclasses(((Boolean) value).booleanValue());
>> -			} else if ("FilterListener".equals(k))
>> -				addFilterListener(Filters.hintToFilterListener(value, _query
>> -						.getBroker().getClassLoader()));
>> -			else if ("FilterListeners".equals(k)) {
>> -				FilterListener[] arr = Filters.hintToFilterListeners(value,
>> -						_query.getBroker().getClassLoader());
>> -				for (int i = 0; i < arr.length; i++)
>> -					addFilterListener(arr[i]);
>> -			} else if ("AggregateListener".equals(k))
>> -				addAggregateListener(Filters.hintToAggregateListener(value,
>> -						_query.getBroker().getClassLoader()));
>> -			else if ("FilterListeners".equals(k)) {
>> -				AggregateListener[] arr = Filters.hintToAggregateListeners(
>> -						value, _query.getBroker().getClassLoader());
>> -				for (int i = 0; i < arr.length; i++)
>> -					addAggregateListener(arr[i]);
>> -			} else if (k.startsWith("FetchPlan.")) {
>> -				k = k.substring("FetchPlan.".length());
>> -				hintToSetter(getFetchPlan(), k, value);
>> -			} else if (k.startsWith("hint.")) {
>> -				if ("hint.OptimizeResultCount".equals(k)) {
>> -					if (value instanceof String) {
>> -						try {
>> -							value = new Integer((String) value);
>> -						} catch (NumberFormatException nfe) {
>> -						}
>> -					}
>> -					if (!(value instanceof Number)
>> -							|| ((Number) value).intValue() < 0)
>> -						throw new ArgumentException(_loc.get(
>> -								"bad-query-hint-value", key, value), null,
>> -								null, false);
>> -                    _query.getFetchConfiguration().setHint(key, value);
>> -				}  else if (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
>> -                    (key)) {
>> -                    _query.getFetchConfiguration().setHint(key,
>> (Boolean)value);
>> -                    invalidatePreparedQuery();
>> -                } else if
>> (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>> -                    _query.getFetchConfiguration().setHint(key,
>> (Boolean)value);
>> -                    ignorePreparedQuery();
>> -                } else {
>> -                    _query.getFetchConfiguration().setHint(key, value);
>> -                }
>> -            } else
>> -				throw new ArgumentException(_loc.get("bad-query-hint", key),
>> -						null, null, false);
>> -			return this;
>> -		} catch (Exception e) {
>> -			throw PersistenceExceptions.toPersistenceException(e);
>> -		}
>> -	}
>> -
>> -	private void hintToSetter(FetchPlan fetchPlan, String k, Object value)
>> {
>> -		if (fetchPlan == null || k == null)
>> -			return;
>> -
>> -		Method setter = Reflection.findSetter(fetchPlan.getClass(), k, true);
>> -		Class paramType = setter.getParameterTypes()[0];
>> -		if (Enum.class.isAssignableFrom(paramType) && value instanceof String)
>> -			value = Enum.valueOf(paramType, (String) value);
>> -
>> -		Filters.hintToSetter(fetchPlan, k, value);
>> +		_hintHandler.setHint(key, value);
>> +		return this;
>>  	}
>>  
>>  	public OpenJPAQuery setParameter(int position, Calendar value,
>> @@ -613,7 +539,7 @@
>>  	 */
>>      //TODO: JPA 2.0 Hints that are not set to FetchConfiguration 
>>      public Map<String, Object> getHints() {
>> -        return _query.getFetchConfiguration().getHints();
>> +        return _hintHandler.getHints();
>>      }
>>  
>>      public LockModeType getLockMode() {
>> @@ -622,8 +548,7 @@
>>      }
>>  
>>      public Set<String> getSupportedHints() {
>> -        throw new UnsupportedOperationException(
>> -            "JPA 2.0 - Method not yet implemented");
>> +        return _hintHandler.getSupportedHints();
>>      }
>>  
>>      public Query setLockMode(LockModeType lockMode) {
>> @@ -704,7 +629,7 @@
>>      /**
>>       * Remove this query from PreparedQueryCache. 
>>       */
>> -    private boolean invalidatePreparedQuery() {
>> +    boolean invalidatePreparedQuery() {
>>          PreparedQueryCache cache = _em.getPreparedQueryCache();
>>          if (cache == null)
>>              return false;
>> @@ -716,7 +641,7 @@
>>       * Ignores this query from PreparedQueryCache by recreating the
>> original
>>       * query if it has been cached. 
>>       */
>> -    private void ignorePreparedQuery() {
>> +    void ignorePreparedQuery() {
>>          PreparedQuery cached = _em.getPreparedQuery(_id);
>>          if (cached == null)
>>              return;
>> 
>> Modified:
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>> (original)
>> +++
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties
>> Wed Feb 11 16:57:26 2009
>> @@ -70,7 +70,7 @@
>>  mult-results: Query returned multiple results: "{0}".
>>  no-pos-named-params-mix: Cannot mix named and positional parameters in
>> query \
>>  	"{0}".
>> -bad-query-hint: "{0}" is not a recognized query hint.
>> +bad-query-hint: "{0}" is not a supported query hint. May be you meant
>> "{1}"?
>>  bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
>>  detached: Cannot perform this operation on detached entity "{0}".
>>  removed: Cannot perform this operation on removed entity "{0}".
>> 
>> Modified:
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>> URL:
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff
>> ==============================================================================
>> ---
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>> (original)
>> +++
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java
>> Wed Feb 11 16:57:26 2009
>> @@ -18,7 +18,9 @@
>>   */
>>  package org.apache.openjpa.slice;
>>  
>> +import java.util.Collections;
>>  import java.util.Map;
>> +import java.util.Set;
>>  
>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>> @@ -98,4 +100,8 @@
>>          	log.warn(_loc.get("forced-set-config", 
>>          		prefix+"."+v.getProperty(), forced));
>>      }
>> +    
>> +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.singleton(HINT_TARGET);
>> +    }
>>  }
>> 
>> 
>> 
> 
> 

-- 
View this message in context: http://n2.nabble.com/Re%3A-svn-commit%3A-r743396---in--openjpa-trunk%3A-openjpa-jdbc-src-main-java-org-apache-openjpa-jdbc-conf--openjpa-kernel-src-main-java-org-apache-openjpa-enhance--openjpa-kernel-src-main-java-org-ap-tp2310190p2311099.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.


Re: svn commit: r743396 - Question about TestQueryHints.java

Posted by Donald Woods <dw...@apache.org>.

Donald Woods wrote:
> In 
> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java 
> 
> 
> Is this right?
> +    public void testJPAHintSetsFetchPlan() {
> +        String jpaKey = "javax.persistence.query.timeout";
> +        query.setHint(jpaKey, 5671);
> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
> +    }
> +
> 
> Shouldn't it be fetching the QueryTimeout instead?
> +    public void testJPAHintSetsFetchPlan() {
> +        String jpaKey = "javax.persistence.query.timeout";
> +        query.setHint(jpaKey, 5671);
> +        assertEquals(5671, query.getFetchPlan().getQueryTimeout());
> +    }
> +
> 
> 
> -Donald
> 
> ppoddar@apache.org wrote:
>> Author: ppoddar
>> Date: Wed Feb 11 16:57:26 2009
>> New Revision: 743396
>>
>> URL: http://svn.apache.org/viewvc?rev=743396&view=rev
>> Log:
>> OPENJPA-900: Consolidate Query Hint related processing. As a 
>> byproduct, support new JPA 2.0 getHins() and getSupportedHints().
>>
>> Added:
>>     
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java 
>>
>>     
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java 
>>
>>     
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java 
>>
>> Modified:
>>     
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java 
>>
>>     
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java 
>>
>>     
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java 
>>
>>     
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java 
>>
>>     
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java 
>>
>>     
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java 
>>
>>     
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java 
>>
>>     
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java 
>>
>>     
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties 
>>
>>     
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java 
>>
>>
>> Modified: 
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -18,11 +18,14 @@
>>   */
>>  package org.apache.openjpa.jdbc.conf;
>>  
>> +import java.util.Collections;
>>  import java.util.Map;
>> +import java.util.Set;
>>  
>>  import org.apache.openjpa.conf.BrokerFactoryValue;
>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>  import org.apache.openjpa.jdbc.kernel.JDBCBrokerFactory;
>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>>  import org.apache.openjpa.lib.conf.ConfigurationProvider;
>>  
>> @@ -48,4 +51,9 @@
>>          }
>>          return false;
>>      }
>> +    +    @Override
>> +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.singleton(OracleDictionary.SELECT_HINT);
>> +    }
>>  }
>>
>> Modified: 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/enhance/Reflection.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -21,13 +21,18 @@
>>  import java.lang.reflect.AccessibleObject;
>>  import java.lang.reflect.Field;
>>  import java.lang.reflect.InvocationTargetException;
>> +import java.lang.reflect.Member;
>>  import java.lang.reflect.Method;
>>  import java.lang.reflect.Modifier;
>>  import java.security.AccessController;
>> +import java.util.Collections;
>> +import java.util.Set;
>> +import java.util.TreeSet;
>>  
>>  import org.apache.commons.lang.StringUtils;
>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>>  import org.apache.openjpa.lib.util.Localizer;
>> +import org.apache.openjpa.lib.util.Reflectable;
>>  import org.apache.openjpa.util.GeneralException;  import 
>> org.apache.openjpa.util.UserException;  
>> @@ -754,4 +759,166 @@
>>      public static void set(Object target, Method setter, short value) {
>>          set(target, setter, new Short(value));
>>      }
>> +    +    /**
>> +     * Gets all bean-style property names of the given Class or its 
>> superclass.
>> +     * A bean-style property 'abc' exists in Class C iff C has 
>> declared +     * following pair of methods:
>> +     *   public void setAbc(Y y) or public C setAbc(Y y)
>> +     *   public Y getAbc();
>> +     *   +     * If a getter property is annotated with {@link 
>> Reflectable}, then
>> +     * it is ignored.
>> +     *   +     */
>> +    public static Set<String> getBeanStylePropertyNames(Class c) {
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Method[] methods = c.getMethods();
>> +        if (methods == null || methods.length < 2)
>> +            return Collections.EMPTY_SET;
>> +        Set<String> result = new TreeSet<String>();
>> +        for (Method m : methods) {
>> +            if (m.getName().startsWith("get")) {
>> +                if (!canReflect(m))
>> +                    continue;
>> +                String prop = StringUtils.capitalize(m.getName()
>> +                    .substring("get".length()));
>> +                Class rtype = m.getReturnType();
>> +                try {
>> +                  Method setter = c.getMethod("set"+prop, new 
>> Class[]{rtype});
>> +                  if (setter.getReturnType() == void.class || 
>> +                      setter.getReturnType().isAssignableFrom(c))
>> +                  result.add(prop);
>> +                } catch (NoSuchMethodException e) {
>> +                    +                }
>> +            }
>> +        }
>> +        return result;
>> +    }
>> +    +    /**
>> +     * Gets all public field names of the given Class.
>> +     *   +     */
>> +    public static Set<String> getPublicFieldNames(Class c) {
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Field[] fields = c.getFields();
>> +        if (fields == null || fields.length == 0)
>> +            return Collections.EMPTY_SET;
>> +        Set<String> result = new TreeSet<String>();
>> +        for (Field f : fields) {
>> +            if (canReflect(f))
>> +                result.add(f.getName());
>> +        }
>> +        return result;
>> +    }
>> +    +    /**
>> +     * Gets values of all field f the given class such that f exactly 
>> +     * match the given modifiers and are of given type (Object 
>> implies any type)
>> +     * unless f is annotated as {@link Reflectable}. +     *   +     */
>> +    public static <T> Set<T> getFieldValues(Class c, int mods, 
>> Class<T> t){
>> +        if (c == null)
>> +            return Collections.EMPTY_SET;
>> +        Field[] fields = c.getFields();
>> +        if (fields == null || fields.length == 0)
>> +            return Collections.EMPTY_SET;
>> +        Set<T> result = new TreeSet<T>();
>> +        for (Field f : fields) {
>> +            if (mods == f.getModifiers() +            && (t == 
>> Object.class || t.isAssignableFrom(f.getType()))
>> +            && canReflect(f)) {
>> +                try {
>> +                    result.add((T)f.get(null));
>> +                } catch (IllegalArgumentException e) {
>> +                } catch (IllegalAccessException e) {
>> +                }
>> +            }
>> +        }
>> +        return result;
>> +    }
>> +
>> +    /**
>> +     * Affirms if the given member is selected for reflection. The 
>> decision is
>> +     * based on the following truth table on both the class-level and 
>> +     * member-level annotation (null annotation represents MAYBE) 
>> +     * +     * Class   member  +     * MAYBE   MAYBE   YES
>> +     * MAYBE   YES     YES
>> +     * MAYBE   NO      NO
>> +     *
>> +     * YES     MAYBE   YES
>> +     * YES     YES     YES
>> +     * YES     NO      NO
>> +     *
>> +     * NO      YES     YES
>> +     * NO      MAYBE   NO
>> +     * NO      NO      NO +     * +    */   +    static boolean 
>> canReflect(Reflectable cls, Reflectable member) {
>> +        if (cls == null || cls.value()) {
>> +            return member == null || member.value() == true;
>> +        } else {
>> +            return member != null && member.value() == true;
>> +        }
>> +    }
>> +    +    /**
>> +     * Affirms if the original declaration the given field is annotated
>> +     * for reflection. +     */
>> +    static boolean canReflect(Field field) {
>> +        Class cls = field.getDeclaringClass();
>> +        return 
>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>> +            field.getAnnotation(Reflectable.class));
>> +    }
>> +    +    /**
>> +     * Affirms if the original declaration the given method is annotated
>> +     * for reflection. +     */
>> +    static boolean canReflect(Method method) {
>> +        Class cls = getDeclaringClass(method);
>> +        if (cls != method.getDeclaringClass())
>> +            method = getDeclaringMethod(cls, method);
>> +        return 
>> canReflect((Reflectable)cls.getAnnotation(Reflectable.class), 
>> +            method.getAnnotation(Reflectable.class));
>> +    }
>> +    +    /**
>> +     * Gets the declaring class of the given method signature but 
>> also checks
>> +     * if the method is declared in an interface. If yes, then 
>> returns the
>> +     * interface. +     */
>> +    public static Class getDeclaringClass(Method m) {
>> +        if (m == null)
>> +            return null;
>> +        Class cls = m.getDeclaringClass();
>> +        Class[] intfs =  cls.getInterfaces();
>> +        for (Class intf : intfs) {
>> +            if (getDeclaringMethod(intf, m) != null)
>> +                cls = intf;
>> +        }
>> +        return cls;
>> +    }
>> +    +    /**
>> +     * Gets the method in the given class that has the same signature 
>> of the
>> +     * given method, if exists. Otherwise, null.
>> +     */
>> +    public static Method getDeclaringMethod(Class c, Method m) {
>> +        try {
>> +            Method m0 = c.getMethod(m.getName(), m.getParameterTypes());
>> +            return m0;
>> +        } catch (Exception e) {
>> +            return null;
>> +        }
>> +    }     }
>>
>> Modified: 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-kernel/src/main/java/org/apache/openjpa/kernel/Filters.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -247,12 +247,23 @@
>>          if (type.isAssignableFrom(o.getClass()))
>>              return o;
>>  
>> -        // the only non-numeric conversions we do are to string, or from
>> +        // the non-numeric conversions we do are to string, or from
>>          // string/char to number, or calendar/date
>> +        // String to Boolean
>> +        // String to Integer
>>          boolean num = o instanceof Number;
>>          if (!num) {
>>              if (type == String.class)
>>                  return o.toString();
>> +            else if (type == Boolean.class && o instanceof String) 
>> +                return Boolean.valueOf(o.toString());
>> +            else if (type == Integer.class && o instanceof String)
>> +                try {
>> +                    return new Integer(o.toString());
>> +                } catch (NumberFormatException e) {
>> +                    throw new 
>> ClassCastException(_loc.get("cant-convert", o,
>> +                        o.getClass(), type).getMessage());
>> +                }
>>              else if (type == Character.class) {
>>                  String str = o.toString();
>>                  if (str != null && str.length() == 1)
>>
>> Modified: 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/AbstractProductDerivation.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -19,7 +19,9 @@
>>  package org.apache.openjpa.lib.conf;
>>  
>>  import java.io.File;
>> +import java.util.Collections;
>>  import java.util.List;
>> +import java.util.Set;
>>  
>>  /**
>>   * Abstract no-op product derivation for easy extension.
>> @@ -85,4 +87,8 @@
>>           public void beforeConfigurationClose(Configuration conf) {
>>      }
>> +        +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.EMPTY_SET;
>> +    }
>>  }
>>
>> Modified: 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivation.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -21,6 +21,7 @@
>>  import java.io.File;
>>  import java.io.IOException;
>>  import java.util.List;
>> +import java.util.Set;
>>  
>>  /**
>>   * Hooks for deriving products with additional functionality.
>> @@ -157,4 +158,13 @@
>>       * @since 0.9.7
>>       */
>>      public void beforeConfigurationClose(Configuration conf);
>> +    +       +    /**
>> +     * Return set of Query hint keys recognized by this receiver. 
>> +     * +     * @since 2.0.0
>> +     */
>> +    public Set<String> getSupportedQueryHints();
>> +
>>  }
>>
>> Modified: 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/conf/ProductDerivations.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -29,6 +29,8 @@
>>  import java.util.List;
>>  import java.util.Map;
>>  import java.util.MissingResourceException;
>> +import java.util.Set;
>> +import java.util.TreeSet;
>>  
>>  import org.apache.commons.lang.StringUtils;
>>  import org.apache.openjpa.lib.util.J2DoPrivHelper;
>> @@ -434,6 +436,19 @@
>>                  collection.add(fqLoc);
>>          }
>>      }
>> +    +    +    public static Set<String> getSupportedQueryHints() {
>> +        Set<String> result = new TreeSet<String>();
>> +        // most specific to least
>> +        for (int i = _derivations.length - 1; i >= 0; i--) {
>> +            Set<String> members = 
>> _derivations[i].getSupportedQueryHints();
>> +            if (members != null || !members.isEmpty())
>> +                result.addAll(members);
>> +        }
>> +        return result;
>> +    }
>> +
>>  
>>      /**
>>       * Compare {@link ProductDerivation}s.
>>
>> Added: 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java?rev=743396&view=auto 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java 
>> (added)
>> +++ 
>> openjpa/trunk/openjpa-lib/src/main/java/org/apache/openjpa/lib/util/Reflectable.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,25 @@
>> +package org.apache.openjpa.lib.util;
>> +
>> +import static java.lang.annotation.ElementType.FIELD;
>> +import static java.lang.annotation.ElementType.METHOD;
>> +import static java.lang.annotation.ElementType.TYPE;
>> +import static java.lang.annotation.RetentionPolicy.RUNTIME;
>> +
>> +import java.lang.annotation.Retention;
>> +import java.lang.annotation.Target;
>> +
>> +/**
>> + * Annotates a getter method or field so {@link Reflection reflection 
>> + * utility} to control whether the annotated member is recorded 
>> during scanning + * for bean-style method or field.
>> + * + * @author Pinaki Poddar
>> + * + * @since 2.0.0
>> + *
>> + */
>> +@Target({TYPE, METHOD, FIELD})
>> +@Retention(RUNTIME)
>> +public @interface Reflectable {
>> +    boolean value() default true;
>> +}
>>
>> Added: 
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java?rev=743396&view=auto 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java 
>> (added)
>> +++ 
>> openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/conf/TestQueryHints.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,167 @@
>> +/*
>> + * 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.conf;
>> +
>> +import javax.persistence.EntityManager;
>> +import javax.persistence.Query;
>> +
>> +import org.apache.openjpa.jdbc.sql.OracleDictionary;
>> +import org.apache.openjpa.kernel.QueryHints;
>> +import org.apache.openjpa.persistence.HintHandler;
>> +import org.apache.openjpa.persistence.OpenJPAPersistence;
>> +import org.apache.openjpa.persistence.OpenJPAQuery;
>> +import org.apache.openjpa.persistence.test.SingleEMFTestCase;
>> +
>> +/**
>> + * Tests JPA 2.0 API methods {@link Query#getSupportedHints()} and + 
>> * {@link Query#getHints()}. + * + * @author Pinaki Poddar
>> + *
>> + */
>> +public class TestQueryHints extends SingleEMFTestCase {
>> +    EntityManager em;
>> +    OpenJPAQuery query;
>> +    +    public void setUp() {
>> +       super.setUp((Object[])null);
>> +       em = emf.createEntityManager();
>> +       String sql = "select * from Person";
>> +       query = OpenJPAPersistence.cast(em.createNativeQuery(sql));
>> +    }
>> +    +    public void testSupportedHintsContainProductDerivationHints() {
>> +        assertSupportedHint(OracleDictionary.SELECT_HINT, true);
>> +    }
>> +    +    public void testSupportedHintsContainFetchPlanHints() {
>> +        assertSupportedHint("openjpa.FetchPlan.LockTimeout", true);
>> +    }
>> +
>> +    public void 
>> testSupportedHintsIgnoresSomeFetchPlanBeanStyleProperty() {
>> +        assertSupportedHint("openjpa.FetchPlan.QueryResultCache", 
>> false);
>> +    }
>> +    +    public void testSupportedHintsContainQueryProperty() {
>> +        assertSupportedHint("openjpa.Subclasses", true);
>> +    }
>> +    +    public void testSupportedHintsContainKernelQueryHints() {
>> +        assertSupportedHint(QueryHints.HINT_IGNORE_PREPARED_QUERY, 
>> true);
>> +    }
>> +    +    public void testSupportedHintsContainJPAQueryHints() {
>> +        assertSupportedHint("javax.persistence.query.timeout", true);
>> +    }
>> +    +    public void testUnrecognizedKeyIsIgnored() {
>> +        String unrecognizedKey = "acme.org.hint.SomeThingUnknown";
>> +        query.setHint(unrecognizedKey, "xyz");
>> +        assertFalse(query.getHints().containsKey(unrecognizedKey));
>> +        
>> assertNull(query.getFetchPlan().getDelegate().getHint(unrecognizedKey));
>> +     }
>> +    +    public void testRecognizedKeyIsNotRecordedButAvailable() {
>> +        String recognizedKey = "openjpa.some.derivation.hint";
>> +        query.setHint(recognizedKey, "abc");
>> +        assertFalse(query.getHints().containsKey(recognizedKey));
>> +        assertEquals("abc", 
>> query.getFetchPlan().getDelegate().getHint(recognizedKey));
>> +    }
>> +
>> +    public void testSupportedKeyIsRecordedAndAvailable() {
>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>> +        query.setHint(supportedKey, 42);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(42, query.getFetchPlan().getFetchBatchSize());
>> +    }
>> +    +    public void testSupportedKeyWrongValue() {
>> +        String supportedKey = "openjpa.FetchPlan.FetchBatchSize";
>> +        short goodValue = (short)42;
>> +        float badValue = 57.9f;
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(goodValue, 
>> query.getFetchPlan().getFetchBatchSize());
>> +        +        try {
>> +            query.setHint(supportedKey, badValue);
>> +            fail("Expected to fail to set " + supportedKey + " hint 
>> to " + badValue);
>> +        } catch (IllegalArgumentException e) {
>> +            // good
>> +        }
>> +    }
>> +    +    public void testSupportedKeyIntegerValueConversion() {
>> +        String supportedKey = "openjpa.hint.OptimizeResultCount";
>> +        String goodValue = "57";
>> +        int badValue = -3;
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(57, 
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +        +        try {
>> +            query.setHint(supportedKey, badValue);
>> +            fail("Expected to fail to set " + supportedKey + " hint 
>> to " + badValue);
>> +        } catch (IllegalArgumentException e) {
>> +            // good
>> +        }
>> +    }
>> +
>> +    public void testSupportedKeyBooleanValueConversion() {
>> +        String supportedKey = QueryHints.HINT_IGNORE_PREPARED_QUERY;
>> +        String goodValue = "true";
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(true, 
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +        +        goodValue = "false";
>> +        query.setHint(supportedKey, goodValue);
>> +        assertTrue(query.getHints().containsKey(supportedKey));
>> +        assertEquals(false, 
>> query.getFetchPlan().getDelegate().getHint(supportedKey));
>> +    }
>> +    +    public void testJPAHintSetsFetchPlan() {
>> +        String jpaKey = "javax.persistence.query.timeout";
>> +        query.setHint(jpaKey, 5671);
>> +        assertEquals(5671, query.getFetchPlan().getLockTimeout());
>> +    }
>> +    +    public void testParts() {
>> +        HintHandler.HintKeyComparator test = new 
>> HintHandler.HintKeyComparator();
>> +        assertEquals(0, test.countDots("a"));
>> +        assertEquals(1, test.countDots("a.b"));
>> +        assertEquals(2, test.countDots("a.b.c"));
>> +        assertEquals(3, test.countDots("a.b.c.d"));
>> +        assertEquals(4, test.countDots("a.b.c.d."));
>> +        assertEquals(1, test.countDots("."));
>> +        assertEquals(0, test.countDots(""));
>> +        assertEquals(0, test.countDots(null));
>> +    }
>> +    +    void assertSupportedHint(String hint, boolean contains) {
>> +        if (contains)
>> +            assertTrue("Expeceted suppoerted hint [" + hint + "]",
>> +                query.getSupportedHints().contains(hint));
>> +        else
>> +            assertFalse("Unexpeceted suppoerted hint [" + hint + "]",
>> +                query.getSupportedHints().contains(hint));
>> +    }
>> +    +    +}
>>
>> Modified: 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/FetchPlan.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -22,6 +22,7 @@
>>  import javax.persistence.LockModeType;
>>  
>>  import org.apache.openjpa.kernel.FetchConfiguration;
>> +import org.apache.openjpa.lib.util.Reflectable;
>>  import org.apache.openjpa.meta.FetchGroup;
>>  
>>  /**
>> @@ -88,6 +89,7 @@
>>       *
>>       * @since 1.0.0
>>       */
>> +    @Reflectable(false)
>>      public boolean getQueryResultCacheEnabled();
>>  
>>      /**
>> @@ -102,6 +104,7 @@
>>      /**
>>       * @deprecated use {@link #getQueryResultCacheEnabled()} instead.
>>       */
>> +    @Reflectable(false)
>>      public boolean getQueryResultCache();
>>  
>>      /**
>> @@ -293,5 +296,6 @@
>>       * @deprecated cast to {@link FetchPlanImpl} instead. This
>>       * method pierces the published-API boundary, as does the SPI cast.
>>       */
>> +    @Reflectable(false)
>>      public org.apache.openjpa.kernel.FetchConfiguration getDelegate();
>>  }
>>
>> Added: 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java?rev=743396&view=auto 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java 
>> (added)
>> +++ 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/HintHandler.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -0,0 +1,365 @@
>> +package org.apache.openjpa.persistence;
>> +
>> +import static 
>> org.apache.openjpa.kernel.QueryHints.HINT_IGNORE_PREPARED_QUERY;
>> +import static 
>> org.apache.openjpa.kernel.QueryHints.HINT_INVALIDATE_PREPARED_QUERY;
>> +import static org.apache.openjpa.kernel.QueryHints.HINT_RESULT_COUNT;
>> +
>> +import java.lang.reflect.Method;
>> +import java.lang.reflect.Modifier;
>> +import java.util.Collections;
>> +import java.util.Comparator;
>> +import java.util.Map;
>> +import java.util.Set;
>> +import java.util.TreeMap;
>> +import java.util.TreeSet;
>> +
>> +import org.apache.openjpa.conf.OpenJPAConfiguration;
>> +import org.apache.openjpa.enhance.Reflection;
>> +import org.apache.openjpa.kernel.FetchConfiguration;
>> +import org.apache.openjpa.kernel.Filters;
>> +import org.apache.openjpa.kernel.exps.AggregateListener;
>> +import org.apache.openjpa.kernel.exps.FilterListener;
>> +import org.apache.openjpa.lib.conf.ProductDerivation;
>> +import org.apache.openjpa.lib.conf.ProductDerivations;
>> +import org.apache.openjpa.lib.log.Log;
>> +import org.apache.openjpa.lib.util.Localizer;
>> +import org.apache.openjpa.lib.util.StringDistance;
>> +
>> +
>> +/**
>> + * Manages query hint keys and handles their values on behalf of a 
>> owning
>> + * {@link QueryImpl}. Uses specific knowledge of hint keys declared in
>> + * different parts of the system.
>> + * + * This receiver collects hint keys from different parts of the 
>> system. The
>> + * keys are implicitly or explicitly declared by several different 
>> mechanics.
>> + * This receiver sets the values on behalf of a owning {@link QueryImpl}
>> + * based on the its specific knowledge of these keys.
>> + * + * The hint keys from following sources are collected and 
>> handled: + * + * 1. {@link org.apache.openjpa.kernel.QueryHints} 
>> interface declares hint keys
>> + *    as public static final fields. These fields are collected by 
>> reflection.
>> + *    The values are handled by invoking methods on the owning {@link 
>> QueryImpl}
>> + *    + * 2. Some hint keys are collected from bean-style property 
>> names of {@link + *    JDBCFetchPlan} by {@link 
>> Reflection#getBeanStylePropertyNames(Class) + *    reflection} and 
>> prefixed with <code>openjpa.FetchPlan</code>. + *    Their values are 
>> used to set the corresponding property of {@link + *    FetchPlan} via 
>> {@link #hintToSetter(FetchPlan, String, Object) reflection}
>> + *      + * 3. Currently defined <code>javax.persistence.*</code> 
>> hint keys have  + *    a equivalent counterpart to one of these 
>> FetchPlan keys. + *    The JPA keys are mapped to equivalent FetchPlan 
>> hint keys.
>> + *    + * 4. Some keys directly invoke setters or add listeners to 
>> the owning + *    {@link QueryImpl}. These hint keys are statically 
>> declared in + *    this receiver itself. + *    + * 5. 
>> ProductDerivation may introduce their own query hint keys via {@link + 
>> *    ProductDerivation#getSupportedQueryHints()}. Their values are set 
>> in the + *    {@link FetchConfiguration#setHint(String, Object)}
>> + *     + *  A hint key is classified into one of the following three 
>> categories:
>> + *  + *  1. Supported: A key is known to this receiver as collected 
>> from different + *     parts of the system. The value of a supported 
>> key is recorded and + *     available via {@link #getHints()} method. 
>> + *  2. Recognized: A key is not known to this receiver but has a 
>> prefix which
>> + *     is known to this receiver. The value of a recognized key is 
>> not recorded + *     but its value is available via {@link 
>> FetchConfiguration#getHint(String)}
>> + *  3. Unrecognized: A key is neither supported nor recognized. The 
>> value of a + *     unrecognized key is neither recorded nor set anywhere.
>> + *  + *  If an incompatible value is supplied for a supported key, a 
>> non-fatal
>> + *  {@link ArgumentException} is raised.
>> + *  + * @author Pinaki Poddar
>> + *
>> + * @since 2.0.0
>> + * + * @nojavadoc
>> + */
>> +public class HintHandler {
>> +    private final QueryImpl owner;
>> +    private Map<String, Object> _hints;
>> +    private Set<String> _supportedKeys;
>> +    private Set<String> _supportedPrefixes;
>> +    +    static final String PREFIX_JPA = "javax.persistence.";
>> +    static final String PREFIX_FETCHPLAN = "openjpa.FetchPlan.";
>> +    +    // These keys are directly handled in {@link QueryImpl} class.
>> +    // Declaring a public static final String variable in this class 
>> will +    // make it register as a supported hint key
>> +    // if you do not want that then annotate as {@link 
>> Reflectable(false)}.
>> +    public static final String HINT_SUBCLASSES = "openjpa.Subclasses";
>> +    public static final String HINT_FILTER_LISTENER = 
>> "openjpa.FilterListener";
>> +    public static final String HINT_FILTER_LISTENERS = +        
>> "openjpa.FilterListeners";
>> +    public static final String HINT_AGGREGATE_LISTENER = +        
>> "openjpa.AggregateListener";
>> +    public static final String HINT_AGGREGATE_LISTENERS = +        
>> "openjpa.AggregateListeners";
>> +    +    // JPA Specification 2.0 keys are mapped to equivalent 
>> FetchPlan keys
>> +    public static Map<String,String> _jpaKeys = new TreeMap<String, 
>> String>();
>> +    static {
>> +        _jpaKeys.put(addPrefix(PREFIX_JPA, "query.timeout"), 
>> +            addPrefix(PREFIX_FETCHPLAN, "LockTimeout"));
>> +    }
>> +    +    private static final String DOT = ".";
>> +    private static final String BLANK = "";
>> +    private static final Localizer _loc = Localizer.forPackage(
>> +        HintHandler.class);
>> +    +    public HintHandler(QueryImpl impl) {
>> +        owner = impl;
>> +    }
>> +    +    /**
>> +     * Gets all the recorded hint keys and their values.
>> +     */
>> +    @SuppressWarnings("unchecked")
>> +    public Map<String, Object> getHints() {
>> +        return _hints == null ? Collections.EMPTY_MAP +            : 
>> Collections.unmodifiableMap(_hints);
>> +    }
>> +    +    /**
>> +     * Record a key-value pair only only if the given key is supported.
>> +     * +     * @return FALSE if the key is unrecognized. +     
>> *         null (i.e. MAY BE) if the key is recognized, but not supported.
>> +     *         TRUE if the key is supported.
>> +     */
>> +    public Boolean record(String hint, Object value) {
>> +        if (hint == null)
>> +            return Boolean.FALSE;
>> +        if (isSupported(hint)) {
>> +            if (_hints == null)
>> +                _hints = new TreeMap<String, Object>();
>> +            _hints.put(hint, value);
>> +            return Boolean.TRUE;
>> +        }
>> +        +        Log log = 
>> owner.getDelegate().getBroker().getConfiguration()
>> +            .getLog(OpenJPAConfiguration.LOG_RUNTIME);
>> +        String possible = 
>> StringDistance.getClosestLevenshteinDistance(hint, +            
>> getSupportedHints());
>> +        if (log.isWarnEnabled()) {
>> +            log.warn(_loc.get("bad-query-hint", hint, possible));
>> +        }
>> +        return (isKnownHintPrefix(hint)) ? null : Boolean.FALSE;
>> +    }
>> +    +    /**
>> +     * Gets all the supported hint keys. The set of supported hint 
>> keys is
>> +     * statically determined by collecting hint keys from the 
>> ProductDerivations
>> +     * and reflecting upon some of the known classes.
>> +     */
>> +    public Set<String> getSupportedHints() {
>> +        if (_supportedKeys == null) {
>> +            _supportedKeys = new TreeSet<String>(new 
>> HintKeyComparator());
>> +            _supportedPrefixes = new TreeSet<String>();
>> +            +            
>> _supportedKeys.addAll(Reflection.getFieldValues(
>> +                org.apache.openjpa.kernel.QueryHints.class, 
>> +                Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL, 
>> +                String.class));
>> +
>> +            _supportedKeys.addAll(addPrefix(PREFIX_FETCHPLAN, 
>> +                Reflection.getBeanStylePropertyNames(
>> +                    owner.getFetchPlan().getClass())));
>> +
>> +            _supportedKeys.addAll(_jpaKeys.keySet());
>> +
>> +            _supportedKeys.addAll(Reflection.getFieldValues(
>> +                HintHandler.class, +                Modifier.PUBLIC | 
>> Modifier.STATIC | Modifier.FINAL, +                String.class));
>> +
>> +            
>> _supportedKeys.addAll(ProductDerivations.getSupportedQueryHints());    
>> +            +            for (String key : _supportedKeys) {
>> +                _supportedPrefixes.add(getPrefixOf(key));
>> +            }
>> +        }
>> +        return _supportedKeys;
>> +    }
>> +    +    /**
>> +     * Add a hint key to the set of supported hint keys.
>> +     */
>> +    public void addHintKey(String key) {
>> +        getSupportedHints().add(key);
>> +        _supportedPrefixes.add(getPrefixOf(key));
>> +    }
>> +    +    public Set<String> getKnownPrefixes() {
>> +        getSupportedHints();
>> +        return _supportedPrefixes;
>> +    }
>> +    +    /**
>> +     * Affirms the given key matches one of the supported keys.
>> +     */
>> +    public boolean isSupported(String key) {
>> +        return getSupportedHints().contains(key);
>> +    }
>> +    +    /**
>> +     * Affirms the given key has a prefix that matches with any of 
>> the +     * supported prefixes.
>> +     */
>> +    public boolean isSupportedPrefix(String key) {
>> +        return getKnownPrefixes().contains(getPrefixOf(key));
>> +    }
>> +    +    static Set<String> addPrefix(String prefix, Set<String> 
>> original) {
>> +        Set<String> result = new TreeSet<String>();
>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>> +        for (String o : original)
>> +            result.add(prefix + join + o);
>> +        return result;
>> +    }
>> +    +    static String addPrefix(String prefix, String original) {
>> +        String join = prefix.endsWith(DOT) ? BLANK : DOT;
>> +        return prefix + join + original;
>> +    }
>> +    +    public static String removePrefix(String key, String prefix) {
>> +        if (prefix == null)
>> +            return key;
>> +        if (!prefix.endsWith(DOT))
>> +            prefix = prefix + DOT;
>> +        if (key != null && key.startsWith(prefix))
>> +            return key.substring(prefix.length());
>> +        return key;
>> +    }
>> +    +    static String getPrefixOf(String key) {
>> +        int index = key == null ? -1 : key.indexOf(DOT);
>> +        return (index != -1) ? key.substring(0,index) : key;
>> +    }
>> +    +    boolean isKnownHintPrefix(String key) {
>> +        String prefix = getPrefixOf(key);
>> +        return getKnownPrefixes().contains(prefix);
>> +    }
>> +    +    public static boolean hasPrefix(String key, String prefix) {
>> +        if (key == null || prefix == null)
>> +            return false;
>> +        if (!prefix.endsWith(DOT))
>> +            prefix = prefix + DOT;
>> +        return key.startsWith(prefix);
>> +    }
>> +    +    public void setHint(String key, Object value) {
>> +        Boolean record = record(key, value);
>> +        FetchConfiguration fetch = 
>> owner.getDelegate().getFetchConfiguration();
>> +        ClassLoader loader = 
>> owner.getDelegate().getBroker().getClassLoader();
>> +        if (record == Boolean.FALSE)
>> +            return;
>> +        if (record == null) {
>> +            fetch.setHint(key, value);
>> +            return;
>> +        }
>> +        try {
>> +            if (HINT_SUBCLASSES.equals(key)) {
>> +                if (value instanceof String)
>> +                    value = Boolean.valueOf((String) value);
>> +                owner.setSubclasses(((Boolean) value).booleanValue());
>> +            } else if (HINT_FILTER_LISTENER.equals(key))
>> +                
>> owner.addFilterListener(Filters.hintToFilterListener(value, 
>> +                    loader));
>> +            else if (HINT_FILTER_LISTENERS.equals(key)) {
>> +                FilterListener[] arr = 
>> Filters.hintToFilterListeners(value, +                    loader);
>> +                for (int i = 0; i < arr.length; i++)
>> +                    owner.addFilterListener(arr[i]);
>> +            } else if (HINT_AGGREGATE_LISTENER.equals(key))
>> +                
>> owner.addAggregateListener(Filters.hintToAggregateListener(
>> +                    value, loader));
>> +            else if (HINT_AGGREGATE_LISTENERS.equals(key)) {
>> +                AggregateListener[] arr = 
>> Filters.hintToAggregateListeners(
>> +                        value, loader);
>> +                for (int i = 0; i < arr.length; i++)
>> +                    owner.addAggregateListener(arr[i]);
>> +            } else if (isFetchPlanHint(key)) {
>> +                hintToSetter(owner.getFetchPlan(), 
>> +                    getFetchPlanProperty(key), value);
>> +            } else if (HINT_RESULT_COUNT.equals(key)) {
>> +                int v = (Integer)Filters.convert(value, Integer.class);
>> +                if (v < 0)
>> +                    throw new 
>> ArgumentException(_loc.get("bad-query-hint-value", 
>> +                        key, value), null,  null, false);
>> +                    fetch.setHint(key, v);
>> +            }  else if (HINT_INVALIDATE_PREPARED_QUERY.equals(key)) {
>> +                fetch.setHint(key, Filters.convert(value, 
>> Boolean.class));
>> +                owner.invalidatePreparedQuery();
>> +            } else if (HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>> +                fetch.setHint(key, Filters.convert(value, 
>> Boolean.class));
>> +                owner.ignorePreparedQuery();
>> +            } else { // default +                fetch.setHint(key, 
>> value);
>> +            }
>> +            return;
>> +        } catch (IllegalArgumentException iae) {
>> +            throw new 
>> ArgumentException(_loc.get("bad-query-hint-value", +                
>> key, value), null,  null, false);
>> +        } catch (ClassCastException ce) {
>> +            throw new 
>> ArgumentException(_loc.get("bad-query-hint-value", +                
>> key, ce.getMessage()), null,  null, false);
>> +        } catch (Exception e) {
>> +            throw PersistenceExceptions.toPersistenceException(e);
>> +        }
>> +    }
>> +    +    private boolean isFetchPlanHint(String key) {
>> +        return key.startsWith(PREFIX_FETCHPLAN) +           || 
>> (_jpaKeys.containsKey(key) && isFetchPlanHint(_jpaKeys.get(key)));
>> +    }
>> +    +    private String getFetchPlanProperty(String key) {
>> +        if (key.startsWith(PREFIX_FETCHPLAN))
>> +            return removePrefix(key, PREFIX_FETCHPLAN);
>> +        else if (_jpaKeys.containsKey(key))
>> +            return getFetchPlanProperty(_jpaKeys.get(key));
>> +        else
>> +            return key;
>> +    }
>> +    +    private void hintToSetter(FetchPlan fetchPlan, String k, 
>> Object value) {
>> +        if (fetchPlan == null || k == null)
>> +            return;
>> +
>> +        Method setter = Reflection.findSetter(fetchPlan.getClass(), 
>> k, true);
>> +        Class paramType = setter.getParameterTypes()[0];
>> +        if (Enum.class.isAssignableFrom(paramType) && value 
>> instanceof String)
>> +            value = Enum.valueOf(paramType, (String) value);
>> +
>> +        Filters.hintToSetter(fetchPlan, k, value);
>> +    }
>> +    +    public static class HintKeyComparator implements 
>> Comparator<String> {
>> +        public int compare(String s1, String s2) {
>> +            if (getPrefixOf(s1).equals(getPrefixOf(s2))) {
>> +                int n1 = countDots(s1);
>> +                int n2 = countDots(s2);
>> +                return (n1 == n2) ? s1.compareTo(s2) : (n1 - n2);
>> +            } else
>> +                return s1.compareTo(s2);
>> +        }
>> +        +        public int countDots(String s) {
>> +            if (s == null || s.length() == 0)
>> +                return 0;
>> +            int index = s.indexOf(DOT);
>> +            return (index == -1) ? 0 : 
>> countDots(s.substring(index+1)) + 1;
>> +        }
>> +    }
>> +}
>>
>> Modified: 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/QueryImpl.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -18,8 +18,9 @@
>>   */
>>  package org.apache.openjpa.persistence;
>>  
>> +import static 
>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>> +
>>  import java.io.Serializable;
>> -import java.lang.reflect.Method;
>>  import java.sql.Time;
>>  import java.sql.Timestamp;
>>  import java.util.ArrayList;
>> @@ -41,15 +42,12 @@
>>  import javax.persistence.TemporalType;
>>  
>>  import org.apache.openjpa.conf.OpenJPAConfiguration;
>> -import org.apache.openjpa.enhance.Reflection;
>>  import org.apache.openjpa.kernel.Broker;
>>  import org.apache.openjpa.kernel.DelegatingQuery;
>>  import org.apache.openjpa.kernel.DelegatingResultList;
>>  import org.apache.openjpa.kernel.FetchConfiguration;
>> -import org.apache.openjpa.kernel.Filters;
>>  import org.apache.openjpa.kernel.PreparedQuery;
>>  import org.apache.openjpa.kernel.PreparedQueryCache;
>> -import org.apache.openjpa.kernel.QueryHints;
>>  import org.apache.openjpa.kernel.QueryLanguages;
>>  import org.apache.openjpa.kernel.QueryOperations;
>>  import org.apache.openjpa.kernel.QueryStatistics;
>> @@ -63,8 +61,6 @@
>>  import org.apache.openjpa.util.RuntimeExceptionTranslator;
>>  import org.apache.openjpa.util.UserException;
>>  
>> -import static 
>> org.apache.openjpa.kernel.QueryLanguages.LANG_PREPARED_SQL;
>> -
>>  /**
>>   * Implementation of {@link Query} interface.
>>   * @@ -85,6 +81,8 @@
>>      private Map<String, Object> _named;
>>      private Map<Integer, Object> _positional;
>>      private String _id;
>> +   
>> +    private final HintHandler _hintHandler;
>>  
>>      /**
>>       * Constructor; supply factory exception translator and delegate.
>> @@ -97,6 +95,7 @@
>>              org.apache.openjpa.kernel.Query query) {
>>          _em = em;
>>          _query = new DelegatingQuery(query, ret);
>> +        _hintHandler = new HintHandler(this);
>>      }
>>  
>>      /**
>> @@ -254,7 +253,7 @@
>>      }
>>     
>>      private Object execute() {
>> -        if (_query.getOperation() != QueryOperations.OP_SELECT)
>> +        if (!isNative() && _query.getOperation() != 
>> QueryOperations.OP_SELECT)
>>              throw new 
>> InvalidStateException(_loc.get("not-select-query", _query
>>                      .getQueryString()), null, null, false);
>>         
>> @@ -351,81 +350,8 @@
>>  
>>      public OpenJPAQuery setHint(String key, Object value) {
>>          _em.assertNotCloseInvoked();
>> -        if (key == null)
>> -            return this;
>> -        if (!key.startsWith("openjpa.")) {
>> -            _query.getFetchConfiguration().setHint(key, value);
>> -            return this;
>> -        }
>> -        String k = key.substring("openjpa.".length());
>> -
>> -        try {
>> -            if ("Subclasses".equals(k)) {
>> -                if (value instanceof String)
>> -                    value = Boolean.valueOf((String) value);
>> -                setSubclasses(((Boolean) value).booleanValue());
>> -            } else if ("FilterListener".equals(k))
>> -                addFilterListener(Filters.hintToFilterListener(value, 
>> _query
>> -                        .getBroker().getClassLoader()));
>> -            else if ("FilterListeners".equals(k)) {
>> -                FilterListener[] arr = 
>> Filters.hintToFilterListeners(value,
>> -                        _query.getBroker().getClassLoader());
>> -                for (int i = 0; i < arr.length; i++)
>> -                    addFilterListener(arr[i]);
>> -            } else if ("AggregateListener".equals(k))
>> -                
>> addAggregateListener(Filters.hintToAggregateListener(value,
>> -                        _query.getBroker().getClassLoader()));
>> -            else if ("FilterListeners".equals(k)) {
>> -                AggregateListener[] arr = 
>> Filters.hintToAggregateListeners(
>> -                        value, _query.getBroker().getClassLoader());
>> -                for (int i = 0; i < arr.length; i++)
>> -                    addAggregateListener(arr[i]);
>> -            } else if (k.startsWith("FetchPlan.")) {
>> -                k = k.substring("FetchPlan.".length());
>> -                hintToSetter(getFetchPlan(), k, value);
>> -            } else if (k.startsWith("hint.")) {
>> -                if ("hint.OptimizeResultCount".equals(k)) {
>> -                    if (value instanceof String) {
>> -                        try {
>> -                            value = new Integer((String) value);
>> -                        } catch (NumberFormatException nfe) {
>> -                        }
>> -                    }
>> -                    if (!(value instanceof Number)
>> -                            || ((Number) value).intValue() < 0)
>> -                        throw new ArgumentException(_loc.get(
>> -                                "bad-query-hint-value", key, value), 
>> null,
>> -                                null, false);
>> -                    _query.getFetchConfiguration().setHint(key, value);
>> -                }  else if 
>> (QueryHints.HINT_INVALIDATE_PREPARED_QUERY.equals
>> -                    (key)) {
>> -                    _query.getFetchConfiguration().setHint(key, 
>> (Boolean)value);
>> -                    invalidatePreparedQuery();
>> -                } else if 
>> (QueryHints.HINT_IGNORE_PREPARED_QUERY.equals(key)) {
>> -                    _query.getFetchConfiguration().setHint(key, 
>> (Boolean)value);
>> -                    ignorePreparedQuery();
>> -                } else {
>> -                    _query.getFetchConfiguration().setHint(key, value);
>> -                }
>> -            } else
>> -                throw new 
>> ArgumentException(_loc.get("bad-query-hint", key),
>> -                        null, null, false);
>> -            return this;
>> -        } catch (Exception e) {
>> -            throw PersistenceExceptions.toPersistenceException(e);
>> -        }
>> -    }
>> -
>> -    private void hintToSetter(FetchPlan fetchPlan, String k, Object 
>> value) {
>> -        if (fetchPlan == null || k == null)
>> -            return;
>> -
>> -        Method setter = Reflection.findSetter(fetchPlan.getClass(), 
>> k, true);
>> -        Class paramType = setter.getParameterTypes()[0];
>> -        if (Enum.class.isAssignableFrom(paramType) && value 
>> instanceof String)
>> -            value = Enum.valueOf(paramType, (String) value);
>> -
>> -        Filters.hintToSetter(fetchPlan, k, value);
>> +        _hintHandler.setHint(key, value);
>> +        return this;
>>      }
>>  
>>      public OpenJPAQuery setParameter(int position, Calendar value,
>> @@ -613,7 +539,7 @@
>>       */
>>      //TODO: JPA 2.0 Hints that are not set to FetchConfiguration      
>> public Map<String, Object> getHints() {
>> -        return _query.getFetchConfiguration().getHints();
>> +        return _hintHandler.getHints();
>>      }
>>  
>>      public LockModeType getLockMode() {
>> @@ -622,8 +548,7 @@
>>      }
>>  
>>      public Set<String> getSupportedHints() {
>> -        throw new UnsupportedOperationException(
>> -            "JPA 2.0 - Method not yet implemented");
>> +        return _hintHandler.getSupportedHints();
>>      }
>>  
>>      public Query setLockMode(LockModeType lockMode) {
>> @@ -704,7 +629,7 @@
>>      /**
>>       * Remove this query from PreparedQueryCache.       */
>> -    private boolean invalidatePreparedQuery() {
>> +    boolean invalidatePreparedQuery() {
>>          PreparedQueryCache cache = _em.getPreparedQueryCache();
>>          if (cache == null)
>>              return false;
>> @@ -716,7 +641,7 @@
>>       * Ignores this query from PreparedQueryCache by recreating the 
>> original
>>       * query if it has been cached.       */
>> -    private void ignorePreparedQuery() {
>> +    void ignorePreparedQuery() {
>>          PreparedQuery cached = _em.getPreparedQuery(_id);
>>          if (cached == null)
>>              return;
>>
>> Modified: 
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-persistence/src/main/resources/org/apache/openjpa/persistence/localizer.properties 
>> Wed Feb 11 16:57:26 2009
>> @@ -70,7 +70,7 @@
>>  mult-results: Query returned multiple results: "{0}".
>>  no-pos-named-params-mix: Cannot mix named and positional parameters 
>> in query \
>>      "{0}".
>> -bad-query-hint: "{0}" is not a recognized query hint.
>> +bad-query-hint: "{0}" is not a supported query hint. May be you meant 
>> "{1}"?
>>  bad-query-hint-value: Invalid value specified for query hint "{0}": {1}
>>  detached: Cannot perform this operation on detached entity "{0}".
>>  removed: Cannot perform this operation on removed entity "{0}".
>>
>> Modified: 
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java 
>>
>> URL: 
>> http://svn.apache.org/viewvc/openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java?rev=743396&r1=743395&r2=743396&view=diff 
>>
>> ============================================================================== 
>>
>> --- 
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java 
>> (original)
>> +++ 
>> openjpa/trunk/openjpa-slice/src/main/java/org/apache/openjpa/slice/ProductDerivation.java 
>> Wed Feb 11 16:57:26 2009
>> @@ -18,7 +18,9 @@
>>   */
>>  package org.apache.openjpa.slice;
>>  
>> +import java.util.Collections;
>>  import java.util.Map;
>> +import java.util.Set;
>>  
>>  import org.apache.openjpa.conf.OpenJPAProductDerivation;
>>  import org.apache.openjpa.lib.conf.AbstractProductDerivation;
>> @@ -98,4 +100,8 @@
>>              log.warn(_loc.get("forced-set-config",                  
>> prefix+"."+v.getProperty(), forced));
>>      }
>> +    +    public Set<String> getSupportedQueryHints() {
>> +        return Collections.singleton(HINT_TARGET);
>> +    }
>>  }
>>
>>
>>
>