KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > ProcedureManager


1 /**
2  * com.mckoi.database.ProcedureManager 27 Feb 2003
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import java.lang.reflect.*;
28 import com.mckoi.database.global.BlobAccessor;
29 import com.mckoi.database.global.StringAccessor;
30 import com.mckoi.util.BigNumber;
31 import java.io.IOException JavaDoc;
32 import java.io.InputStream JavaDoc;
33 import java.util.StringTokenizer JavaDoc;
34 import java.util.ArrayList JavaDoc;
35
36 /**
37  * A DatabaseConnection procedure manager. This controls adding, updating,
38  * deleting and querying/calling stored procedures.
39  *
40  * @author Tobias Downer
41  */

42
43 public class ProcedureManager {
44
45   /**
46    * The DatabaseConnection.
47    */

48   private DatabaseConnection connection;
49
50   /**
51    * The context.
52    */

53   private DatabaseQueryContext context;
54
55   /**
56    * Constructs the ProcedureManager for a DatabaseConnection.
57    */

58   ProcedureManager(DatabaseConnection connection) {
59     this.connection = connection;
60     this.context = new DatabaseQueryContext(connection);
61   }
62
63   /**
64    * Given the SYS_FUNCTION table, this returns a new table that contains the
65    * entry with the given procedure name, or an empty result if nothing found.
66    * Generates an error if more than 1 entry found.
67    */

68   private Table findProcedureEntry(DataTable table,
69                                    ProcedureName procedure_name) {
70
71     Operator EQUALS = Operator.get("=");
72
73     Variable schemav = table.getResolvedVariable(0);
74     Variable namev = table.getResolvedVariable(1);
75
76     Table t = table.simpleSelect(context, namev, EQUALS,
77                   new Expression(TObject.stringVal(procedure_name.getName())));
78     t = t.exhaustiveSelect(context, Expression.simple(
79               schemav, EQUALS, TObject.stringVal(procedure_name.getSchema())));
80
81     // This should be at most 1 row in size
82
if (t.getRowCount() > 1) {
83       throw new RuntimeException JavaDoc(
84                "Assert failed: multiple procedure names for " + procedure_name);
85     }
86
87     // Return the entries found.
88
return t;
89
90   }
91
92   /**
93    * Formats a string that gives information about the procedure, return
94    * type and param types.
95    */

96   private static String JavaDoc procedureInfoString(ProcedureName name,
97                                             TType ret, TType[] params) {
98     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
99     if (ret != null) {
100       buf.append(ret.asSQLString());
101       buf.append(" ");
102     }
103     buf.append(name.getName());
104     buf.append("(");
105     for (int i = 0; i < params.length; ++i) {
106       buf.append(params[i].asSQLString());
107       if (i < params.length - 1) {
108         buf.append(", ");
109       }
110     }
111     buf.append(")");
112     return new String JavaDoc(buf);
113   }
114
115   /**
116    * Given a location string as defined for a Java stored procedure, this
117    * parses the string into the various parts. For example, given the
118    * string 'com.mycompany.storedprocedures.MyFunctions.minFunction()' this
119    * will parse the string out to the class called
120    * 'com.mycompany.storedprocedures.MyFunctions' and the method 'minFunction'
121    * with no arguments. This function will work event if the method name is
122    * not given, or the method name does not have an arguments specification.
123    */

124   public static String JavaDoc[] parseJavaLocationString(final String JavaDoc str) {
125     // Look for the first parenthese
126
int parenthese_delim = str.indexOf("(");
127     String JavaDoc class_method;
128
129     if (parenthese_delim != -1) {
130       // This represents class/method
131
class_method = str.substring(0, parenthese_delim);
132       // This will be deliminated by a '.'
133
int method_delim = class_method.lastIndexOf(".");
134       if (method_delim == -1) {
135         throw new StatementException(
136                           "Incorrectly formatted Java method string: " + str);
137       }
138       String JavaDoc class_str = class_method.substring(0, method_delim);
139       String JavaDoc method_str = class_method.substring(method_delim + 1);
140       // Next parse the argument list
141
int end_parenthese_delim = str.lastIndexOf(")");
142       if (end_parenthese_delim == -1) {
143         throw new StatementException(
144                           "Incorrectly formatted Java method string: " + str);
145       }
146       String JavaDoc arg_list_str =
147                     str.substring(parenthese_delim + 1, end_parenthese_delim);
148       // Now parse the list of arguments
149
ArrayList JavaDoc arg_list = new ArrayList JavaDoc();
150       StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(arg_list_str, ",");
151       while (tok.hasMoreTokens()) {
152         String JavaDoc arg = tok.nextToken();
153         arg_list.add(arg);
154       }
155
156       // Form the parsed array and return it
157
int sz = arg_list.size();
158       String JavaDoc[] return_array = new String JavaDoc[2 + sz];
159       return_array[0] = class_str;
160       return_array[1] = method_str;
161       for (int i = 0; i < sz; ++i) {
162         return_array[i + 2] = (String JavaDoc) arg_list.get(i);
163       }
164       return return_array;
165
166     }
167     else {
168       // No parenthese so we assume this is a java class
169
return new String JavaDoc[] { str };
170     }
171
172   }
173
174   /**
175    * Returns true if the procedure with the given name exists.
176    */

177   public boolean procedureExists(ProcedureName procedure_name) {
178
179     DataTable table = connection.getTable(Database.SYS_FUNCTION);
180     return findProcedureEntry(table, procedure_name).getRowCount() == 1;
181
182   }
183
184   /**
185    * Returns true if the procedure with the given table name exists.
186    */

187   public boolean procedureExists(TableName procedure_name) {
188     return procedureExists(new ProcedureName(procedure_name));
189   }
190
191   /**
192    * Defines a Java stored procedure. If the procedure with the name has not
193    * been defined it is defined. If the procedure has been defined then it is
194    * overwritten with this information.
195    * <p>
196    * If 'return_type' is null then the procedure does not return a value.
197    */

198   public void defineJavaProcedure(ProcedureName procedure_name,
199                                   String JavaDoc java_specification,
200                                   TType return_type, TType[] param_types,
201                                   String JavaDoc username)
202                                                     throws DatabaseException {
203
204     TableName proc_table_name =
205            new TableName(procedure_name.getSchema(), procedure_name.getName());
206
207     // Check this name is not reserved
208
DatabaseConnection.checkAllowCreate(proc_table_name);
209
210     DataTable table = connection.getTable(Database.SYS_FUNCTION);
211
212     // The new row to insert/update
213
RowData row_data = new RowData(table);
214     row_data.setColumnDataFromObject(0, procedure_name.getSchema());
215     row_data.setColumnDataFromObject(1, procedure_name.getName());
216     row_data.setColumnDataFromObject(2, "Java-1");
217     row_data.setColumnDataFromObject(3, java_specification);
218     if (return_type != null) {
219       row_data.setColumnDataFromObject(4, TType.asEncodedString(return_type));
220     }
221     row_data.setColumnDataFromObject(5, TType.asEncodedString(param_types));
222     row_data.setColumnDataFromObject(6, username);
223
224     // Find the entry from the procedure table that equal this name
225
Table t = findProcedureEntry(table, procedure_name);
226
227     // Delete the entry if it already exists.
228
if (t.getRowCount() == 1) {
229       table.delete(t);
230     }
231
232     // Insert the new entry,
233
table.add(row_data);
234
235     // Notify that this database object has been successfully created.
236
connection.databaseObjectCreated(proc_table_name);
237
238   }
239
240   /**
241    * Deletes the procedure with the given name, or generates an error if the
242    * procedure doesn't exist.
243    */

244   public void deleteProcedure(ProcedureName procedure_name)
245                                                     throws DatabaseException {
246
247     DataTable table = connection.getTable(Database.SYS_FUNCTION);
248
249     // Find the entry from the procedure table that equal this name
250
Table t = findProcedureEntry(table, procedure_name);
251
252     // If no entries then generate error.
253
if (t.getRowCount() == 0) {
254       throw new StatementException("Procedure " + procedure_name +
255                                    " doesn't exist.");
256     }
257
258     table.delete(t);
259
260     // Notify that this database object has been successfully dropped.
261
connection.databaseObjectDropped(
262          new TableName(procedure_name.getSchema(), procedure_name.getName()));
263
264   }
265
266   /**
267    * Returns an InternalTableInfo object used to model the list of procedures
268    * that are accessible within the given Transaction object. This is used to
269    * model all procedures that have been defined as tables.
270    */

271   static InternalTableInfo createInternalTableInfo(Transaction transaction) {
272     return new ProcedureInternalTableInfo(transaction);
273   }
274   
275   /**
276    * Invokes the procedure with the given name and the given parameters and
277    * returns the procedure return value.
278    */

279   public TObject invokeProcedure(ProcedureName procedure_name,
280                                  TObject[] params) {
281
282     DataTable table = connection.getTable(Database.SYS_FUNCTION);
283     
284     // Find the entry from the procedure table that equals this name
285
Table t = findProcedureEntry(table, procedure_name);
286     if (t.getRowCount() == 0) {
287       throw new StatementException("Procedure " + procedure_name +
288                                    " doesn't exist.");
289     }
290
291     int row_index = t.rowEnumeration().nextRowIndex();
292     TObject type_ob = t.getCellContents(2, row_index);
293     TObject location_ob = t.getCellContents(3, row_index);
294     TObject return_type_ob = t.getCellContents(4, row_index);
295     TObject param_types_ob = t.getCellContents(5, row_index);
296     TObject owner_ob = t.getCellContents(6, row_index);
297
298     String JavaDoc type = type_ob.getObject().toString();
299     String JavaDoc location = location_ob.getObject().toString();
300     TType return_type = null;
301     if (!return_type_ob.isNull()) {
302       return_type = TType.decodeString(return_type_ob.getObject().toString());
303     }
304     TType[] param_types =
305                      TType.decodeTypes(param_types_ob.getObject().toString());
306     String JavaDoc owner = owner_ob.getObject().toString();
307
308     // Check the number of parameters given match the function parameters length
309
if (params.length != param_types.length) {
310       throw new StatementException(
311           "Parameters given do not match the parameters of the procedure: " +
312           procedureInfoString(procedure_name, return_type, param_types));
313     }
314
315     // The different procedure types,
316
if (type.equals("Java-1")) {
317       return invokeJavaV1Procedure(procedure_name, location,
318                                    return_type, param_types, owner, params);
319     }
320     else {
321       throw new RuntimeException JavaDoc("Unknown procedure type: " + type);
322     }
323     
324   }
325
326   /**
327    * Resolves a Java class specification string to a Java Class object. For
328    * example, "String" becomes 'java.lang.String.class' and "boolean[]" becomes
329    * 'boolean[].class', etc.
330    */

331   private static Class JavaDoc resolveToClass(String JavaDoc java_spec) {
332     // Trim the string
333
java_spec = java_spec.trim();
334     // Is this an array? Count the number of array dimensions.
335
int dimensions = -1;
336     int last_index = java_spec.length();
337     while (last_index > 0) {
338       ++dimensions;
339       last_index = java_spec.lastIndexOf("[]", last_index) - 1;
340     }
341     // Remove the array part
342
int array_end = java_spec.length() - (dimensions * 2);
343     String JavaDoc class_part = java_spec.substring(0, array_end);
344     // Check there's no array parts in the class part
345
if (class_part.indexOf("[]") != -1) {
346       throw new RuntimeException JavaDoc(
347                "Java class specification incorrectly formatted: " + java_spec);
348     }
349
350     // Convert the java specification to a Java class. For example,
351
// String is converted to java.lang.String.class, etc.
352
Class JavaDoc cl;
353     // Is there a '.' in the class specification?
354
if (class_part.indexOf(".") != -1) {
355       // Must be a specification such as 'java.net.URL' or 'java.util.List'.
356
try {
357         cl = Class.forName(class_part);
358       }
359       catch (ClassNotFoundException JavaDoc i) {
360         throw new RuntimeException JavaDoc("Java class not found: " + class_part);
361       }
362     }
363
364     // Try for a primitive types
365
else if (class_part.equals("boolean")) {
366       cl = boolean.class;
367     }
368     else if (class_part.equals("byte")) {
369       cl = byte.class;
370     }
371     else if (class_part.equals("short")) {
372       cl = short.class;
373     }
374     else if (class_part.equals("char")) {
375       cl = char.class;
376     }
377     else if (class_part.equals("int")) {
378       cl = int.class;
379     }
380     else if (class_part.equals("long")) {
381       cl = long.class;
382     }
383     else if (class_part.equals("float")) {
384       cl = float.class;
385     }
386     else if (class_part.equals("double")) {
387       cl = double.class;
388     }
389     else {
390       // Not a primitive type so try resolving against java.lang.* or some
391
// key classes in com.mckoi.database.*
392
if (class_part.equals("ProcedureConnection")) {
393         cl = ProcedureConnection.class;
394       }
395       else {
396         try {
397           cl = Class.forName("java.lang." + class_part);
398         }
399         catch (ClassNotFoundException JavaDoc i) {
400           // No luck so give up,
401
throw new RuntimeException JavaDoc("Java class not found: " + class_part);
402         }
403       }
404     }
405
406     // Finally make into a dimension if necessary
407
if (dimensions > 0) {
408       // This is a little untidy way of doing this. Perhaps a better approach
409
// would be to make an array encoded string (eg. "[[Ljava.langString;").
410
cl = java.lang.reflect.Array.newInstance(cl,
411                                                new int[dimensions]).getClass();
412     }
413
414     return cl;
415     
416   }
417
418   
419   
420   
421   /**
422    * Given a Java location_str and a list of parameter types, returns an
423    * immutable 'Method' object that can be used to invoke a Java stored
424    * procedure. The returned object can be cached if necessary. Note that
425    * this method will generate an error for the following situations:
426    * a) The invokation class or method was not found, b) there is not an
427    * invokation method with the required number of arguments or that matches
428    * the method specification.
429    * <p>
430    * Returns null if the invokation method could not be found.
431    */

432   public static Method javaProcedureMethod(
433                                   String JavaDoc location_str, TType[] param_types) {
434     // Parse the location string
435
String JavaDoc[] loc_parts = parseJavaLocationString(location_str);
436
437     // The name of the class
438
String JavaDoc class_name;
439     // The name of the invokation method in the class.
440
String JavaDoc method_name;
441     // The object specification that must be matched. If any entry is 'null'
442
// then the argument parameter is discovered.
443
Class JavaDoc[] object_specification;
444     boolean firstProcedureConnectionIgnore;
445
446     if (loc_parts.length == 1) {
447       // This means the location_str only specifies a class name, so we use
448
// 'invoke' as the static method to call, and discover the arguments.
449
class_name = loc_parts[0];
450       method_name = "invoke";
451       // All null which means we discover the arg types dynamically
452
object_specification = new Class JavaDoc[param_types.length];
453       // ignore ProcedureConnection is first argument
454
firstProcedureConnectionIgnore = true;
455     }
456     else {
457       // This means we specify a class and method name and argument
458
// specification.
459
class_name = loc_parts[0];
460       method_name = loc_parts[1];
461       object_specification = new Class JavaDoc[loc_parts.length - 2];
462
463       for (int i = 0; i < loc_parts.length - 2; ++i) {
464         String JavaDoc java_spec = loc_parts[i + 2];
465         object_specification[i] = resolveToClass(java_spec);
466       }
467
468       firstProcedureConnectionIgnore = false;
469     }
470
471     Class JavaDoc procedure_class;
472     try {
473       // Reference the procedure's class.
474
procedure_class = Class.forName(class_name);
475     }
476     catch (ClassNotFoundException JavaDoc e) {
477       throw new RuntimeException JavaDoc("Procedure class not found: " +
478                                  class_name);
479     }
480
481     // Get all the methods in this class
482
Method[] methods = procedure_class.getMethods();
483     Method invoke_method = null;
484     // Search for the invoke method
485
for (int i = 0; i < methods.length; ++i) {
486       Method method = methods[i];
487       int modifier = method.getModifiers();
488
489       if (Modifier.isStatic(modifier) && Modifier.isPublic(modifier) &&
490           method.getName().equals(method_name)) {
491
492         boolean params_match;
493
494         // Get the parameters for this method
495
Class JavaDoc[] method_args = method.getParameterTypes();
496         // If no methods, and object_specification has no args then this is a
497
// match.
498
if (method_args.length == 0 && object_specification.length == 0) {
499           params_match = true;
500         }
501         else {
502           int search_start = 0;
503           // Is the first arugments a ProcedureConnection implementation?
504
if (firstProcedureConnectionIgnore &&
505               ProcedureConnection.class.isAssignableFrom(method_args[0])) {
506             search_start = 1;
507           }
508           // Do the number of arguments match
509
if (object_specification.length ==
510                                         method_args.length - search_start) {
511             // Do they match the specification?
512
boolean match_spec = true;
513             for (int n = 0;
514                  n < object_specification.length && match_spec == true;
515                  ++n) {
516               Class JavaDoc ob_spec = object_specification[n];
517               if (ob_spec != null &&
518                   ob_spec != method_args[n + search_start]) {
519                 match_spec = false;
520               }
521             }
522             params_match = match_spec;
523           }
524           else {
525             params_match = false;
526           }
527         }
528
529         if (params_match) {
530           if (invoke_method == null) {
531             invoke_method = method;
532           }
533           else {
534             throw new RuntimeException JavaDoc("Ambiguous public static " +
535                      method_name + " methods in stored procedure class '" +
536                      class_name + "'");
537           }
538         }
539
540       }
541
542     }
543
544     // Return the invoke method we found
545
return invoke_method;
546
547   }
548   
549   
550   
551   // ---------- Various procedure type invokation methods ----------
552

553   /**
554    * Invokes a Java (type 1) procedure. A type 1 procedure is represented by
555    * a single class with a static invokation method (called invoke). The
556    * parameters of the static 'invoke' method must be compatible class
557    * parameters defined for the procedure, and the return class must also be
558    * compatible with the procedure return type.
559    * <p>
560    * If the invoke method does not contain arguments that are compatible with
561    * the parameters given an exception is generated.
562    * <p>
563    * The class must only have a single public static 'invoke' method. If there
564    * are multiple 'invoke' methods a runtime exception is generated.
565    */

566   private TObject invokeJavaV1Procedure(ProcedureName procedure_name,
567             String JavaDoc location_str, TType return_type, TType[] param_types,
568             String JavaDoc owner, TObject[] param_values) {
569
570     // Search for the invokation method for this stored procedure
571
Method invoke_method = javaProcedureMethod(location_str, param_types);
572
573     // Did we find an invoke method?
574
if (invoke_method == null) {
575       throw new RuntimeException JavaDoc("Could not find the invokation method for " +
576                       "the Java location string '" + location_str + "'");
577     }
578
579     // Go through each argument of this class and work out how we are going
580
// cast from the database engine object to the Java object.
581
Class JavaDoc[] java_param_types = invoke_method.getParameterTypes();
582
583     // Is the first param a ProcedureConnection implementation?
584
int start_param;
585     Object JavaDoc[] java_values;
586     if (java_param_types.length > 0 &&
587         ProcedureConnection.class.isAssignableFrom(java_param_types[0])) {
588       start_param = 1;
589       java_values = new Object JavaDoc[param_types.length + 1];
590     }
591     else {
592       start_param = 0;
593       java_values = new Object JavaDoc[param_types.length];
594     }
595
596     // For each type
597
for (int i = 0; i < param_types.length; ++i) {
598       TObject value = param_values[i];
599       TType proc_type = param_types[i];
600       Class JavaDoc java_type = java_param_types[i + start_param];
601       String JavaDoc java_type_str = java_type.getName();
602
603       // First null check,
604
if (value.isNull()) {
605         java_values[i + start_param] = null;
606       }
607       else {
608         TType value_type = value.getTType();
609         // If not null, is the value and the procedure type compatible
610
if (proc_type.comparableTypes(value_type)) {
611
612           boolean error_cast = false;
613           Object JavaDoc cast_value = null;
614
615           // Compatible types,
616
// Now we need to convert the parameter value into a Java object,
617
if (value_type instanceof TStringType) {
618             // A String type can be represented in Java as a java.lang.String,
619
// or as a java.io.Reader.
620
StringAccessor accessor = (StringAccessor) value.getObject();
621             if (java_type == java.lang.String JavaDoc.class) {
622               cast_value = accessor.toString();
623             }
624             else if (java_type == java.io.Reader JavaDoc.class) {
625               cast_value = accessor.getReader();
626             }
627             else {
628               error_cast = true;
629             }
630           }
631           else if (value_type instanceof TBooleanType) {
632             // A boolean in Java is either java.lang.Boolean or primitive
633
// boolean.
634
if (java_type == java.lang.Boolean JavaDoc.class ||
635                 java_type == boolean.class) {
636               cast_value = value.getObject();
637             }
638             else {
639               error_cast = true;
640             }
641           }
642           else if (value_type instanceof TDateType) {
643             // A date translates to either java.util.Date, java.sql.Date,
644
// java.sql.Timestamp, java.sql.Time.
645
java.util.Date JavaDoc d = (java.util.Date JavaDoc) value.getObject();
646             if (java_type == java.util.Date JavaDoc.class) {
647               cast_value = d;
648             }
649             else if (java_type == java.sql.Date JavaDoc.class) {
650               cast_value = new java.sql.Date JavaDoc(d.getTime());
651             }
652             else if (java_type == java.sql.Time JavaDoc.class) {
653               cast_value = new java.sql.Time JavaDoc(d.getTime());
654             }
655             else if (java_type == java.sql.Timestamp JavaDoc.class) {
656               cast_value = new java.sql.Timestamp JavaDoc(d.getTime());
657             }
658             else {
659               error_cast = true;
660             }
661           }
662           else if (value_type instanceof TNumericType) {
663             // Number can be cast to any one of the Java numeric types
664
BigNumber num = (BigNumber) value.getObject();
665             if (java_type == BigNumber.class) {
666               cast_value = num;
667             }
668             else if (java_type == java.lang.Byte JavaDoc.class ||
669                      java_type == byte.class) {
670               cast_value = new Byte JavaDoc(num.byteValue());
671             }
672             else if (java_type == java.lang.Short JavaDoc.class ||
673                      java_type == short.class) {
674               cast_value = new Short JavaDoc(num.shortValue());
675             }
676             else if (java_type == java.lang.Integer JavaDoc.class ||
677                      java_type == int.class) {
678               cast_value = new Integer JavaDoc(num.intValue());
679             }
680             else if (java_type == java.lang.Long JavaDoc.class ||
681                      java_type == long.class) {
682               cast_value = new Long JavaDoc(num.longValue());
683             }
684             else if (java_type == java.lang.Float JavaDoc.class ||
685                      java_type == float.class) {
686               cast_value = new Float JavaDoc(num.floatValue());
687             }
688             else if (java_type == java.lang.Double JavaDoc.class ||
689                      java_type == double.class) {
690               cast_value = new Double JavaDoc(num.doubleValue());
691             }
692             else if (java_type == java.math.BigDecimal JavaDoc.class) {
693               cast_value = num.asBigDecimal();
694             }
695             else {
696               error_cast = true;
697             }
698           }
699           else if (value_type instanceof TBinaryType) {
700             // A binary type can translate to a java.io.InputStream or a
701
// byte[] array.
702
BlobAccessor blob = (BlobAccessor) value.getObject();
703             if (java_type == java.io.InputStream JavaDoc.class) {
704               cast_value = blob.getInputStream();
705             }
706             else if (java_type == byte[].class) {
707               byte[] buf = new byte[blob.length()];
708               try {
709                 InputStream JavaDoc in = blob.getInputStream();
710                 int n = 0;
711                 int len = blob.length();
712                 while (len > 0) {
713                   int count = in.read(buf, n, len);
714                   if (count == -1) {
715                     throw new IOException JavaDoc("End of stream.");
716                   }
717                   n += count;
718                   len -= count;
719                 }
720               }
721               catch (IOException JavaDoc e) {
722                 throw new RuntimeException JavaDoc("IO Error: " + e.getMessage());
723               }
724               cast_value = buf;
725             }
726             else {
727               error_cast = true;
728             }
729
730           }
731
732           // If the cast of the parameter was not possible, report the error.
733
if (error_cast) {
734             throw new StatementException("Unable to cast argument " + i +
735                " ... " + value_type.asSQLString() + " to " + java_type_str +
736                " for procedure: " +
737                procedureInfoString(procedure_name, return_type, param_types));
738           }
739
740           // Set the java value for this parameter
741
java_values[i + start_param] = cast_value;
742
743         }
744         else {
745           // The parameter is not compatible -
746
throw new StatementException("Parameter (" + i + ") not compatible " +
747             value.getTType().asSQLString() + " -> " + proc_type.asSQLString() +
748             " for procedure: " +
749             procedureInfoString(procedure_name, return_type, param_types));
750         }
751
752       } // if not null
753

754     } // for each parameter
755

756     // Create the user that has the privs of this procedure.
757
User priv_user = new User(owner, connection.getDatabase(),
758                            "/Internal/Procedure/", System.currentTimeMillis());
759     
760     // Create the ProcedureConnection object.
761
ProcedureConnection proc_connection =
762                                connection.createProcedureConnection(priv_user);
763     Object JavaDoc result;
764     try {
765       // Now the 'connection' will be set to the owner's user privs.
766

767       // Set the ProcedureConnection object as an argument if necessary.
768
if (start_param > 0) {
769         java_values[0] = proc_connection;
770       }
771
772       // The java_values array should now contain the parameter values formatted
773
// as Java objects.
774

775       // Invoke the method
776
try {
777         result = invoke_method.invoke(null, java_values);
778       }
779       catch (IllegalAccessException JavaDoc e) {
780         connection.Debug().writeException(e);
781         throw new StatementException("Illegal access exception when invoking " +
782                                      "stored procedure: " + e.getMessage());
783       }
784       catch (InvocationTargetException e) {
785         Throwable JavaDoc real_e = e.getTargetException();
786         connection.Debug().writeException(real_e);
787         throw new StatementException("Procedure Exception: " +
788                                      real_e.getMessage());
789       }
790
791     }
792     finally {
793       connection.disposeProcedureConnection(proc_connection);
794     }
795
796     // If return_type is null, there is no result from this procedure (void)
797
if (return_type == null) {
798       return null;
799     }
800     else {
801       // Cast to a valid return object and return.
802
return TObject.createAndCastFromObject(return_type, result);
803     }
804
805   }
806
807   // ---------- Inner classes ----------
808

809   /**
810    * An object that models the list of procedures as table objects in a
811    * transaction.
812    */

813   private static class ProcedureInternalTableInfo
814                                        extends AbstractInternalTableInfo2 {
815
816     ProcedureInternalTableInfo(Transaction transaction) {
817       super(transaction, Database.SYS_FUNCTION);
818     }
819
820     private static DataTableDef createDataTableDef(String JavaDoc schema, String JavaDoc name) {
821       // Create the DataTableDef that describes this entry
822
DataTableDef def = new DataTableDef();
823       def.setTableName(new TableName(schema, name));
824
825       // Add column definitions
826
def.addColumn(DataTableColumnDef.createStringColumn("type"));
827       def.addColumn(DataTableColumnDef.createStringColumn("location"));
828       def.addColumn(DataTableColumnDef.createStringColumn("return_type"));
829       def.addColumn(DataTableColumnDef.createStringColumn("param_args"));
830       def.addColumn(DataTableColumnDef.createStringColumn("owner"));
831
832       // Set to immutable
833
def.setImmutable();
834
835       // Return the data table def
836
return def;
837     }
838
839     
840     public String JavaDoc getTableType(int i) {
841       return "FUNCTION";
842     }
843
844     public DataTableDef getDataTableDef(int i) {
845       TableName table_name = getTableName(i);
846       return createDataTableDef(table_name.getSchema(), table_name.getName());
847     }
848
849     public MutableTableDataSource createInternalTable(int index) {
850       MutableTableDataSource table =
851                                    transaction.getTable(Database.SYS_FUNCTION);
852       RowEnumeration row_e = table.rowEnumeration();
853       int p = 0;
854       int i;
855       int row_i = -1;
856       while (row_e.hasMoreRows()) {
857         i = row_e.nextRowIndex();
858         if (p == index) {
859           row_i = i;
860         }
861         else {
862           ++p;
863         }
864       }
865       if (p == index) {
866         String JavaDoc schema = table.getCellContents(0, row_i).getObject().toString();
867         String JavaDoc name = table.getCellContents(1, row_i).getObject().toString();
868
869         final DataTableDef table_def = createDataTableDef(schema, name);
870         final TObject type = table.getCellContents(2, row_i);
871         final TObject location = table.getCellContents(3, row_i);
872         final TObject return_type = table.getCellContents(4, row_i);
873         final TObject param_types = table.getCellContents(5, row_i);
874         final TObject owner = table.getCellContents(6, row_i);
875
876         // Implementation of MutableTableDataSource that describes this
877
// procedure.
878
return new GTDataSource(transaction.getSystem()) {
879           public DataTableDef getDataTableDef() {
880             return table_def;
881           }
882           public int getRowCount() {
883             return 1;
884           }
885           public TObject getCellContents(int col, int row) {
886             switch (col) {
887               case 0:
888                 return type;
889               case 1:
890                 return location;
891               case 2:
892                 return return_type;
893               case 3:
894                 return param_types;
895               case 4:
896                 return owner;
897               default:
898                 throw new RuntimeException JavaDoc("Column out of bounds.");
899             }
900           }
901         };
902
903       }
904       else {
905         throw new RuntimeException JavaDoc("Index out of bounds.");
906       }
907
908     }
909
910   }
911
912 }
913
914
Popular Tags