View | Details | Raw Unified | Return to bug 52128
Collapse All | Expand All

(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/AbstractJdbcTestElement.java (+568 lines)
Line 0 Link Here
1
package org.apache.jmeter.protocol.jdbc;
2
3
import java.io.IOException;
4
import java.io.UnsupportedEncodingException;
5
import java.lang.reflect.Field;
6
import java.sql.CallableStatement;
7
import java.sql.Connection;
8
import java.sql.PreparedStatement;
9
import java.sql.ResultSet;
10
import java.sql.ResultSetMetaData;
11
import java.sql.SQLException;
12
import java.sql.Statement;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.LinkedHashMap;
17
import java.util.List;
18
import java.util.Map;
19
20
import org.apache.commons.lang.text.StrBuilder;
21
import org.apache.jmeter.samplers.SampleResult;
22
import org.apache.jmeter.save.CSVSaveService;
23
import org.apache.jmeter.testelement.AbstractTestElement;
24
import org.apache.jmeter.threads.JMeterVariables;
25
import org.apache.jmeter.util.JMeterUtils;
26
import org.apache.jorphan.logging.LoggingManager;
27
import org.apache.log.Logger;
28
29
public abstract class AbstractJdbcTestElement extends AbstractTestElement {
30
    private static final long serialVersionUID = 235L;
31
32
    private static final Logger log = LoggingManager.getLoggerForClass();
33
34
    private static final String COMMA = ","; // $NON-NLS-1$
35
    private static final char COMMA_CHAR = ',';
36
37
    private static final String UNDERSCORE = "_"; // $NON-NLS-1$
38
39
    // This value is used for both the connection (perConnCache) and statement (preparedStatementMap) caches.
40
    // TODO - do they have to be the same size?
41
    private static final int MAX_ENTRIES =
42
        JMeterUtils.getPropDefault("jdbcsampler.cachesize",200); // $NON-NLS-1$
43
44
    // String used to indicate a null value
45
    private static final String NULL_MARKER =
46
        JMeterUtils.getPropDefault("jdbcsampler.nullmarker","]NULL["); // $NON-NLS-1$
47
48
    private static final String INOUT = "INOUT"; // $NON-NLS-1$
49
50
    private static final String OUT = "OUT"; // $NON-NLS-1$
51
52
    // TODO - should the encoding be configurable?
53
    protected static final String ENCODING = "UTF-8"; // $NON-NLS-1$
54
55
    // key: name (lowercase) from java.sql.Types; entry: corresponding int value
56
    private static final Map<String, Integer> mapJdbcNameToInt;
57
    // read-only after class init
58
59
    static {
60
        // based on e291. Getting the Name of a JDBC Type from javaalmanac.com
61
        // http://javaalmanac.com/egs/java.sql/JdbcInt2Str.html
62
        mapJdbcNameToInt = new HashMap<String, Integer>();
63
64
        //Get all fields in java.sql.Types and store the corresponding int values
65
        Field[] fields = java.sql.Types.class.getFields();
66
        for (int i=0; i<fields.length; i++) {
67
            try {
68
                String name = fields[i].getName();
69
                Integer value = (Integer)fields[i].get(null);
70
                mapJdbcNameToInt.put(name.toLowerCase(java.util.Locale.ENGLISH),value);
71
            } catch (IllegalAccessException e) {
72
                throw new RuntimeException(e); // should not happen
73
            }
74
        }
75
    }
76
77
    // Query types (used to communicate with GUI)
78
    // N.B. These must not be changed, as they are used in the JMX files
79
    static final String SELECT   = "Select Statement"; // $NON-NLS-1$
80
    static final String UPDATE   = "Update Statement"; // $NON-NLS-1$
81
    static final String CALLABLE = "Callable Statement"; // $NON-NLS-1$
82
    static final String PREPARED_SELECT = "Prepared Select Statement"; // $NON-NLS-1$
83
    static final String PREPARED_UPDATE = "Prepared Update Statement"; // $NON-NLS-1$
84
    static final String COMMIT   = "Commit"; // $NON-NLS-1$
85
    static final String ROLLBACK = "Rollback"; // $NON-NLS-1$
86
    static final String AUTOCOMMIT_FALSE = "AutoCommit(false)"; // $NON-NLS-1$
87
    static final String AUTOCOMMIT_TRUE  = "AutoCommit(true)"; // $NON-NLS-1$
88
89
    private String query = ""; // $NON-NLS-1$
90
91
    private String dataSource = ""; // $NON-NLS-1$
92
93
    private boolean collectDiagnostics = false;
94
95
    private String queryType = SELECT;
96
    private String queryArguments = ""; // $NON-NLS-1$
97
    private String queryArgumentsTypes = ""; // $NON-NLS-1$
98
    private String variableNames = ""; // $NON-NLS-1$
99
    private String resultVariable = "";
100
101
    /**
102
     *  Cache of PreparedStatements stored in a per-connection basis. Each entry of this
103
     *  cache is another Map mapping the statement string to the actual PreparedStatement.
104
     *  The cache has a fixed size of MAX_ENTRIES and it will throw away all PreparedStatements
105
     *  from the least recently used connections.
106
     */
107
    private static final Map<Connection, Map<String, PreparedStatement>> perConnCache =
108
        new LinkedHashMap<Connection, Map<String, PreparedStatement>>(MAX_ENTRIES){
109
        private static final long serialVersionUID = 1L;
110
        @Override
111
        protected boolean removeEldestEntry(Map.Entry<Connection, Map<String, PreparedStatement>> arg0) {
112
            if (size() > MAX_ENTRIES) {
113
                final  Map<String, PreparedStatement> value = arg0.getValue();
114
                closeAllStatements(value.values());
115
                return true;
116
            }
117
            return false;
118
        }
119
    };
120
121
    /**
122
     * Creates a JDBCSampler.
123
     */
124
    protected AbstractJdbcTestElement() {
125
    }
126
    
127
    /**
128
     * Execute the test element.
129
     * 
130
     * @param res a {@link SampleResult} in case the test should sample; <code>null</code> if only execution is requested
131
     * @throws UnsupportedOperationException if the user provided incorrect query type 
132
     */
133
    protected byte[] execute(Connection conn) throws SQLException, UnsupportedEncodingException, IOException, UnsupportedOperationException {
134
        log.debug("executing jdbc");
135
        Statement stmt = null;
136
        
137
        try {
138
            // Based on query return value, get results
139
            String _queryType = getQueryType();
140
            if (SELECT.equals(_queryType)) {
141
                stmt = conn.createStatement();
142
                ResultSet rs = null;
143
                try {
144
                    rs = stmt.executeQuery(getQuery());
145
                    return getStringFromResultSet(rs).getBytes(ENCODING);
146
                } finally {
147
                    close(rs);
148
                }
149
            } else if (CALLABLE.equals(_queryType)) {
150
                CallableStatement cstmt = getCallableStatement(conn);
151
                int out[]=setArguments(cstmt);
152
                // A CallableStatement can return more than 1 ResultSets
153
                // plus a number of update counts.
154
                boolean hasResultSet = cstmt.execute();
155
                String sb = resultSetsToString(cstmt,hasResultSet, out);
156
                return sb.getBytes(ENCODING);
157
            } else if (UPDATE.equals(_queryType)) {
158
                stmt = conn.createStatement();
159
                stmt.executeUpdate(getQuery());
160
                int updateCount = stmt.getUpdateCount();
161
                String results = updateCount + " updates";
162
                return results.getBytes(ENCODING);
163
            } else if (PREPARED_SELECT.equals(_queryType)) {
164
                PreparedStatement pstmt = getPreparedStatement(conn);
165
                setArguments(pstmt);
166
                boolean hasResultSet = pstmt.execute();
167
                String sb = resultSetsToString(pstmt,hasResultSet,null);
168
                return sb.getBytes(ENCODING);
169
            } else if (PREPARED_UPDATE.equals(_queryType)) {
170
                PreparedStatement pstmt = getPreparedStatement(conn);
171
                setArguments(pstmt);
172
                pstmt.executeUpdate();
173
                String sb = resultSetsToString(pstmt,false,null);
174
                return sb.getBytes(ENCODING);
175
            } else if (ROLLBACK.equals(_queryType)){
176
                conn.rollback();
177
                return ROLLBACK.getBytes(ENCODING);
178
            } else if (COMMIT.equals(_queryType)){
179
                conn.commit();
180
                return COMMIT.getBytes(ENCODING);
181
            } else if (AUTOCOMMIT_FALSE.equals(_queryType)){
182
                conn.setAutoCommit(false);
183
                return AUTOCOMMIT_FALSE.getBytes(ENCODING);
184
            } else if (AUTOCOMMIT_TRUE.equals(_queryType)){
185
                conn.setAutoCommit(true);
186
                return AUTOCOMMIT_TRUE.getBytes(ENCODING);
187
            } else { // User provided incorrect query type
188
                throw new UnsupportedOperationException("Unexpected query type: "+_queryType);
189
            }
190
        } finally {
191
            close(stmt);
192
        }
193
    }
194
195
    private String resultSetsToString(PreparedStatement pstmt, boolean result, int[] out) throws SQLException, UnsupportedEncodingException {
196
        StrBuilder sb = new StrBuilder();
197
        int updateCount = 0;
198
        if (!result) {
199
            updateCount = pstmt.getUpdateCount();
200
        }
201
        do {
202
            if (result) {
203
                ResultSet rs = null;
204
                try {
205
                    rs = pstmt.getResultSet();
206
                    sb.append(getStringFromResultSet(rs)).append("\n"); // $NON-NLS-1$
207
                } finally {
208
                    close(rs);
209
                }
210
            } else {
211
                sb.append(updateCount).append(" updates.\n");
212
            }
213
            result = pstmt.getMoreResults();
214
            if (!result) {
215
                updateCount = pstmt.getUpdateCount();
216
            }
217
        } while (result || (updateCount != -1));
218
        if (out!=null && pstmt instanceof CallableStatement){
219
            CallableStatement cs = (CallableStatement) pstmt;
220
            sb.append("Output variables by position:\n");
221
            for(int i=0; i < out.length; i++){
222
                if (out[i]!=java.sql.Types.NULL){
223
                    sb.append("[");
224
                    sb.append(i+1);
225
                    sb.append("] ");
226
                    sb.append(cs.getObject(i+1));
227
                    sb.append("\n");
228
                }
229
            }
230
        }
231
        return sb.toString();
232
    }
233
234
235
    private int[] setArguments(PreparedStatement pstmt) throws SQLException, IOException {
236
        if (getQueryArguments().trim().length()==0) {
237
            return new int[]{};
238
        }
239
        String[] arguments = CSVSaveService.csvSplitString(getQueryArguments(), COMMA_CHAR);
240
        String[] argumentsTypes = getQueryArgumentsTypes().split(COMMA);
241
        if (arguments.length != argumentsTypes.length) {
242
            throw new SQLException("number of arguments ("+arguments.length+") and number of types ("+argumentsTypes.length+") are not equal");
243
        }
244
        int[] outputs= new int[arguments.length];
245
        for (int i = 0; i < arguments.length; i++) {
246
            String argument = arguments[i];
247
            String argumentType = argumentsTypes[i];
248
            String[] arg = argumentType.split(" ");
249
            String inputOutput="";
250
            if (arg.length > 1) {
251
                argumentType = arg[1];
252
                inputOutput=arg[0];
253
            }
254
            int targetSqlType = getJdbcType(argumentType);
255
            try {
256
                if (!OUT.equalsIgnoreCase(inputOutput)){
257
                    if (argument.equals(NULL_MARKER)){
258
                        pstmt.setNull(i+1, targetSqlType);
259
                    } else {
260
                        pstmt.setObject(i+1, argument, targetSqlType);
261
                    }
262
                }
263
                if (OUT.equalsIgnoreCase(inputOutput)||INOUT.equalsIgnoreCase(inputOutput)) {
264
                    CallableStatement cs = (CallableStatement) pstmt;
265
                    cs.registerOutParameter(i+1, targetSqlType);
266
                    outputs[i]=targetSqlType;
267
                } else {
268
                    outputs[i]=java.sql.Types.NULL; // can't have an output parameter type null
269
                }
270
            } catch (NullPointerException e) { // thrown by Derby JDBC (at least) if there are no "?" markers in statement
271
                throw new SQLException("Could not set argument no: "+(i+1)+" - missing parameter marker?");
272
            }
273
        }
274
        return outputs;
275
    }
276
277
278
    private static int getJdbcType(String jdbcType) throws SQLException {
279
        Integer entry = mapJdbcNameToInt.get(jdbcType.toLowerCase(java.util.Locale.ENGLISH));
280
        if (entry == null) {
281
            try {
282
                entry = Integer.decode(jdbcType);
283
            } catch (NumberFormatException e) {
284
                throw new SQLException("Invalid data type: "+jdbcType);
285
            }
286
        }
287
        return (entry).intValue();
288
    }
289
290
291
    private CallableStatement getCallableStatement(Connection conn) throws SQLException {
292
        return (CallableStatement) getPreparedStatement(conn,true);
293
294
    }
295
    private PreparedStatement getPreparedStatement(Connection conn) throws SQLException {
296
        return getPreparedStatement(conn,false);
297
    }
298
299
    private PreparedStatement getPreparedStatement(Connection conn, boolean callable) throws SQLException {
300
        Map<String, PreparedStatement> preparedStatementMap = perConnCache.get(conn);
301
        if (null == preparedStatementMap ) {
302
            // MRU PreparedStatements cache.
303
            preparedStatementMap = new LinkedHashMap<String, PreparedStatement>(MAX_ENTRIES) {
304
                private static final long serialVersionUID = 240L;
305
306
                @Override
307
                protected boolean removeEldestEntry(Map.Entry<String, PreparedStatement> arg0) {
308
                    final int theSize = size();
309
                    if (theSize > MAX_ENTRIES) {
310
                        Object value = arg0.getValue();
311
                        if (value instanceof PreparedStatement) {
312
                            PreparedStatement pstmt = (PreparedStatement) value;
313
                            close(pstmt);
314
                        }
315
                        return true;
316
                    }
317
                    return false;
318
                }
319
            };
320
            perConnCache.put(conn, preparedStatementMap);
321
        }
322
        PreparedStatement pstmt = preparedStatementMap.get(getQuery());
323
        if (null == pstmt) {
324
            if (callable) {
325
                pstmt = conn.prepareCall(getQuery());
326
            } else {
327
                pstmt = conn.prepareStatement(getQuery());
328
            }
329
            preparedStatementMap.put(getQuery(), pstmt);
330
        }
331
        pstmt.clearParameters();
332
        return pstmt;
333
    }
334
335
    private static void closeAllStatements(Collection<PreparedStatement> collection) {
336
        for (PreparedStatement pstmt : collection) {
337
            close(pstmt);
338
        }
339
    }
340
341
    /**
342
     * Gets a Data object from a ResultSet.
343
     *
344
     * @param rs
345
     *            ResultSet passed in from a database query
346
     * @return a Data object
347
     * @throws java.sql.SQLException
348
     * @throws UnsupportedEncodingException
349
     */
350
    private String getStringFromResultSet(ResultSet rs) throws SQLException, UnsupportedEncodingException {
351
        ResultSetMetaData meta = rs.getMetaData();
352
353
        StrBuilder sb = new StrBuilder();
354
355
        int numColumns = meta.getColumnCount();
356
        for (int i = 1; i <= numColumns; i++) {
357
            sb.append(meta.getColumnName(i));
358
            if (i==numColumns){
359
                sb.append('\n');
360
            } else {
361
                sb.append('\t');
362
            }
363
        }
364
        
365
366
        JMeterVariables jmvars = getThreadContext().getVariables();
367
        String varnames[] = getVariableNames().split(COMMA);
368
        String resultVariable = getResultVariable().trim();
369
        List<Map<String, Object> > results = null;
370
        if(resultVariable.length() > 0) {
371
            results = new ArrayList<Map<String,Object> >();
372
            jmvars.putObject(resultVariable, results);
373
        }
374
        int j = 0;
375
        while (rs.next()) {
376
            Map<String, Object> row = null;
377
            j++;
378
            for (int i = 1; i <= numColumns; i++) {
379
                Object o = rs.getObject(i);
380
                if(results != null) {
381
                    if(row == null) {
382
                        row = new HashMap<String, Object>(numColumns);
383
                        results.add(row);
384
                    }
385
                    row.put(meta.getColumnName(i), o);
386
                }
387
                if (o instanceof byte[]) {
388
                    o = new String((byte[]) o, ENCODING);
389
                }
390
                sb.append(o);
391
                if (i==numColumns){
392
                    sb.append('\n');
393
                } else {
394
                    sb.append('\t');
395
                }
396
                if (i <= varnames.length) { // i starts at 1
397
                    String name = varnames[i - 1].trim();
398
                    if (name.length()>0){ // Save the value in the variable if present
399
                        jmvars.put(name+UNDERSCORE+j, o == null ? null : o.toString());
400
                    }
401
                }
402
            }
403
        }
404
        // Remove any additional values from previous sample
405
        for(int i=0; i < varnames.length; i++){
406
            String name = varnames[i].trim();
407
            if (name.length()>0 && jmvars != null){
408
                final String varCount = name+"_#"; // $NON-NLS-1$
409
                // Get the previous count
410
                String prevCount = jmvars.get(varCount);
411
                if (prevCount != null){
412
                    int prev = Integer.parseInt(prevCount);
413
                    for (int n=j+1; n <= prev; n++ ){
414
                        jmvars.remove(name+UNDERSCORE+n);
415
                    }
416
                }
417
                jmvars.put(varCount, Integer.toString(j)); // save the current count
418
            }
419
        }
420
421
        return sb.toString();
422
    }
423
424
    public static void close(Connection c) {
425
        try {
426
            if (c != null) {
427
                c.close();
428
            }
429
        } catch (SQLException e) {
430
            log.warn("Error closing Connection", e);
431
        }
432
    }
433
434
    public static void close(Statement s) {
435
        try {
436
            if (s != null) {
437
                s.close();
438
            }
439
        } catch (SQLException e) {
440
            log.warn("Error closing Statement " + s.toString(), e);
441
        }
442
    }
443
444
    public static void close(ResultSet rs) {
445
        try {
446
            if (rs != null) {
447
                rs.close();
448
            }
449
        } catch (SQLException e) {
450
            log.warn("Error closing ResultSet", e);
451
        }
452
    }
453
454
    public String getQuery() {
455
        return query;
456
    }
457
458
    @Override
459
    public String toString() {
460
        StrBuilder sb = new StrBuilder(80);
461
        sb.append("["); // $NON-NLS-1$
462
        sb.append(getQueryType());
463
        sb.append("] "); // $NON-NLS-1$
464
        sb.append(getQuery());
465
        sb.append("\n");
466
        sb.append(getQueryArguments());
467
        sb.append("\n");
468
        sb.append(getQueryArgumentsTypes());
469
        return sb.toString();
470
    }
471
472
    /**
473
     * @param query
474
     *            The query to set.
475
     */
476
    public void setQuery(String query) {
477
        this.query = query;
478
    }
479
480
    /**
481
     * @return Returns the dataSource.
482
     */
483
    public String getDataSource() {
484
        return dataSource;
485
    }
486
487
    /**
488
     * @param dataSource
489
     *            The dataSource to set.
490
     */
491
    public void setDataSource(String dataSource) {
492
        this.dataSource = dataSource;
493
    }
494
495
    /**
496
     * @return the collectDiagnostics
497
     */
498
    public boolean getCollectDiagnostics() {
499
        return collectDiagnostics;
500
    }
501
502
    /**
503
     * @param collectDiagnostics
504
     *            the collectDiagnostics to set
505
     */
506
    public void setCollectDiagnostics(boolean collectDiagnostics) {
507
        this.collectDiagnostics = collectDiagnostics;
508
    }
509
510
    /**
511
     * @return Returns the queryType.
512
     */
513
    public String getQueryType() {
514
        return queryType;
515
    }
516
517
    /**
518
     * @param queryType The queryType to set.
519
     */
520
    public void setQueryType(String queryType) {
521
        this.queryType = queryType;
522
    }
523
524
    public String getQueryArguments() {
525
        return queryArguments;
526
    }
527
528
    public void setQueryArguments(String queryArguments) {
529
        this.queryArguments = queryArguments;
530
    }
531
532
    public String getQueryArgumentsTypes() {
533
        return queryArgumentsTypes;
534
    }
535
536
    public void setQueryArgumentsTypes(String queryArgumentsType) {
537
        this.queryArgumentsTypes = queryArgumentsType;
538
    }
539
540
    /**
541
     * @return the variableNames
542
     */
543
    public String getVariableNames() {
544
        return variableNames;
545
    }
546
547
    /**
548
     * @param variableNames the variableNames to set
549
     */
550
    public void setVariableNames(String variableNames) {
551
        this.variableNames = variableNames;
552
    }
553
554
    /**
555
     * @return the resultVariable
556
     */
557
    public String getResultVariable() {
558
        return resultVariable ;
559
    }
560
561
    /**
562
     * @param resultVariable the variable name in which results will be stored
563
     */
564
    public void setResultVariable(String resultVariable) {
565
        this.resultVariable = resultVariable;
566
    }
567
    
568
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/JDBCBeanInfoSupport.java (+100 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
19
/*
20
 * Created on May 16, 2004
21
 *
22
 */
23
package org.apache.jmeter.protocol.jdbc;
24
25
import java.beans.PropertyDescriptor;
26
27
import org.apache.jmeter.testbeans.BeanInfoSupport;
28
import org.apache.jmeter.testbeans.gui.TextAreaEditor;
29
30
public abstract class JDBCBeanInfoSupport extends BeanInfoSupport {
31
32
    /**
33
     *
34
     */
35
    public JDBCBeanInfoSupport(Class<?> beanClass) {
36
        super(beanClass);
37
38
        createPropertyGroup("varName", // $NON-NLS-1$
39
                new String[]{"dataSource" }); // $NON-NLS-1$
40
41
        createPropertyGroup("diagnostics", // $NON-NLS-1$
42
                new String[] { "collectDiagnostics" }); // $NON-NLS-1$
43
44
        createPropertyGroup("sql", // $NON-NLS-1$
45
                new String[] {
46
                "queryType", // $NON-NLS-1$
47
                "query", // $NON-NLS-1$
48
                "queryArguments", // $NON-NLS-1$
49
                "queryArgumentsTypes", // $NON-NLS-1$
50
                "variableNames", // $NON-NLS-1$
51
                "resultVariable", // $NON-NLS-1$
52
                });
53
54
        PropertyDescriptor p = property("collectDiagnostics"); // $NON-NLS-1$
55
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
56
        p.setValue(DEFAULT, Boolean.FALSE);
57
58
        p = property("dataSource"); // $NON-NLS-1$
59
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
60
        p.setValue(DEFAULT, "");
61
62
        p = property("queryArguments"); // $NON-NLS-1$
63
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
64
        p.setValue(DEFAULT, "");
65
66
        p = property("queryArgumentsTypes"); // $NON-NLS-1$
67
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
68
        p.setValue(DEFAULT, "");
69
70
        p = property("variableNames"); // $NON-NLS-1$
71
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
72
        p.setValue(DEFAULT, "");
73
74
        p = property("resultVariable"); // $NON-NLS-1$
75
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
76
        p.setValue(DEFAULT, "");
77
78
        p = property("queryType"); // $NON-NLS-1$
79
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
80
        p.setValue(DEFAULT, AbstractJdbcTestElement.SELECT);
81
        p.setValue(NOT_OTHER,Boolean.TRUE);
82
        p.setValue(TAGS,new String[]{
83
                AbstractJdbcTestElement.SELECT,
84
                AbstractJdbcTestElement.UPDATE,
85
                AbstractJdbcTestElement.CALLABLE,
86
                AbstractJdbcTestElement.PREPARED_SELECT,
87
                AbstractJdbcTestElement.PREPARED_UPDATE,
88
                AbstractJdbcTestElement.COMMIT,
89
                AbstractJdbcTestElement.ROLLBACK,
90
                AbstractJdbcTestElement.AUTOCOMMIT_FALSE,
91
                AbstractJdbcTestElement.AUTOCOMMIT_TRUE,
92
                });
93
94
        p = property("query"); // $NON-NLS-1$
95
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
96
        p.setValue(DEFAULT, "");
97
        p.setPropertyEditorClass(TextAreaEditor.class);
98
99
    }
100
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/JDBCBeanInfoSupportResources.properties (+35 lines)
Line 0 Link Here
1
#   Licensed to the Apache Software Foundation (ASF) under one or more
2
#   contributor license agreements.  See the NOTICE file distributed with
3
#   this work for additional information regarding copyright ownership.
4
#   The ASF licenses this file to You under the Apache License, Version 2.0
5
#   (the "License"); you may not use this file except in compliance with
6
#   the License.  You may obtain a copy of the License at
7
# 
8
#       http://www.apache.org/licenses/LICENSE-2.0
9
# 
10
#   Unless required by applicable law or agreed to in writing, software
11
#   distributed under the License is distributed on an "AS IS" BASIS,
12
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
#   See the License for the specific language governing permissions and
14
#   limitations under the License.
15
16
displayName=JDBC Request
17
varName.displayName=Variable Name Bound to Pool
18
sql.displayName=SQL Query
19
query.displayName=Query
20
query.shortDescription=SQL Query to send to database
21
queryType.displayName=Query Type
22
queryType.shortDescription=Determines if the SQL statement should be run as a select statement or an update statement.
23
dataSource.displayName=Variable Name
24
dataSource.shortDescription=Name of the JMeter variable that the connection pool is bound to.
25
queryArguments.displayName=Parameter values
26
queryArguments.shortDescription=SQL parameter values (comma separated)
27
queryArgumentsTypes.displayName=Parameter types
28
queryArgumentsTypes.shortDescription=JDBC Type names from java.sql.Types. VARCHAR, INTEGER, etc. (comma separated)
29
variableNames.displayName=Variable names
30
variableNames.shortDescription=Output variable names for each column  (comma separated)
31
resultVariable.displayName=Result variable name
32
resultVariable.shortDescription=Name of the JMeter variable that stores the result set objects in a list of maps for looking up results by column name.
33
diagnostics.displayName=Diagnostics
34
collectDiagnostics.displayName=Collect diagnostic messages
35
collectDiagnostics.shortDescription=Retrieve diagnostic messages like Warnings and other information from the database
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/AbstractJDBCProcessor.java (+29 lines)
Line 0 Link Here
1
package org.apache.jmeter.protocol.jdbc.processor;
2
3
import java.io.IOException;
4
import java.sql.SQLException;
5
6
import org.apache.jmeter.protocol.jdbc.AbstractJdbcTestElement;
7
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
8
import org.apache.jorphan.logging.LoggingManager;
9
import org.apache.log.Logger;
10
11
public abstract class AbstractJDBCProcessor extends AbstractJdbcTestElement {
12
    
13
    private static final Logger log = LoggingManager.getLoggerForClass();
14
15
    private static final long serialVersionUID = 232L;
16
17
    protected void process() {
18
        try {
19
            execute(DataSourceElement.getConnection(getDataSource()));
20
        } catch (SQLException ex) {
21
            log.warn("SQL Problem in  "+ getName() + ": " + ex.toString());
22
        } catch (IOException ex) {
23
            log.warn("IO Problem in  "+ getName() + ": " + ex.toString());
24
        } catch (UnsupportedOperationException ex) {
25
            log.warn("Execution Problem in "+ getName() + ": " + ex.toString());
26
        }
27
    }
28
29
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPostProcessor.java (+13 lines)
Line 0 Link Here
1
package org.apache.jmeter.protocol.jdbc.processor;
2
3
import org.apache.jmeter.processor.PostProcessor;
4
import org.apache.jmeter.testbeans.TestBean;
5
6
public class JDBCPostProcessor extends AbstractJDBCProcessor implements TestBean, PostProcessor {
7
8
    @Override
9
    public void process() {
10
        super.process();
11
    }
12
    
13
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPostProcessorBeanInfo.java (+36 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
19
/*
20
 * Created on May 16, 2004
21
 *
22
 */
23
package org.apache.jmeter.protocol.jdbc.processor;
24
25
import org.apache.jmeter.protocol.jdbc.JDBCBeanInfoSupport;
26
27
28
public class JDBCPostProcessorBeanInfo extends JDBCBeanInfoSupport {
29
30
    /**
31
     *
32
     */
33
    public JDBCPostProcessorBeanInfo() {
34
        super(JDBCPostProcessor.class);
35
    }
36
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPostProcessorResources.properties (+16 lines)
Line 0 Link Here
1
#   Licensed to the Apache Software Foundation (ASF) under one or more
2
#   contributor license agreements.  See the NOTICE file distributed with
3
#   this work for additional information regarding copyright ownership.
4
#   The ASF licenses this file to You under the Apache License, Version 2.0
5
#   (the "License"); you may not use this file except in compliance with
6
#   the License.  You may obtain a copy of the License at
7
# 
8
#       http://www.apache.org/licenses/LICENSE-2.0
9
# 
10
#   Unless required by applicable law or agreed to in writing, software
11
#   distributed under the License is distributed on an "AS IS" BASIS,
12
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
#   See the License for the specific language governing permissions and
14
#   limitations under the License.
15
16
displayName=JDBC PostProcessor Request
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPreProcessor.java (+13 lines)
Line 0 Link Here
1
package org.apache.jmeter.protocol.jdbc.processor;
2
3
import org.apache.jmeter.processor.PreProcessor;
4
import org.apache.jmeter.testbeans.TestBean;
5
6
public class JDBCPreProcessor extends AbstractJDBCProcessor implements TestBean, PreProcessor {
7
8
    @Override
9
    public void process() {
10
        super.process();
11
    }
12
    
13
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPreProcessorBeanInfo.java (+36 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *   http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 *
17
 */
18
19
/*
20
 * Created on May 16, 2004
21
 *
22
 */
23
package org.apache.jmeter.protocol.jdbc.processor;
24
25
import org.apache.jmeter.protocol.jdbc.JDBCBeanInfoSupport;
26
27
28
public class JDBCPreProcessorBeanInfo extends JDBCBeanInfoSupport {
29
30
    /**
31
     *
32
     */
33
    public JDBCPreProcessorBeanInfo() {
34
        super(JDBCPreProcessor.class);
35
    }
36
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/processor/JDBCPreProcessorResources.properties (+16 lines)
Line 0 Link Here
1
#   Licensed to the Apache Software Foundation (ASF) under one or more
2
#   contributor license agreements.  See the NOTICE file distributed with
3
#   this work for additional information regarding copyright ownership.
4
#   The ASF licenses this file to You under the Apache License, Version 2.0
5
#   (the "License"); you may not use this file except in compliance with
6
#   the License.  You may obtain a copy of the License at
7
# 
8
#       http://www.apache.org/licenses/LICENSE-2.0
9
# 
10
#   Unless required by applicable law or agreed to in writing, software
11
#   distributed under the License is distributed on an "AS IS" BASIS,
12
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
#   See the License for the specific language governing permissions and
14
#   limitations under the License.
15
16
displayName=JDBC PreProcessor Request
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSampler.java (-523 / +16 lines)
Lines 19-147 Link Here
19
package org.apache.jmeter.protocol.jdbc.sampler;
19
package org.apache.jmeter.protocol.jdbc.sampler;
20
20
21
import java.io.IOException;
21
import java.io.IOException;
22
import java.io.UnsupportedEncodingException;
23
import java.lang.reflect.Field;
24
import java.sql.CallableStatement;
25
import java.sql.Connection;
22
import java.sql.Connection;
26
import java.sql.PreparedStatement;
27
import java.sql.ResultSet;
28
import java.sql.ResultSetMetaData;
29
import java.sql.SQLException;
23
import java.sql.SQLException;
30
import java.sql.Statement;
31
import java.util.ArrayList;
32
import java.util.Collection;
33
import java.util.HashMap;
34
import java.util.LinkedHashMap;
35
import java.util.List;
36
import java.util.Map;
37
24
38
import org.apache.commons.lang.text.StrBuilder;
25
import org.apache.jmeter.protocol.jdbc.AbstractJdbcTestElement;
39
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
26
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
40
import org.apache.jmeter.samplers.AbstractSampler;
41
import org.apache.jmeter.samplers.Entry;
27
import org.apache.jmeter.samplers.Entry;
42
import org.apache.jmeter.samplers.SampleResult;
28
import org.apache.jmeter.samplers.SampleResult;
43
import org.apache.jmeter.save.CSVSaveService;
29
import org.apache.jmeter.samplers.Sampler;
44
import org.apache.jmeter.testbeans.TestBean;
30
import org.apache.jmeter.testbeans.TestBean;
45
import org.apache.jmeter.threads.JMeterVariables;
46
import org.apache.jmeter.util.JMeterUtils;
47
import org.apache.jorphan.logging.LoggingManager;
31
import org.apache.jorphan.logging.LoggingManager;
48
import org.apache.log.Logger;
32
import org.apache.log.Logger;
49
33
34
import ch.infonic.jmeter.jdbc.JDBCSamplerDiagnosticListener;
35
50
/**
36
/**
51
 * A sampler which understands JDBC database requests.
37
 * A sampler which understands JDBC database requests.
52
 *
38
 *
53
 */
39
 */
54
public class JDBCSampler extends AbstractSampler implements TestBean {
40
public class JDBCSampler extends AbstractJdbcTestElement implements Sampler, TestBean {
55
    private static final long serialVersionUID = 233L;
41
    
56
57
    private static final Logger log = LoggingManager.getLoggerForClass();
42
    private static final Logger log = LoggingManager.getLoggerForClass();
58
43
59
    private static final String COMMA = ","; // $NON-NLS-1$
60
    private static final char COMMA_CHAR = ',';
61
62
    private static final String UNDERSCORE = "_"; // $NON-NLS-1$
63
64
    // This value is used for both the connection (perConnCache) and statement (preparedStatementMap) caches.
65
    // TODO - do they have to be the same size?
66
    private static final int MAX_ENTRIES =
67
        JMeterUtils.getPropDefault("jdbcsampler.cachesize",200); // $NON-NLS-1$
68
69
    // String used to indicate a null value
70
    private static final String NULL_MARKER =
71
        JMeterUtils.getPropDefault("jdbcsampler.nullmarker","]NULL["); // $NON-NLS-1$
72
73
    private static final String INOUT = "INOUT"; // $NON-NLS-1$
74
75
    private static final String OUT = "OUT"; // $NON-NLS-1$
76
77
    // TODO - should the encoding be configurable?
78
    private static final String ENCODING = "UTF-8"; // $NON-NLS-1$
79
80
    // key: name (lowercase) from java.sql.Types; entry: corresponding int value
81
    private static final Map<String, Integer> mapJdbcNameToInt;
82
    // read-only after class init
83
84
    static {
85
        // based on e291. Getting the Name of a JDBC Type from javaalmanac.com
86
        // http://javaalmanac.com/egs/java.sql/JdbcInt2Str.html
87
        mapJdbcNameToInt = new HashMap<String, Integer>();
88
89
        //Get all fields in java.sql.Types and store the corresponding int values
90
        Field[] fields = java.sql.Types.class.getFields();
91
        for (int i=0; i<fields.length; i++) {
92
            try {
93
                String name = fields[i].getName();
94
                Integer value = (Integer)fields[i].get(null);
95
                mapJdbcNameToInt.put(name.toLowerCase(java.util.Locale.ENGLISH),value);
96
            } catch (IllegalAccessException e) {
97
                throw new RuntimeException(e); // should not happen
98
            }
99
        }
100
    }
101
102
    // Query types (used to communicate with GUI)
103
    // N.B. These must not be changed, as they are used in the JMX files
104
    static final String SELECT   = "Select Statement"; // $NON-NLS-1$
105
    static final String UPDATE   = "Update Statement"; // $NON-NLS-1$
106
    static final String CALLABLE = "Callable Statement"; // $NON-NLS-1$
107
    static final String PREPARED_SELECT = "Prepared Select Statement"; // $NON-NLS-1$
108
    static final String PREPARED_UPDATE = "Prepared Update Statement"; // $NON-NLS-1$
109
    static final String COMMIT   = "Commit"; // $NON-NLS-1$
110
    static final String ROLLBACK = "Rollback"; // $NON-NLS-1$
111
    static final String AUTOCOMMIT_FALSE = "AutoCommit(false)"; // $NON-NLS-1$
112
    static final String AUTOCOMMIT_TRUE  = "AutoCommit(true)"; // $NON-NLS-1$
113
114
    private String query = ""; // $NON-NLS-1$
115
116
    private String dataSource = ""; // $NON-NLS-1$
117
118
    private String queryType = SELECT;
119
    private String queryArguments = ""; // $NON-NLS-1$
120
    private String queryArgumentsTypes = ""; // $NON-NLS-1$
121
    private String variableNames = ""; // $NON-NLS-1$
122
    private String resultVariable = "";
123
124
    /**
44
    /**
125
     *  Cache of PreparedStatements stored in a per-connection basis. Each entry of this
126
     *  cache is another Map mapping the statement string to the actual PreparedStatement.
127
     *  The cache has a fixed size of MAX_ENTRIES and it will throw away all PreparedStatements
128
     *  from the least recently used connections.
129
     */
130
    private static final Map<Connection, Map<String, PreparedStatement>> perConnCache =
131
        new LinkedHashMap<Connection, Map<String, PreparedStatement>>(MAX_ENTRIES){
132
        private static final long serialVersionUID = 1L;
133
        @Override
134
        protected boolean removeEldestEntry(Map.Entry<Connection, Map<String, PreparedStatement>> arg0) {
135
            if (size() > MAX_ENTRIES) {
136
                final  Map<String, PreparedStatement> value = arg0.getValue();
137
                closeAllStatements(value.values());
138
                return true;
139
            }
140
            return false;
141
        }
142
    };
143
144
    /**
145
     * Creates a JDBCSampler.
45
     * Creates a JDBCSampler.
146
     */
46
     */
147
    public JDBCSampler() {
47
    public JDBCSampler() {
Lines 165-171 Link Here
165
65
166
        res.sampleStart();
66
        res.sampleStart();
167
        Connection conn = null;
67
        Connection conn = null;
168
        Statement stmt = null;
68
        JDBCSamplerDiagnosticListener diagnosticListener = getCollectDiagnostics() ? new JDBCSamplerDiagnosticListener()
69
                : null;
169
70
170
        try {
71
        try {
171
72
Lines 175-242 Link Here
175
                res.latencyEnd(); // use latency to measure connection time
76
                res.latencyEnd(); // use latency to measure connection time
176
            }
77
            }
177
            res.setResponseHeaders(conn.toString());
78
            res.setResponseHeaders(conn.toString());
79
            res.setResponseData(execute(conn));
178
80
179
            // Based on query return value, get results
180
            String _queryType = getQueryType();
181
            if (SELECT.equals(_queryType)) {
182
                stmt = conn.createStatement();
183
                ResultSet rs = null;
184
                try {
185
                    rs = stmt.executeQuery(getQuery());
186
                    res.setResponseData(getStringFromResultSet(rs).getBytes(ENCODING));
187
                } finally {
188
                    close(rs);
189
                }
190
            } else if (CALLABLE.equals(_queryType)) {
191
                CallableStatement cstmt = getCallableStatement(conn);
192
                int out[]=setArguments(cstmt);
193
                // A CallableStatement can return more than 1 ResultSets
194
                // plus a number of update counts.
195
                boolean hasResultSet = cstmt.execute();
196
                String sb = resultSetsToString(cstmt,hasResultSet, out);
197
                res.setResponseData(sb.getBytes(ENCODING));
198
            } else if (UPDATE.equals(_queryType)) {
199
                stmt = conn.createStatement();
200
                stmt.executeUpdate(getQuery());
201
                int updateCount = stmt.getUpdateCount();
202
                String results = updateCount + " updates";
203
                res.setResponseData(results.getBytes(ENCODING));
204
            } else if (PREPARED_SELECT.equals(_queryType)) {
205
                PreparedStatement pstmt = getPreparedStatement(conn);
206
                setArguments(pstmt);
207
                boolean hasResultSet = pstmt.execute();
208
                String sb = resultSetsToString(pstmt,hasResultSet,null);
209
                res.setResponseData(sb.getBytes(ENCODING));
210
            } else if (PREPARED_UPDATE.equals(_queryType)) {
211
                PreparedStatement pstmt = getPreparedStatement(conn);
212
                setArguments(pstmt);
213
                pstmt.executeUpdate();
214
                String sb = resultSetsToString(pstmt,false,null);
215
                res.setResponseData(sb.getBytes(ENCODING));
216
            } else if (ROLLBACK.equals(_queryType)){
217
                conn.rollback();
218
                res.setResponseData(ROLLBACK.getBytes(ENCODING));
219
            } else if (COMMIT.equals(_queryType)){
220
                conn.commit();
221
                res.setResponseData(COMMIT.getBytes(ENCODING));
222
            } else if (AUTOCOMMIT_FALSE.equals(_queryType)){
223
                conn.setAutoCommit(false);
224
                res.setResponseData(AUTOCOMMIT_FALSE.getBytes(ENCODING));
225
            } else if (AUTOCOMMIT_TRUE.equals(_queryType)){
226
                conn.setAutoCommit(true);
227
                res.setResponseData(AUTOCOMMIT_TRUE.getBytes(ENCODING));
228
            } else { // User provided incorrect query type
229
                String results="Unexpected query type: "+_queryType;
230
                res.setResponseMessage(results);
231
                res.setSuccessful(false);
232
            }
233
234
        } catch (SQLException ex) {
81
        } catch (SQLException ex) {
235
            final String errCode = Integer.toString(ex.getErrorCode());
82
            final String errCode = Integer.toString(ex.getErrorCode());
236
            res.setResponseMessage(ex.toString());
83
            res.setResponseMessage(ex.toString());
237
            res.setResponseCode(ex.getSQLState()+ " " +errCode);
84
            res.setResponseCode(ex.getSQLState()+ " " +errCode);
238
            res.setSuccessful(false);
85
            res.setSuccessful(false);
239
        } catch (UnsupportedEncodingException ex) {
86
            if (diagnosticListener != null) {
87
                res.setResponseData(diagnosticListener.getDiagnosticMessages(), ENCODING);
88
            }
89
        } catch (UnsupportedOperationException ex) {
240
            res.setResponseMessage(ex.toString());
90
            res.setResponseMessage(ex.toString());
241
            res.setResponseCode("000"); // TODO - is this correct?
91
            res.setResponseCode("000"); // TODO - is this correct?
242
            res.setSuccessful(false);
92
            res.setSuccessful(false);
Lines 245-251 Link Here
245
            res.setResponseCode("000"); // TODO - is this correct?
95
            res.setResponseCode("000"); // TODO - is this correct?
246
            res.setSuccessful(false);
96
            res.setSuccessful(false);
247
        } finally {
97
        } finally {
248
            close(stmt);
98
            if (diagnosticListener != null) {
99
                diagnosticListener.detach();
100
            }
249
            close(conn);
101
            close(conn);
250
        }
102
        }
251
103
Lines 253-615 Link Here
253
        res.sampleEnd();
105
        res.sampleEnd();
254
        return res;
106
        return res;
255
    }
107
    }
256
257
    private String resultSetsToString(PreparedStatement pstmt, boolean result, int[] out) throws SQLException, UnsupportedEncodingException {
258
        StrBuilder sb = new StrBuilder();
259
        int updateCount = 0;
260
        if (!result) {
261
            updateCount = pstmt.getUpdateCount();
262
        }
263
        do {
264
            if (result) {
265
                ResultSet rs = null;
266
                try {
267
                    rs = pstmt.getResultSet();
268
                    sb.append(getStringFromResultSet(rs)).append("\n"); // $NON-NLS-1$
269
                } finally {
270
                    close(rs);
271
                }
272
            } else {
273
                sb.append(updateCount).append(" updates.\n");
274
            }
275
            result = pstmt.getMoreResults();
276
            if (!result) {
277
                updateCount = pstmt.getUpdateCount();
278
            }
279
        } while (result || (updateCount != -1));
280
        if (out!=null && pstmt instanceof CallableStatement){
281
            CallableStatement cs = (CallableStatement) pstmt;
282
            sb.append("Output variables by position:\n");
283
            for(int i=0; i < out.length; i++){
284
                if (out[i]!=java.sql.Types.NULL){
285
                    sb.append("[");
286
                    sb.append(i+1);
287
                    sb.append("] ");
288
                    sb.append(cs.getObject(i+1));
289
                    sb.append("\n");
290
                }
291
            }
292
        }
293
        return sb.toString();
294
    }
295
296
297
    private int[] setArguments(PreparedStatement pstmt) throws SQLException, IOException {
298
        if (getQueryArguments().trim().length()==0) {
299
            return new int[]{};
300
        }
301
        String[] arguments = CSVSaveService.csvSplitString(getQueryArguments(), COMMA_CHAR);
302
        String[] argumentsTypes = getQueryArgumentsTypes().split(COMMA);
303
        if (arguments.length != argumentsTypes.length) {
304
            throw new SQLException("number of arguments ("+arguments.length+") and number of types ("+argumentsTypes.length+") are not equal");
305
        }
306
        int[] outputs= new int[arguments.length];
307
        for (int i = 0; i < arguments.length; i++) {
308
            String argument = arguments[i];
309
            String argumentType = argumentsTypes[i];
310
            String[] arg = argumentType.split(" ");
311
            String inputOutput="";
312
            if (arg.length > 1) {
313
                argumentType = arg[1];
314
                inputOutput=arg[0];
315
            }
316
            int targetSqlType = getJdbcType(argumentType);
317
            try {
318
                if (!OUT.equalsIgnoreCase(inputOutput)){
319
                    if (argument.equals(NULL_MARKER)){
320
                        pstmt.setNull(i+1, targetSqlType);
321
                    } else {
322
                        pstmt.setObject(i+1, argument, targetSqlType);
323
                    }
324
                }
325
                if (OUT.equalsIgnoreCase(inputOutput)||INOUT.equalsIgnoreCase(inputOutput)) {
326
                    CallableStatement cs = (CallableStatement) pstmt;
327
                    cs.registerOutParameter(i+1, targetSqlType);
328
                    outputs[i]=targetSqlType;
329
                } else {
330
                    outputs[i]=java.sql.Types.NULL; // can't have an output parameter type null
331
                }
332
            } catch (NullPointerException e) { // thrown by Derby JDBC (at least) if there are no "?" markers in statement
333
                throw new SQLException("Could not set argument no: "+(i+1)+" - missing parameter marker?");
334
            }
335
        }
336
        return outputs;
337
    }
338
339
340
    private static int getJdbcType(String jdbcType) throws SQLException {
341
        Integer entry = mapJdbcNameToInt.get(jdbcType.toLowerCase(java.util.Locale.ENGLISH));
342
        if (entry == null) {
343
            try {
344
                entry = Integer.decode(jdbcType);
345
            } catch (NumberFormatException e) {
346
                throw new SQLException("Invalid data type: "+jdbcType);
347
            }
348
        }
349
        return (entry).intValue();
350
    }
351
352
353
    private CallableStatement getCallableStatement(Connection conn) throws SQLException {
354
        return (CallableStatement) getPreparedStatement(conn,true);
355
356
    }
357
    private PreparedStatement getPreparedStatement(Connection conn) throws SQLException {
358
        return getPreparedStatement(conn,false);
359
    }
360
361
    private PreparedStatement getPreparedStatement(Connection conn, boolean callable) throws SQLException {
362
        Map<String, PreparedStatement> preparedStatementMap = perConnCache.get(conn);
363
        if (null == preparedStatementMap ) {
364
            // MRU PreparedStatements cache.
365
            preparedStatementMap = new LinkedHashMap<String, PreparedStatement>(MAX_ENTRIES) {
366
                private static final long serialVersionUID = 240L;
367
368
                @Override
369
                protected boolean removeEldestEntry(Map.Entry<String, PreparedStatement> arg0) {
370
                    final int theSize = size();
371
                    if (theSize > MAX_ENTRIES) {
372
                        Object value = arg0.getValue();
373
                        if (value instanceof PreparedStatement) {
374
                            PreparedStatement pstmt = (PreparedStatement) value;
375
                            close(pstmt);
376
                        }
377
                        return true;
378
                    }
379
                    return false;
380
                }
381
            };
382
            perConnCache.put(conn, preparedStatementMap);
383
        }
384
        PreparedStatement pstmt = preparedStatementMap.get(getQuery());
385
        if (null == pstmt) {
386
            if (callable) {
387
                pstmt = conn.prepareCall(getQuery());
388
            } else {
389
                pstmt = conn.prepareStatement(getQuery());
390
            }
391
            preparedStatementMap.put(getQuery(), pstmt);
392
        }
393
        pstmt.clearParameters();
394
        return pstmt;
395
    }
396
397
    private static void closeAllStatements(Collection<PreparedStatement> collection) {
398
        for (PreparedStatement pstmt : collection) {
399
            close(pstmt);
400
        }
401
    }
402
403
    /**
404
     * Gets a Data object from a ResultSet.
405
     *
406
     * @param rs
407
     *            ResultSet passed in from a database query
408
     * @return a Data object
409
     * @throws java.sql.SQLException
410
     * @throws UnsupportedEncodingException
411
     */
412
    private String getStringFromResultSet(ResultSet rs) throws SQLException, UnsupportedEncodingException {
413
        ResultSetMetaData meta = rs.getMetaData();
414
415
        StrBuilder sb = new StrBuilder();
416
417
        int numColumns = meta.getColumnCount();
418
        for (int i = 1; i <= numColumns; i++) {
419
            sb.append(meta.getColumnName(i));
420
            if (i==numColumns){
421
                sb.append('\n');
422
            } else {
423
                sb.append('\t');
424
            }
425
        }
426
        
427
428
        JMeterVariables jmvars = getThreadContext().getVariables();
429
        String varnames[] = getVariableNames().split(COMMA);
430
        String resultVariable = getResultVariable().trim();
431
        List<Map<String, Object> > results = null;
432
        if(resultVariable.length() > 0) {
433
            results = new ArrayList<Map<String,Object> >();
434
            jmvars.putObject(resultVariable, results);
435
        }
436
        int j = 0;
437
        while (rs.next()) {
438
        	Map<String, Object> row = null;
439
            j++;
440
            for (int i = 1; i <= numColumns; i++) {
441
                Object o = rs.getObject(i);
442
                if(results != null) {
443
                	if(row == null) {
444
                		row = new HashMap<String, Object>(numColumns);
445
                		results.add(row);
446
                	}
447
                	row.put(meta.getColumnName(i), o);
448
                }
449
                if (o instanceof byte[]) {
450
                    o = new String((byte[]) o, ENCODING);
451
                }
452
                sb.append(o);
453
                if (i==numColumns){
454
                    sb.append('\n');
455
                } else {
456
                    sb.append('\t');
457
                }
458
                if (i <= varnames.length) { // i starts at 1
459
                    String name = varnames[i - 1].trim();
460
                    if (name.length()>0){ // Save the value in the variable if present
461
                        jmvars.put(name+UNDERSCORE+j, o == null ? null : o.toString());
462
                    }
463
                }
464
            }
465
        }
466
        // Remove any additional values from previous sample
467
        for(int i=0; i < varnames.length; i++){
468
            String name = varnames[i].trim();
469
            if (name.length()>0 && jmvars != null){
470
                final String varCount = name+"_#"; // $NON-NLS-1$
471
                // Get the previous count
472
                String prevCount = jmvars.get(varCount);
473
                if (prevCount != null){
474
                    int prev = Integer.parseInt(prevCount);
475
                    for (int n=j+1; n <= prev; n++ ){
476
                        jmvars.remove(name+UNDERSCORE+n);
477
                    }
478
                }
479
                jmvars.put(varCount, Integer.toString(j)); // save the current count
480
            }
481
        }
482
483
        return sb.toString();
484
    }
485
486
    public static void close(Connection c) {
487
        try {
488
            if (c != null) {
489
                c.close();
490
            }
491
        } catch (SQLException e) {
492
            log.warn("Error closing Connection", e);
493
        }
494
    }
495
496
    public static void close(Statement s) {
497
        try {
498
            if (s != null) {
499
                s.close();
500
            }
501
        } catch (SQLException e) {
502
            log.warn("Error closing Statement " + s.toString(), e);
503
        }
504
    }
505
506
    public static void close(ResultSet rs) {
507
        try {
508
            if (rs != null) {
509
                rs.close();
510
            }
511
        } catch (SQLException e) {
512
            log.warn("Error closing ResultSet", e);
513
        }
514
    }
515
516
    public String getQuery() {
517
        return query;
518
    }
519
520
    @Override
521
    public String toString() {
522
        StrBuilder sb = new StrBuilder(80);
523
        sb.append("["); // $NON-NLS-1$
524
        sb.append(getQueryType());
525
        sb.append("] "); // $NON-NLS-1$
526
        sb.append(getQuery());
527
        sb.append("\n");
528
        sb.append(getQueryArguments());
529
        sb.append("\n");
530
        sb.append(getQueryArgumentsTypes());
531
        return sb.toString();
532
    }
533
534
    /**
535
     * @param query
536
     *            The query to set.
537
     */
538
    public void setQuery(String query) {
539
        this.query = query;
540
    }
541
542
    /**
543
     * @return Returns the dataSource.
544
     */
545
    public String getDataSource() {
546
        return dataSource;
547
    }
548
549
    /**
550
     * @param dataSource
551
     *            The dataSource to set.
552
     */
553
    public void setDataSource(String dataSource) {
554
        this.dataSource = dataSource;
555
    }
556
557
    /**
558
     * @return Returns the queryType.
559
     */
560
    public String getQueryType() {
561
        return queryType;
562
    }
563
564
    /**
565
     * @param queryType The queryType to set.
566
     */
567
    public void setQueryType(String queryType) {
568
        this.queryType = queryType;
569
    }
570
571
    public String getQueryArguments() {
572
        return queryArguments;
573
    }
574
575
    public void setQueryArguments(String queryArguments) {
576
        this.queryArguments = queryArguments;
577
    }
578
579
    public String getQueryArgumentsTypes() {
580
        return queryArgumentsTypes;
581
    }
582
583
    public void setQueryArgumentsTypes(String queryArgumentsType) {
584
        this.queryArgumentsTypes = queryArgumentsType;
585
    }
586
587
    /**
588
     * @return the variableNames
589
     */
590
    public String getVariableNames() {
591
        return variableNames;
592
    }
593
594
    /**
595
     * @param variableNames the variableNames to set
596
     */
597
    public void setVariableNames(String variableNames) {
598
        this.variableNames = variableNames;
599
    }
600
601
    /**
602
     * @return the resultVariable
603
     */
604
	public String getResultVariable() {
605
		return resultVariable ;
606
	}
607
608
    /**
609
     * @param resultVariable the variable name in which results will be stored
610
     */
611
	public void setResultVariable(String resultVariable) {
612
		this.resultVariable = resultVariable;
613
	}
614
    
615
}
108
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerBeanInfo.java (-59 / +2 lines)
Lines 22-93 Link Here
22
 */
22
 */
23
package org.apache.jmeter.protocol.jdbc.sampler;
23
package org.apache.jmeter.protocol.jdbc.sampler;
24
24
25
import java.beans.PropertyDescriptor;
25
import org.apache.jmeter.protocol.jdbc.JDBCBeanInfoSupport;
26
26
27
import org.apache.jmeter.testbeans.BeanInfoSupport;
28
import org.apache.jmeter.testbeans.gui.TextAreaEditor;
29
27
30
public class JDBCSamplerBeanInfo extends BeanInfoSupport {
28
public class JDBCSamplerBeanInfo extends JDBCBeanInfoSupport {
31
29
32
    /**
30
    /**
33
     *
31
     *
34
     */
32
     */
35
    public JDBCSamplerBeanInfo() {
33
    public JDBCSamplerBeanInfo() {
36
        super(JDBCSampler.class);
34
        super(JDBCSampler.class);
37
38
        createPropertyGroup("varName", // $NON-NLS-1$
39
                new String[]{"dataSource" }); // $NON-NLS-1$
40
41
        createPropertyGroup("sql", // $NON-NLS-1$
42
                new String[] {
43
                "queryType", // $NON-NLS-1$
44
                "query", // $NON-NLS-1$
45
                "queryArguments", // $NON-NLS-1$
46
                "queryArgumentsTypes", // $NON-NLS-1$
47
                "variableNames", // $NON-NLS-1$
48
                "resultVariable", // $NON-NLS-1$
49
                });
50
51
        PropertyDescriptor p = property("dataSource"); // $NON-NLS-1$
52
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
53
        p.setValue(DEFAULT, "");
54
55
        p = property("queryArguments"); // $NON-NLS-1$
56
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
57
        p.setValue(DEFAULT, "");
58
59
        p = property("queryArgumentsTypes"); // $NON-NLS-1$
60
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
61
        p.setValue(DEFAULT, "");
62
63
        p = property("variableNames"); // $NON-NLS-1$
64
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
65
        p.setValue(DEFAULT, "");
66
67
        p = property("resultVariable"); // $NON-NLS-1$
68
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
69
        p.setValue(DEFAULT, "");
70
71
        p = property("queryType"); // $NON-NLS-1$
72
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
73
        p.setValue(DEFAULT, JDBCSampler.SELECT);
74
        p.setValue(NOT_OTHER,Boolean.TRUE);
75
        p.setValue(TAGS,new String[]{
76
                JDBCSampler.SELECT,
77
                JDBCSampler.UPDATE,
78
                JDBCSampler.CALLABLE,
79
                JDBCSampler.PREPARED_SELECT,
80
                JDBCSampler.PREPARED_UPDATE,
81
                JDBCSampler.COMMIT,
82
                JDBCSampler.ROLLBACK,
83
                JDBCSampler.AUTOCOMMIT_FALSE,
84
                JDBCSampler.AUTOCOMMIT_TRUE,
85
                });
86
87
        p = property("query"); // $NON-NLS-1$
88
        p.setValue(NOT_UNDEFINED, Boolean.TRUE);
89
        p.setValue(DEFAULT, "");
90
        p.setPropertyEditorClass(TextAreaEditor.class);
91
92
    }
35
    }
93
}
36
}
(-)src/protocol/jdbc/org/apache/jmeter/protocol/jdbc/sampler/JDBCSamplerResources.properties (-17 lines)
Lines 14-33 Link Here
14
#   limitations under the License.
14
#   limitations under the License.
15
15
16
displayName=JDBC Request
16
displayName=JDBC Request
17
varName.displayName=Variable Name Bound to Pool
18
sql.displayName=SQL Query
19
query.displayName=Query
20
query.shortDescription=SQL Query to send to database
21
queryType.displayName=Query Type
22
queryType.shortDescription=Determines if the SQL statement should be run as a select statement or an update statement.
23
dataSource.displayName=Variable Name
24
dataSource.shortDescription=Name of the JMeter variable that the connection pool is bound to.
25
queryArguments.displayName=Parameter values
26
queryArguments.shortDescription=SQL parameter values (comma separated)
27
queryArgumentsTypes.displayName=Parameter types
28
queryArgumentsTypes.shortDescription=JDBC Type names from java.sql.Types. VARCHAR, INTEGER, etc. (comma separated)
29
variableNames.displayName=Variable names
30
variableNames.shortDescription=Output variable names for each column  (comma separated)
31
resultVariable.displayName=Result variable name
32
resultVariable.shortDescription=Name of the JMeter variable that stores the result set objects in a list of maps for looking up results by column name.
33

Return to bug 52128