001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.ejb.cmp3.base;
038:
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.Map;
042:
043: import oracle.toplink.essentials.config.*;
044:
045: import oracle.toplink.essentials.queryframework.DatabaseQuery;
046: import oracle.toplink.essentials.queryframework.ObjectLevelReadQuery;
047: import oracle.toplink.essentials.internal.localization.ExceptionLocalization;
048: import oracle.toplink.essentials.internal.sessions.AbstractSession;
049: import oracle.toplink.essentials.logging.SessionLog;
050:
051: /**
052: *
053: * The class processes query hints.
054: *
055: * TopLink query hints and their values defined in oracle.toplink.essentials.config package.
056: *
057: * To add a new query hint:
058: * Define a new hint in TopLinkQueryHints;
059: * Add a class containing hint's values if required to config package (like CacheUsage);
060: * Alternatively values defined in HintValues may be used - Refresh and BindParameters hints do that.
061: * Add an inner class to this class extending Hint corresponding to the new hint (like CacheUsageHint);
062: * The first constructor parameter is hint name; the second is default value;
063: * In constructor
064: * provide 2-dimensional value array in case the values should be translated (currently all Hint classes do that);
065: * in case translation is not required provide a single-dimension array (no such examples yet).
066: * In inner class Hint static initializer addHint an instance of the new hint class (like addHint(new CacheUsageHint())).
067: *
068: * @see TopLinkQueryHints
069: * @see HintValues
070: * @see CacheUsage
071: * @see PessimisticLock
072: *
073: */
074: public class QueryHintsHandler {
075:
076: /**
077: * INTERNAL:
078: * Verifies the hints.
079: *
080: * If session != null then logs a FINEST message for each hint.
081: * queryName parameter used only for identifying the query in messages,
082: * if it's null then "null" will be used.
083: * Throws IllegalArgumentException in case the hint value is illegal.
084: */
085: public static void verify(Map hints, String queryName,
086: AbstractSession session) {
087: if (hints == null) {
088: return;
089: }
090: Iterator it = hints.entrySet().iterator();
091: while (it.hasNext()) {
092: Map.Entry entry = (Map.Entry) it.next();
093: String hintName = (String) entry.getKey();
094: verify(hintName, entry.getValue(), queryName, session);
095: }
096: }
097:
098: /**
099: * INTERNAL:
100: * Verifies the hint.
101: *
102: * If session != null then logs a FINEST message.
103: * queryName parameter used only for identifying the query in messages,
104: * if it's null then "null" will be used.
105: * Throws IllegalArgumentException in case the hint value is illegal.
106: */
107: public static void verify(String hintName, Object hintValue,
108: String queryName, AbstractSession session) {
109: Hint.verify(hintName, shouldUseDefault(hintValue), hintValue,
110: queryName, session);
111: }
112:
113: /**
114: * INTERNAL:
115: * Applies the hints to the query.
116: * Throws IllegalArgumentException in case the hint value is illegal.
117: */
118: public static void apply(Map hints, DatabaseQuery query) {
119: if (hints == null) {
120: return;
121: }
122: Iterator it = hints.entrySet().iterator();
123: while (it.hasNext()) {
124: Map.Entry entry = (Map.Entry) it.next();
125: String hintName = (String) entry.getKey();
126: apply(hintName, entry.getValue(), query);
127: }
128: }
129:
130: /**
131: * INTERNAL:
132: * Applies the hint to the query.
133: * Throws IllegalArgumentException in case the hint value is illegal.
134: */
135: public static void apply(String hintName, Object hintValue,
136: DatabaseQuery query) {
137: Hint.apply(hintName, shouldUseDefault(hintValue), hintValue,
138: query);
139: }
140:
141: /**
142: * INTERNAL:
143: * Empty String hintValue indicates that the default hint value
144: * should be used.
145: */
146: protected static boolean shouldUseDefault(Object hintValue) {
147: return (hintValue != null) && (hintValue instanceof String)
148: && (((String) hintValue).length() == 0);
149: }
150:
151: protected static abstract class Hint {
152: static HashMap mainMap = new HashMap();
153: Object[] valueArray;
154: HashMap valueMap;
155: String name;
156: String defaultValue;
157: Object defaultValueToApply;
158: boolean valueToApplyMayBeNull;
159:
160: static {
161: addHint(new BindParametersHint());
162: addHint(new CacheUsageHint());
163: addHint(new PessimisticLockHint());
164: addHint(new RefreshHint());
165: addHint(new CascadePolicyHint());
166: }
167:
168: Hint(String name, String defaultValue) {
169: this .name = name;
170: this .defaultValue = defaultValue;
171: }
172:
173: abstract void applyToDatabaseQuery(Object valueToApply,
174: DatabaseQuery query);
175:
176: static void verify(String hintName, boolean shouldUseDefault,
177: Object hintValue, String queryName,
178: AbstractSession session) {
179: Hint hint = (Hint) mainMap.get(hintName);
180: if (hint == null) {
181: if (session != null) {
182: session.log(SessionLog.FINEST, SessionLog.QUERY,
183: "unknown_query_hint",
184: new Object[] { getPrintValue(queryName),
185: hintName });
186: }
187: return;
188: }
189:
190: hint
191: .verify(hintValue, shouldUseDefault, queryName,
192: session);
193: }
194:
195: void verify(Object hintValue, boolean shouldUseDefault,
196: String queryName, AbstractSession session) {
197: if (shouldUseDefault) {
198: hintValue = defaultValue;
199: }
200: if (session != null) {
201: session.log(SessionLog.FINEST, SessionLog.QUERY,
202: "query_hint", new Object[] {
203: getPrintValue(queryName), name,
204: getPrintValue(hintValue) });
205: }
206: if (!shouldUseDefault
207: && valueMap != null
208: && !valueMap
209: .containsKey(getUpperCaseString(hintValue))) {
210: throw new IllegalArgumentException(
211: ExceptionLocalization.buildMessage(
212: "ejb30-wrong-query-hint-value",
213: new Object[] {
214: getPrintValue(queryName), name,
215: getPrintValue(hintValue) }));
216: }
217: }
218:
219: static void apply(String hintName, boolean shouldUseDefault,
220: Object hintValue, DatabaseQuery query) {
221: Hint hint = (Hint) mainMap.get(hintName);
222: if (hint == null) {
223: // unknown hint name - silently ignored.
224: return;
225: }
226:
227: hint.apply(hintValue, shouldUseDefault, query);
228: }
229:
230: void apply(Object hintValue, boolean shouldUseDefault,
231: DatabaseQuery query) {
232: Object valueToApply = hintValue;
233: if (shouldUseDefault) {
234: valueToApply = defaultValueToApply;
235: } else {
236: if (valueMap != null) {
237: String key = getUpperCaseString(hintValue);
238: valueToApply = valueMap.get(key);
239: if (valueToApply == null) {
240: boolean wrongKey = true;
241: if (valueToApplyMayBeNull) {
242: wrongKey = !valueMap.containsKey(key);
243: }
244: if (wrongKey) {
245: throw new IllegalArgumentException(
246: ExceptionLocalization
247: .buildMessage(
248: "ejb30-wrong-query-hint-value",
249: new Object[] {
250: getQueryId(query),
251: name,
252: getPrintValue(hintValue) }));
253: }
254: }
255: }
256: }
257: applyToDatabaseQuery(valueToApply, query);
258: }
259:
260: static String getQueryId(DatabaseQuery query) {
261: String queryId = query.getName();
262: if (queryId == null) {
263: queryId = query.getEJBQLString();
264: }
265: return getPrintValue(queryId);
266: }
267:
268: static String getPrintValue(Object hintValue) {
269: return hintValue != null ? hintValue.toString() : "null";
270: }
271:
272: static String getUpperCaseString(Object hintValue) {
273: return hintValue != null ? hintValue.toString()
274: .toUpperCase() : null;
275: }
276:
277: void initialize() {
278: if (valueArray != null) {
279: valueMap = new HashMap(valueArray.length);
280: if (valueArray instanceof Object[][]) {
281: Object[][] valueArray2 = (Object[][]) valueArray;
282: for (int i = 0; i < valueArray2.length; i++) {
283: valueMap.put(
284: getUpperCaseString(valueArray2[i][0]),
285: valueArray2[i][1]);
286: if (valueArray2[i][1] == null) {
287: valueToApplyMayBeNull = true;
288: }
289: }
290: } else {
291: for (int i = 0; i < valueArray.length; i++) {
292: valueMap.put(getUpperCaseString(valueArray[i]),
293: valueArray[i]);
294: if (valueArray[i] == null) {
295: valueToApplyMayBeNull = true;
296: }
297: }
298: }
299: defaultValueToApply = valueMap.get(defaultValue
300: .toUpperCase());
301: }
302: }
303:
304: static void addHint(Hint hint) {
305: hint.initialize();
306: mainMap.put(hint.name, hint);
307: }
308: }
309:
310: protected static class BindParametersHint extends Hint {
311: BindParametersHint() {
312: super (TopLinkQueryHints.BIND_PARAMETERS,
313: HintValues.PERSISTENCE_UNIT_DEFAULT);
314: valueArray = new Object[][] {
315: { HintValues.PERSISTENCE_UNIT_DEFAULT, null },
316: { HintValues.TRUE, Boolean.TRUE },
317: { HintValues.FALSE, Boolean.FALSE } };
318: }
319:
320: void applyToDatabaseQuery(Object valueToApply,
321: DatabaseQuery query) {
322: if (valueToApply == null) {
323: query.ignoreBindAllParameters();
324: } else {
325: query
326: .setShouldBindAllParameters(((Boolean) valueToApply)
327: .booleanValue());
328: }
329: }
330: }
331:
332: protected static class CacheUsageHint extends Hint {
333: CacheUsageHint() {
334: super (TopLinkQueryHints.CACHE_USAGE, CacheUsage.DEFAULT);
335: valueArray = new Object[][] {
336: { CacheUsage.UseEntityDefault,
337: ObjectLevelReadQuery.UseDescriptorSetting },
338: { CacheUsage.DoNotCheckCache,
339: ObjectLevelReadQuery.DoNotCheckCache },
340: {
341: CacheUsage.CheckCacheByExactPrimaryKey,
342: ObjectLevelReadQuery.CheckCacheByExactPrimaryKey },
343: { CacheUsage.CheckCacheByPrimaryKey,
344: ObjectLevelReadQuery.CheckCacheByPrimaryKey },
345: { CacheUsage.CheckCacheThenDatabase,
346: ObjectLevelReadQuery.CheckCacheThenDatabase },
347: { CacheUsage.CheckCacheOnly,
348: ObjectLevelReadQuery.CheckCacheOnly },
349: {
350: CacheUsage.ConformResultsInUnitOfWork,
351: ObjectLevelReadQuery.ConformResultsInUnitOfWork } };
352: }
353:
354: void applyToDatabaseQuery(Object valueToApply,
355: DatabaseQuery query) {
356: if (query.isObjectLevelReadQuery()) {
357: ((ObjectLevelReadQuery) query)
358: .setCacheUsage(((Integer) valueToApply)
359: .intValue());
360: }
361: }
362: }
363:
364: protected static class CascadePolicyHint extends Hint {
365: CascadePolicyHint() {
366: super (TopLinkQueryHints.REFRESH_CASCADE,
367: CascadePolicy.DEFAULT);
368: valueArray = new Object[][] {
369: { CascadePolicy.NoCascading,
370: DatabaseQuery.NoCascading },
371: { CascadePolicy.CascadePrivateParts,
372: DatabaseQuery.CascadePrivateParts },
373: { CascadePolicy.CascadeAllParts,
374: DatabaseQuery.CascadeAllParts },
375: { CascadePolicy.CascadeByMapping,
376: DatabaseQuery.CascadeByMapping } };
377: }
378:
379: void applyToDatabaseQuery(Object valueToApply,
380: DatabaseQuery query) {
381: // this time cascade policy make sense only for read query with refresh option
382: // However cascade policy is generic property for DatabaseQuery,
383: // therefore can have a meaning for other types of query in the future.
384: if (query.isObjectLevelReadQuery()) {
385: query.setCascadePolicy((Integer) valueToApply);
386: }
387: }
388: }
389:
390: protected static class PessimisticLockHint extends Hint {
391: PessimisticLockHint() {
392: super (TopLinkQueryHints.PESSIMISTIC_LOCK,
393: PessimisticLock.DEFAULT);
394: valueArray = new Object[][] {
395: { PessimisticLock.NoLock,
396: ObjectLevelReadQuery.NO_LOCK },
397: { PessimisticLock.Lock, ObjectLevelReadQuery.LOCK },
398: { PessimisticLock.LockNoWait,
399: ObjectLevelReadQuery.LOCK_NOWAIT } };
400: }
401:
402: void applyToDatabaseQuery(Object valueToApply,
403: DatabaseQuery query) {
404: if (query.isObjectLevelReadQuery()) {
405: ((ObjectLevelReadQuery) query)
406: .setLockMode(((Short) valueToApply)
407: .shortValue());
408: }
409: }
410: }
411:
412: protected static class RefreshHint extends Hint {
413: RefreshHint() {
414: super (TopLinkQueryHints.REFRESH, HintValues.FALSE);
415: valueArray = new Object[][] {
416: { HintValues.FALSE, Boolean.FALSE },
417: { HintValues.TRUE, Boolean.TRUE } };
418: }
419:
420: void applyToDatabaseQuery(Object valueToApply,
421: DatabaseQuery query) {
422: if (query.isObjectLevelReadQuery()) {
423: ((ObjectLevelReadQuery) query)
424: .setShouldRefreshIdentityMapResult(((Boolean) valueToApply)
425: .booleanValue());
426: }
427: }
428: }
429:
430: }
|