001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019:
020: package org.apache.beehive.controls.system.jdbc;
021:
022: import org.apache.beehive.controls.api.bean.AnnotationConstraints;
023: import org.apache.beehive.controls.api.bean.AnnotationMemberTypes;
024: import org.apache.beehive.controls.api.bean.ControlInterface;
025: import org.apache.beehive.controls.api.properties.PropertySet;
026:
027: import javax.naming.NamingException;
028: import javax.naming.Context;
029: import java.lang.annotation.ElementType;
030: import java.lang.annotation.Inherited;
031: import java.lang.annotation.Retention;
032: import java.lang.annotation.RetentionPolicy;
033: import java.lang.annotation.Target;
034: import java.sql.Connection;
035: import java.sql.ResultSet;
036: import java.sql.SQLException;
037: import java.sql.Types;
038: import java.sql.SQLData;
039: import java.util.Calendar;
040: import java.util.List;
041: import java.util.Arrays;
042:
043: /**
044: * Simplifies access to a relational database from your Java code using SQL commands.
045: * The Jdbc Control handles the work of connecting to, sending queries to, and ResultSet mapping from
046: * the database. You don't need to know how to use JDBC in order to use the Jdbc Control, just basic SQL.
047: * <p/>
048: * To use a Jdbc Control create a .jcx file (java file with a .jcx extension) which extends this interface.
049: * Add annotations to the jcx to tell the Jdbc Control how to connect to your database instance (either
050: * ConnectionDataSource or ConnectionDriver), then add methods which include SQL annotations to access the database.
051: */
052: @ControlInterface(checker=JdbcControlChecker.class)
053: public interface JdbcControl {
054:
055: /**
056: * Returns a database connection to the server associated
057: * with the control. It is typically not necessary to call this method
058: * when using the control.
059: *
060: * @return A Connection a database.
061: */
062: public Connection getConnection() throws SQLException;
063:
064: /**
065: * Sets the Calendar instance that should be used when setting and getting
066: * {@link java.sql.Date Date}, {@link java.sql.Time Time}, and
067: * {@link java.sql.Timestamp Timestamp} values.
068: *
069: * @see java.sql.ResultSet#getDate(int, Calendar) java.sql.ResultSet#getDate(int, Calendar)
070: * @see java.sql.ResultSet#getTime(int, Calendar) java.sql.ResultSet#getTime(int, Calendar)
071: * @see java.sql.ResultSet#getTimestamp(int, Calendar) java.sql.ResultSet#getTimestamp(int, Calendar)
072: * @see java.sql.PreparedStatement#setDate(int, java.sql.Date, Calendar) java.sql.PreparedStatement#setDate(int, Date, Calendar)
073: * @see java.sql.PreparedStatement#setTime(int, java.sql.Time, Calendar) java.sql.PreparedStatement#setTime(int, Time, Calendar)
074: * @see java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp, Calendar) java.sql.PreparedStatement#setTimestamp(int, Timestamp, Calendar)
075: */
076: public void setDataSourceCalendar(Calendar cal);
077:
078: /**
079: * Gets the Calendar instance used when setting and getting
080: * {@link java.sql.Date Date}, {@link java.sql.Time Time}, and
081: * {@link java.sql.Timestamp Timestamp} values. This is the Calendar
082: * set by the setDataSourceCalendar method.
083: *
084: * @return The Calendar instance.
085: */
086: public Calendar getDataSourceCalendar();
087:
088: // ********************************************************************************************************************
089: // ********************************************************************************************************************
090: // Class-level Database Connection Annotations and Supporting Constructs
091: // ********************************************************************************************************************
092: // ********************************************************************************************************************
093:
094: /**
095: * Abstract base class for a user defined Jndi Context factory which can be used
096: * as a value for the jndiContextFactory member of the ConnectionDataSource
097: * annotation.
098: */
099: public static abstract class JndiContextFactory {
100:
101: /**
102: * Get a JNDI InitialContext instance.
103: *
104: * @return InitialContext instance
105: * @throws NamingException if context could not be found.
106: */
107: public abstract Context getContext() throws NamingException;
108: }
109:
110: /**
111: * Class-level annotation for making a DataSource available for use with the Jdbc Control. Either this annotation or
112: * the ConnectionDriver annotation must be set for a jcx which extends the JdbcControl interface.
113: */
114: @PropertySet(prefix="ConnectionDataSource")
115: @Inherited
116: @AnnotationConstraints.AllowExternalOverride
117: @Retention(RetentionPolicy.RUNTIME)
118: @Target({ElementType.TYPE,ElementType.FIELD})
119: public @interface ConnectionDataSource {
120:
121: /**
122: * The jndi name of the DataSource. This is a required element for this annotation.
123: */
124: @AnnotationMemberTypes.JndiName(resourceType=AnnotationMemberTypes.JndiName.ResourceType.DATASOURCE)
125: String jndiName();
126:
127: /**
128: * The name of a class which implements the IJndiContextFactory interface. This is an optional element of this annotation.
129: */
130: @AnnotationMemberTypes.Optional
131: Class<? extends JndiContextFactory> jndiContextFactory() default DefaultJndiContextFactory.class;
132: }
133:
134: /**
135: * Class-level annotation for making a ConnectionDriver available for use with the Jdbc Control. Either this
136: * annotation or the ConnectionDataSource annotation must be set for a jcx which extends the JdbcControl interface.
137: * See java.sql.DatabaseConnection for additional information about the elements of this annotation.
138: */
139: @PropertySet(prefix="ConnectionDriver")
140: @Inherited
141: @AnnotationConstraints.AllowExternalOverride
142: @Retention(RetentionPolicy.RUNTIME)
143: @Target({ElementType.TYPE,ElementType.FIELD})
144: public @interface ConnectionDriver {
145:
146: /**
147: * A String containing the fully qualified name of the database driver class. Required element.
148: */
149: String databaseDriverClass();
150:
151: /**
152: * A String containing the database URL to connect to. Required element.
153: */
154: String databaseURL();
155:
156: /**
157: * A String containing the user name to connect to the database as. Optional element.
158: */
159: @AnnotationMemberTypes.Optional
160: String userName() default "";
161:
162: /**
163: * A String containing the password associated with userName. Optional element.
164: */
165: @AnnotationMemberTypes.Optional
166: String password() default "";
167:
168: /**
169: * A String containing a semicolon seperated list of name/value pairs for the DatabaseConnection.
170: * The string must have the format of propertyName=propertyValue;propertyName=propertyValue;...
171: * The properties will only be used if the userName and password elements of this annotation are
172: * NOT set.
173: *
174: * Optional element.
175: */
176: @AnnotationMemberTypes.Optional
177: String properties() default "";
178: }
179:
180: /**
181: * Class level annotation used to set options on the JDBC connnection.
182: */
183: @PropertySet(prefix="ConnectionOptions")
184: @Inherited
185: @AnnotationConstraints.AllowExternalOverride
186: @Retention(RetentionPolicy.RUNTIME)
187: @Target({ElementType.TYPE,ElementType.FIELD})
188: public @interface ConnectionOptions {
189:
190: /**
191: * If set to true, database connection will optimize for read only queries, writes still permitted.
192: * Optional, defaults to false.
193: */
194: @AnnotationMemberTypes.Optional
195: boolean readOnly() default false;
196:
197: /**
198: * Specifies ResultSet holdability for the connection. May be overridden at method level.
199: * Optional, defaults to jdbc driver's default setting.
200: */
201: @AnnotationMemberTypes.Optional
202: HoldabilityType resultSetHoldability() default HoldabilityType.DRIVER_DEFAULT;
203:
204: /**
205: * Specifies type mappings for SQL user defined types (UDTs). Any type mappings set here will be used
206: * by the underlying JDBC Connection for UDT type mappings. These mappings can be overridden by using
207: * the SQL annotations methodTypeMappers element. Optional element.
208: */
209: @AnnotationMemberTypes.Optional
210: TypeMapper[] typeMappers() default {};
211: }
212:
213: /**
214: * Class / method level annotation for mapping SQL user defined types (UDTs) to and from java objects.
215: * The mapper class element must implement the java.sql.SQLData interface.
216: */
217: @PropertySet(prefix="TypeMapper")
218: @Inherited
219: @AnnotationConstraints.AllowExternalOverride
220: @Retention(RetentionPolicy.RUNTIME)
221: @Target({ElementType.TYPE,ElementType.METHOD})
222: public @interface TypeMapper {
223: String UDTName();
224:
225: Class<? extends SQLData> mapperClass();
226: }
227:
228: // ********************************************************************************************************************
229: // ********************************************************************************************************************
230: // SQL Method-level Annotation and Supporting Constructs
231: // ********************************************************************************************************************
232: // ********************************************************************************************************************
233:
234: /**
235: * This constant can be used as the value for the maxRows element of the SQL annotation.
236: * It indicates that all rows should be returned (i.e. no limit)
237: */
238: public final int MAXROWS_ALL = 0;
239:
240: /**
241: * The default fetch size for result sets, indicates the database should determine the fetch size.
242: */
243: public final int DEFAULT_FETCH_SIZE = 0;
244:
245: /**
246: * Default value for the iteratorElementType element of the
247: * SQL annotation. It signals that no type has been defined for the method
248: * (common if the method return type isn't itself an iterator)
249: */
250: public interface UndefinedIteratorType {
251: }
252:
253: /**
254: * Default value for the resultSetMapper element of the
255: * SQL annotation. It signals that no type has been defined for the method.
256: */
257: public interface UndefinedResultSetMapper {
258: }
259:
260: /**
261: * Enumeration of supported types of scrolling ResultSets
262: */
263: public enum ScrollType {
264: DRIVER_DEFAULT(-1, -1), FORWARD_ONLY(
265: ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY), SCROLL_INSENSITIVE(
266: ResultSet.TYPE_SCROLL_INSENSITIVE,
267: ResultSet.CONCUR_READ_ONLY), SCROLL_SENSITIVE(
268: ResultSet.TYPE_SCROLL_SENSITIVE,
269: ResultSet.CONCUR_READ_ONLY), FORWARD_ONLY_UPDATABLE(
270: ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE), SCROLL_INSENSITIVE_UPDATABLE(
271: ResultSet.TYPE_SCROLL_INSENSITIVE,
272: ResultSet.CONCUR_UPDATABLE), SCROLL_SENSITIVE_UPDATABLE(
273: ResultSet.TYPE_SCROLL_SENSITIVE,
274: ResultSet.CONCUR_UPDATABLE);
275:
276: private final int _type;
277: private final int _concurrencyType;
278:
279: ScrollType(int scrollType, int concurrencyType) {
280: _type = scrollType;
281: _concurrencyType = concurrencyType;
282: }
283:
284: public int getType() {
285: return _type;
286: }
287:
288: public int getConcurrencyType() {
289: return _concurrencyType;
290: }
291:
292: public String toString() {
293: StringBuilder sb = new StringBuilder();
294: if (_type == ResultSet.TYPE_FORWARD_ONLY) {
295: sb.append("Foward Only, ");
296: } else if (_type == ResultSet.TYPE_SCROLL_INSENSITIVE) {
297: sb.append("Scroll Insensitive, ");
298: } else if (_type == ResultSet.TYPE_SCROLL_SENSITIVE) {
299: sb.append("Scroll Sensitive, ");
300: } else {
301: sb.append("Jdbc Driver Default Direction");
302: }
303:
304: if (_concurrencyType == ResultSet.CONCUR_READ_ONLY) {
305: sb.append("Read Only");
306: } else if (_concurrencyType == ResultSet.CONCUR_UPDATABLE) {
307: sb.append("Updatable");
308: } else {
309: sb.append("Jdbc Driver Default");
310: }
311: return sb.toString();
312: }
313: }
314:
315: /**
316: * Enumeration of supported fetch directions.
317: */
318: public enum FetchDirection {
319: FORWARD(ResultSet.FETCH_FORWARD), REVERSE(
320: ResultSet.FETCH_REVERSE), UNKNOWN(
321: ResultSet.FETCH_UNKNOWN);
322:
323: private final int _direction;
324:
325: FetchDirection(int direction) {
326: _direction = direction;
327: }
328:
329: public int getDirection() {
330: return _direction;
331: }
332: }
333:
334: /**
335: * Enumeration of supported fetch directions.
336: */
337: public enum HoldabilityType {
338: DRIVER_DEFAULT(0), HOLD_CURSORS(
339: ResultSet.HOLD_CURSORS_OVER_COMMIT), CLOSE_CURSORS(
340: ResultSet.CLOSE_CURSORS_AT_COMMIT);
341:
342: private final int _holdability;
343:
344: HoldabilityType(int holdability) {
345: _holdability = holdability;
346: }
347:
348: public int getHoldability() {
349: return _holdability;
350: }
351:
352: public String toString() {
353: if (_holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT) {
354: return "HOLD_CURSORS_OVER_COMMIT";
355: } else if (_holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT) {
356: return "CLOSE_CURSORS_AT_COMMIT";
357: } else {
358: return "Default driver holdability";
359: }
360: }
361: }
362:
363: /**
364: * Method-level annotation for methods in a jcx which wish to access a database instance.
365: */
366: @PropertySet(prefix="SQL")
367: @Inherited
368: @Retention(RetentionPolicy.RUNTIME)
369: @AnnotationConstraints.AllowExternalOverride
370: @Target({ElementType.METHOD})
371: public @interface SQL {
372:
373: /**
374: * The SQL statement to send to the database. Required annotation element.
375: */
376: String statement();
377:
378: /**
379: * Maximum array length.
380: * Optional element.
381: * This element has no effect on the call unless the method return type is an array.
382: * When used in conjunction with the maxRows element, the size of the array generated
383: * from the result set will be the smaller of maxRows and arrayMaxLength.
384: * <p>
385: * arrayMaxLength's default value is 1024, but may be set to zero to specify that
386: * there is no size limit for the array generated from the ResultSet.
387: * Since the generated array is stored in-memory, care should be taken when dealing
388: * with very large ResultSets when the value of this element is set to zero.
389: */
390: @AnnotationMemberTypes.Optional
391: int arrayMaxLength() default 1024;
392:
393: /**
394: * Max number of ResultSet rows to return.
395: * If used with arrayMaxLength the smaller value is used.
396: * Optional element, default value is no limit on number of rows returned.
397: */
398: @AnnotationMemberTypes.Optional
399: int maxRows() default MAXROWS_ALL;
400:
401: /**
402: * Execute the SQL statement as a batch update.
403: * Methods which have this element set to true must return an array of ints.
404: * Optional element, defaults to false.
405: */
406: @AnnotationMemberTypes.Optional
407: boolean batchUpdate() default false;
408:
409: /**
410: * Specify the fetch size for the ResultSet. Optional element, defaults to 0.
411: */
412: @AnnotationMemberTypes.Optional
413: int fetchSize() default DEFAULT_FETCH_SIZE;
414:
415: /**
416: * Specify the fetch direction for the ResultSEt. Optional element, defaults to FORWARD.
417: */
418: @AnnotationMemberTypes.Optional
419: FetchDirection fetchDirection() default FetchDirection.FORWARD;
420:
421: /**
422: * Return the generated key values generated by the SQL statement. Optional element, defaults to false.
423: */
424: @AnnotationMemberTypes.Optional
425: boolean getGeneratedKeys() default false;
426:
427: /**
428: * Specify generated key columns by column names to return when the getGeneratedKeys element is true.
429: * May only be set if getGeneratedKeys is set to true, otherwise a compile time error is generated.
430: * Optional element.
431: */
432: @AnnotationMemberTypes.Optional
433: String[] generatedKeyColumnNames() default {};
434:
435: /**
436: * Specify generated key columns by column number to return when the getGeneratedKeys element is true.
437: * May only be set if getGeneratedKeys is set to true, otherwise a compile time error is generated
438: * Optional element.
439: */
440: @AnnotationMemberTypes.Optional
441: int[] generatedKeyColumnIndexes() default {};
442:
443: /**
444: * Specify the holdability type for the annotated method. Overrides the holability annotation element
445: * of the ConnectionOptions annotation. The holdability type will be in effect for the duration of this
446: * method call. Optional, defaults to DRIVER_DEFAULT.
447: */
448: @AnnotationMemberTypes.Optional
449: HoldabilityType resultSetHoldabilityOverride() default HoldabilityType.DRIVER_DEFAULT;
450:
451: /**
452: * Specifies type mappings for SQL user defined types (UDTs). Any type mappings set here will be used
453: * by the underlying JDBC Connection for UDT type mappings. These type mappings will REPLACE any set on
454: * the JDBC connection for the duration of the method call. Optional element.
455: */
456: @AnnotationMemberTypes.Optional
457: TypeMapper[] typeMappersOverride() default {};
458:
459: /**
460: * Specify the type of element to be interated over when the method's return type is java.util.Iterator.
461: * Optional element.
462: */
463: @AnnotationMemberTypes.Optional
464: Class iteratorElementType() default UndefinedIteratorType.class;
465:
466: /**
467: * Specify a custom result set mapper for the ResultSet generated by the SQL statement.
468: * ResultSet mappers must extend the ResultSetMapper abstract base class. If a value is specified
469: * it will be used to map the ResultSet of the query to the return type of the method.
470: * See org.apache.beehive.controls.system.jdbc.ResultSetMapper for additional information.
471: * Optional element.
472: */
473: @AnnotationMemberTypes.Optional
474: Class resultSetMapper() default UndefinedResultSetMapper.class;
475:
476: /**
477: * Specify that the ResultSet returned by the method is scrollable. Valid only for methods which
478: * return a ResultSet, otherwise a compile-time error will occur. Valid element values
479: * are defined by the ScrollType enumeration.
480: * Optional element, defaults to JDBC driver's default setting.
481: */
482: @AnnotationMemberTypes.Optional
483: ScrollType scrollableResultSet() default ScrollType.DRIVER_DEFAULT;
484: } // SQL annotation declaration
485:
486: // ********************************************************************************************************************
487: // ********************************************************************************************************************
488: // Inner Classes
489: // ********************************************************************************************************************
490: // ********************************************************************************************************************
491:
492: /**
493: * Nested class used for specifing parameters for a callable statement. If a method in a control extension takes an array of
494: * SQLParameter, the JdbcControl treats the SQL as a CallableStatement and inserts values into the statement from
495: * the SQLParameter array. After the CallableStatement executes, results are mapped into OUT type parameters found
496: * int the SQLParameter array.
497: * NOTE: To invoke a callable statement which does not take any arguments, an SQLParameter array of size zero must
498: * be passed to the JDBCControl method.
499: */
500: public static class SQLParameter {
501: /**
502: * IN direction constant.
503: */
504: public static final int IN = 1;
505: /**
506: * OUT direction constant.
507: */
508: public static final int OUT = 2;
509: /**
510: * IN and OUT directions constant.
511: */
512: public static final int INOUT = IN | OUT;
513:
514: /**
515: * Parameter value. For parameters of type OUT this value should be set to null.
516: */
517: public Object value = null;
518:
519: /**
520: * Parameter SQL data type. See java.sql.Types.
521: */
522: public int type = Types.NULL;
523:
524: /**
525: * Parameter direction.
526: */
527: public int dir = IN;
528:
529: /**
530: * Create a new SQLParameter with the specified value.
531: *
532: * @param value The parameter value.
533: */
534: public SQLParameter(Object value) {
535: this .value = value;
536: }
537:
538: /**
539: * Create a new SQLParameter with the specified value and SQL data type.
540: *
541: * @param value The parameter value.
542: * @param type SQL data type.
543: */
544: public SQLParameter(Object value, int type) {
545: this (value);
546: this .type = type;
547: }
548:
549: /**
550: * Create a new SQLParameter with the specified value, SQL data type and direction.
551: *
552: * @param value The parameter value.
553: * @param type SQL data type.
554: * @param dir IN / OUT or INOUT
555: */
556: public SQLParameter(Object value, int type, int dir) {
557: this (value, type);
558: this .dir = dir;
559: }
560:
561: /**
562: * Clone this parameter.
563: *
564: * @return A copy of this parameter.
565: */
566: public Object clone() {
567: return new SQLParameter(value, type, dir);
568: }
569: }
570:
571: /**
572: * A ComplexSqlFragment can be used as a return value from a parameter reflection operation for
573: * return values which contain BOTH SQL text and parameters. For Example, the text portion
574: * could be something like 'where NAME = ?' and the parameter value is 'Fred'.
575: */
576: public static class ComplexSqlFragment {
577:
578: protected CharSequence sql;
579: protected List<SQLParameter> parameters;
580:
581: /**
582: * Create a new SQLFragment.
583: */
584: public ComplexSqlFragment() {
585: sql = null;
586: parameters = null;
587: }
588:
589: /**
590: * Create a new SQLFragment with the specified SQL and parameter list.
591: *
592: * @param sql SQL contents of the fragment.
593: * @param parameters Substitution parameters.
594: */
595: public ComplexSqlFragment(String sql, SQLParameter[] parameters) {
596: this .sql = sql;
597: if (null != parameters)
598: this .parameters = Arrays.asList(parameters);
599: }
600:
601: /**
602: * Get the SQL of this fragment.
603: *
604: * @return String.
605: */
606: public String getSQL() {
607: return sql.toString();
608: }
609:
610: /**
611: * Get the parameters contained within this fragment.
612: * Returns a zero-based array.
613: *
614: * @return SQLParameter array.
615: */
616: public SQLParameter[] getParameters() {
617: if (null == parameters)
618: return new SQLParameter[0];
619: return parameters.toArray(new SQLParameter[parameters
620: .size()]);
621: }
622:
623: /**
624: * Get the SQL string contained within this fragment.
625: * @return String.
626: */
627: public String toString() {
628: return sql.toString();
629: }
630: }
631: }
|