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