KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > hp > hpl > jena > db > impl > SQLCache


1 /*
2  * (c) Copyright 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
3  * All rights reserved.
4  *
5  */

6
7 //=======================================================================
8
// Package
9
package com.hp.hpl.jena.db.impl;
10
11 //=======================================================================
12
// Imports
13
import java.sql.*;
14 import java.util.*;
15 import java.io.*;
16
17 import com.hp.hpl.jena.db.*;
18 import com.hp.hpl.jena.shared.JenaException;
19 import com.hp.hpl.jena.util.CollectionFactory;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24 //=======================================================================
25
/**
26 * Stores a set of sql statements loaded from a resource file.
27 * Caches prepared versions of the statements for a given db connection.
28 * <p>
29 * The resource file is located on the classpath and has the format:
30 * <pre>
31 * # comment at start of line
32 * operationName1
33 * sql code line 1
34 * ...
35 * sql code last line
36 *
37 * operationName2
38 * ...
39 * </pre>
40 * where the blank lines delimit one sql block from the next.
41 * <p>The sql code is typically a single SQL statement but some operations,
42 * specifically database initialization and cleanup may require a variable number
43 * of statments. To cater for this terminate each statement in those groups with
44 * the string ";;". Note that a single ";" is not used because these compound
45 * statements are often stored procedure definitions which end to have ";" line
46 * terminators!
47 *
48 * @author <a HREF="mailto:der@hplb.hpl.hp.com">Dave Reynolds</a>. Updated by hkuno to support GraphRDB.
49 * @version $Revision: 1.14 $ on $Date: 2005/02/21 12:03:11 $
50 */

51
52 public class SQLCache {
53
54 //=======================================================================
55
// Variables
56

57     /** Set of sql statements indexed by operation name. */
58     protected Properties m_sql;
59
60     /** Cache of prepared versions of the statements. Each map entry is a list
61      * of copies of the prepared statement for multi-threaded apps. */

62     protected Map m_preparedStatements = CollectionFactory.createHashedMap();
63
64     /** Track which cached, prepared statements are in use and the corresponding
65      * list to which the statement should be returned. */

66     protected Map m_cachedStmtInUse = CollectionFactory.createHashedMap();
67
68     /** the packaged jdbc connection to the database itself. */
69     protected IDBConnection m_connection;
70
71     /** Maximum number of pre-prepared statements to keep for each operator. */
72     protected static final int MAX_PS_CACHE = 4;
73
74     /** Set to true to enable cache of pre-prepared statements. */
75     protected boolean CACHE_PREPARED_STATEMENTS = true;
76
77     static protected Log logger = LogFactory.getLog( SQLCache.class );
78 //=======================================================================
79
// Public interface
80

81     /**
82      * Constructor. Creates a new cache sql statements for interfacing to
83      * a specific database.
84      * @param sqlFile the name of the file of sql statements to load, this is
85      * loaded from the classpath.
86      * @param defaultOps Properties table which provides the default
87      * sql statements, any definitions of a given operation in the loaded file
88      * will override the default.
89      * @param connection the jdbc connection to the database itself
90      * @param idType the sql string to use for id types (substitutes for $id in files)
91      */

92     public SQLCache(String JavaDoc sqlFile, Properties defaultOps, IDBConnection connection, String JavaDoc idType) throws IOException {
93         m_sql = loadSQLFile(sqlFile, defaultOps, idType);
94         m_connection = connection;
95     }
96
97     /**
98      * Set to true to enable cache of pre-prepared statements.
99      */

100     public void setCachePreparedStatements(boolean state) {
101         CACHE_PREPARED_STATEMENTS = state;
102     }
103
104     /**
105      * Return true if cache of pre-prepared statements is enabled.
106      */

107     public boolean getCachePreparedStatements() {
108         return CACHE_PREPARED_STATEMENTS;
109     }
110
111     /**
112      * Flush the cache of all currently prepared statements.
113      */

114     public void flushPreparedStatementCache() throws RDFRDBException {
115         try {
116             Iterator it = m_preparedStatements.values().iterator();
117             while (it.hasNext()) {
118                 Iterator psit = ((List)it.next()).iterator();
119                 while (psit.hasNext()) {
120                     ((PreparedStatement)psit.next()).close();
121                 }
122             }
123         } catch (SQLException e) {
124             throw new RDFRDBException("Problem flushing PS cache", e);
125         } finally {
126             m_preparedStatements = CollectionFactory.createHashedMap();
127             m_cachedStmtInUse = CollectionFactory.createHashedMap();
128         }
129     }
130     /**
131      * Return the associated jdbc connection.
132      */

133     public Connection getConnection() throws SQLException {
134         return m_connection.getConnection();
135     }
136
137     /**
138      * Set the associated jdbc connection.
139      */

140     public void setConnection(IDBConnection connection) {
141         m_connection = connection;
142     }
143
144     /**
145      * Return the raw SQL statement corresponding to the named operation.
146      */

147     public String JavaDoc getSQLStatement(String JavaDoc opname) throws SQLException {
148         return getSQLStatement(opname, (String JavaDoc[]) null);
149     }
150
151     /**
152      * Return the raw SQL statement corresponding to the named operation.
153      * Substitute the ${a} attribute macro for the current attribute number.
154      */

155     public String JavaDoc getSQLStatement(String JavaDoc opname, String JavaDoc[] attr) throws SQLException {
156         String JavaDoc cmd = m_sql.getProperty(opname);
157         if (cmd == null) {
158             if ( opname.startsWith("*") ) {
159                 cmd = genSQLStatement(opname);
160                 m_sql.setProperty(opname, cmd);
161             } else {
162                 logger.error("Unable to find SQL for operation: " + opname);
163                 throw new SQLException("Unable to find SQL for operation: " + opname);
164             }
165         }
166         int attrCnt = (attr == null) ? 0 : attr.length;
167         if ( attrCnt > 0 ) cmd = substitute(cmd, "${a}", attr[0]);
168         if ( attrCnt > 1 ) cmd = substitute(cmd, "${b}", attr[1]);
169         if ( attrCnt > 2 ) cmd = substitute(cmd, "${c}", attr[2]);
170         if ( attrCnt > 3 ) throw new JenaException("Too many arguments");
171
172         return cmd;
173     }
174
175     
176     
177     public String JavaDoc getSQLStatement(String JavaDoc opname, String JavaDoc attr) throws SQLException {
178         String JavaDoc[] param = {attr};
179         return getSQLStatement(opname,param);
180     }
181
182     /**
183      * Return the raw SQL statement corresponding to the named operation.
184      * Attribute version - substitute the ${a} attribute macro for
185      * the current attribute number.
186      */

187     public String JavaDoc getSQLStatement(String JavaDoc opname, String JavaDoc attrA, String JavaDoc attrB) throws SQLException {
188         String JavaDoc[] param = {attrA,attrB};
189         return getSQLStatement(opname,param);
190     }
191
192     /**
193      * Return a set of raw SQL statements corresponding to the named operation.
194      * This is used for compound operations where more than one SQL command is needed to
195      * implement the operation (e.g. database formating and clean up). The
196      * individual statements should be separated by double-semicolons at the end of the line.
197      * <p>Needs refactoring to clarify what operations are and are not compound but for now
198      * it is assumed the caller knows which is correct. Compound statements are not called
199      * repeatedly so don't currently cache the parsed statement set.
200      */

201     public Collection getSQLStatementGroup(String JavaDoc opname) throws SQLException {
202         String JavaDoc statementSrc = m_sql.getProperty(opname);
203         if (statementSrc == null) {
204             throw new SQLException("Unable to find SQL for operation: " + opname);
205         }
206         int start = 0;
207         int split = 0;
208         List statements = new LinkedList();
209         while (split != -1) {
210             split = statementSrc.indexOf(";;\n", start);
211             String JavaDoc statement = null;
212             if (split == -1) {
213                 statement = statementSrc.substring(start);
214             } else {
215                 statement = statementSrc.substring(start, split);
216                 start = split +2;
217             }
218             if (!statement.trim().equals(""))
219                 statements.add(statement);
220         }
221         return statements;
222     }
223
224     /**
225      * Return a prepared SQL statement corresponding to the named operation.
226      * The statement should either be closed after use or returned to the
227      * prepared statement pool using {@link #returnPreparedSQLStatement returnPreparedSQLStatement}
228      *
229      * <p>Only works for single statements, not compound statements.
230      * @param con the jdbc connection to use for preparing statements
231      * @param opname the name of the sql operation to locate
232      * @return a prepared SQL statement appropriate for the JDBC connection
233      * used when this SQLCache was constructed or null if there is no such
234      * operation or no such connection
235      *
236      *
237      */

238     
239     public synchronized PreparedStatement getPreparedSQLStatement(String JavaDoc opname, String JavaDoc [] attr) throws SQLException {
240         /* TODO extended calling format or statement format to support different
241          * result sets and conconcurrency modes.
242          */

243         PreparedStatement ps;
244         if (m_connection == null || opname == null) return null;
245         int attrCnt = (attr == null) ? 0 : attr.length;
246         String JavaDoc aop = opname;
247         if ( attrCnt > 0 ) aop = concatOpName(aop, attr[0]);
248         if ( attrCnt > 1 ) aop = concatOpName(aop, attr[1]);
249         if ( attrCnt > 2 ) aop = concatOpName(aop, attr[2]);
250         if ( attrCnt > 3 ) throw new JenaException("Too many arguments");
251         
252         List psl = (List) m_preparedStatements.get(aop);
253         if (psl == null || psl.isEmpty()) {
254             String JavaDoc sql = getSQLStatement(opname, attr);
255             if (sql == null) {
256                 throw new SQLException("No SQL defined for operation: " + opname);
257             }
258             if (psl == null && CACHE_PREPARED_STATEMENTS) {
259                 psl = new LinkedList();
260                 m_preparedStatements.put(aop, psl);
261             }
262             ps = doPrepareSQLStatement(sql);
263         } else {
264             ps = (PreparedStatement) psl.remove(0);
265         }
266         if ( CACHE_PREPARED_STATEMENTS ) m_cachedStmtInUse.put(ps,psl);
267         return ps;
268     }
269     
270     /**
271      * Prepare a SQL statement for the given statement string.
272      *
273      * <p>Only works for single statements, not compound statements.
274      * @param stmt the sql statement to prepare.
275      * @return a prepared SQL statement appropriate for the JDBC connection
276      * used when this SQLCache was constructed or null if there is no such
277      * connection.
278      */

279
280     private synchronized PreparedStatement doPrepareSQLStatement(String JavaDoc sql) throws SQLException {
281         if (m_connection == null) return null;
282         return getConnection().prepareStatement(sql);
283     }
284
285     /**
286      * Return a prepared SQL statement for the given statement string.
287      * The statement should either be closed after use.
288      *
289      * <p>Only works for single statements, not compound statements.
290      * @param stmt the sql statement to prepare.
291      * @return a prepared SQL statement appropriate for the JDBC connection
292      * used when this SQLCache was constructed or null if there is no such
293      * connection.
294      */

295
296     public synchronized PreparedStatement prepareSQLStatement(String JavaDoc sql) throws SQLException {
297         if (m_connection == null) return null;
298         return doPrepareSQLStatement(sql);
299     }
300
301     public synchronized PreparedStatement getPreparedSQLStatement(String JavaDoc opname) throws SQLException {
302         return getPreparedSQLStatement(opname, (String JavaDoc[]) null);
303     }
304
305     /**
306      * Variant on {@link #getPreparedSQLStatement getPreparedSQLStatement} which
307      * accesses the attribute variant correspond to the given attribute suffix.
308      */

309     public synchronized PreparedStatement getPreparedSQLStatement(String JavaDoc opname, String JavaDoc attr) throws SQLException {
310         String JavaDoc[] param = {attr};
311         return getPreparedSQLStatement(opname,param);
312     }
313
314     /**
315      * Variant on {@link #getPreparedSQLStatement getPreparedSQLStatement} which
316      * access the attribute variant correspond to the given attribute suffix.
317      */

318     public synchronized PreparedStatement getPreparedSQLStatement(String JavaDoc opname, String JavaDoc attrA, String JavaDoc attrB) throws SQLException {
319         String JavaDoc[] param = {attrA,attrB};
320         return getPreparedSQLStatement(opname,param);
321     }
322     
323     /**
324      * Return a prepared statement to the statement pool for reuse by
325      * another caller. Any close problems logged rather than raising exception
326      * so that iterator close() operations can be silent so that they can meet
327      * the ClosableIterator signature.
328      */

329     public synchronized void returnPreparedSQLStatement(PreparedStatement ps) {
330         if (!CACHE_PREPARED_STATEMENTS) {
331             try {
332                 ps.close();
333             } catch (SQLException e) {
334                 logger.warn("Problem discarded prepared statement", e);
335             }
336             return;
337         }
338         List psl = (List) m_cachedStmtInUse.get(ps);
339         if (psl != null) {
340             if (psl.size() >= MAX_PS_CACHE) {
341                 try {
342                     ps.close();
343                 } catch (SQLException e) {
344                     logger.warn("Problem discarded prepared statement", e);
345                 }
346             } else {
347                 psl.add(ps);
348             }
349             m_cachedStmtInUse.remove(ps);
350         } else {
351             throw new JenaException("Attempt to return unused prepared statement");
352         }
353     }
354
355     /**
356      * Execute a named pre-prepared SQL query statement taking a set of arguments and return
357      * a set of results as an iterator (probably a subclass of ResultSetIterator. Returns null
358      * if they query is an update (as opposed to an empty iterator for a true query which happens
359      * to return no answers).
360      * <p>
361      * Not sure this is a good design. Reducing this to a general interface leads to lots of clunky
362      * wrapping and unwrapping of primitive types, coercions and lack of compile-time type checking.
363      * On the other hand letting the clients do this themselves with direct jdbc calls leaves us up
364      * to the mercy of the client to correctly use returnPreparedSQLStatement and on average seems
365      * to lead to more duplication of boiler plate code. Currently the client can chose either approach.
366      * <p>
367      * The calling arguments are passed in as an array.
368      */

369     public ResultSetIterator runSQLQuery(String JavaDoc opname, Object JavaDoc[] args) throws SQLException {
370         PreparedStatement ps = getPreparedSQLStatement(opname);
371         if (args != null) {
372             for (int i = 0; i < args.length; i++) {
373                 ps.setObject(i+1, args[i]);
374             }
375         }
376         return executeSQL(ps, opname, new ResultSetIterator());
377     }
378
379     /**
380      * Variant on {@link #runSQLQuery} which
381      * access the attribute variant correspond to the given attribute suffix.
382      */

383     public ResultSetIterator runSQLQuery(String JavaDoc opname,String JavaDoc attr, Object JavaDoc[] args) throws SQLException {
384         String JavaDoc aop = concatOpName(opname, attr);
385         PreparedStatement ps = getPreparedSQLStatement(aop);
386         
387         if (args != null) {
388             for (int i = 0; i < args.length; i++) {
389                 ps.setObject(i+1, args[i]);
390             }
391         }
392         return executeSQL(ps, aop, new ResultSetIterator());
393     }
394
395     /**
396      * Variant on {@link #runSQLQuery} which
397      * access the attribute variant correspond to the given attribute suffix.
398      */

399     public ResultSetIterator runSQLQuery(String JavaDoc opname,String JavaDoc attrA, String JavaDoc attrB, Object JavaDoc[] args) throws SQLException {
400         String JavaDoc aop = concatOpName(opname, attrA, attrB);
401         PreparedStatement ps = getPreparedSQLStatement(aop);
402         
403         if (args != null) {
404             for (int i = 0; i < args.length; i++) {
405                 ps.setObject(i+1, args[i]);
406             }
407         }
408         return executeSQL(ps, aop, new ResultSetIterator());
409     }
410
411
412     /**
413      * Execute a named pre-prepared SQL update statement taking a set of arguments and returning
414      * the update count.
415      */

416     public int runSQLUpdate(String JavaDoc opname, Object JavaDoc[] args) throws SQLException {
417         PreparedStatement ps = getPreparedSQLStatement(opname);
418         if (args != null) {
419             for (int i = 0; i < args.length; i++) {
420                 ps.setObject(i+1, args[i]);
421             }
422         }
423         int result = ps.executeUpdate();
424         returnPreparedSQLStatement(ps);
425         return result;
426     }
427
428     /**
429      * Variant on {@link #runSQLUpdate} which
430      * access the attribute variant correspond to the given attribute suffix.
431      */

432     public int runSQLUpdate(String JavaDoc opname,String JavaDoc attrA, Object JavaDoc[] args) throws SQLException {
433         String JavaDoc aop = concatOpName(opname, attrA);
434         PreparedStatement ps = getPreparedSQLStatement(aop);
435         if (args != null) {
436             for (int i = 0; i < args.length; i++) {
437                 ps.setObject(i+1, args[i]);
438             }
439         }
440         int result = ps.executeUpdate();
441         returnPreparedSQLStatement(ps);
442         return result;
443     }
444
445     /**
446      * Variant on {@link #runSQLUpdate} which
447      * access the attribute variant correspond to the given attribute suffix.
448      */

449     public int runSQLUpdate(String JavaDoc opname,String JavaDoc attrA, String JavaDoc attrB, Object JavaDoc[] args) throws SQLException {
450         String JavaDoc aop = concatOpName(opname, attrA, attrB);
451         PreparedStatement ps = getPreparedSQLStatement(aop);
452         if (args != null) {
453             for (int i = 0; i < args.length; i++) {
454                 ps.setObject(i+1, args[i]);
455             }
456         }
457         int result = ps.executeUpdate();
458         returnPreparedSQLStatement(ps);
459         return result;
460     }
461
462
463
464
465
466
467
468     /**
469      * Execute a named pre-prepared SQL query statement taking a set of arguments and return
470      * a set of results as an iterator (probably a subclass of ResultSetIterator. Returns null
471      * if they query is an update (as opposed to an empty iterator for a true query which happens
472      * to return no answers).
473      * <p>
474      * Not sure this is a good design. Reducing this to a general interface leads to lots of clunky
475      * wrapping and unwrapping of primitive types, coercions and lack of compile-time type checking.
476      * On the other hand letting the clients do this themselves with direct jdbc calls leaves us up
477      * to the mercy of the client to correctly use returnPreparedSQLStatement and on average seems
478      * to lead to more duplication of boiler plate code. Currently the client can chose either approach.
479      * <p>
480      * @param opname the name of the SQL operation to perform
481      * @param args the arguments to pass to the SQL operation as an array of Objects
482      * @param iterator the iterator to use to return the results
483      */

484     public ResultSetIterator runSQLQuery(String JavaDoc opname, Object JavaDoc[] args, ResultSetIterator iterator) throws SQLException {
485         PreparedStatement ps = getPreparedSQLStatement(opname);
486         if (args != null) {
487             for (int i = 0; i < args.length; i++) {
488                 ps.setObject(i+1, args[i]);
489             }
490         }
491         return executeSQL(ps, opname, iterator);
492     }
493
494     /**
495      * Variant on {@link #runSQLQuery} which
496      * access the attribute variant correspond to the given attribute suffix.
497      */

498     public ResultSetIterator runSQLQuery(String JavaDoc opname, String JavaDoc attrA, Object JavaDoc[] args, ResultSetIterator iterator) throws SQLException {
499         String JavaDoc aop = concatOpName(opname,attrA);
500         PreparedStatement ps = getPreparedSQLStatement(aop);
501         if (args != null) {
502             for (int i = 0; i < args.length; i++) {
503                 ps.setObject(i+1, args[i]);
504             }
505         }
506         
507         return executeSQL(ps, aop, iterator);
508     }
509
510     /**
511      * Variant on {@link #runSQLQuery} which
512      * access the attribute variant correspond to the given attribute suffix.
513      */

514     public ResultSetIterator runSQLQuery(String JavaDoc opname, String JavaDoc attrA, String JavaDoc attrB, Object JavaDoc[] args, ResultSetIterator iterator) throws SQLException {
515         String JavaDoc aop = concatOpName(opname,attrA, attrB);
516         PreparedStatement ps = getPreparedSQLStatement(aop);
517         if (args != null) {
518             for (int i = 0; i < args.length; i++) {
519                 ps.setObject(i+1, args[i]);
520             }
521         }
522         
523         return executeSQL(ps, aop, iterator);
524     }
525   
526
527     /**
528      * Run a group of sql statements - normally used for db formating and clean up.
529      * All statements are executed even if one raises an error then the error is
530      * reported at the end.
531      *
532      * Attribute version -- substitute the ${a} attribute macro
533      * for the current attribute
534      */

535     public void runSQLGroup(String JavaDoc opname, String JavaDoc [] attr) throws SQLException {
536         String JavaDoc op = null;
537         SQLException eignore = null;
538         String JavaDoc operror = null;
539         java.sql.Statement JavaDoc sql = getConnection().createStatement();
540         Iterator ops = getSQLStatementGroup(opname).iterator();
541         int attrCnt = attr == null ? 0 : attr.length;
542         if ( attrCnt > 6 )
543             throw new RDFRDBException("Too many parameters");
544         while (ops.hasNext()) {
545             op = (String JavaDoc) ops.next();
546             if ( attrCnt > 0 ) op = substitute(op,"${a}",attr[0]);
547             if ( attrCnt > 1 ) op = substitute(op,"${b}",attr[1]);
548             if ( attrCnt > 2 ) op = substitute(op,"${c}",attr[2]);
549             if ( attrCnt > 3 ) op = substitute(op,"${d}",attr[3]);
550             if ( attrCnt > 4 ) op = substitute(op,"${e}",attr[4]);
551             if ( attrCnt > 5 ) op = substitute(op,"${f}",attr[5]);
552             try {
553                 sql.execute(op);
554             } catch (SQLException e) {
555                 // This is debugging legacy, exception is still reported at the end
556
// System.out.println("Exec failure: " + op + ": " + e);
557
operror = op;
558                 eignore = e;
559             }
560         }
561         sql.close();
562         if (eignore != null) {
563             // operror records the failed operator, mostly internal debugging use
564
throw eignore;
565         }
566     }
567
568
569
570     /**
571      * Run a group of sql statements - normally used for db formating and clean up.
572      * All statements are executed even if one raises an error then the error is
573      * reported at the end.
574      */

575     public void runSQLGroup(String JavaDoc opname) throws SQLException {
576         runSQLGroup(opname,(String JavaDoc[])null);
577    }
578
579     /**
580      * Run a group of sql statements - normally used for db formating and clean up.
581      * All statements are executed even if one raises an error then the error is
582      * reported at the end.
583      *
584      * Attribute version -- substitute the ${a} attribute macro
585      * for the current attribute
586      */

587     public void runSQLGroup(String JavaDoc opname, String JavaDoc attr) throws SQLException {
588         String JavaDoc[] param = {attr};
589         runSQLGroup(opname,param);
590     }
591
592     /**
593      * Run a group of sql statements - normally used for db formating and clean up.
594      * All statements are executed even if one raises an error then the error is
595      * reported at the end.
596      *
597      * Attribute version -- substitute the ${a} attribute macro
598      * for the current attribute
599      */

600     public void runSQLGroup(String JavaDoc opname, String JavaDoc attrA, String JavaDoc attrB) throws SQLException {
601         String JavaDoc[] param = {attrA,attrB};
602         runSQLGroup(opname,param);
603     }
604
605
606
607     /**
608      * Close all prepared statements
609      */

610     public void close() throws SQLException {
611         Iterator it = m_preparedStatements.values().iterator();
612         while (it.hasNext()) {
613             List psl = (List) it.next();
614             Iterator itl = psl.iterator();
615             while (itl.hasNext()) {
616                 PreparedStatement ps = (PreparedStatement)itl.next();
617                 ps.close();
618             }
619             it.remove();
620         }
621         it = m_cachedStmtInUse.values().iterator();
622         while (it.hasNext()) {
623             it.remove();
624         }
625     }
626
627     /**
628      * Load in a defined set of sql statements - see class comment for format.
629      * The loaded file is return as a Property table. This call is static
630      * to support the loading of a default sql mapping.
631      * @param sqlFile the name of the file of sql statements to load, this is
632      * loaded from the classpath.
633      * @param defaultOps a Properties table of default sql definitions.
634      * @param idType the sql string to use for id types (substitutes for $id in files)
635      */

636     public static Properties loadSQLFile(String JavaDoc sqlFile, Properties defaultOps, String JavaDoc idType) throws IOException {
637         Properties sqlTable = new Properties(defaultOps);
638         BufferedReader src = openResourceFile(sqlFile);
639         String JavaDoc line = null;
640         while ((line = src.readLine()) != null) {
641             if (line.startsWith("#")) {
642                 continue; // Comment line so skip it
643
}
644             String JavaDoc opName = line.trim();
645             StringBuffer JavaDoc sql = new StringBuffer JavaDoc();
646             while (true) {
647                 line = src.readLine();
648                 if (line == null || line.trim().equals("")) {
649                         // Blank line terminates sql block
650
sqlTable.setProperty(opName, sql.toString());
651                     break;
652                 } else if (line.startsWith("#")) {
653                     continue;
654                 } else {
655                     sql.append(substitute(line.trim(), "${id}", idType));
656                     sql.append("\n");
657                 }
658             }
659             if (line == null) break; // Check if read to end of file
660
}
661         return sqlTable;
662     }
663
664
665     /** Helper function calculate op name given substitutions */
666     public static String JavaDoc concatOpName(String JavaDoc opName, String JavaDoc attr) {
667         return (opName + attr);
668     }
669     
670     /** Helper function calculate op name given substitutions */
671     public static String JavaDoc concatOpName(String JavaDoc opName, String JavaDoc attrA, String JavaDoc attrB) {
672         return (opName + attrA + attrB);
673     }
674
675     /** Helper function substitute all occurances of macro with subs */
676     public static String JavaDoc substitute(String JavaDoc line, String JavaDoc macro, String JavaDoc subs) {
677         int loc = line.indexOf(macro);
678         if (loc != -1) {
679             return line.substring(0, loc) + subs + substitute(line.substring(loc+macro.length()),macro, subs);
680         } else {
681             return line;
682         }
683     }
684     
685
686 //=======================================================================
687
// Internal support
688

689     /**
690      * Accessor. Returns the Properties table which maps operation names to
691      * the plain text sql statements. This is using internally in the constructor.
692      */

693     protected Properties getSQLTable() {
694         return m_sql;
695     }
696
697     /**
698      * Open a resource file for reading. The file is found on the classpath.
699      */

700     public static BufferedReader openResourceFile(String JavaDoc filename) throws IOException {
701         InputStream is = SQLCache.class.getClassLoader().getResourceAsStream(filename);
702         if (is == null)
703             throw new IOException("Can't open resource " + filename);
704         return new BufferedReader(new InputStreamReader(is, "US-ASCII"));
705     }
706
707     /**
708      * Execute the given statement, return null if the statement appears to be
709      * just an update or return an iterator for the result set if the statement appears
710      * to be a query
711      */

712     protected ResultSetIterator executeSQL(PreparedStatement ps, String JavaDoc opname, ResultSetIterator iterator) throws SQLException {
713         if (ps.execute()) {
714             ResultSet rs = ps.getResultSet();
715             iterator.reset(rs, ps, this, opname);
716             return iterator;
717         } else {
718             returnPreparedSQLStatement(ps);
719             return null;
720         }
721     }
722     
723     
724     /**
725      * Return dynamically generated SQL for the specified operation.
726      * @param opname the command to generate; must start with "*", the opname and then op params.
727      * @return the generated command as a String.
728      */

729     
730     protected String JavaDoc genSQLStatement ( String JavaDoc opname ) throws SQLException {
731         /* for testing. for now, we only generate one operation, findReif,
732          * to find reified statements from a triple match pattern.
733          */

734         String JavaDoc sql = "";
735         boolean badop = false;
736         if ( opname.startsWith("*") ) {
737             // a space separate the operation name from its parameters.
738
int delim = opname.indexOf(' ');
739             String JavaDoc op = opname.substring(1,delim);
740             String JavaDoc args = opname.substring(delim+1);
741             if ( op.equals("findReif") ) {
742                 sql = genSQLStmtFindReif(op,args);
743             } else badop = true;
744         } else badop = true;
745         if ( badop ) {
746             logger.error("Unable to generate SQL for operation: " + opname);
747             throw new JenaException("Unable to generate SQL for operation: " + opname);
748         }
749         return sql;
750     }
751     
752     /**
753      * Return generate SQL for finding reified statements from a triple pattern.
754      * @param op the command to generate. should be findReif.
755      * @param args a string describing which command to generate.
756      * it has the form [N][PS|PP|PO|PT][O[C]] where N means to search
757      * for the statement URI; Px means to search for reified subjects, properties,
758      * objects or types; O means to search for reified objects; OC means the object
759      * value is rdf:Statement.
760      */

761     
762     protected String JavaDoc genSQLStmtFindReif ( String JavaDoc op, String JavaDoc args ) throws SQLException {
763         /* for a reified triple pattern <S,P,O>, there are 8 cases.
764          * 1. <-,-,-> this means retrieve all reified triples. args="".
765          * 2. <S,-,-> retrieve all reified triples for this subject. args="N".
766          * 3. <S,-,O> retrieve all reified triples for this subject and
767          * object value. args="NO" or "NOC".
768          * 4. <-,-,O> retrieve all reified triples with this object value.
769          * args="O" or "OC"
770          * 5. <-,P,-> retrieve all reified triples with this property. args="Px".
771          * property must be either rdf:subject, rdf:predicate,
772          * rdf:object, rdf:type.
773          * 6. <-,P,O> retrieve all reified triples with this property and object
774          * value. args="PxO" or "PxOC".
775          * 7. <S,P,-> retrieve all reified triples with this subject and property.
776          * args="NPx".
777          * 8. <S,P,O> retrieve all reified triples with this subject, property and
778          * object value. args="NPxO" or "NPxOC".
779          */

780
781         String JavaDoc stmtStr = getSQLStatement("selectReified");
782         String JavaDoc qual = "";
783         IRDBDriver driver = m_connection.getDriver();
784         
785         if ( args.equals("") ) {
786             // case 1 <-,-,-> nothing to do.
787
} else {
788             int ix = 0;
789             boolean hasSubj = false;
790             boolean hasProp = false;
791             boolean hasObj = false;
792             boolean objIsStmt = false;
793             char reifProp = ' ';
794             int argLen = args.length();
795             
796             if ( args.charAt(ix) == 'N' ) {
797                 hasSubj = true;
798                 ix++;
799             }
800             hasProp = (ix < argLen) && (args.charAt(ix) == 'P');
801             if ( hasProp && (ix < argLen) ) {
802                 ix++;
803                 reifProp = args.charAt(ix++);
804             }
805             hasObj = (ix < argLen) && (args.charAt(ix) == 'O');
806             if ( hasObj ) {
807                 ix++;
808                 objIsStmt = (ix < argLen) && (args.charAt(ix) == 'C');
809             }
810             if ( !hasProp ) {
811                 if ( hasSubj ) {
812                     // cases 2 and 3
813
qual += driver.genSQLReifQualStmt();
814                     if ( hasObj ) {
815                         // case 3 above
816
qual += " AND " + driver.genSQLReifQualAnyObj(objIsStmt);
817                     }
818                 } else {
819                     // case 4 above
820
qual += driver.genSQLReifQualAnyObj(objIsStmt);
821                 }
822             } else {
823                 // have a reified property
824
if ( hasSubj ) qual += driver.genSQLReifQualStmt() + " AND ";
825                 qual += driver.genSQLReifQualObj(reifProp,hasObj);
826             }
827             stmtStr += " AND " + qual;
828         }
829         return stmtStr;
830     }
831     
832 }
833
834 /*
835  * (c) Copyright 2002, 2003, 2004, 2005 Hewlett-Packard Development Company, LP
836  * All rights reserved.
837  *
838  * Redistribution and use in source and binary forms, with or without
839  * modification, are permitted provided that the following conditions
840  * are met:
841  * 1. Redistributions of source code must retain the above copyright
842  * notice, this list of conditions and the following disclaimer.
843  * 2. Redistributions in binary form must reproduce the above copyright
844  * notice, this list of conditions and the following disclaimer in the
845  * documentation and/or other materials provided with the distribution.
846  * 3. The name of the author may not be used to endorse or promote products
847  * derived from this software without specific prior written permission.
848
849  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
850  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
851  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
852  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
853  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
854  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
855  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
856  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
857  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
858  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
859  */

860
Popular Tags