001: /*
002: * This file is part of the GeOxygene project source files.
003: *
004: * GeOxygene aims at providing an open framework which implements OGC/ISO specifications for
005: * the development and deployment of geographic (GIS) applications. It is a open source
006: * contribution of the COGIT laboratory at the Institut Géographique National (the French
007: * National Mapping Agency).
008: *
009: * See: http://oxygene-project.sourceforge.net
010: *
011: * Copyright (C) 2005 Institut Géographique National
012: *
013: * This library is free software; you can redistribute it and/or modify it under the terms
014: * of the GNU Lesser General Public License as published by the Free Software Foundation;
015: * either version 2.1 of the License, or any later version.
016: *
017: * This library is distributed in the hope that it will be useful, but WITHOUT ANY
018: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
019: * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser General Public License along with
022: * this library (see file LICENSE if present); if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: *
025: */
026:
027: package fr.ign.cogit.geoxygene.datatools.ojb;
028:
029: import java.sql.Connection;
030: import java.sql.ResultSet;
031: import java.sql.ResultSetMetaData;
032: import java.sql.Statement;
033: import java.util.ArrayList;
034: import java.util.Iterator;
035: import java.util.List;
036:
037: import org.apache.ojb.broker.PersistenceBroker;
038: import org.apache.ojb.broker.metadata.ClassDescriptor;
039: import org.apache.ojb.broker.metadata.DescriptorRepository;
040: import org.apache.ojb.broker.metadata.FieldDescriptor;
041: import org.apache.ojb.odmg.HasBroker;
042: import org.apache.ojb.odmg.OJB;
043: import org.odmg.DList;
044: import org.odmg.Database;
045: import org.odmg.Implementation;
046: import org.odmg.OQLQuery;
047: import org.odmg.Transaction;
048:
049: import fr.ign.cogit.geoxygene.datatools.Metadata;
050: import fr.ign.cogit.geoxygene.feature.FT_Feature;
051: import fr.ign.cogit.geoxygene.feature.FT_FeatureCollection;
052:
053: /**
054: * Implementation d'une Geodatabase utilisant OJB comme mappeur.
055: * On utilise la partie ODMG de OJB.
056: * Ne pas utiliser directement :
057: * classe a specialiser en fonction du SGBD geographique utilise.
058: * Attention pour les entiers :
059: * pour Oracle, caster en BigDecimal,
060: * pour Postgis caster en Long ...
061: *
062: * @author Thierry Badard & Arnaud Braun
063: * @version 1.1
064: */
065:
066: public class GeodatabaseOjb {
067:
068: /////////////////////////////////////////////////////////////////////////////////////////
069: ///// attributs /////////////////////////////////////////////////////////////////////////
070: /////////////////////////////////////////////////////////////////////////////////////////
071: protected Connection _conn; // connection JDBC
072: protected Implementation _odmg; // implementation ODMG
073: protected Database _db; // interaction avec une base ODMG
074: protected Transaction _tx; // represente une transaction
075: protected List _metadataList; // liste des metadonnnees pour les classes persistantes.
076:
077: /////////////////////////////////////////////////////////////////////////////////////////
078: ///// constructeur //////////////////////////////////////////////////////////////////////
079: /////////////////////////////////////////////////////////////////////////////////////////
080: /** Constructeur.
081: * @param jcdAlias : l'alias de connection dans repository_database.xml */
082: GeodatabaseOjb(String jcdAlias) {
083: initODMG(jcdAlias);
084: initConnection();
085: initMetadata();
086: }
087:
088: /** Constructeur avec la connection par defaut dans repository_database.xml */
089: protected GeodatabaseOjb() {
090: this (null);
091: }
092:
093: /////////////////////////////////////////////////////////////////////////////////////
094: /// initialisation des attributs ////////////////////////////////////////////////////
095: /////////////////////////////////////////////////////////////////////////////////////
096: /** Initialise la base ODMG et une transaction */
097: protected void initODMG(String jcdAlias) {
098: try {
099: _odmg = OJB.getInstance();
100: _db = _odmg.newDatabase();
101: if (jcdAlias != null)
102: _db.open(jcdAlias, Database.OPEN_READ_WRITE);
103: else
104: _db.open(null, Database.OPEN_READ_WRITE);
105: _tx = _odmg.newTransaction();
106: } catch (Exception except) {
107: System.err
108: .println(" ### PROBLEME A LA LECTURE DES FICHIERS DE MAPPING OJB ... ### ");
109: System.err.println(" ### PROGRAMME ARRETE ! ### ");
110: System.err.println("");
111: except.printStackTrace();
112: System.exit(0);
113: }
114: }
115:
116: /** Initialise la connection JDBC. */
117: protected void initConnection() {
118: try {
119: _tx.begin();
120: PersistenceBroker broker = ((HasBroker) _tx).getBroker();
121: _conn = broker.serviceConnectionManager().getConnection();
122: _tx.commit();
123: } catch (Exception e) {
124: e.printStackTrace();
125: }
126: }
127:
128: /** Renseigne l'attribut _metadataList. */
129: protected void initMetadata() {
130: try {
131: _tx.begin();
132: PersistenceBroker broker = ((HasBroker) _tx).getBroker();
133: DescriptorRepository desc = broker
134: .getDescriptorRepository();
135: Iterator enDesc = desc.getDescriptorTable().values()
136: .iterator();
137: _metadataList = new ArrayList();
138:
139: while (enDesc.hasNext()) {
140: ClassDescriptor cd = (ClassDescriptor) enDesc.next();
141: String className = (cd.getClassNameOfObject());
142: if (!(className
143: .equals("org.apache.ojb.broker.util.sequence.HighLowSequence")
144: || className
145: .equals("org.apache.ojb.odmg.collections.DListImpl_2")
146: || className
147: .equals("org.apache.ojb.odmg.collections.DListEntry_2")
148: || className
149: .equals("org.apache.ojb.odmg.collections.DListImpl") || className
150: .equals("org.apache.ojb.odmg.collections.DListEntry"))) {
151: Metadata metadataElt = new Metadata();
152: metadataElt.setClassName(className);
153: metadataElt.setTableName(cd.getFullTableName());
154: FieldDescriptor[] fdPK = cd.getPkFields();
155: if (fdPK.length == 0) {
156: System.out
157: .println("WARNING - classe sans identifiant : "
158: + cd.getClassNameOfObject());
159: continue;
160: }
161: if (fdPK.length > 1) {
162: if (cd
163: .getClassNameOfObject()
164: .compareToIgnoreCase(
165: "org.apache.ojb.broker.util.sequence.HighLowSequence") != 0)
166: System.out
167: .println("WARNING - cle primaire composee : "
168: + cd.getClassNameOfObject());
169: continue;
170: }
171: metadataElt
172: .setIdColumnName(fdPK[0].getColumnName());
173: metadataElt.setIdFieldName(fdPK[0]
174: .getAttributeName());
175: _metadataList.add(metadataElt);
176: }
177: }
178: _tx.commit();
179:
180: } catch (Exception e) {
181: System.err
182: .println(" ### PROBLEME A LA LECTURE DES FICHIERS DE MAPPING OJB ... ### ");
183: System.err.println(" ### PROGRAMME ARRETE ! ### ");
184: System.err.println("");
185: e.printStackTrace();
186: System.exit(0);
187: }
188: }
189:
190: /////////////////////////////////////////////////////////////////////////////////////////
191: ///// gestion des transactions //////////////////////////////////////////////////////////
192: /////////////////////////////////////////////////////////////////////////////////////////
193: /** Ouvre une transaction. */
194: public void begin() {
195: try {
196: _tx.begin();
197: } catch (Exception e) {
198: e.printStackTrace();
199: }
200: }
201:
202: /** Commit la transaction sans la fermer. */
203: public void checkpoint() {
204: try {
205: _tx.checkpoint();
206: } catch (Exception e) {
207: e.printStackTrace();
208: }
209: }
210:
211: /** Commite et ferme la transaction. */
212: public void commit() {
213: try {
214: _tx.commit();
215: } catch (Exception e) {
216: e.printStackTrace();
217: }
218: }
219:
220: /** Annule et ferme la transaction. */
221: public void abort() {
222: try {
223: _tx.abort();
224: } catch (Exception e) {
225: e.printStackTrace();
226: }
227: }
228:
229: /** Renvoie true si la transaction est active. */
230: public boolean isOpen() {
231: return _tx.isOpen();
232: }
233:
234: /** Ferme la connection (libere les ressources). */
235: public void close() {
236: try {
237: _db.close();
238: } catch (Exception e) {
239: e.printStackTrace();
240: }
241: }
242:
243: /** Vide le cache de la transaction.
244: A appeler a l'interieur d'une transaction ouverte. */
245: public void clearCache() {
246: PersistenceBroker broker = ((HasBroker) _tx).getBroker();
247: broker.clearCache();
248: }
249:
250: /////////////////////////////////////////////////////////////////////////////////////////
251: ///// gestion de la persistance /////////////////////////////////////////////////////////
252: /////////////////////////////////////////////////////////////////////////////////////////
253: /** Rend persistant un objet.
254: A appeler a l'interieur d'une transaction ouverte.*/
255: public void makePersistent(Object obj) {
256: try {
257: _db.makePersistent(obj);
258: } catch (Exception e) {
259: e.printStackTrace();
260: }
261: }
262:
263: /** Detruit un objet persistant.
264: A appeler a l'interieur d'une transaction ouverte. */
265: public void deletePersistent(Object obj) {
266: try {
267: _db.deletePersistent(obj);
268: } catch (Exception e) {
269: e.printStackTrace();
270: }
271: }
272:
273: /////////////////////////////////////////////////////////////////////////////////////////
274: ///// chargement d'objets ///////////////////////////////////////////////////////////////
275: /////////////////////////////////////////////////////////////////////////////////////////
276: /** Charge l'objet d'identifiant id.
277: Passer un Integer pour id, si l'identifiant est un int.
278: Renvoie null si l'objet d'identifiant id n'existe pas.*
279: A appeler a l'interieur d'une transaction ouverte. */
280: public java.lang.Object load(Class clazz, Object id) {
281: try {
282: OQLQuery query = _odmg.newOQLQuery();
283: query.create("select x from " + clazz.getName()
284: + " where id = $0");
285: query.bind(id);
286: DList result = (DList) query.execute();
287: if (result.size() > 0)
288: return result.get(0);
289: else {
290: System.out.println("objet non trouve - id = " + id);
291: return null;
292: }
293: } catch (Exception ee) {
294: ee.printStackTrace();
295: return null;
296: }
297: }
298:
299: /** Charge tous les objets persistants de la classe theClass et les met dans une liste.
300: A appeler a l'interieur d'une transaction ouverte. */
301: public List loadAll(Class theClass) {
302: try {
303: OQLQuery query = _odmg.newOQLQuery();
304: query.create("select x from " + theClass.getName());
305: DList result = (DList) query.execute();
306: return result;
307: } catch (Exception e) {
308: e.printStackTrace();
309: return null;
310: }
311: }
312:
313: /** Charge tous les FT_Feature de la classe theClass dans la classe FT_FeatureCollection.
314: A appeler a l'interieur d'une transaction ouverte.
315: La classe theClass doit etre une sous-classe de FT_Feature, sinon renvoie une liste vide. */
316: public FT_FeatureCollection loadAllFeatures(Class featureClass) {
317: FT_FeatureCollection result = new FT_FeatureCollection();
318: if ((FT_Feature.class).isAssignableFrom(featureClass)) {
319: try {
320: OQLQuery query = _odmg.newOQLQuery();
321: query.create("select x from " + featureClass.getName());
322: DList list = (DList) query.execute();
323: Iterator iter = list.iterator();
324: while (iter.hasNext()) {
325: FT_Feature feature = (FT_Feature) iter.next();
326: result.add(feature);
327: }
328: } catch (Exception e) {
329: e.printStackTrace();
330: }
331: } else {
332: System.out
333: .println("loadAllFeatures() : La classe passee en parametre n'est pas une sous-classe de FT_Feature");
334: }
335: return result;
336: }
337:
338: /** Charge tous les FT_Feature de la classe theClass dans la classe featureListClass.
339: A appeler a l'interieur d'une transaction ouverte.
340: La classe theClass doit etre une sous-classe de FT_Feature, sinon renvoie une liste vide.
341: La classe featureListClass doit etre un sous classe de FT_FeatureCollection.*/
342: public Object loadAllFeatures(Class featureClass,
343: Class featureListClass) {
344: Object result = null;
345: try {
346: result = featureListClass.newInstance();
347: } catch (Exception e) {
348: e.printStackTrace();
349: }
350: if ((FT_Feature.class).isAssignableFrom(featureClass)) {
351: try {
352: OQLQuery query = _odmg.newOQLQuery();
353: query.create("select x from " + featureClass.getName());
354: DList list = (DList) query.execute();
355: Iterator iter = list.iterator();
356: while (iter.hasNext()) {
357: FT_Feature feature = (FT_Feature) iter.next();
358: result.getClass().getMethod("add",
359: new Class[] { FT_Feature.class }).invoke(
360: result, new Object[] { feature });
361: }
362: } catch (Exception e) {
363: e.printStackTrace();
364: }
365: } else {
366: System.out
367: .println("loadAllFeatures() : La classe passee en parametre n'est pas une sous-classe de FT_Feature");
368: }
369: return result;
370: }
371:
372: /////////////////////////////////////////////////////////////////////////////////////////
373: ///// OQL ///////////////////////////////////////////////////////////////////////////////
374: /////////////////////////////////////////////////////////////////////////////////////////
375: /** Execute la requete OQL query, la lie avec le parametre param, et met le resultat dans une liste.
376: A appeler a l'interieur d'une transaction ouverte.
377: On peut passer null pour param, si on ne souhaite lier la requete a aucune variable. */
378: public List loadOQL(String query, Object param) {
379: OQLQuery oqlQuery = _odmg.newOQLQuery();
380: try {
381: oqlQuery.create(query);
382: oqlQuery.bind(param);
383: DList result = (DList) oqlQuery.execute();
384: return result;
385: } catch (Exception e) {
386: e.printStackTrace();
387: return null;
388: }
389: }
390:
391: /** Cree une requete OQL */
392: public OQLQuery newOQLQuery() {
393: return _odmg.newOQLQuery();
394: }
395:
396: /////////////////////////////////////////////////////////////////////////////////////////
397: ///// Metadonnees sur le mapping ////////////////////////////////////////////////////////
398: /////////////////////////////////////////////////////////////////////////////////////////
399: /** Renvoie le tableau des metadonnees. */
400: public List getMetadata() {
401: return _metadataList;
402: }
403:
404: /** Renvoie les metadonnees de la classe theClass.
405: theClass doit etre une classe definie dans le mapping.*/
406: public Metadata getMetadata(Class theClass) {
407: for (int i = 0; i < _metadataList.size(); i++)
408: if (theClass.getName().compareTo(
409: ((Metadata) _metadataList.get(i)).getClassName()) == 0)
410: return (Metadata) _metadataList.get(i);
411: System.out
412: .println("La classe n'est pas dans le fichier de mapping : "
413: + theClass.getName());
414: return null;
415: }
416:
417: /** Renvoie les metadonnees de la classe mappee avec la table theTable.
418: theTable doit etre une table definie dans le mapping.
419: Si theTable est mappee avec plusieurs classes, en renvoie une. */
420: public Metadata getMetadata(String theTable) {
421: for (int i = 0; i < _metadataList.size(); i++)
422: if (((Metadata) _metadataList.get(i)).getTableName() != null)
423: if (theTable
424: .compareToIgnoreCase(((Metadata) _metadataList
425: .get(i)).getTableName()) == 0)
426: return (Metadata) _metadataList.get(i);
427: System.out
428: .println("La table n'est pas dans le fichier de mapping : "
429: + theTable);
430: return null;
431: }
432:
433: /////////////////////////////////////////////////////////////////////////////////////////
434: ///// SQL ///////////////////////////////////////////////////////////////////////////////
435: /////////////////////////////////////////////////////////////////////////////////////////
436: /** Renvoie la connection JDBC sous-jacente. */
437: public Connection getConnection() {
438: return _conn;
439: }
440:
441: /** Execute une commande SQL.
442: Cette commande ne doit pas renvoyer de resultat : INSERT, UPDATE, DELETE, mais pas SELECT. */
443: public void exeSQL(String query) {
444: try {
445: Connection conn = getConnection();
446: Statement stm = conn.createStatement();
447: stm.executeQuery(query);
448: stm.close();
449: conn.commit();
450: } catch (Exception e) {
451: e.printStackTrace();
452: }
453: }
454:
455: /** Execute une requete et met les resultats dans une liste de tableau d'objets.
456: Les tableaux ont la taille du nombre d'objets demandes dans le SELECT.
457: Exemple d'utilisation du resultat :
458: <tt> List edges = db.exeSQLQuery("SELECT edgeID FROM tableName WHERE ..."). </tt>
459: Pour recuperer le premier resultat :
460: <tt> edgeId = ( (BigDecimal) ((Object[]) (edges.get(0)) )[0] ).intValue(); </tt> */
461: public List exeSQLQuery(String query) {
462: List result = new ArrayList();
463: try {
464: Connection conn = getConnection();
465: Statement stm = conn.createStatement();
466: ResultSet rs = (ResultSet) stm.executeQuery(query);
467: ResultSetMetaData rsmd = rs.getMetaData();
468: int nbCol = rsmd.getColumnCount();
469: while (rs.next()) {
470: Object[] array = new Object[nbCol];
471: for (int i = 1; i <= nbCol; i++)
472: array[i - 1] = rs.getObject(i);
473: result.add(array);
474: }
475: stm.close();
476: } catch (Exception e) {
477: e.printStackTrace();
478: }
479: return result;
480: }
481:
482: ///////////////////////////////////////////////////////////////////////////////////////////////////////////
483: // getters ODMG ///////////////////////////////////////////////////////////////////////////////////////////
484: ///////////////////////////////////////////////////////////////////////////////////////////////////////////
485: public Implementation getODMGImplementation() {
486: return _odmg;
487: }
488:
489: public Database getODMGDatabase() {
490: return _db;
491: }
492:
493: public Transaction getODMGTransaction() {
494: return _tx;
495: }
496:
497: }
|