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 19:24:16 UTC

Re: svn commit: r743396 - OracleDictionary.SELECT_HINT

In 
openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java

Should we be loading the Oracle hints by default?
+
+    @Override
+    public Set<String> getSupportedQueryHints() {
+        return Collections.singleton(OracleDictionary.SELECT_HINT);
+    }

Or does this get set elsewhere based on the apps configured dictionary?


-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 - OracleDictionary.SELECT_HINT

Posted by Pinaki Poddar <pp...@apache.org>.
Hi,
  Yes pondered over it too...

  The decision that reflects in the current check-in is based on the
following rationale:
1. ProductDerivation.getSupportedQueryHints() should announce whatever hint
keys they are aware of.
The effect of this announcement is that they appear in
jpa.Query.getSupportedHints(). 
2. The semantics of getSupportedHints() is whatever hints known to the
system -- not only the ones that are in effect. 
3. OracleDictionary exists at openjpa-jdbc module -- so corresponding PD
appeared to be the right place for announcement.

   But ready to listen to alternative arguments. 

   
Pinaki



Donald Woods wrote:
> 
> In 
> openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/conf/JDBCProductDerivation.java
> 
> Should we be loading the Oracle hints by default?
> +
> +    @Override
> +    public Set<String> getSupportedQueryHints() {
> +        return Collections.singleton(OracleDictionary.SELECT_HINT);
> +    }
> 
> Or does this get set elsewhere based on the apps configured dictionary?
> 
> 
> -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---OracleDictionary.SELECT_HINT-tp2310398p2310687.html
Sent from the OpenJPA Developers mailing list archive at Nabble.com.