You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beehive.apache.org by ek...@apache.org on 2005/05/09 22:18:03 UTC

svn commit: r169351 [8/12] - in /incubator/beehive/trunk/system-controls: ./ ant/ external/ external/commons/ external/ejb/ external/jms/ src/ src/ejb/ src/ejb/org/ src/ejb/org/apache/ src/ejb/org/apache/beehive/ src/ejb/org/apache/beehive/controls/ src/ejb/org/apache/beehive/controls/system/ src/ejb/org/apache/beehive/controls/system/ejb/ src/ejb/schema/ src/jdbc/ src/jdbc/org/ src/jdbc/org/apache/ src/jdbc/org/apache/beehive/ src/jdbc/org/apache/beehive/controls/ src/jdbc/org/apache/beehive/controls/system/ src/jdbc/org/apache/beehive/controls/system/jdbc/ src/jdbc/org/apache/beehive/controls/system/jdbc/parser/ src/jms/ src/jms/org/ src/jms/org/apache/ src/jms/org/apache/beehive/ src/jms/org/apache/beehive/controls/ src/jms/org/apache/beehive/controls/system/ src/jms/org/apache/beehive/controls/system/jms/ src/jms/org/apache/beehive/controls/system/jms/impl/ src/jms/org/apache/beehive/controls/system/jndi/ src/jms/org/apache/beehive/controls/system/jndi/impl/ src/webservice/ src/webservice/org/ src/webservice/org/apache/ src/webservice/org/apache/beehive/ src/webservice/org/apache/beehive/controls/ src/webservice/org/apache/beehive/controls/system/ src/webservice/org/apache/beehive/controls/system/webservice/ src/webservice/org/apache/beehive/controls/system/webservice/generator/ src/webservice/org/apache/beehive/controls/system/webservice/jaxrpc/ src/webservice/org/apache/beehive/controls/system/webservice/utils/ test/ test/ant/ test/conf/ test/src/ test/src/jdbc/ test/src/jdbc/controls/ test/src/jdbc/controls/org/ test/src/jdbc/controls/org/apache/ test/src/jdbc/controls/org/apache/beehive/ test/src/jdbc/controls/org/apache/beehive/controls/ test/src/jdbc/controls/org/apache/beehive/controls/system/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/dbconnection/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/errors/ test/src/jdbc/controls/org/apache/beehive/controls/system/jdbc/test/results/ test/src/jdbc/controls/schemas/ test/src/jdbc/controls/schemas/badusers/ test/src/jdbc/controls/schemas/users/ test/src/jdbc/jdbc-container/ test/src/jdbc/jdbc-container/application/ test/src/jdbc/jdbc-container/src/ test/src/jdbc/jdbc-container/src/org/ test/src/jdbc/jdbc-container/src/org/apache/ test/src/jdbc/jdbc-container/src/org/apache/beehive/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/jdbc-container/src/org/apache/beehive/controls/system/jdbc/containertest/ test/src/jdbc/jdbc-container/webapp/ test/src/jdbc/jdbc-container/webapp/WEB-INF/ test/src/jdbc/jdbc-container/webapp/conf/ test/src/jdbc/jdbc-container/webapp/conf/Catalina/ test/src/jdbc/jdbc-container/webapp/conf/Catalina/localhost/ test/src/jdbc/junitTests/ test/src/jdbc/junitTests/org/ test/src/jdbc/junitTests/org/apache/ test/src/jdbc/junitTests/org/apache/beehive/ test/src/jdbc/junitTests/org/apache/beehive/controls/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/dbconnection/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/errors/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/results/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/sqlparser/ test/src/jdbc/junitTests/org/apache/beehive/controls/system/jdbc/units/utils/

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlStatement.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlStatement.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlStatement.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlStatement.java Mon May  9 13:17:58 2005
@@ -0,0 +1,522 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc.parser;
+
+import org.apache.beehive.controls.api.ControlException;
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+import org.apache.beehive.controls.system.jdbc.JdbcControl;
+import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
+
+import javax.sql.RowSet;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Calendar;
+
+/**
+ * Represents a fully parsed SQL statement. SqlStatements can be used to generated a java.sql.PreparedStatement.
+ */
+public final class SqlStatement extends SqlFragmentContainer implements Serializable {
+
+    private static final TypeMappingsFactory _tmf = TypeMappingsFactory.getInstance();
+    private boolean _callableStatement = false;
+    private boolean _cacheableStatement = true;
+
+    //
+    // set from SQL annotation element values
+    //
+    private boolean _batchUpdate;
+    private boolean _getGeneratedKeys;
+    private String[] _genKeyColumnNames;
+    private int _fetchSize;
+    private int _maxArray;
+    private int _maxRows;
+    private int[] _genKeyColumnIndexes;
+    private JdbcControl.ScrollType _scrollType;
+    private JdbcControl.FetchDirection _fetchDirection;
+    private JdbcControl.HoldabilityType _holdability;
+
+    /**
+     * Create a new SqlStatement.
+     */
+    SqlStatement() {
+        super();
+    }
+
+    /**
+     * Append a SqlFragment to the end of this statement.
+     *
+     * @param frag SqlFragment to append.
+     */
+    void addChild(SqlFragment frag) {
+        super.addChild(frag);
+
+        if (frag.isDynamicFragment()) {
+            _cacheableStatement = false;
+        }
+    }
+
+
+    /**
+     * Can the PreparedStatement generated by this class be cached?
+     *
+     * @return true if this statement can be cached by the SqlParser.
+     */
+    boolean isCacheable() { return _cacheableStatement; }
+
+
+    /**
+     * Does this statement generate a callable or prepared statement?
+     *
+     * @return true if this statement generates callable statement.
+     */
+    public boolean isCallableStatement() { return _callableStatement; }
+
+    /**
+     * Does this statement do a batch update?
+     *
+     * @return true if this statement should be executed as a batch update.
+     */
+    public boolean isBatchUpdate() { return _batchUpdate; }
+
+    /**
+     * Does this statement return generatedKeys?
+     *
+     * @return true if getGeneratedKeys set to true.
+     */
+    public boolean getsGeneratedKeys() { return _getGeneratedKeys; }
+
+    /**
+     * Generates the PreparedStatement the SQL statement.
+     *
+     * @param context    ControlBeanContext instance.
+     * @param connection Connection to database.
+     * @param calendar   Calendar instance which can be used to resolve date/time values.
+     * @param method     Method the SQL is associated with.
+     * @param arguments  Method parameters.
+     * @return The PreparedStatement generated by this statement.
+     * @throws SQLException If PreparedStatement cannot be created.
+     */
+    public PreparedStatement createPreparedStatement(ControlBeanContext context, Connection connection,
+                                                     Calendar calendar, Method method, Object[] arguments)
+            throws SQLException {
+
+        PreparedStatement preparedStatement = null;
+        loadSQLAnnotationStatmentOptions(context, method);
+        checkJdbcSupport(connection.getMetaData());
+
+        _callableStatement = setCallableStatement(arguments);
+
+        try {
+            final String sql = getPreparedStatementText(context, method, arguments);
+
+            //
+            // is this a request for generatedKeys ?
+            //
+            if (_getGeneratedKeys) {
+
+                if (_callableStatement) {
+                    throw new ControlException("getGeneratedKeys not supported for CallableStatements");
+                }
+
+                if (_genKeyColumnNames.length > 0) {
+                    preparedStatement = connection.prepareStatement(sql, _genKeyColumnNames);
+                } else if (_genKeyColumnIndexes.length > 0) {
+                    preparedStatement = connection.prepareStatement(sql, _genKeyColumnIndexes);
+                } else {
+                    preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
+                }
+
+            } else {
+
+                if (_holdability == JdbcControl.HoldabilityType.DRIVER_DEFAULT) {
+                    if (_scrollType == JdbcControl.ScrollType.DRIVER_DEFAULT) {
+                        preparedStatement = (_callableStatement) ? connection.prepareCall(sql) : connection.prepareStatement(sql);
+                    } else {
+                        preparedStatement = (_callableStatement)
+                                ? connection.prepareCall(sql, _scrollType.getType(), _scrollType.getConcurrencyType())
+                                : connection.prepareStatement(sql, _scrollType.getType(), _scrollType.getConcurrencyType());
+                    }
+                } else {
+                    preparedStatement = (_callableStatement)
+                            ? connection.prepareCall(sql, _scrollType.getType(), _scrollType.getConcurrencyType(), _holdability.getHoldability())
+                            : connection.prepareStatement(sql, _scrollType.getType(), _scrollType.getConcurrencyType(), _holdability.getHoldability());
+                }
+            }
+
+            //
+            // If the method argument is of type SQLParameter, treat this statement as a CallableStatement,
+            //
+            if (_callableStatement) {
+                for (SqlFragment sf : _children) {
+                    if (sf.hasParamValue()) {
+                        throw new ControlException("Cannot use parameter substution and SQLParameter array in the same method.");
+                    }
+                }
+                JdbcControl.SQLParameter[] params = (JdbcControl.SQLParameter[]) arguments[0];
+                if (params == null) {
+                    return preparedStatement;
+                }
+                for (int i = 0; i < params.length; i++) {
+                    JdbcControl.SQLParameter p = params[i];
+                    if (p.dir != JdbcControl.SQLParameter.OUT) {
+                        Object value = params[i].value;
+                        setPreparedStatementParameter(preparedStatement, i + 1, value, params[i].type, calendar);
+                    }
+
+                    if (p.dir != JdbcControl.SQLParameter.IN) {
+                        ((CallableStatement) preparedStatement).registerOutParameter(i + 1, params[i].type);
+                    }
+                }
+
+
+                //
+                // special handling for batch updates
+                //
+            } else if (_batchUpdate) {
+                doBatchUpdate(preparedStatement, arguments, calendar);
+
+
+                //
+                // standard case, not a batch or callable
+                //
+            } else {
+                int pIndex = 1;
+                for (SqlFragment sf : _children) {
+                    if (sf.hasParamValue()) {
+                        Object values[] = sf.getParameterValues(context, method, arguments);
+                        for (Object value : values) {
+                            setPreparedStatementParameter(preparedStatement, pIndex++, value, sf.getParamSqlDataType(), calendar);
+                        }
+                    }
+                }
+            }
+        } catch (SQLException e) {
+            if (preparedStatement != null) preparedStatement.close();
+            throw e;
+        }
+
+        preparedStatement.setFetchDirection(_fetchDirection.getDirection());
+        preparedStatement.setFetchSize(_fetchSize);
+        preparedStatement.setMaxRows(computeMaxRows(method));
+
+        return preparedStatement;
+    }
+
+    /**
+     * Generates the PreparedStatement the SQL statement.
+     *
+     * @param context    ControlBeanContext instance.
+     * @param connection Connection to database.
+     * @param method     Method the SQL is associated with.
+     * @param arguments  Method parameters.
+     * @return The PreparedStatement generated by this statement.
+     */
+    public String createPreparedStatementString(ControlBeanContext context, Connection connection,
+                                                Method method, Object[] arguments) {
+
+        final boolean callableStatement = setCallableStatement(arguments);
+        StringBuilder sqlString = new StringBuilder(getPreparedStatementText(context, method, arguments));
+
+        if (callableStatement) {
+            JdbcControl.SQLParameter[] params = (JdbcControl.SQLParameter[]) arguments[0];
+            if (params == null) {
+                return sqlString.toString();
+            }
+
+            sqlString.append(" Params: {");
+            for (int i = 0; i < params.length; i++) {
+                if (i > 0) { sqlString.append(params[i].value.toString()); }
+            }
+            sqlString.append("}");
+
+        } else if (_batchUpdate) {
+            sqlString.append(" Params: batch update.");
+
+        } else {
+            sqlString.append(" Params: {");
+            boolean first = true;
+            for (SqlFragment sf : _children) {
+                if (sf.hasParamValue()) {
+                    Object values[] = sf.getParameterValues(context, method, arguments);
+                    for (Object value : values) {
+
+                        if (!first) sqlString.append(", "); else first = false;
+                        sqlString.append(value);
+                    }
+                }
+            }
+            sqlString.append("}");
+        }
+        return sqlString.toString();
+    }
+
+
+    // /////////////////////////////////////////////////// PRIVATE METHODS ///////////////////////////////////////////
+
+    /**
+     * Sets the specified parameter in the prepared statement.
+     *
+     * @param ps      A PreparedStatement.
+     * @param i       index of parameter to set.
+     * @param value   value of the parameter.
+     * @param sqlType SQL type of value.
+     * @param cal     A calendar instance used to resolve date/time values.
+     * @throws SQLException If the parameter cannot be set.
+     */
+    private void setPreparedStatementParameter(PreparedStatement ps, int i, Object value, int sqlType, Calendar cal)
+            throws SQLException {
+
+        if (sqlType == Types.NULL) {
+            sqlType = _tmf.getSqlType(value);
+        }
+
+        if (value == null) {
+            ps.setNull(i, Types.NULL == sqlType ? Types.VARCHAR : sqlType);
+            return;
+        }
+
+        switch (sqlType) {
+
+            case Types.VARCHAR:
+                if (!(value instanceof String)) value = value.toString();
+                break;
+
+            case Types.BOOLEAN:
+                if (value instanceof Boolean) {
+                    ps.setBoolean(i, ((Boolean) value).booleanValue());
+                    return;
+                }
+                break;
+
+            case Types.TIMESTAMP:
+                if (value instanceof java.util.Calendar) {
+                    Calendar calValue = (Calendar) value;
+
+// @todo: validate it is correct to comment out call to deprectated method
+//                    if (cal == null) {
+//                        /* NOTE: drivers are inconsistent in their handling of setTimestamp(i,date,cal)
+//                         * so we won't use that, unless the user calls setCalendar().
+//                         * I'm going with the theory that it makes sense to store
+//                         * the time relative to the Calendar's timezone rather than
+//                         * the system timezone otherwise, using a Calendar would be a no-op.
+//                         */
+//                        value = new java._sql.Timestamp(calValue.get(Calendar.YEAR) - 1900,
+//                                                       calValue.get(Calendar.MONTH),
+//                                                       calValue.get(Calendar.DATE),
+//                                                       calValue.get(Calendar.HOUR_OF_DAY),
+//                                                       calValue.get(Calendar.MINUTE),
+//                                                       calValue.get(Calendar.SECOND),
+//                                                       calValue.get(Calendar.MILLISECOND));
+//                    } else {
+                    value = new java.sql.Timestamp(calValue.getTimeInMillis());
+//                    }
+                } else if (java.util.Date.class.equals(value.getClass())) {
+                    // some drivers don't like java.util.Date
+                    value = new java.sql.Timestamp(((java.util.Date) value).getTime());
+                }
+
+                if (value instanceof java.sql.Timestamp) {
+                    if (cal == null)
+                        ps.setTimestamp(i, (java.sql.Timestamp) value);
+                    else
+                        ps.setTimestamp(i, (java.sql.Timestamp) value, cal);
+                    return;
+                }
+                break;
+
+            case Types.DATE:
+                if (value instanceof java.util.Calendar) {
+                    /* NOTE: see note above
+                     Calendar cal = (Calendar)value;
+                     value = new java._sql.Date(cal.getTimeInMillis());
+                     ps.setDate(i, (java._sql.Date)value, cal);
+                     return;
+                     */
+                    Calendar calValue = (Calendar) value;
+
+                    // @todo: validate that commenting out deprected method is correct behavior
+//                    if (cal == null) {
+//                        value = new java._sql.Date(calValue.get(Calendar.YEAR - 1900),
+//                                                  calValue.get(Calendar.MONTH),
+//                                                  calValue.get(Calendar.DATE));
+//                    } else {
+                    value = new java.sql.Date(calValue.getTimeInMillis());
+//                    }
+                } else if (value.getClass() == java.util.Date.class) {
+                    // some drivers don't like java.util.Date
+                    value = new java.sql.Date(((java.util.Date) value).getTime());
+                }
+
+                if (value instanceof java.sql.Date) {
+                    if (cal == null) {
+                        ps.setDate(i, (java.sql.Date) value);
+                    } else {
+                        ps.setDate(i, (java.sql.Date) value, cal);
+                    }
+                    return;
+                }
+                break;
+
+            case Types.TIME:
+                if (value instanceof java.sql.Time) {
+                    if (cal == null) {
+                        ps.setTime(i, (java.sql.Time) value);
+                    } else {
+                        ps.setTime(i, (java.sql.Time) value, cal);
+                    }
+                    return;
+                }
+                break;
+        }
+
+        if (sqlType == Types.NULL) {
+            ps.setObject(i, value);
+        } else {
+            ps.setObject(i, value, sqlType);
+        }
+    }
+
+    /**
+     * Determine if this SQL will generate a callable or prepared statement.
+     *
+     * @param args The method's argument list which this SQL annotation was assocatied with.
+     * @return true if this statement will generated a CallableStatement
+     */
+    private boolean setCallableStatement(Object[] args) {
+
+        // CallableStatement vs. PreparedStatement
+        if (args != null && args.length == 1 && args[0] != null) {
+            Class argClass = args[0].getClass();
+            if (argClass.isArray() && JdbcControl.SQLParameter.class.isAssignableFrom(argClass.getComponentType())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Build a prepared statement for a batch update.
+     *
+     * @param ps   The PreparedStatement object.
+     * @param args The parameter list of the jdbccontrol method.
+     * @param cal  A Calendar instance used to resolve date/time values.
+     * @throws SQLException If a batch update cannot be performed.
+     */
+    private void doBatchUpdate(PreparedStatement ps, Object[] args, Calendar cal) throws SQLException {
+
+        final int[] sqlTypes = new int[args.length];
+        final Object[] objArrays = new Object[args.length];
+
+        // build an array of type values and object arrays
+        for (int i = 0; i < args.length; i++) {
+            sqlTypes[i] = _tmf.getSqlType(args[i].getClass().getComponentType());
+            objArrays[i] = TypeMappingsFactory.toObjectArray(args[i]);
+        }
+
+        final int rowCount = ((Object[]) objArrays[0]).length;
+        for (int i = 0; i < rowCount; i++) {
+            for (int j = 0; j < args.length; j++) {
+                setPreparedStatementParameter(ps, j + 1, ((Object[]) objArrays[j])[i], sqlTypes[j], cal);
+            }
+            ps.addBatch();
+        }
+    }
+
+    /**
+     * Load element values from the SQL annotation which apply to Statements.
+     *
+     * @param context ControlBeanContext instance.
+     * @param method  Annotated method.
+     */
+    private void loadSQLAnnotationStatmentOptions(ControlBeanContext context, Method method) {
+
+        final JdbcControl.SQL methodSQL = (JdbcControl.SQL) context.getMethodPropertySet(method, JdbcControl.SQL.class);
+
+        _batchUpdate = methodSQL.batchUpdate();
+        _getGeneratedKeys = methodSQL.getGeneratedKeys();
+        _genKeyColumnNames = methodSQL.generatedKeyColumnNames();
+        _genKeyColumnIndexes = methodSQL.generatedKeyColumnIndexes();
+        _scrollType = methodSQL.scrollableResultSet();
+        _fetchDirection = methodSQL.fetchDirection();
+        _fetchSize = methodSQL.fetchSize();
+        _maxRows = methodSQL.maxRows();
+        _maxArray = methodSQL.arrayMaxLength();
+
+        _holdability = methodSQL.resultSetHoldabilityOverride();
+    }
+
+    /**
+     * Checks that all statement options specified in annotation are supported by the database.
+     *
+     * @param metaData
+     * @throws SQLException
+     */
+    private void checkJdbcSupport(DatabaseMetaData metaData) throws SQLException {
+
+        if (_getGeneratedKeys && !metaData.supportsGetGeneratedKeys()) {
+            throw new ControlException("The database does not support getGeneratedKeys.");
+        }
+
+        if (_batchUpdate && !metaData.supportsBatchUpdates()) {
+            throw new ControlException("The database does not support batchUpdates.");
+        }
+
+        if (_scrollType != JdbcControl.ScrollType.DRIVER_DEFAULT
+                && !metaData.supportsResultSetConcurrency(_scrollType.getType(), _scrollType.getConcurrencyType())) {
+            throw new ControlException("The database does not support the ResultSet concurrecy type: " + _scrollType.toString());
+        }
+
+        if (_holdability != JdbcControl.HoldabilityType.DRIVER_DEFAULT
+                && !metaData.supportsResultSetHoldability(_holdability.getHoldability())) {
+            throw new ControlException("The database does not support the ResultSet holdability type: " + _holdability.toString());
+        }
+    }
+
+    /**
+     * The much maligned method for computing the maximum number of ResultSet rows this statement should return.
+     * The values of maxRows and arrayMaxLength are enforced at compile-time by the JdbcControlChecker to be the
+     * following: MAXROWS_ALL <= maxRows, 0 < arrayMaxLength
+     *
+     * @param method The annotated method.
+     * @return max number of resultSet rows to return from the query.
+     */
+    private int computeMaxRows(Method method) {
+
+        Class returnType = method.getReturnType();
+
+        final boolean isArray = returnType.isArray();
+        final boolean isRowSet = returnType.equals(RowSet.class);
+
+        int maxSet = _maxRows;
+        if (isArray && _maxArray != JdbcControl.MAXROWS_ALL) {
+            maxSet = _maxRows == JdbcControl.MAXROWS_ALL ? _maxArray + 1 : Math.min(_maxArray + 1, _maxRows);
+        } else if (isRowSet && _maxRows > 0) {
+            maxSet = _maxRows + 1;
+        }
+
+        return maxSet;
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/SqlSubstitutionFragment.java Mon May  9 13:17:58 2005
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jdbc.parser;
+
+import org.apache.beehive.controls.api.context.ControlBeanContext;
+import org.apache.beehive.controls.system.jdbc.TypeMappingsFactory;
+
+import java.lang.reflect.Method;
+
+/**
+ * Represents a fragement from the SQL annotation's statement member which begins with '{sql:'.
+ * Substitution fragements are unique in that they are fully evaluated BEFORE a PreparedStatement
+ * is generated.
+ * <p/>
+ * Supported 'sql:' escapes are subst and fn. subst is the default mode, and will be used if 'sql: ' 
+ * is specified.
+ *
+ * The <tt>fn</tt> variant of this construct has a very ridgid syntax at this point.  It must conform to:
+ *
+ * <pre>
+ *   {sql:fn in(x,{y})}
+ * </pre>
+ *
+ * where the '{y}' could also be some literal term.
+ */
+public class SqlSubstitutionFragment extends SqlFragmentContainer {
+
+    /**
+     * Constructor for subst or function with no param substitution
+     *
+     * @param child An child which is contained in this fragment.
+     */
+    SqlSubstitutionFragment(SqlFragment child) {
+        super();
+        addChild(child);
+    }
+
+    /**
+     * Constructor for a function which includes a ReflectionFragment
+     *
+     * @param lf A LiteralFragment which contains the text up to the parameter substitution.
+     * @param rf The ReflectionFragment containing the parameter substitution
+     * @param lff A LiteralFragment which contains any text which occures after the parameter substitution.
+     */
+    SqlSubstitutionFragment(LiteralFragment lf, ReflectionFragment rf, LiteralFragment lff) {
+        super();
+        addChild(lf);
+        addChild(rf);
+        addChild(lff);
+    }
+
+    /**
+     * Always true for this fragment type
+     * @return true
+     */
+    boolean isDynamicFragment() { return true; }
+
+    /**
+     * Always false for this fragment type, since all param values are resolved
+     * before the prepared statement is created.
+     * @return false
+     */
+    boolean hasParamValue() { return false; }
+
+    /**
+     * Return the text for a PreparedStatement from this fragment.
+     *
+     * @param context A ControlBeanContext instance
+     * @param m The annotated method
+     * @param args The method parameters
+     * @return A String containing the value of this fragment and its children
+     */
+    String getPreparedStatementText(ControlBeanContext context, Method m, Object[] args) {
+
+        StringBuilder sb = new StringBuilder();
+        for (SqlFragment frag : _children) {
+            if (frag.hasParamValue()) {
+                Object[] pValues = frag.getParameterValues(context, m, args);
+                for (Object o : pValues) {
+                    sb.append(processSqlParams(o));
+                }
+            } else {
+                sb.append(frag.getPreparedStatementText(context, m, args));
+            }
+        }
+        return sb.toString();
+    }
+
+
+// ////////////////////////////////////////////// Private Methods //////////////////////////////////////////////
+
+
+    /**
+     * Check for the cases of a null or array type param value. If array type build a string of the array values
+     * seperated by commas.
+     *
+     * @param value
+     * @return
+     */
+    private String processSqlParams(Object value) {
+
+        Object[] arr = null;
+        if (value != null) {
+            arr = TypeMappingsFactory.toObjectArray(value);
+        }
+
+        if (value == null || (arr != null && arr.length == 0)) {
+            return "";
+        } else if (arr != null) {
+            StringBuilder result = new StringBuilder();
+            for (int i = 0; i < arr.length; i++) {
+                if (i > 0) {
+                    result.append(',');
+                    result.append(arr[i].toString());
+                } else {
+                    result.append(arr[i].toString());
+                }
+            }
+            return result.toString();
+        } else {
+            return value.toString();
+        }
+    }
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/Token.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/Token.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/Token.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/Token.java Mon May  9 13:17:58 2005
@@ -0,0 +1,81 @@
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 3.0 */
+package org.apache.beehive.controls.system.jdbc.parser;
+
+/**
+ * Describes the input token stream.
+ */
+
+public class Token {
+
+  /**
+   * An integer that describes the kind of this token.  This numbering
+   * system is determined by JavaCCParser, and a table of these numbers is
+   * stored in the file ...Constants.java.
+   */
+  public int kind;
+
+  /**
+   * beginLine and beginColumn describe the position of the first character
+   * of this token; endLine and endColumn describe the position of the
+   * last character of this token.
+   */
+  public int beginLine, beginColumn, endLine, endColumn;
+
+  /**
+   * The string image of the token.
+   */
+  public String image;
+
+  /**
+   * A reference to the next regular (non-special) token from the input
+   * stream.  If this is the last token from the input stream, or if the
+   * token manager has not read tokens beyond this one, this field is
+   * set to null.  This is true only if this token is also a regular
+   * token.  Otherwise, see below for a description of the contents of
+   * this field.
+   */
+  public Token next;
+
+  /**
+   * This field is used to access special tokens that occur prior to this
+   * token, but after the immediately preceding regular (non-special) token.
+   * If there are no such special tokens, this field is set to null.
+   * When there are more than one such special token, this field refers
+   * to the last of these special tokens, which in turn refers to the next
+   * previous special token through its specialToken field, and so on
+   * until the first special token (whose specialToken field is null).
+   * The next fields of special tokens refer to other special tokens that
+   * immediately follow it (without an intervening regular token).  If there
+   * is no such token, this field is null.
+   */
+  public Token specialToken;
+
+  /**
+   * Returns the image.
+   */
+  public String toString()
+  {
+     return image;
+  }
+
+  /**
+   * Returns a new Token object, by default. However, if you want, you
+   * can create and return subclass objects based on the value of ofKind.
+   * Simply add the cases to the switch for all those special cases.
+   * For example, if you have a subclass of Token called IDToken that
+   * you want to create if ofKind is ID, simlpy add something like :
+   *
+   *    case MyParserConstants.ID : return new IDToken();
+   *
+   * to the following switch statement. Then you can cast matchedToken
+   * variable to the appropriate type and use it in your lexical actions.
+   */
+  public static final Token newToken(int ofKind)
+  {
+     switch(ofKind)
+     {
+       default : return new Token();
+     }
+  }
+
+}

Added: incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/TokenMgrError.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/TokenMgrError.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/TokenMgrError.java (added)
+++ incubator/beehive/trunk/system-controls/src/jdbc/org/apache/beehive/controls/system/jdbc/parser/TokenMgrError.java Mon May  9 13:17:58 2005
@@ -0,0 +1,133 @@
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 3.0 */
+package org.apache.beehive.controls.system.jdbc.parser;
+
+public class TokenMgrError extends Error
+{
+   /*
+    * Ordinals for various reasons why an Error of this type can be thrown.
+    */
+
+   /**
+    * Lexical error occured.
+    */
+   static final int LEXICAL_ERROR = 0;
+
+   /**
+    * An attempt wass made to create a second instance of a static token manager.
+    */
+   static final int STATIC_LEXER_ERROR = 1;
+
+   /**
+    * Tried to change to an invalid lexical state.
+    */
+   static final int INVALID_LEXICAL_STATE = 2;
+
+   /**
+    * Detected (and bailed out of) an infinite loop in the token manager.
+    */
+   static final int LOOP_DETECTED = 3;
+
+   /**
+    * Indicates the reason why the exception is thrown. It will have
+    * one of the above 4 values.
+    */
+   int errorCode;
+
+   /**
+    * Replaces unprintable characters by their espaced (or unicode escaped)
+    * equivalents in the given string
+    */
+   protected static final String addEscapes(String str) {
+      StringBuffer retval = new StringBuffer();
+      char ch;
+      for (int i = 0; i < str.length(); i++) {
+        switch (str.charAt(i))
+        {
+           case 0 :
+              continue;
+           case '\b':
+              retval.append("\\b");
+              continue;
+           case '\t':
+              retval.append("\\t");
+              continue;
+           case '\n':
+              retval.append("\\n");
+              continue;
+           case '\f':
+              retval.append("\\f");
+              continue;
+           case '\r':
+              retval.append("\\r");
+              continue;
+           case '\"':
+              retval.append("\\\"");
+              continue;
+           case '\'':
+              retval.append("\\\'");
+              continue;
+           case '\\':
+              retval.append("\\\\");
+              continue;
+           default:
+              if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+                 String s = "0000" + Integer.toString(ch, 16);
+                 retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+              } else {
+                 retval.append(ch);
+              }
+              continue;
+        }
+      }
+      return retval.toString();
+   }
+
+   /**
+    * Returns a detailed message for the Error when it is thrown by the
+    * token manager to indicate a lexical error.
+    * Parameters : 
+    *    EOFSeen     : indicates if EOF caused the lexicl error
+    *    curLexState : lexical state in which this error occured
+    *    errorLine   : line number when the error occured
+    *    errorColumn : column number when the error occured
+    *    errorAfter  : prefix that was seen before this error occured
+    *    curchar     : the offending character
+    * Note: You can customize the lexical error message by modifying this method.
+    */
+   protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+      return("Lexical error at line " +
+           errorLine + ", column " +
+           errorColumn + ".  Encountered: " +
+           (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+           "after : \"" + addEscapes(errorAfter) + "\"");
+   }
+
+   /**
+    * You can also modify the body of this method to customize your error messages.
+    * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+    * of end-users concern, so you can return something like : 
+    *
+    *     "Internal Error : Please file a bug report .... "
+    *
+    * from this method for such cases in the release version of your parser.
+    */
+   public String getMessage() {
+      return super.getMessage();
+   }
+
+   /*
+    * Constructors of various flavors follow.
+    */
+
+   public TokenMgrError() {
+   }
+
+   public TokenMgrError(String message, int reason) {
+      super(message);
+      errorCode = reason;
+   }
+
+   public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+      this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+   }
+}

Added: incubator/beehive/trunk/system-controls/src/jms/build.xml
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jms/build.xml?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jms/build.xml (added)
+++ incubator/beehive/trunk/system-controls/src/jms/build.xml Mon May  9 13:17:58 2005
@@ -0,0 +1,48 @@
+<?xml version="1.0" ?>
+
+<project name="jms-control" default="build" basedir=".">
+
+    <import file="../../systemcontrols-imports.xml"/>
+
+    <property name="module.name" value="jms-control"/>
+    <property name="module.dir" location="${src.dir}/jms"/>
+
+    <path id="module.classpath">
+        <path refid="ejb.dependency.path"/>
+        <path refid="controls.dependency.path"/>
+        <path refid="velocity.dependency.path"/>
+        <path refid="log4j.dependency.path"/>
+        <path refid="xbean.dependency.path"/>
+    </path>
+
+    <taskdef name="control-jar"
+        classname="org.apache.beehive.controls.runtime.packaging.ControlJarTask"
+        onerror="report"
+        classpathref="module.classpath"/>
+
+
+    <!-- =================================================================== -->
+    <!-- clean - cleans the Jdbc Control                                     -->
+    <!-- =================================================================== -->
+    <target name="clean" description="Clean the JMS control build area.">
+        <delete dir="${classes.dir}/${module.name}"/>
+        <delete dir="${gen.src.dir}/${module.name}"/>
+        <delete file="${build.lib.dir}/${jmscontrol.jar.name}"/>
+    </target>
+
+
+    <!-- =================================================================== -->
+    <!-- build - build the Jms Control                                       -->
+    <!-- =================================================================== -->
+    <target name="build" description="Builds the JMS control.">
+
+        <mkdir dir="${classes.dir}/${module.name}"/>
+        <mkdir dir="${gen.src.dir}/${module.name}"/>
+
+        <build-controls srcdir="${module.dir}" destdir="${classes.dir}/${module.name}"
+            tempdir="${gen.src.dir}/${module.name}" classpathref="module.classpath"/>
+
+        <control-jar destfile="${build.lib.dir}/${jmscontrol.jar.name}" basedir="${classes.dir}/${module.name}"/>
+
+    </target>
+</project>

Added: incubator/beehive/trunk/system-controls/src/jms/org/apache/beehive/controls/system/jms/JMSControl.java
URL: http://svn.apache.org/viewcvs/incubator/beehive/trunk/system-controls/src/jms/org/apache/beehive/controls/system/jms/JMSControl.java?rev=169351&view=auto
==============================================================================
--- incubator/beehive/trunk/system-controls/src/jms/org/apache/beehive/controls/system/jms/JMSControl.java (added)
+++ incubator/beehive/trunk/system-controls/src/jms/org/apache/beehive/controls/system/jms/JMSControl.java Mon May  9 13:17:58 2005
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * $Header:$
+ */
+
+package org.apache.beehive.controls.system.jms;
+
+import org.apache.beehive.controls.api.ControlException;
+import org.apache.beehive.controls.api.bean.AnnotationMemberTypes;
+import org.apache.beehive.controls.api.bean.Control;
+import org.apache.beehive.controls.api.bean.ControlInterface;
+import org.apache.beehive.controls.api.bean.AnnotationConstraints;
+import org.apache.beehive.controls.api.properties.PropertySet;
+import org.apache.beehive.controls.api.packaging.*;
+import org.apache.beehive.controls.api.events.EventSet;
+import java.lang.annotation.*;
+import java.util.Date;
+import java.util.Map;
+
+import javax.jms.Session;
+
+/**
+ * The control interface for the jms control.
+ */
+@ControlInterface (defaultBinding="org.apache.beehive.controls.system.jms.impl.JMSControlImpl")
+public interface JMSControl
+{
+    /**
+     * The destination type. 
+     */
+    enum DestinationType 
+    { 
+        /** The destination is set from the object obtained from JNDI. */
+        Auto, 
+        /** The destination must be a javax.jms.QueueSender. */
+        Queue, 
+        /** The destination must be a javax.jms.TopicPublisher. */
+        Topic 
+    };
+    
+    /**
+     * The header type. Corresponds to the JMS* bean properties on a JMS message.
+     */
+    enum HeaderType 
+    { 
+        /** see javax.jms.Message.getJMSCorrelationID() */
+        JMSCorrelationID, 
+        /** see javax.jms.Message.getJMSDeliveryMode */
+        JMSDeliveryMode, 
+        /** see javax.jms.Message.getJMSPriority */
+        JMSPriority, 
+        /** see javax.jms.Message.getJMSExpiration */
+        JMSExpiration, 
+        /** see javax.jms.Message.getJMSMessageID */
+        JMSMessageID, 
+        /** see javax.jms.Message.getJMSType */
+        JMSType, 
+        /** see javax.jms.Message.getJMSRedelivered */
+        JMSRedelivered, 
+        /** see javax.jms.Message.getJMSTimestamp */
+        JMSTimestamp 
+    };
+    
+    /**
+     * The message type. 
+     */
+    enum MessageType 
+    { 
+        /** Message is determined from the body instance class. If the method is not annotated with Body, then the message type is Map. */
+        Auto, 
+        /** Message is a javax.jms.TextMessage */
+        Text, 
+        /** Message is a javax.jms.BytesMessage */
+        Bytes, 
+        /** Message is a javax.jms.ObjectMessage */
+        Object, 
+        /** Message is a javax.jms.MapMessage */
+        Map, 
+        /** Message is a javax.jms.Message as given by the Body parameter */
+        JMSMessage 
+    };
+
+    /**
+     * The delivery mode.
+     */
+    enum DeliveryMode 
+    { 
+        /** see javax.jms.DeliveryMode.NON_PERSISTENT */
+        NonPersistent, 
+        /** see javax.jms.DeliveryMode.PERSISTENT */
+        Persistent,
+        /** The default for the provider */
+        Auto
+    };
+    
+    /**
+     * The acknowledge mode.
+     */   
+    enum AcknowledgeMode 
+    { 
+        /** see javax.jms.Session.AUTO_ACKNOWLEDGE */
+        Auto, 
+        /** see javax.jms.Session.CLIENT_ACKNOWLEDGE */
+        Client, 
+        /** see javax.jms.Session.DUPS_OK_ACKNOWLEDGE */
+        DupsOk 
+    };
+
+    /**
+     * Indicates the JMSCorrelationID message header. 
+     *
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_CORRELATIONID = HeaderType.JMSCorrelationID.toString();
+    /**
+     * Indicates the JMSDeliveryMode message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_DELIVERYMODE = HeaderType.JMSDeliveryMode.toString();
+    /**
+     * Indicates the JMSExpiration message header. 
+     * Use with the getHeaders and setHeaders methods.
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_EXPIRATION = HeaderType.JMSExpiration.toString();
+    /**
+     * Indicates the JMSMessageID message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_MESSAGEID = HeaderType.JMSMessageID.toString();
+    /**
+     * Indicates the JMSPriority message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_PRIORITY = HeaderType.JMSPriority.toString();
+    /**
+     * Indicates the JMSRedelivered message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_REDELIVERED = HeaderType.JMSRedelivered.toString();
+    /**
+     * Indicates the JMSTimestamp message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_TIMESTAMP = HeaderType.JMSTimestamp.toString();
+    /**
+     * Indicates the JMSType message header. 
+     * 
+     * @deprecated
+     * @see HeaderType
+     */
+    public static final String HEADER_TYPE = HeaderType.JMSType.toString();
+    
+    /**
+     * Get the JMS Session object.
+     * @return the session.
+     */
+    public Session getSession() throws ControlException;
+    
+    /**
+     * Get the JMS Connection.
+     * @return the connection.
+     */
+    public javax.jms.Connection getConnection() throws ControlException;
+    
+    /**
+     * Get the jms-destination.
+     * 
+     * @return an instance destination object.
+     */
+    public javax.jms.Destination getDestination() throws ControlException;
+
+
+    /**
+     * Sets the JMS headers to be assigned to the next JMS message
+     * sent. Note that these headers are set only on the next message,
+     * subsequent messages will not get these headers. Also note that
+     * if the body is a message itself,
+     * then any header set through this map will override headers set
+     * in the message.
+     * 
+     * @param headers A map of header names (Strings or HeaderType) to header values.
+     */
+    public void setHeaders(Map headers);
+    
+    /**
+     * Sets a JMS header to be assigned to the next JMS message
+     * sent. Note that this headers is set only on the next message,
+     * subsequent messages will not get this header. Also note that
+     * if the body is a message itself,
+     * then the header set here will override the header set
+     * in the message.
+     * 
+     * @param type the header type.
+     * @param value the value for the header.
+     */
+    public void setHeader(JMSControl.HeaderType type,Object value);
+    
+    /**
+     * Sets the JMS properties to be assigned to the next JMS message
+     * sent. Note that these properties are set only on the next
+     * message, subsequent messages will not get these
+     * properties. Also note that if the next message is sent through
+     * a publish method, then any property set through this
+     * map will override properties set in the message itself.
+     * 
+     * @param properties A map of property names (Strings) to property
+     * values.
+     */
+    public void setProperties(Map properties); 
+    
+    /**
+     * Set the given JMS property to be assigned to the next JMS message
+     * sent. Note that this property is set only on the next
+     * message, subsequent messages will not get this
+     * property. Also note that
+     * if the body is a message itself,
+     * then the property set here will override the property set
+     * in the message.
+     * 
+     * @param name the property name.
+     * @param value the property value.
+     */
+    public void setProperty(String name,Object value); 
+
+    /**
+     * The message type used by the method. The default is
+     * to use the type of the body parameter.
+     */
+    @PropertySet(prefix="Message")
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Message
+    {             
+        @FeatureInfo(shortDescription="The message type")
+    	public JMSControl.MessageType value() default JMSControl.MessageType.Auto;
+    }
+
+    /**
+     * The method parameter representing a message property with the given name.
+     * see javax.jms.Message.getProperty()/setProperty().
+     */    
+    @Target({ElementType.PARAMETER})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Property
+    {
+        /**
+         * The property name.
+         */
+        public String name();
+    }
+    
+    /**
+     * The method parameter representing a message property with the given name and value.
+     * see javax.jms.Message.getProperty()/setProperty().
+     */  
+    @PropertySet(prefix="Property")
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface PropertyValue
+    {
+        /**
+         * The property name.
+         */
+        public String name();
+        
+       /**
+        * The property value.
+        */
+        public String value();
+        
+        /**
+         * The property type.
+         */
+        public Class type() default String.class;
+        
+    }
+    
+    /**
+     * The method/parameter annotation representing a message priority. If not given
+     * then the default for the JMS provider is used.
+     */ 
+    @PropertySet(prefix="Priority")
+    @Target({ElementType.PARAMETER,ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @AnnotationConstraints.AllowExternalOverride
+    public @interface Priority
+    {
+        @AnnotationMemberTypes.Optional
+        public int value() default -1;
+    }
+    /**
+     * The method/parameter representing the message JMS type. 
+     */ 
+    @PropertySet(prefix="Type")
+    @Target({ElementType.PARAMETER,ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Type
+    {
+        public String value() default "";
+    } 
+    /**
+     * The method/parameter representing the message JMS CorrelationID. 
+     */ 
+    @PropertySet(prefix="CorrelationId")
+    @Target({ElementType.PARAMETER,ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface CorrelationId
+    {
+        public String value() default "";
+    }  
+    /**
+     * The method parameter representing a message expiration in milliseconds. 
+     * If not given then the default for the JMS provider is used.
+     */ 
+    @PropertySet(prefix="Expiration")
+    @Target({ElementType.PARAMETER,ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    @AnnotationConstraints.AllowExternalOverride
+    public @interface Expiration
+    {
+        @AnnotationMemberTypes.Optional
+        public long value() default -1L;
+    }
+    /**
+     * The method parameter representing a message delivery mode. 
+     * If not given then the default for the JMS provider is used.
+     */ 
+    @PropertySet(prefix="Delivery")
+    @Target({ElementType.PARAMETER,ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Delivery
+    {
+        public JMSControl.DeliveryMode value() default JMSControl.DeliveryMode.Auto;
+    }
+    /**
+     * The method parameter representing one or more properties. 
+     */ 
+    @PropertySet(prefix="Properties")
+    @Target({ElementType.METHOD})
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface Properties
+    {
+        public PropertyValue[] value();
+    }
+    /**
+     * The JMS destination annotation for a extended class method.
+     */     
+    @PropertySet
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target({ElementType.TYPE,ElementType.FIELD})
+    @AnnotationConstraints.AllowExternalOverride
+    public @interface Destination
+    {
+        /**
+         * The JNDI name of the queue or topic.
+         */
+        // BUG: There should be a JMS_TOPIC_OR_QUEUE resource type.
+        @FeatureInfo(shortDescription="JNDI name of the queue or topic")
+        @AnnotationMemberTypes.JndiName( resourceType = AnnotationMemberTypes.JndiName.ResourceType.OTHER )
+    	public String sendJndiName();
+        
+        /**
+         * The Correlation-Id for messages.
+         */
+        @FeatureInfo(shortDescription="Correlation-Id for messages")
+        @AnnotationMemberTypes.Optional
+    	public String sendCorrelationProperty() default "";
+        
+        /**
+         * The JNDI name of queue connection factory.
+         */
+        @FeatureInfo(shortDescription="JNDI name of queue connection factory")
+    	public String jndiConnectionFactory();
+        
+        /**
+         * The destination type (DestinationType). The default is to use the type of the destination object named by the JNDI name.
+         */
+        @FeatureInfo(shortDescription="The destination type (DestinationType). The default is to use the type of the destination object named by the JNDI name")
+        @AnnotationMemberTypes.Optional
+        public JMSControl.DestinationType sendType() default JMSControl.DestinationType.Auto; 
+        
+        /**
+         * True if send is transacted. The default is transacted.
+         */
+        @FeatureInfo(shortDescription="True if send is transacted. The default is transacted")
+        @AnnotationMemberTypes.Optional
+        public boolean transacted() default true;
+        
+        /**
+         * The acknowledge mode. The default is to use auto-acknowledge.
+         */
+        @FeatureInfo(shortDescription="The acknowledge mode. The default is to use auto-acknowledge")
+        @AnnotationMemberTypes.Optional
+        public JMSControl.AcknowledgeMode acknowledgeMode() default JMSControl.AcknowledgeMode.Auto;
+        
+        /**
+         * The JNDI context factory.
+         */
+        @FeatureInfo(shortDescription="JNDI context factory")
+        @AnnotationMemberTypes.Optional
+    	public String jndiContextFactory() default "";
+        
+        /**
+         * The JNDI provider URL.
+         */
+        @FeatureInfo(shortDescription="JNDI provider URL")      
+        @AnnotationMemberTypes.Optional
+        @AnnotationMemberTypes.URI
+    	public String jndiProviderURL() default "";
+        
+        /**
+         * The JNDI security principal.
+         */
+        @FeatureInfo(shortDescription="JNDI security principal")      
+        @AnnotationMemberTypes.Optional
+    	public String jndiUsername() default "";
+        
+        
+        /**
+         * The JNDI security credentials.
+         */
+        @FeatureInfo(shortDescription="JNDI security credentials")      
+        @AnnotationMemberTypes.Optional
+    	public String jndiPassword() default "";
+    }
+
+
+}