001    package hep.aida.ref.sql;
002    
003    // AIDA
004    import hep.aida.ref.AidaUtils;
005    import hep.aida.dev.IStore;
006    import hep.aida.dev.IDevTree;
007    
008    // SQL
009    import java.sql.ResultSet;
010    import java.sql.SQLException;
011    
012    // Log4J
013    import org.apache.log4j.Logger;
014    
015    // Java
016    import java.util.ArrayList;
017    import java.util.Map;
018    import java.io.IOException;
019    
020    /** <code>AidaSQLStore</code> implements {@link IStore} with
021      * support for {@link SQLTuple}s. Created {@link hep.aida.ITree} has
022      * a flat structure, it contains just {@link hep.aida.ITuple}s corresponding
023      * to SQL table names.
024      * <p><font color="#880088">
025      * <pre>
026      * $Log: AidaSQLStore.java,v $
027      * Revision 1.46  2007/06/11 14:02:29  hrivnac
028      * better support for tags in Oracle
029      *
030      * Revision 1.45  2007/05/23 16:38:43  hrivnac
031      * logical connections for Plotter; better UML
032      *
033      * Revision 1.44  2006/12/12 15:21:42  hrivnac
034      * McKoi replaced with Derby; uppercase-only dbs handled better; moving to JAS 0.8.3
035      *
036      * Revision 1.43  2006/11/20 13:51:59  hrivnac
037      * Connection is closed except within JAS3
038      *
039      * Revision 1.42  2006/11/15 16:16:01  hrivnac
040      * Oracle work fine in CERN, cache enabled
041      *
042      * Revision 1.41  2006/11/10 17:40:03  hrivnac
043      * WebService updated, long-format Oracle URL allowed, complete db not parsed if only table requested (speed)
044      *
045      * Revision 1.40  2005/09/28 16:21:06  hrivnac
046      * code cleaning
047      *
048      * Revision 1.39  2005/01/25 16:45:03  hrivnac
049      * Adding rows uses PreparedStatement
050      *
051      * Revision 1.38  2004/12/07 01:04:56  hrivnac
052      * name pattern can be specified
053      *
054      * Revision 1.37  2004/12/02 15:44:54  hrivnac
055      * fixes for ColMan JAS3 plugin
056      *
057      * Revision 1.36  2004/11/24 14:32:32  hrivnac
058      * better support for schemas
059      *
060      * Revision 1.35  2004/11/22 18:09:52  hrivnac
061      * Oracle treatment
062      *
063      * Revision 1.34  2004/11/05 10:59:19  hrivnac
064      * Doc fix
065      *
066      * Revision 1.33  2004/10/29 22:27:25  hrivnac
067      * imports corrected
068      *
069      * Revision 1.32  2004/10/28 15:25:40  hrivnac
070      * *** empty log message ***
071      *
072      * Revision 1.31  2004/10/12 13:25:40  hrivnac
073      * small improvements
074      *
075      * Revision 1.30  2004/10/08 15:22:33  hrivnac
076      * JAS3 plugin works
077      *
078      * Revision 1.29  2004/09/02 12:16:37  hrivnac
079      * Filter works correctly between different dbs
080      *
081      * Revision 1.28  2004/07/06 14:24:50  hrivnac
082      * support for Oracle
083      *
084      * Revision 1.27  2004/06/23 08:04:53  hrivnac
085      * documentation fixes
086      *
087      * Revision 1.26  2004/05/22 15:12:59  hrivnac
088      * class id reformated
089      *
090      * Revision 1.25  2004/05/03 19:22:30  hrivnac
091      * basic support for JAS3
092      *
093      * Revision 1.24  2004/04/14 13:39:46  hrivnac
094      * 1.5 warnings fixed
095      *
096      * Revision 1.23  2004/04/13 15:45:54  hrivnac
097      * AIDA URL introduced
098      *
099      * Revision 1.22  2004/02/12 16:39:40  hrivnac
100      * niceing
101      *
102      * Revision 1.21  2004/02/10 14:50:58  hrivnac
103      * JavaDoc tags completed
104      *
105      * Revision 1.20  2003/11/26 16:09:46  hrivnac
106      * Functional EventSelector WebService
107      *
108      * Revision 1.19  2003/11/24 15:13:21  hrivnac
109      * Logging improved.
110      *
111      * Revision 1.18  2003/11/20 17:21:57  hrivnac
112      * Java 1.5 natively supported, Log4J reporting improved.
113      *
114      * Revision 1.17  2003/11/17 10:18:28  hrivnac
115      * Cleaning.
116      *
117      * Revision 1.16  2003/11/05 19:46:22  hrivnac
118      * - FreeHEP 1.2.1
119      * - JAIDA 3.2.1
120      *
121      * Revision 1.9  2003/10/21 13:24:51  hrivnac
122      * Constructor from columnString implemented.
123      *
124      * </pre>
125      * </font></p>
126      * @opt attributes
127      * @opt operations
128      * @opt types
129      * @opt visibility
130      * @version $Id: AidaSQLStore.java,v 1.46 2007/06/11 14:02:29 hrivnac Exp $
131      * @author <a href="mailto:Julius.Hrivnac@cern.ch">J.Hrivnac</a> */
132    public class AidaSQLStore implements IStore {
133    
134      /** SQLStore is not read-only => false.
135        * @return <code>false</code>. */
136      public boolean isReadOnly() {
137        return false;
138        }
139    
140      /** Get all tables from the database described by the <code>Properties</code>
141        * entry in the <code>optionsMap</code>. It expects that all tables represent
142        * NTuples.
143        * If <em>optionsMap</em> contains <code>hep.aida.ref.sq.table</code>,
144        * only that table is requested.
145        * @param  tree       The Tree to read.
146        * @param  optionsMap The map of options to be used.
147        * @param  readOnly   If the Tree should be opened in read-only mode.
148        * @param  createNew  If the Tree should be created.
149        * @throws IOException if the Tree can't be read. */
150      public void read(IDevTree tree,
151                       Map      optionsMap,
152                       boolean  readOnly,
153                       boolean  createNew) throws IOException {
154    
155        // Get information about all tables
156        ArrayList<String> tables = new ArrayList<String>();
157        String schema = ((String)optionsMap.get("hep.aida.ref.sql.schema"));
158        String name   = ((String)optionsMap.get("hep.aida.ref.sql.name"));
159        String table  = ((String)optionsMap.get("hep.aida.ref.sql.table"));
160        if (table != null) {
161          tables.add(table);
162          }
163        else {
164          try {
165            ResultSet rs = accessor(optionsMap).connection().getMetaData().getTables(null, schema, name, new String[]{"TABLE", "VIEW", "ALIAS", "SYNONYM"});
166            while(rs.next()) {
167              tables.add(rs.getString("TABLE_NAME"));
168              }
169                  }
170                catch (Exception e) {
171            log.error(Util.report("Can't read from database defined by Accessor " + _accessor, e), e);
172            IOException ioe = new IOException("Couldn't read from database defined by Accessor " + _accessor);
173            ioe.initCause(e);
174            throw ioe;
175            }
176          }
177    
178        // Map tables to ITuples
179        SQLTuple tuple = null;
180        for (String s : tables) {
181          try {
182            SQLTupleFactory tf = new SQLTupleFactory(tree);
183            Object[] description = tf.tupleDescription(s, accessor(optionsMap));
184            String[] names = (String[])(description[0]);
185            Class[]  types = (Class[] )(description[1]);
186            if (s.contains("$")) {
187              throw new IllegalArgumentException("Can't map tables with names containing '$' to SQLTuples");
188              }
189            //tuple = new SQLTuple(s, s, names, types, AidaUtils.parseOptions(optionsMap));
190            tuple = new SQLTuple(s, s, names, types, optionsMap.toString().replace('{', ' ').replace('}', ' '));
191           tree.add("/", tuple);
192                  }
193                catch (SQLTupleException e) {
194            log.warn("Can't create SQLTuple " + s);
195            log.debug(Util.report("Can't create SQLTuple " + s, e), e);
196            }
197                catch (IllegalArgumentException e) {
198            log.debug(Util.report("Can't create SQLTuple " + s, e), e);
199            }
200          }
201    
202        // Inform ITree about status
203        tree.hasBeenFilled("/");
204    
205        }
206    
207      /** Commit all tables of the Tree.
208        * @param  tree       The Tree to be commited into.
209        * @param  optionsMap The map of options to be used.
210        * @throws IOException if the Tree can't be commited. */
211      public void commit(IDevTree tree,
212                         Map optionsMap) throws IOException {
213        try {
214          accessor(optionsMap).commit();
215          }
216              catch (SQLTupleException e) {
217          log.error(Util.report("Can't commit", e), e);
218          try {
219            accessor(optionsMap).rollback();
220            throw new IOException("Couldn't commit, rolled back");
221            }
222          catch (SQLTupleException e1) {
223            log.error(Util.report("Can't rollback", e1), e);
224            throw new IOException("Couldn't commit nor rollback");
225            }
226          }
227       }
228    
229      /** Disconnect from DB (if not running as JAS3 plugin). */
230      public void close() throws IOException {
231        if (_accessor != null && ! _accessor.isWinhinJAS()) {
232          try  {
233            _accessor.commit();
234            _accessor.close();
235            }
236          catch (SQLTupleException e) {
237            log.error(Util.report("Can't close", e), e);
238            throw new IOException("Couldn't close");
239            }
240          finally {
241            _accessor = null;
242            }
243          }
244        else {
245          log.warn("Don't close because Accessor is unknown or running within JAS");
246          }
247        }
248    
249      /** Get {@link Accessor}:
250        * <ol>
251        * <li>If there is one already associated one, use it.</li>
252        * <li>If there isn't already associated one, ask {@link Accessor} Factory for it.
253        *     {@link Accessor} Fatory will try to reuse {@link Accessor} corresponding
254        *     to requested options {@link Map} if it exists. Otherwise, it will
255        *     creae a new one.</li>
256        * </ol>
257        * @param optionsMap The map of options to be used.
258        * @return           The correspondng {@link Accessor}.
259        * @throws SQLTupleException if {@link Accessor} can't be created. */
260      private Accessor accessor(Map optionsMap) throws SQLTupleException {
261        if (_accessor == null) {
262          _accessor = Accessor.getAccessor(optionsMap);
263          }
264        return _accessor;
265        }
266    
267      private Accessor _accessor;
268    
269      /** Logging . */
270      private static Logger log = Logger.getLogger(AidaSQLStore.class);
271    
272      }