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 }