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 }