001: /**
002: * Speedo: an implementation of JDO compliant personality on top of JORM generic
003: * I/O sub-system.
004: * Copyright (C) 2001-2005 France Telecom R&D
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 of the License, or (at your option) 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 USA
019: *
020: *
021: *
022: * Authors: S.Chassande-Barrioz.
023: * Created on 10 February 2005
024: *
025: */package org.objectweb.speedo.query.jdo;
026:
027: import java.lang.reflect.Constructor;
028:
029: import javax.jdo.JDOUserException;
030: import javax.jdo.PersistenceManager;
031:
032: import org.objectweb.jorm.naming.api.PName;
033: import org.objectweb.medor.api.MedorException;
034: import org.objectweb.medor.tuple.api.Tuple;
035: import org.objectweb.medor.tuple.api.TupleCollection;
036: import org.objectweb.perseus.persistence.api.ConnectionHolder;
037: import org.objectweb.perseus.persistence.api.PersistenceException;
038: import org.objectweb.perseus.persistence.api.WorkingSet;
039: import org.objectweb.speedo.api.SpeedoException;
040: import org.objectweb.speedo.mim.api.PersistentObjectItf;
041: import org.objectweb.speedo.workingset.jdo.api.JDOTransactionItf;
042: import org.objectweb.util.monolog.api.BasicLevel;
043: import org.objectweb.util.monolog.api.Logger;
044:
045: /**
046: * Is a common classes used for compute the query result. This implementation
047: * supports user class or persistent class as query result.
048: *
049: * @author S.Chassande-Barrioz
050: */
051: public class JDOQueryResultCommon {
052: /**
053: * The inner medor TupleCollection
054: */
055: protected TupleCollection tc = null;
056:
057: /**
058: * The underlying connection used to evalue the query. These connections
059: * will be closed when the result will be no more used.
060: */
061: protected Object[] conns = null;
062:
063: /**
064: * The po manager representing the current execution context. The
065: * persistenceManager is used to fetch the real object from the identifier.
066: */
067: protected PersistenceManager pm = null;
068:
069: protected Logger logger;
070:
071: protected boolean debug;
072:
073: /**
074: * is the Class encapsulating the result of the query. A null value means
075: * the result of the query is primitive field or a persistent class.
076: */
077: private Class resultClass;
078:
079: /**
080: * indicates if the result is directly the first selected field.
081: * Of course if there are several selected fields, this fies must be false
082: */
083: private boolean firstIsResult;
084:
085: /**
086: * is the table of field types which compose the query result.
087: */
088: private Class[] selectedFieldTypes;
089:
090: /**
091: * is the constructor method to use for filling user class. This field is
092: * null when not result class has been defined.
093: */
094: private Constructor cons;
095:
096: /**
097: * index of the first element in the tuple collection
098: * This design supports data prefetching.
099: * 0: indicates that the indexes must be computed each time
100: * 1: is the first element
101: */
102: private int firstElementIdx;
103:
104: private boolean returnIdentifierOnly;
105:
106: /**
107: * Builds a QueryResultList.
108: * @param _tc the tuple collection representing the query result
109: * @param _pm is the peristence manager linked to the query
110: * @param _conns is the connection to the underlying support to close in
111: * same time than the query.
112: * @param _resultClazz is the class encapsulated the result
113: */
114: public JDOQueryResultCommon(TupleCollection _tc,
115: PersistenceManager _pm, Object[] _conns,
116: Class _resultClazz, Class[] _selectedFieldTypes,
117: boolean staticFirstElementIndex,
118: boolean returnIdentifierOnly, Logger _logger)
119: throws MedorException, SpeedoException {
120: this .tc = _tc;
121: this .pm = _pm;
122: this .conns = _conns;
123: this .resultClass = _resultClazz;
124: this .selectedFieldTypes = _selectedFieldTypes;
125: this .logger = _logger;
126: this .firstElementIdx = 0;
127: this .returnIdentifierOnly = returnIdentifierOnly;
128:
129: debug = logger != null && logger.isLoggable(BasicLevel.DEBUG);
130: if (pm == null
131: || pm.isClosed()
132: || ((JDOTransactionItf) pm.currentTransaction())
133: .getStatus() == WorkingSet.CTX_CLOSED) {
134: throw new JDOUserException(
135: "Impossible to use a query result without opened persistence manager");
136: }
137: firstIsResult = selectedFieldTypes.length == 1 //one projected field
138: && (resultClass == null // no result class
139: || resultClass == selectedFieldTypes[0] // result class is the projected field type
140: );
141:
142: if (staticFirstElementIndex) {
143: //compute once time the first element index
144: this .firstElementIdx = getFirstElementIndex();
145: if (debug) {
146: logger.log(BasicLevel.DEBUG, "index of the pname: "
147: + firstElementIdx);
148: }
149: } //else dynamic pname index computed each time.
150:
151: if (resultClass != null) {
152: try {
153: cons = resultClass.getConstructor(selectedFieldTypes);
154: } catch (Exception e) {
155: StringBuffer sb = new StringBuffer();
156: sb.append("No constructor '");
157: sb.append(resultClass.getName());
158: sb.append("(");
159: String sep = "";
160: for (int i = 0; i < selectedFieldTypes.length; i++) {
161: Class c = selectedFieldTypes[i];
162: sb.append(sep);
163: sep = ", ";
164: sb.append(c == null ? "null"
165: : selectedFieldTypes[i].getName());
166: }
167: sb
168: .append(")' found. This constructor is required in a query result. Be careful to not use non static inner class.");
169: throw new JDOUserException(sb.toString(), e);
170: }
171: }
172: }
173:
174: /**
175: * Close the connection to the data store. The query result will be no
176: * more usable after.
177: */
178: public void close() {
179: pm = null;
180: if (tc != null) {
181: try {
182: tc.close();
183: } catch (MedorException e) {
184: logger.log(BasicLevel.WARN,
185: "Error during the connection closing: ", e);
186: }
187: tc = null;
188: }
189: if (conns != null) {
190: for (int i = 0; i < conns.length; i++) {
191: closeConnection(conns[i]);
192: }
193: }
194: conns = null;
195: }
196:
197: public static void closeConnection(Object conn) {
198: if (conn instanceof ConnectionHolder) {
199: try {
200: ((ConnectionHolder) conn).releaseCHConnection();
201: } catch (PersistenceException e) {
202: }
203: }
204: }
205:
206: /**
207: * Retrieves the PName index from the TupleStructure.
208: * @return
209: * @throws MedorException
210: */
211: private int getFirstElementIndex() throws MedorException {
212: if (firstElementIdx == 0) {
213: return tc.getMetaData().getSize()
214: - selectedFieldTypes.length + 1;
215: } else {
216: return firstElementIdx;
217: }
218: }
219:
220: /**
221: * Fetch the persistent instance corresponding to an identifier
222: * @param pn is the persistent object identifier
223: * @return a persistent instance if it has been found, otherwise a null
224: * value.
225: */
226: protected Object pname2Object(PName pn) {
227: if (pn != null && !pn.isNull()) {
228: try {
229: return pm.getObjectById(pn, false);
230: } catch (JDOUserException e) {
231: if (debug) {
232: logger.log(BasicLevel.DEBUG,
233: "Ignore a deleted object, oid=" + pn, e);
234: }
235: // Do nothing, the persistent object does not exist
236: // ==> ignore it and go to the next
237: } catch (Exception e) {
238: logger.log(BasicLevel.WARN, "Unexpected exception", e);
239: }
240: }
241: return null;
242: }
243:
244: /**
245: * When the array #selectedFieldTypes contains a single element, the result
246: * of the query can or cannot be encapsulated into a user class. When this
247: * array contains several
248: * element and the #resultClass field is not definied, the
249: * @param tuple contains the result
250: * @return the query result
251: */
252: protected Object getValue(Tuple tuple) throws MedorException {
253: if (firstIsResult) {
254: //return directly the first element
255: Object o = tuple.getObject(getFirstElementIndex());
256: if (returnIdentifierOnly) {
257: if (o instanceof PersistentObjectItf) {
258: o = ((PersistentObjectItf) o).getCeIdentifier();
259: }
260: return o;
261: } else {
262: if (o instanceof PName) {
263: //the element is a PName, then fetch the persistent object
264: // from the identifier
265: return pname2Object((PName) o);
266: } else {
267: return o;
268: }
269: }
270: } else {
271: //Result is encapsulated into a user class or there are several
272: // selected fields
273:
274: //fetch all values into an array
275: Object[] values = new Object[selectedFieldTypes.length];
276: for (int i = 0; i < values.length; i++) {
277: values[i] = tuple.getObject(getFirstElementIndex() + i);
278: if (values[i] instanceof PName) {
279: //the element is a PName, then fetch the persistent object
280: // from the identifier
281: values[i] = pname2Object((PName) values[i]);
282: }
283: }
284: if (resultClass == null) {
285: //no user class specified but there are several selected fields
286: //then return an Object[] containing the values.
287: return values;
288: }
289: try {
290: //instanciate the user class with values
291: return cons.newInstance(values);
292: } catch (Exception e) {
293: StringBuffer sb = new StringBuffer();
294: sb.append("Class '");
295: sb.append(resultClass.getName());
296: sb
297: .append("' used as query result is incompatible with the following result: [");
298: String sep = "";
299: for (int i = 0; i < selectedFieldTypes.length; i++) {
300: sb.append(sep);
301: sep = ", ";
302: if (values[i] != null) {
303: sb.append(": ");
304: sb.append(values[i].getClass());
305: }
306: sb.append(values[i]);
307: }
308: sb.append("]");
309: throw new JDOUserException(sb.toString());
310: }
311: }
312: }
313: }
|