001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999-2004 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: EjbqlQueryTreeHolder.java 6140 2005-01-22 01:42:53Z rhs $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_ejb.lib;
025:
026: import java.util.Collection;
027: import java.util.Iterator;
028: import java.util.Map;
029:
030: import org.objectweb.jonas_ejb.container.TraceEjb;
031: import org.objectweb.jonas_ejb.deployment.api.DeploymentDescEjb2;
032: import org.objectweb.jonas_ejb.deployment.api.EntityCmp2Desc;
033: import org.objectweb.jonas_ejb.deployment.api.MethodCmp2Desc;
034: import org.objectweb.jonas_ejb.deployment.ejbql.ASTEJBQL;
035: import org.objectweb.jorm.api.PMapper;
036: import org.objectweb.jorm.metainfo.api.Manager;
037: import org.objectweb.medor.api.Field;
038: import org.objectweb.medor.eval.api.QueryEvaluator;
039: import org.objectweb.medor.eval.lib.MedorEvaluator;
040: import org.objectweb.medor.expression.api.Expression;
041: import org.objectweb.medor.optim.api.QueryTransformer;
042: import org.objectweb.medor.query.api.OrderField;
043: import org.objectweb.medor.query.api.QueryTree;
044: import org.objectweb.medor.query.api.QueryTreeField;
045: import org.objectweb.medor.query.jorm.api.JormExtent;
046: import org.objectweb.medor.query.jorm.lib.ClassExtent;
047: import org.objectweb.medor.query.jorm.lib.JormQueryTreeHelper;
048: import org.objectweb.medor.query.jorm.lib.QueryBuilder;
049: import org.objectweb.medor.query.lib.QueryTreePrinter;
050: import org.objectweb.medor.query.lib.SelectProject;
051: import org.objectweb.util.monolog.api.BasicLevel;
052:
053: /**
054: * Class to hold the query tree of a given EJBQL request. This allows walking the
055: * JormExtents of the tree to set the mappers.
056: * @author Christophe Ney - cney@batisseurs.com : Initial developer
057: * @author Helene Joanin: Take into account the ORDER BY clause.
058: * @author Sebastien Chassande-Barrioz & Helene Joanin: prefetch code
059: * @author Helene Joanin: Take into account the aggregate select expression.
060: * @author Cyrille Blot: Take into account the LIMIT clause
061: */
062: public class EjbqlQueryTreeHolder {
063:
064: QueryTree queryTree = null;
065: QueryTransformer queryOptimizer = null;
066: QueryEvaluator optimizedQuery = null;
067: Field resField = null;
068: EjbqlLimiterRange[] limiterRanges = null;
069:
070: private String beanName;
071: private String methodName;
072:
073: /**
074: * Indicates the index of the identifier (PName) in the TupleCollection of
075: * the SelectProject node (root of of the tree).
076: */
077: private int prefetchIdentifierIndex;
078:
079: /**
080: * constructor of a holder for a specific query.
081: * @param mDesc JOnAS meta-information for the corresponding finder/select method
082: * @param ejbql root of the lexical tree of the query
083: * @param mapper mapper to associate at each leaves of the QueryTree.
084: * The mapper may be null in case of the QueryTree is build in the GenIC phase.
085: */
086: public EjbqlQueryTreeHolder(MethodCmp2Desc mDesc, ASTEJBQL ejbql,
087: PMapper mapper) throws Exception {
088:
089: EntityCmp2Desc eDesc = (EntityCmp2Desc) mDesc.getBeanDesc();
090: DeploymentDescEjb2 dd = (DeploymentDescEjb2) eDesc
091: .getDeploymentDesc();
092: Manager jormManager = dd.getJormManager();
093:
094: // create identifiers Map (visit ALL the query)
095: QueryBuilder qb = new QueryBuilder();
096: Map fields = (new EjbqlVariableVisitor(ejbql, dd, qb))
097: .getFields();
098:
099: // Begin PREFETCHING CODE (by Seb)
100: SelectProject sp = new SelectProject("");
101: if (mDesc.getPrefetch() && (mapper != null)) {
102: // Find the QueryTree of the ClassExtend of the bean of the current finder method
103: // WARNING: This only works for finder methods, and not for ejbSelect methods!!!
104: // (Indeed the result type of the query must be the bean type in which
105: // the method is defined)
106: ClassExtent ce = null;
107: QueryTree qt = null;
108: String jormClassName = ((EntityCmp2Desc) mDesc
109: .getBeanDesc()).getJormClassName();
110: Iterator itFields = fields.values().iterator();
111: boolean found = false;
112: while (itFields.hasNext() && !found) {
113: QueryTree cqt = ((QueryTreeField) itFields.next())
114: .getQueryTree();
115: Collection extents = JormQueryTreeHelper
116: .getJormExtents(cqt);
117: for (Iterator it = extents.iterator(); it.hasNext()
118: && !found;) {
119: JormExtent je = (JormExtent) it.next();
120: if (jormClassName.equals(je.getJormName())) {
121: found = true;
122: ce = (ClassExtent) je;
123: qt = cqt;
124: }
125: }
126: }
127: if ((ce == null) && (qt == null)) {
128: throw new Error(
129: "EjbqlQueryTreeHolder: cannot do the prefetch !!!");
130: }
131: setMapper(qt, mapper);
132: // Add fields of the class which the values must be prefetched, at
133: // the begin of the the TupleCollection
134: JormQueryTreeHelper.addPrefetchFields(ce, qt, sp);
135: prefetchIdentifierIndex = sp.getTupleStructure().getSize() + 1;
136: }
137: // End PREFETCHING CODE
138:
139: // create SelectProject (visit the SELECT clause)
140: EjbqlSelectVisitor selectVisitor = new EjbqlSelectVisitor(
141: ejbql, fields, sp);
142:
143: // create the Filter's expression (visit the WHERE clause)
144: Expression qf = new EjbqlQueryFilterVisitor(mapper, fields,
145: mDesc.getMethod().getParameterTypes(), ejbql, qb)
146: .getQueryFilter();
147: sp.setQueryFilter(qf);
148:
149: // create the OrderField list (visit the ORDER BY clause)
150: OrderField[] ofs = new EjbqlOrderByVisitor(ejbql, fields)
151: .getOrderFields();
152: if (ofs.length > 0) {
153: sp.setOrderBy(ofs);
154: }
155:
156: // create the EjbqlLimiterRange list (visit the LIMIT clause)
157: limiterRanges = new EjbqlLimitVisitor(ejbql, mDesc.getMethod()
158: .getParameterTypes()).getLimiterRanges();
159:
160: queryTree = selectVisitor.getQueryTree();
161:
162: setMapper(queryTree, mapper);
163:
164: // Get the field description of the result of the query
165: // Nota Bene: TupleStructures of queries non optimized and optimized are differents.
166: resField = queryTree.getTupleStructure().getField(
167: queryTree.getTupleStructure().getSize());
168:
169: // Just needed later for debug trace
170: beanName = eDesc.getJormClassName();
171: methodName = mDesc.getMethod().getName();
172: }
173:
174: private void setMapper(QueryTree tree, PMapper mapper) {
175: // set the mapper of each leaves of the QueryTree
176: if (mapper != null) {
177: Collection extents = JormQueryTreeHelper
178: .getJormExtents(tree);
179: for (Iterator it = extents.iterator(); it.hasNext();) {
180: JormExtent je = (JormExtent) it.next();
181: je.setPMapper(mapper, RdbMappingBuilder
182: .getProjectName());
183: }
184: }
185: }
186:
187: public int getPrefetchIndex() {
188: return prefetchIdentifierIndex;
189: }
190:
191: /**
192: * get the query tree that was built from visiting the lexical tree
193: */
194: public QueryTree getQueryTree() {
195: return queryTree;
196: }
197:
198: /**
199: * Set the optimizer to be used when optimizing the query tree
200: */
201: public void setQueryOptimizer(QueryTransformer qtf) {
202: optimizedQuery = null;
203: queryOptimizer = qtf;
204: }
205:
206: /**
207: * get the current query tree optimizer
208: */
209: public QueryTransformer getQueryOptimizer() {
210: return queryOptimizer;
211: }
212:
213: /**
214: * get the Medor result Field of the query (useful for the ejbSelect method)
215: */
216: public Field getResField() {
217: return resField;
218: }
219:
220: /**
221: * @return returns the limiter ranges of the LIMIT clause.
222: * May be 0 element if no LIMIT clause, 1 or 2 elements otherwise.
223: */
224: public EjbqlLimiterRange[] getLimiterRanges() {
225: return limiterRanges;
226: }
227:
228: /**
229: * Get the query evaluator of the optimized query. The query is optimized on first call.
230: * All mappers of the query should have been already set with the help of the iterator
231: * returned by the <code>getJormExtentIterator</code> method.
232: */
233: public QueryEvaluator getOptimizedQueryTree() throws Exception {
234: if (optimizedQuery == null) {
235: if (TraceEjb.isDebugQuery()) {
236: TraceEjb.query.log(BasicLevel.DEBUG,
237: "Initial QueryTree of the method " + methodName
238: + " of the bean " + beanName);
239: QueryTreePrinter.printQueryTree(queryTree,
240: TraceEjb.query, BasicLevel.DEBUG);
241: }
242: QueryTree qt = queryOptimizer.transform(queryTree);
243: if (TraceEjb.isDebugQuery()) {
244: TraceEjb.query.log(BasicLevel.DEBUG,
245: "Optimized QueryTree of the method "
246: + methodName + " of the bean "
247: + beanName);
248: QueryTreePrinter.printQueryTree(qt, TraceEjb.query,
249: BasicLevel.DEBUG);
250: }
251: optimizedQuery = new MedorEvaluator(qt, 0);
252: }
253: return optimizedQuery;
254: }
255:
256: }
|