From fbe09689d030a004f6ec1e12282b4ae2a9e9374d Mon Sep 17 00:00:00 2001 From: Chrriis Date: Sun, 29 Apr 2012 23:10:51 +0200 Subject: [PATCH] [#1177] Add SQL Console module to jOOQ - Code refactored to allow evolutions (breakpoints, remote execution, filters, etc.) --- .../java/org/jooq/debug/DebugListener.java | 42 +- .../main/java/org/jooq/debug/Debugger.java | 106 ++-- .../java/org/jooq/debug/DebuggerRegistry.java | 21 +- .../java/org/jooq/debug/LocalDebugger.java | 113 ++++ .../java/org/jooq/debug/LoggingListener.java | 46 ++ ...ebuggerData.java => QueryLoggingData.java} | 4 +- ...SetData.java => ResultSetLoggingData.java} | 4 +- .../org/jooq/debug/StatementExecution.java | 60 +++ .../StatementExecutionMessageResult.java | 67 +++ ...ner.java => StatementExecutionResult.java} | 85 ++- .../StatementExecutionResultSetResult.java | 177 ++++++ .../org/jooq/debug/StatementExecutor.java | 46 ++ .../org/jooq/debug/StatementExecutorImpl.java | 301 +++++++++++ .../java/org/jooq/debug/console/Console.java | 71 ++- .../org/jooq/debug/console/EditorPane.java | 502 ++++-------------- .../org/jooq/debug/console/LoggerPane.java | 79 ++- .../remote/ClientDebugQueriesMessage.java | 8 +- .../remote/ClientDebugResultSetMessage.java | 8 +- .../console/remote/RemoteDebuggerClient.java | 116 ++-- .../console/remote/RemoteDebuggerServer.java | 58 +- 20 files changed, 1249 insertions(+), 665 deletions(-) create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/LocalDebugger.java create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/LoggingListener.java rename jOOQ-console/src/main/java/org/jooq/debug/{DebuggerData.java => QueryLoggingData.java} (91%) rename jOOQ-console/src/main/java/org/jooq/debug/{DebuggerResultSetData.java => ResultSetLoggingData.java} (91%) create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/StatementExecution.java create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionMessageResult.java rename jOOQ-console/src/main/java/org/jooq/debug/{DebuggerRegistryListener.java => StatementExecutionResult.java} (89%) create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResultSetResult.java create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/StatementExecutor.java create mode 100644 jOOQ-console/src/main/java/org/jooq/debug/StatementExecutorImpl.java diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java b/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java index a65d8a2c6a..a430a65a7b 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java @@ -121,10 +121,21 @@ public class DebugListener extends DefaultExecuteListener { return; } endExecutionTime = System.currentTimeMillis(); - List sqlQueryDebuggerList = DebuggerRegistry.getDebuggerList(); - if(sqlQueryDebuggerList.isEmpty()) { + List debuggerList = DebuggerRegistry.getDebuggerList(); + if(debuggerList.isEmpty()) { return; } + boolean hasListener = false; + for(Debugger listener: debuggerList) { + LoggingListener loggingListener = listener.getLoggingListener(); + if(loggingListener != null) { + hasListener = true; + break; + } + } + if(!hasListener) { + return; + } ResultSet resultSet = ctx.resultSet(); String[] sql = ctx.batchSQL(); SqlQueryType sqlQueryType = SqlQueryType.detectType(sql[0]); @@ -135,22 +146,31 @@ public class DebugListener extends DefaultExecuteListener { parameterDescription = ((UsageTrackingPreparedStatement) statement).getParameterDescription(); } } - DebuggerData sqlQueryDebuggerData = new DebuggerData(sqlQueryType, sql, parameterDescription, startPreparationTime == 0? null: aggregatedPreparationDuration, startBindTime == 0? null: endBindTime - startBindTime, endExecutionTime - startExecutionTime); - for(Debugger listener: sqlQueryDebuggerList) { - listener.debugQueries(sqlQueryDebuggerData); + QueryLoggingData queryLoggingData = new QueryLoggingData(sqlQueryType, sql, parameterDescription, startPreparationTime == 0? null: aggregatedPreparationDuration, startBindTime == 0? null: endBindTime - startBindTime, endExecutionTime - startExecutionTime); + for(Debugger listener: debuggerList) { + LoggingListener loggingListener = listener.getLoggingListener(); + if(loggingListener != null) { + loggingListener.logQueries(queryLoggingData); + } } if(resultSet != null) { - final int sqlQueryDebuggerDataID = sqlQueryDebuggerData.getID(); + final int queryLoggingDataID = queryLoggingData.getID(); ResultSet newResultSet = new UsageTrackingResultSet(resultSet) { @Override protected void notifyData(long lifeTime, int readRows, int readCount, int writeCount) { - List sqlQueryDebuggerList = DebuggerRegistry.getDebuggerList(); - if(sqlQueryDebuggerList.isEmpty()) { + List debuggerList = DebuggerRegistry.getDebuggerList(); + if(debuggerList.isEmpty()) { return; } - DebuggerResultSetData sqlQueryDebuggerResultSetData = new DebuggerResultSetData(lifeTime, readRows, readCount, writeCount); - for(Debugger listener: sqlQueryDebuggerList) { - listener.debugResultSet(sqlQueryDebuggerDataID, sqlQueryDebuggerResultSetData); + ResultSetLoggingData resultSetLoggingData = null; + for(Debugger debugger: debuggerList) { + LoggingListener loggingListener = debugger.getLoggingListener(); + if(loggingListener != null) { + if(resultSetLoggingData == null) { + resultSetLoggingData = new ResultSetLoggingData(lifeTime, readRows, readCount, writeCount); + } + loggingListener.logResultSet(queryLoggingDataID, resultSetLoggingData); + } } } }; diff --git a/jOOQ-console/src/main/java/org/jooq/debug/Debugger.java b/jOOQ-console/src/main/java/org/jooq/debug/Debugger.java index 0d08d7cf80..640ec3c91e 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/Debugger.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/Debugger.java @@ -1,49 +1,57 @@ -/** - * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com - * Christopher Deckers, chrriis@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOQ" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.jooq.debug; - - -/** - * @author Christopher Deckers - */ -public interface Debugger { - - public void debugQueries(DebuggerData sqlQueryDebuggerData); - - public void debugResultSet(int sqlQueryDebuggerDataID, DebuggerResultSetData sqlQueryDebuggerResultSetData); - -} +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + + + +public interface Debugger { + + public void setLoggingListener(LoggingListener loggingListener); + + public LoggingListener getLoggingListener(); + + public boolean isExecutionSupported(); + + public StatementExecutor createStatementExecutor(String sql, int maxRSRowsParsing, int retainParsedRSDataRowCountThreshold); + + public String[] getTableNames(); + + public String[] getTableColumnNames(); + + public boolean isReadOnly(); + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistry.java b/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistry.java index 67f8db36c1..c9c5e75301 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistry.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistry.java @@ -40,6 +40,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; + /** * @author Christopher Deckers */ @@ -53,32 +54,12 @@ public class DebuggerRegistry { public static void addSqlQueryDebugger(Debugger sqlQueryDebugger) { synchronized(LOCK) { debuggerList.add(sqlQueryDebugger); - for(DebuggerRegistryListener l: debuggerRegisterListenerList) { - l.notifyDebuggerListenersModified(); - } } } public static void removeSqlQueryDebugger(Debugger sqlQueryDebugger) { synchronized(LOCK) { debuggerList.remove(sqlQueryDebugger); - for(DebuggerRegistryListener l: debuggerRegisterListenerList) { - l.notifyDebuggerListenersModified(); - } - } - } - - private static final List debuggerRegisterListenerList = new ArrayList(1); - - public static void addDebuggerRegisterListener(DebuggerRegistryListener listener) { - synchronized(LOCK) { - debuggerRegisterListenerList.add(listener); - } - } - - public static void removeDebuggerRegisterListener(DebuggerRegistryListener listener) { - synchronized(LOCK) { - debuggerRegisterListenerList.remove(listener); } } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/LocalDebugger.java b/jOOQ-console/src/main/java/org/jooq/debug/LocalDebugger.java new file mode 100644 index 0000000000..ad229adf14 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/LocalDebugger.java @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.jooq.Field; +import org.jooq.Record; +import org.jooq.Table; +import org.jooq.debug.console.DatabaseDescriptor; + + +public class LocalDebugger implements Debugger { + + private DatabaseDescriptor databaseDescriptor; + + public LocalDebugger(DatabaseDescriptor databaseDescriptor) { + this.databaseDescriptor = databaseDescriptor; + } + + private LoggingListener loggingListener; + + @Override + public void setLoggingListener(LoggingListener loggingListener) { + this.loggingListener = loggingListener; + } + + @Override + public LoggingListener getLoggingListener() { + return loggingListener; + } + + @Override + public boolean isExecutionSupported() { + return databaseDescriptor != null; + } + + @Override + public StatementExecutor createStatementExecutor(String sql, int maxRSRowsParsing, int retainParsedRSDataRowCountThreshold) { + return new StatementExecutorImpl(databaseDescriptor, sql, maxRSRowsParsing, retainParsedRSDataRowCountThreshold); + } + + @Override + public String[] getTableNames() { + List> tableList = databaseDescriptor.getSchema().getTables(); + List tableNameList = new ArrayList(); + for(Table table: tableList) { + String tableName = table.getName(); + tableNameList.add(tableName); + } + Collections.sort(tableNameList, String.CASE_INSENSITIVE_ORDER); + return tableNameList.toArray(new String[0]); + } + + @Override + public String[] getTableColumnNames() { + Set columnNameSet = new HashSet(); + for(Table table: databaseDescriptor.getSchema().getTables()) { + for(Field field: table.getFields()) { + String columnName = field.getName(); + columnNameSet.add(columnName); + } + } + String[] columnNames = columnNameSet.toArray(new String[0]); + Arrays.sort(columnNames, String.CASE_INSENSITIVE_ORDER); + return columnNames; + } + + @Override + public boolean isReadOnly() { + return databaseDescriptor.isReadOnly(); + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/LoggingListener.java b/jOOQ-console/src/main/java/org/jooq/debug/LoggingListener.java new file mode 100644 index 0000000000..5702ef10a2 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/LoggingListener.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + + +public interface LoggingListener { + + public void logQueries(QueryLoggingData queryLoggingData); + + public void logResultSet(int queryLoggingDataID, ResultSetLoggingData sqlQueryDebuggerResultSetData); + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerData.java b/jOOQ-console/src/main/java/org/jooq/debug/QueryLoggingData.java similarity index 91% rename from jOOQ-console/src/main/java/org/jooq/debug/DebuggerData.java rename to jOOQ-console/src/main/java/org/jooq/debug/QueryLoggingData.java index 82351505d0..8d3df41100 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerData.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/QueryLoggingData.java @@ -42,7 +42,7 @@ import java.io.Serializable; /** * @author Christopher Deckers */ -public class DebuggerData implements Serializable { +public class QueryLoggingData implements Serializable { private static volatile int nextID; @@ -57,7 +57,7 @@ public class DebuggerData implements Serializable { private long threadID; private StackTraceElement[] callerStackTraceElements; - public DebuggerData(SqlQueryType queryType, String[] queries, String parameterDescription, Long preparationDuration, Long bindingDuration, long executionDuration) { + public QueryLoggingData(SqlQueryType queryType, String[] queries, String parameterDescription, Long preparationDuration, Long bindingDuration, long executionDuration) { this.id = nextID++; Thread currentThread = Thread.currentThread(); this.threadName = currentThread.getName(); diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerResultSetData.java b/jOOQ-console/src/main/java/org/jooq/debug/ResultSetLoggingData.java similarity index 91% rename from jOOQ-console/src/main/java/org/jooq/debug/DebuggerResultSetData.java rename to jOOQ-console/src/main/java/org/jooq/debug/ResultSetLoggingData.java index 1a881b8296..10e78aa4ac 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerResultSetData.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/ResultSetLoggingData.java @@ -41,7 +41,7 @@ import java.io.Serializable; /** * @author Christopher Deckers */ -public class DebuggerResultSetData implements Serializable { +public class ResultSetLoggingData implements Serializable { private static volatile int nextID; @@ -51,7 +51,7 @@ public class DebuggerResultSetData implements Serializable { private final int readCount; private final int writeCount; - public DebuggerResultSetData(long lifeTime, final int readRows, final int readCount, final int writeCount) { + public ResultSetLoggingData(long lifeTime, final int readRows, final int readCount, final int writeCount) { this.id = nextID++; this.lifeTime = lifeTime; this.readRows = readRows; diff --git a/jOOQ-console/src/main/java/org/jooq/debug/StatementExecution.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecution.java new file mode 100644 index 0000000000..db308f0f64 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecution.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + + + +public class StatementExecution { + + private long executionDuration; + + private StatementExecutionResult[] results; + + public StatementExecution(long executionDuration, StatementExecutionResult... results) { + this.executionDuration = executionDuration; + this.results = results; + } + + public StatementExecutionResult[] getResults() { + return results; + } + + public long getExecutionDuration() { + return executionDuration; + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionMessageResult.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionMessageResult.java new file mode 100644 index 0000000000..f3c4f76644 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionMessageResult.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class StatementExecutionMessageResult implements StatementExecutionResult { + + private String message; + private boolean isError; + + public StatementExecutionMessageResult(String message, boolean isError) { + this.message = message; + this.isError = isError; + } + + public StatementExecutionMessageResult(Exception e) { + StringWriter stringWriter = new StringWriter(); + e.printStackTrace(new PrintWriter(stringWriter)); + this.message = stringWriter.toString(); + isError = true; + } + + public String getMessage() { + return message; + } + + public boolean isError() { + return isError; + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistryListener.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResult.java similarity index 89% rename from jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistryListener.java rename to jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResult.java index 982051985d..37efef589d 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebuggerRegistryListener.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResult.java @@ -1,43 +1,42 @@ -/** - * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com - * Christopher Deckers, chrriis@gmail.com - * All rights reserved. - * - * This software is licensed to you under the Apache License, Version 2.0 - * (the "License"); You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * . Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * . Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * . Neither the name "jOOQ" nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -package org.jooq.debug; - -public interface DebuggerRegistryListener { - - public void notifyDebuggerListenersModified(); - -} +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + + +public interface StatementExecutionResult { + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResultSetResult.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResultSetResult.java new file mode 100644 index 0000000000..9f489b83b6 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutionResultSetResult.java @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; + +import org.jooq.debug.console.misc.Utils; + +public class StatementExecutionResultSetResult implements StatementExecutionResult { + + public static class TypeInfo { + + private String columnName; + private int precision; + private int scale; + private int nullable = ResultSetMetaData.columnNullableUnknown; + + TypeInfo(ResultSetMetaData metaData, int column) { + try { + columnName = metaData.getColumnTypeName(column + 1); + precision = metaData.getPrecision(column + 1); + scale = metaData.getScale(column + 1); + nullable = metaData.isNullable(column + 1); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(columnName); + if(precision != 0) { + sb.append(" (" + precision + (scale != 0? ", " + scale: "") + ")"); + } + if(nullable != ResultSetMetaData.columnNullableUnknown) { + sb.append(nullable == ResultSetMetaData.columnNoNulls? " not null": " null"); + } + return sb.toString(); + } + + } + + private ResultSet rs; + private String[] columnNames; + private TypeInfo[] typeInfos; + private Class[] columnClasses; + private Object[][] rowData; + private int rowCount; + private long resultSetParsingDuration; + private int retainParsedRSDataRowCountThreshold; + private boolean isReadOnly; + + public StatementExecutionResultSetResult(ResultSet rs, String[] columnNames, TypeInfo[] typeInfos, Class[] columnClasses, Object[][] rowData, int rowCount, long resultSetParsingDuration, int retainParsedRSDataRowCountThreshold, boolean isReadOnly) { + this.rs = rs; + this.columnNames = columnNames; + this.typeInfos = typeInfos; + this.columnClasses = columnClasses; + this.rowData = rowData; + this.rowCount = rowCount; + this.resultSetParsingDuration = resultSetParsingDuration; + this.retainParsedRSDataRowCountThreshold = retainParsedRSDataRowCountThreshold; + this.isReadOnly = isReadOnly; + } + + public String[] getColumnNames() { + return columnNames; + } + + public TypeInfo[] getTypeInfos() { + return typeInfos; + } + + public Class[] getColumnClasses() { + return columnClasses; + } + + /** + * @return the data or an empty array of arrays if rowCount is over retainParsedRSDataRowCountThreshold. + */ + public Object[][] getRowData() { + return rowData; + } + + public int getRowCount() { + return rowCount; + } + + public long getResultSetParsingDuration() { + return resultSetParsingDuration; + } + + public int getRetainParsedRSDataRowCountThreshold() { + return retainParsedRSDataRowCountThreshold; + } + + public boolean deleteRow(int row) { + try { + rs.absolute(row); + rs.deleteRow(); + Object[][] newRowData = new Object[rowData.length - 1][]; + System.arraycopy(rowData, 0, newRowData, 0, row); + System.arraycopy(rowData, row + 1, newRowData, row, newRowData.length - row - 1); + rowData = newRowData; + return true; + } catch (SQLException ex) { + ex.printStackTrace(); + } + return false; + } + + public boolean setValueAt(Object o, int row, int col) { + if(Utils.equals(o, rowData[row][col])) { + return false; + } + try { + rs.absolute(row + 1); + rs.updateObject(col + 1, o); + rs.updateRow(); + rowData[row][col] = o; + return true; + } catch (SQLException e) { + e.printStackTrace(); + try { + rs.cancelRowUpdates(); + } catch (SQLException ex) { + ex.printStackTrace(); + } + } + return false; + } + + public boolean isEditable() { + try { + return rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE && !isReadOnly; + } catch (SQLException e) { + } + return false; + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutor.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutor.java new file mode 100644 index 0000000000..de3a575d26 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutor.java @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + + +public interface StatementExecutor { + + public StatementExecution execute(); + + public void stopExecution(); + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutorImpl.java b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutorImpl.java new file mode 100644 index 0000000000..04f9b26364 --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/StatementExecutorImpl.java @@ -0,0 +1,301 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringWriter; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.jooq.SQLDialect; +import org.jooq.debug.StatementExecutionResultSetResult.TypeInfo; +import org.jooq.debug.console.DatabaseDescriptor; +import org.jooq.debug.console.misc.Utils; + +public class StatementExecutorImpl implements StatementExecutor { + + private DatabaseDescriptor databaseDescriptor; + private String sql; + private int maxRSRowsParsing; + private int retainParsedRSDataRowCountThreshold; + + public StatementExecutorImpl(DatabaseDescriptor databaseDescriptor, String sql, int maxRSRowsParsing, int retainParsedRSDataRowCountThreshold) { + this.databaseDescriptor = databaseDescriptor; + this.sql = sql; + this.maxRSRowsParsing = maxRSRowsParsing; + this.retainParsedRSDataRowCountThreshold = retainParsedRSDataRowCountThreshold; + } + + private volatile Connection conn; + private volatile Statement stmt; + + private volatile Thread evaluationThread; + + @Override + public StatementExecution execute() { + boolean isAllowed = true; + if(databaseDescriptor.isReadOnly()) { + String simplifiedSql = sql.replaceAll("'[^']*'", ""); + Matcher matcher = Pattern.compile("[a-zA-Z_0-9\\$]+").matcher(simplifiedSql); + boolean isFirst = true; + while(matcher.find()) { + String word = simplifiedSql.substring(matcher.start(), matcher.end()).toUpperCase(Locale.ENGLISH); + if(isFirst && !word.equals("SELECT")) { + isAllowed = false; + break; + } + isFirst = false; + for(String keyword: new String[] { + "INSERT", + "UPDATE", + "DELETE", + "ALTER", + "DROP", + "CREATE", + "EXEC", + "EXECUTE", + }) { + if(word.equals(keyword)) { + isAllowed = false; + break; + } + } + } + } + if(!isAllowed) { + return new StatementExecution(0, new StatementExecutionMessageResult("The database is not editable but the statement to evaluate is a modification statement!", true)); + } + closeConnection(); + evaluationThread = Thread.currentThread(); + long start = System.currentTimeMillis(); + try { + conn = databaseDescriptor.createConnection(); + stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + // If no error, adjust start to begining of actual execution. + start = System.currentTimeMillis(); + if(evaluationThread != Thread.currentThread()) { + long executionDuration = System.currentTimeMillis() - start; + return new StatementExecution(executionDuration, new StatementExecutionMessageResult("Interrupted by user after " + Utils.formatDuration(executionDuration), true)); + } + boolean executeResult; + try { + executeResult = stmt.execute(sql); + } catch(SQLException e) { + long executionDuration = System.currentTimeMillis() - start; + if(evaluationThread != Thread.currentThread()) { + return new StatementExecution(executionDuration, new StatementExecutionMessageResult("Interrupted by user after " + Utils.formatDuration(executionDuration), true)); + } + return new StatementExecution(executionDuration, new StatementExecutionMessageResult(e)); + } + final long executionDuration = System.currentTimeMillis() - start; + if(evaluationThread != Thread.currentThread()) { + return new StatementExecution(executionDuration, new StatementExecutionMessageResult("Interrupted by user after " + Utils.formatDuration(executionDuration), true)); + } + List statementExecutionResultList = new ArrayList(); + do { + StatementExecutionResult statementExecutionResult; + if(executeResult) { + final ResultSet rs = stmt.getResultSet(); + ResultSetMetaData metaData = rs.getMetaData(); + final String[] columnNames = new String[metaData.getColumnCount()]; + final int[] columnTypes = new int[columnNames.length]; + final TypeInfo[] typeInfos = new TypeInfo[columnNames.length]; + final Class[] columnClasses = new Class[columnNames.length]; + for(int i=0; i rowDataList = new ArrayList(); + int rowCount = 0; + long rsStart = System.currentTimeMillis(); + while(rs.next() && rowCount < maxRSRowsParsing) { + if(evaluationThread != Thread.currentThread()) { + return new StatementExecution(executionDuration, new StatementExecutionMessageResult("Interrupted by user after " + Utils.formatDuration(executionDuration), true)); + } + rowCount++; + Object[] rowData = new Object[columnNames.length]; + for(int i=0; i=0; ) { + stringWriter.write(chars, 0, count); + } + rowData[i] = stringWriter.toString(); + } else { + rowData[i] = null; + } + break; + } + case Types.BLOB: { + Blob blob = rs.getBlob(i + 1); + if(blob != null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] bytes = new byte[1024]; + InputStream in = new BufferedInputStream(blob.getBinaryStream()); + for(int count; (count=in.read(bytes))>=0; ) { + baos.write(bytes, 0, count); + } + rowData[i] = baos.toByteArray(); + } else { + rowData[i] = null; + } + break; + } + default: + Object object = rs.getObject(i + 1); + if(object != null) { + String className = object.getClass().getName(); + if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { + object = rs.getTimestamp(i + 1); + } + // Probably something to do for oracle.sql.DATE + } + rowData[i] = object; + break; + } + } + if(rowCount <= retainParsedRSDataRowCountThreshold) { + rowDataList.add(rowData); + } else if(rowCount == retainParsedRSDataRowCountThreshold + 1) { + rowDataList.clear(); + } + } + final long resultSetParsingDuration = System.currentTimeMillis() - rsStart; + statementExecutionResult = new StatementExecutionResultSetResult(rs, columnNames, typeInfos, columnClasses, rowDataList.toArray(new Object[0][]), rowCount, resultSetParsingDuration, retainParsedRSDataRowCountThreshold, databaseDescriptor.isReadOnly()); + } else { + final int updateCount = stmt.getUpdateCount(); + statementExecutionResult = new StatementExecutionMessageResult(Utils.formatDuration(executionDuration) + "> " + updateCount + " row(s) affected.", false); + } + if(databaseDescriptor.getSQLDialect() == SQLDialect.SQLSERVER) { + try { + executeResult = stmt.getMoreResults(Statement.KEEP_CURRENT_RESULT); + } catch(Exception e) { + executeResult = stmt.getMoreResults(); + } + } else { + executeResult = false; + } + statementExecutionResultList.add(statementExecutionResult); + } while(executeResult || stmt.getUpdateCount() != -1); + return new StatementExecution(executionDuration, statementExecutionResultList.toArray(new StatementExecutionResult[0])); + } catch(Exception e) { + long executionDuration = System.currentTimeMillis() - start; + return new StatementExecution(executionDuration, new StatementExecutionMessageResult(e)); + } finally { + if(databaseDescriptor.isReadOnly()) { + closeConnection(); + } + } + + // TODO: implement + } + + private void closeConnection() { + if (conn != null) { + if (stmt != null) { + try { + stmt.cancel(); + } catch (Exception e) { + } + try { + stmt.close(); + } catch (Exception e) { + } + } + stmt = null; + try { + conn.close(); + } catch (Exception e) { + } + conn = null; + } + } + + @Override + public void stopExecution() { + if(evaluationThread != null) { + evaluationThread = null; + closeConnection(); + } + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java b/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java index cb8deeea6c..7ee74f3a87 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java @@ -59,7 +59,6 @@ import java.awt.font.TextAttribute; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -89,8 +88,9 @@ import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; -import org.jooq.Record; -import org.jooq.Table; +import org.jooq.debug.Debugger; +import org.jooq.debug.DebuggerRegistry; +import org.jooq.debug.LocalDebugger; import org.jooq.debug.console.remote.RemoteDebuggerClient; /** @@ -99,11 +99,13 @@ import org.jooq.debug.console.remote.RemoteDebuggerClient; @SuppressWarnings("serial") public class Console extends JFrame { + private Debugger debugger; private JTabbedPane mainTabbedPane; private JTabbedPane editorTabbedPane; - public Console(final DatabaseDescriptor editorDatabaseDescriptor, boolean isShowingLoggingTab) { + public Console(final Debugger debugger, boolean isShowingLoggingTab) { setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.debugger = debugger; JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); @@ -193,15 +195,15 @@ public class Console extends JFrame { setJMenuBar(menuBar); mainTabbedPane = new JTabbedPane(); String title = "jOOQ Console"; - if(editorDatabaseDescriptor != null) { - String schemaName = editorDatabaseDescriptor.getSchema().getName(); - if(schemaName != null && schemaName.length() != 0) { - title += " - " + schemaName; - } - } +// if(editorDatabaseDescriptor != null) { +// String schemaName = editorDatabaseDescriptor.getSchema().getName(); +// if(schemaName != null && schemaName.length() != 0) { +// title += " - " + schemaName; +// } +// } setTitle(title); - if(editorDatabaseDescriptor != null) { - addEditorTab(editorDatabaseDescriptor); + if(debugger.isExecutionSupported()) { + addEditorTab(); } if(isShowingLoggingTab) { addLoggerTab(); @@ -210,7 +212,7 @@ public class Console extends JFrame { setLocationByPlatform(true); setSize(800, 600); addNotify(); - if(editorDatabaseDescriptor != null) { + if(debugger.isExecutionSupported()) { getFocusedEditorPane().adjustDefaultFocus(); } addWindowListener(new WindowAdapter() { @@ -231,7 +233,7 @@ public class Console extends JFrame { } if(editorTabbedPane != null) { for(int i=editorTabbedPane.getTabCount()-2; i>=0; i--) { - ((EditorPane)editorTabbedPane.getComponentAt(i)).closeConnection(); + ((EditorPane)editorTabbedPane.getComponentAt(i)).closeLastExecution(); } } } @@ -239,13 +241,13 @@ public class Console extends JFrame { private LoggerPane sqlLoggerPane; private void addLoggerTab() { - sqlLoggerPane = new LoggerPane(); + sqlLoggerPane = new LoggerPane(debugger); mainTabbedPane.addTab("Logger", sqlLoggerPane); } - private void addEditorTab(final DatabaseDescriptor databaseDescriptor) { + private void addEditorTab() { JPanel editorsPane = new JPanel(new BorderLayout()); - final String[] tableNames = getTableNames(databaseDescriptor); + final String[] tableNames = debugger.getTableNames(); final JList tableNamesJList = new JList(tableNames); tableNamesJList.addMouseListener(new MouseAdapter() { @Override @@ -265,7 +267,7 @@ public class Console extends JFrame { @Override public void stateChanged(ChangeEvent e) { if(!isAdjusting && editorTabbedPane.getSelectedIndex() == editorTabbedPane.getTabCount() - 1) { - addSQLEditorPane(databaseDescriptor); + addSQLEditorPane(); } } }); @@ -374,14 +376,21 @@ public class Console extends JFrame { tableNamePane.add(tableNameFilterTextField, BorderLayout.NORTH); tableNamePane.add(new JScrollPane(tableNamesJList), BorderLayout.CENTER); JSplitPane horizontalSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, tableNamePane, editorTabbedPane); - addSQLEditorPane(databaseDescriptor); + addSQLEditorPane(); editorsPane.add(horizontalSplitPane, BorderLayout.CENTER); mainTabbedPane.addTab("Editor", editorsPane); } public static void openConsole(DatabaseDescriptor databaseDescriptor, boolean isLoggingActive) { - Console sqlConsoleFrame = new Console(databaseDescriptor, true); + final LocalDebugger debugger = new LocalDebugger(databaseDescriptor); + Console sqlConsoleFrame = new Console(debugger, true); sqlConsoleFrame.setLoggingActive(isLoggingActive); + sqlConsoleFrame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + DebuggerRegistry.removeSqlQueryDebugger(debugger); + } + }); sqlConsoleFrame.setVisible(true); } @@ -396,10 +405,10 @@ public class Console extends JFrame { private boolean isAdjusting; private int contextCount = 1; - private void addSQLEditorPane(DatabaseDescriptor databaseDescriptor) { + private void addSQLEditorPane() { isAdjusting = true; int index = editorTabbedPane.getTabCount() - 1; - final EditorPane sqlEditorPane = new EditorPane(databaseDescriptor); + final EditorPane sqlEditorPane = new EditorPane(debugger); String title = "Context " + contextCount++; editorTabbedPane.insertTab(title, null, sqlEditorPane, null, index); final JPanel tabComponent = new JPanel(new BorderLayout()); @@ -417,7 +426,7 @@ public class Console extends JFrame { if(editorTabbedPane.getTabCount() > 2) { for(int i=editorTabbedPane.getTabCount()-1; i>=0; i--) { if(editorTabbedPane.getTabComponentAt(i) == tabComponent) { - ((EditorPane)editorTabbedPane.getComponentAt(i)).closeConnection(); + ((EditorPane)editorTabbedPane.getComponentAt(i)).closeLastExecution(); editorTabbedPane.removeTabAt(i); if(i == editorTabbedPane.getTabCount() - 1) { editorTabbedPane.setSelectedIndex(i - 1); @@ -458,25 +467,15 @@ public class Console extends JFrame { return (EditorPane)editorTabbedPane.getSelectedComponent(); } - private static String[] getTableNames(DatabaseDescriptor databaseDescriptor) { - List> tableList = databaseDescriptor.getSchema().getTables(); - List tableNameList = new ArrayList(); - for(Table table: tableList) { - String tableName = table.getName(); - tableNameList.add(tableName); - } - Collections.sort(tableNameList, String.CASE_INSENSITIVE_ORDER); - return tableNameList.toArray(new String[0]); - } - public static void main(String[] args) { if(args.length < 2) { System.out.println("Please specify IP and port of a running RemoteDebuggerServer"); System.out.println("Usage: Console "); return; } + final Debugger debugger; try { - new RemoteDebuggerClient(args[0], Integer.parseInt(args[1])); + debugger = new RemoteDebuggerClient(args[0], Integer.parseInt(args[1])); } catch(Exception e) { e.printStackTrace(); return; @@ -489,7 +488,7 @@ public class Console extends JFrame { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - Console sqlConsoleFrame = new Console(null, true); + Console sqlConsoleFrame = new Console(debugger, true); sqlConsoleFrame.setDefaultCloseOperation(EXIT_ON_CLOSE); sqlConsoleFrame.setVisible(true); } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/EditorPane.java b/jOOQ-console/src/main/java/org/jooq/debug/console/EditorPane.java index eca893b828..74ab233095 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/EditorPane.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/EditorPane.java @@ -53,32 +53,15 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.Reader; -import java.io.StringWriter; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.ResultSetMetaData; -import java.sql.SQLException; -import java.sql.Statement; import java.sql.Timestamp; -import java.sql.Types; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -121,10 +104,12 @@ import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; import javax.swing.text.BadLocationException; -import org.jooq.Field; -import org.jooq.Record; -import org.jooq.SQLDialect; -import org.jooq.Table; +import org.jooq.debug.Debugger; +import org.jooq.debug.StatementExecution; +import org.jooq.debug.StatementExecutionMessageResult; +import org.jooq.debug.StatementExecutionResult; +import org.jooq.debug.StatementExecutionResultSetResult; +import org.jooq.debug.StatementExecutor; import org.jooq.debug.console.misc.JTableX; import org.jooq.debug.console.misc.Utils; @@ -140,17 +125,18 @@ public class EditorPane extends JPanel { private boolean isUsingMaxRowCount = true; private JFormattedTextField displayedRowCountField; - private DatabaseDescriptor databaseDescriptor; private SqlTextArea editorTextArea; private JPanel southPanel = new JPanel(new BorderLayout()); private boolean isDBEditable; + private Debugger debugger; + private StatementExecutor lastStatementExecutor; private JButton startButton; private JButton stopButton; - EditorPane(DatabaseDescriptor databaseDescriptor) { + EditorPane(Debugger debugger) { super(new BorderLayout()); - this.databaseDescriptor = databaseDescriptor; - this.isDBEditable = !databaseDescriptor.isReadOnly(); + this.debugger = debugger; + this.isDBEditable = !debugger.isReadOnly(); setOpaque(false); JPanel northPanel = new JPanel(new BorderLayout()); northPanel.setOpaque(false); @@ -177,8 +163,7 @@ public class EditorPane extends JPanel { stopButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - evaluationThread = null; - closeConnection(); + closeLastExecution(); } }); northWestPanel.add(stopButton); @@ -225,10 +210,7 @@ public class EditorPane extends JPanel { new Thread("SQLConsole - Interruption") { @Override public void run() { - if(evaluationThread != null) { - evaluationThread = null; - closeConnection(); - } + closeLastExecution(); } }.start(); break; @@ -312,58 +294,19 @@ public class EditorPane extends JPanel { southPanel.repaint(); } - private static class TypeInfo { - - private String columnName; - private int precision; - private int scale; - private int nullable = ResultSetMetaData.columnNullableUnknown; - - TypeInfo(ResultSetMetaData metaData, int column) { - try { - columnName = metaData.getColumnTypeName(column); - precision = metaData.getPrecision(column); - scale = metaData.getScale(column); - nullable = metaData.isNullable(column); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(columnName); - if(precision != 0) { - sb.append(" (" + precision + (scale != 0? ", " + scale: "") + ")"); - } - if(nullable != ResultSetMetaData.columnNullableUnknown) { - sb.append(nullable == ResultSetMetaData.columnNoNulls? " not null": " null"); - } - return sb.toString(); - } - - } - - private volatile Connection conn; - private volatile Statement stmt; - - private volatile Thread evaluationThread; - private void evaluate_unrestricted(final String sql) { final int maxDisplayedRowCount = ((Number)displayedRowCountField.getValue()).intValue(); - evaluationThread = new Thread("SQLConsole - Evaluation") { + Thread evaluationThread = new Thread("SQLConsole - Evaluation") { @Override public void run() { evaluate_unrestricted_nothread(sql, maxDisplayedRowCount); - evaluationThread = null; } }; evaluationThread.start(); } private void evaluate_unrestricted_nothread(final String sql, final int maxDisplayedRowCount) { - closeConnection(); + closeLastExecution(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { @@ -372,194 +315,14 @@ public class EditorPane extends JPanel { stopButton.setToolTipText("Query started on " + Utils.formatDateTimeTZ(new Date())); } }); + StatementExecutor statementExecutor; + synchronized (debugger) { + statementExecutor = debugger.createStatementExecutor(sql, isUsingMaxRowCount? MAX_ROW_COUNT: Integer.MAX_VALUE, maxDisplayedRowCount); + lastStatementExecutor = statementExecutor; + } + StatementExecution statementExecution; try { - conn = databaseDescriptor.createConnection(); - stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - final long start = System.currentTimeMillis(); - if(evaluationThread != Thread.currentThread()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), "Interrupted by user after " + Utils.formatDuration(System.currentTimeMillis() - start), true); - } - }); - return; - } - boolean executeResult; - try { - executeResult = stmt.execute(sql); - } catch(SQLException e) { - if(evaluationThread != Thread.currentThread()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), "Interrupted by user after " + Utils.formatDuration(System.currentTimeMillis() - start), true); - } - }); - return; - } - throw e; - } - final long duration = System.currentTimeMillis() - start; - if(evaluationThread != Thread.currentThread()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), "Interrupted by user after " + Utils.formatDuration(duration), true); - } - }); - return; - } - do { - if(executeResult) { - final ResultSet rs = stmt.getResultSet(); - ResultSetMetaData metaData = rs.getMetaData(); - // The first column is the line count - final String[] columnNames = new String[metaData.getColumnCount() + 1]; - final int[] columnTypes = new int[columnNames.length]; - final TypeInfo[] typeInfos = new TypeInfo[columnNames.length]; - final Class[] columnClasses = new Class[columnNames.length]; - columnNames[0] = ""; - columnClasses[0] = Integer.class; - for(int i=1; i rowDataList = new ArrayList(); - int rowCount = 0; - long rsStart = System.currentTimeMillis(); - while(rs.next() && (!isUsingMaxRowCount || rowCount < MAX_ROW_COUNT)) { - if(evaluationThread != Thread.currentThread()) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), "Interrupted by user after " + Utils.formatDuration(duration), true); - } - }); - return; - } - rowCount++; - Object[] rowData = new Object[columnNames.length]; - rowData[0] = rowCount; - for(int i=1; i=0; ) { - stringWriter.write(chars, 0, count); - } - rowData[i] = stringWriter.toString(); - } else { - rowData[i] = null; - } - break; - } - case Types.BLOB: { - Blob blob = rs.getBlob(i); - if(blob != null) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] bytes = new byte[1024]; - InputStream in = new BufferedInputStream(blob.getBinaryStream()); - for(int count; (count=in.read(bytes))>=0; ) { - baos.write(bytes, 0, count); - } - rowData[i] = baos.toByteArray(); - } else { - rowData[i] = null; - } - break; - } - default: - Object object = rs.getObject(i); - if(object != null) { - String className = object.getClass().getName(); - if ("oracle.sql.TIMESTAMP".equals(className) || "oracle.sql.TIMESTAMPTZ".equals(className)) { - object = rs.getTimestamp(i); - } - // Probably something to do for oracle.sql.DATE - } - rowData[i] = object; - break; - } - } - if(rowCount <= maxDisplayedRowCount) { - rowDataList.add(rowData); - } else if(rowCount == maxDisplayedRowCount + 1) { - rowDataList.clear(); - } - } - final long rsDuration = System.currentTimeMillis() - rsStart; - final int rowCount_ = rowCount; - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - addResultTable(sql, duration, rs, columnNames, typeInfos, columnClasses, rowDataList, rowCount_, rsDuration, maxDisplayedRowCount); - } - }); - } else { - final int updateCount = stmt.getUpdateCount(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), Utils.formatDuration(duration) + "> " + updateCount + " row(s) affected.", false); - } - }); - } - if(databaseDescriptor.getSQLDialect() == SQLDialect.SQLSERVER) { - try { - executeResult = stmt.getMoreResults(Statement.KEEP_CURRENT_RESULT); - } catch(Exception e) { - executeResult = stmt.getMoreResults(); - } - } else { - executeResult = false; - } - } while(executeResult || stmt.getUpdateCount() != -1); - } catch(Exception e) { - StringWriter stringWriter = new StringWriter(); - e.printStackTrace(new PrintWriter(stringWriter)); - final String message = stringWriter.toString(); - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setMessage(addResultPane(), message, true); - } - }); + statementExecution = statementExecutor.execute(); } finally { SwingUtilities.invokeLater(new Runnable() { @Override @@ -569,86 +332,40 @@ public class EditorPane extends JPanel { stopButton.setToolTipText(null); } }); - if(!isDBEditable) { - closeConnection(); - } } + final StatementExecutionResult[] results = statementExecution.getResults(); + final long executionDuration = statementExecution.getExecutionDuration(); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + for(StatementExecutionResult result: results) { + if(result instanceof StatementExecutionMessageResult) { + StatementExecutionMessageResult messageResult = (StatementExecutionMessageResult)result; + setMessage(addResultPane(), messageResult.getMessage(), messageResult.isError()); + } else if(result instanceof StatementExecutionResultSetResult) { + addResultTable(sql, executionDuration, (StatementExecutionResultSetResult)result); + } else { + throw new IllegalStateException("Unknown result class: " + result.getClass().getName()); + } + } + } + }); } - private void addResultTable(final String sql, long duration, final ResultSet rs, final String[] columnNames, final TypeInfo[] typeInfos, final Class[] columnClasses, final List rowDataList, int rowCount, long rsDuration, int maxDisplayedRowCount) { + private void addResultTable(final String sql, long duration, final StatementExecutionResultSetResult resultSetResult) { + int rowCount = resultSetResult.getRowCount(); JPanel resultPane = addResultPane(); final JLabel label = new JLabel(" " + rowCount + " rows"); FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT, 0, 0); flowLayout.setAlignOnBaseline(true); JPanel statusCountPane = new JPanel(flowLayout); - if(rowCount <= maxDisplayedRowCount) { - final JTableX table = new JTableX(new AbstractTableModel() { - @Override - public String getColumnName(int column) { - return columnNames[column].toString(); - } - @Override - public int getRowCount() { - return rowDataList.size(); - } - @Override - public int getColumnCount() { - return columnNames.length; - } - @Override - public Object getValueAt(int row, int col) { - return rowDataList.get(row)[col]; - } - @Override - public void setValueAt(Object o, int row, int col) { - if(Utils.equals(o, rowDataList.get(row)[col])) { - return; - } - int dbRow = (Integer)rowDataList.get(row)[0]; - try { - rs.absolute(dbRow); - rs.updateObject(col, o); - rs.updateRow(); - rowDataList.get(row)[col] = o; - } catch (SQLException e) { - e.printStackTrace(); - try { - rs.cancelRowUpdates(); - } catch (SQLException ex) { - ex.printStackTrace(); - } - } - } - @Override - public boolean isCellEditable(int rowIndex, int columnIndex) { - try { - if(rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE) { - return isDBEditable && columnIndex > 0; - } - } catch (SQLException e) { - } - return false; - } - @Override - public Class getColumnClass(int columnIndex) { - return columnClasses[columnIndex]; - } - }) { - @Override - public TableCellEditor getCellEditor(int row, int column) { - TableCellEditor editor = super.getCellEditor(row, column); - if(editor instanceof DefaultCellEditor) { - DefaultCellEditor defaultEditor = (DefaultCellEditor) editor; - defaultEditor.setClickCountToStart(2); - } - return editor; - } - }; + if(rowCount <= resultSetResult.getRetainParsedRSDataRowCountThreshold()) { + final JTableX table = new ResultTable(resultSetResult); JTableHeader tableHeader = new JTableHeader(table.getColumnModel()) { @Override public String getToolTipText(MouseEvent e) { int col = getTable().convertColumnIndexToModel(columnAtPoint(e.getPoint())); - return col == 0? null: typeInfos[col].toString(); + return col == 0? null: resultSetResult.getTypeInfos()[col - 1].toString(); } }; ToolTipManager.sharedInstance().registerComponent(tableHeader); @@ -716,7 +433,7 @@ public class EditorPane extends JPanel { @Override public void valueChanged(ListSelectionEvent e) { int selectedRowCount = table.getSelectedRowCount(); - label.setText(" " + rowDataList.size() + " rows" + (selectedRowCount == 0? "": " - " + selectedRowCount + " selected rows")); + label.setText(" " + resultSetResult.getRowData().length + " rows" + (selectedRowCount == 0? "": " - " + selectedRowCount + " selected rows")); } }); table.addMouseListener(new MouseAdapter() { @@ -737,14 +454,7 @@ public class EditorPane extends JPanel { if(!e.isPopupTrigger()) { return; } - boolean isEditable = false; - try { - if(rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE) { - isEditable = isDBEditable; - } - } catch (SQLException ex) { - isEditable = false; - } + boolean isEditable = resultSetResult.isEditable(); JPopupMenu menu = new JPopupMenu(); int selectedRowCount = table.getSelectedRowCount(); int selectedColumnCount = table.getSelectedColumnCount(); @@ -778,14 +488,9 @@ public class EditorPane extends JPanel { Arrays.sort(selectedRows); for(int i=selectedRows.length-1; i>=0; i--) { int row = selectedRows[i]; - int dbRow = (Integer)rowDataList.get(row)[0]; - try { - rs.absolute(dbRow); - rs.deleteRow(); - rowDataList.remove(row); + boolean isSuccess = resultSetResult.deleteRow(row); + if(isSuccess) { ((AbstractTableModel)table.getModel()).fireTableRowsDeleted(row, row); - } catch (SQLException ex) { - ex.printStackTrace(); } } } @@ -868,7 +573,7 @@ public class EditorPane extends JPanel { } statusCountPane.add(label); southPanel.add(statusCountPane, BorderLayout.WEST); - southPanel.add(new JLabel(Utils.formatDuration(duration) + " - " + Utils.formatDuration(rsDuration)), BorderLayout.EAST); + southPanel.add(new JLabel(Utils.formatDuration(duration) + " - " + Utils.formatDuration(resultSetResult.getResultSetParsingDuration())), BorderLayout.EAST); resultPane.add(southPanel, BorderLayout.SOUTH); southPanel.setToolTipText(sql); resultPane.revalidate(); @@ -924,24 +629,12 @@ public class EditorPane extends JPanel { return resultPane; } - void closeConnection() { - if (conn != null) { - if (stmt != null) { - try { - stmt.cancel(); - } catch (Exception e) { - } - try { - stmt.close(); - } catch (Exception e) { - } + void closeLastExecution() { + synchronized (debugger) { + if(lastStatementExecutor != null) { + lastStatementExecutor.stopExecution(); + lastStatementExecutor = null; } - stmt = null; - try { - conn.close(); - } catch (Exception e) { - } - conn = null; } } @@ -1097,6 +790,66 @@ public class EditorPane extends JPanel { return wordStart; } + private final class ResultTableModel extends AbstractTableModel { + + private final StatementExecutionResultSetResult resultSetResult; + + private ResultTableModel(StatementExecutionResultSetResult resultSetResult) { + this.resultSetResult = resultSetResult; + } + + @Override + public String getColumnName(int column) { + return column == 0? "": resultSetResult.getColumnNames()[column - 1].toString(); + } + + @Override + public int getRowCount() { + return resultSetResult.getRowData().length; + } + + @Override + public int getColumnCount() { + return resultSetResult.getColumnNames().length + 1; + } + + @Override + public Object getValueAt(int row, int col) { + return col == 0? row + 1: resultSetResult.getRowData()[row][col - 1]; + } + + @Override + public void setValueAt(Object o, int row, int col) { + resultSetResult.setValueAt(o, row, col - 1); + } + + @Override + public boolean isCellEditable(int row, int col) { + return col > 0 && resultSetResult.isEditable(); + } + + @Override + public Class getColumnClass(int col) { + return col == 0? Integer.class: resultSetResult.getColumnClasses()[col - 1]; + } + } + + private final class ResultTable extends JTableX { + private ResultTable(StatementExecutionResultSetResult resultSetResult) { + super(new ResultTableModel(resultSetResult)); + } + + @Override + public TableCellEditor getCellEditor(int row, int column) { + TableCellEditor editor = super.getCellEditor(row, column); + if(editor instanceof DefaultCellEditor) { + DefaultCellEditor defaultEditor = (DefaultCellEditor) editor; + defaultEditor.setClickCountToStart(2); + } + return editor; + } + } + private static enum KeyWordType { DYNAMIC_STATEMENT, TABLE, @@ -1124,10 +877,10 @@ public class EditorPane extends JPanel { List candidateList = new ArrayList(); // Here can add more candidates depending on magic word start. if(candidateList.isEmpty()) { - for(String s: getTableNames()) { + for(String s: debugger.getTableNames()) { candidateList.add(new CompletionCandidate(KeyWordType.TABLE, s)); } - for(String s: getTableColumnNames()) { + for(String s: debugger.getTableColumnNames()) { candidateList.add(new CompletionCandidate(KeyWordType.TABLE_COlUMN, s)); } for(String s: new String[] { @@ -1175,29 +928,6 @@ public class EditorPane extends JPanel { return filteredCompletionCandidateList.toArray(new CompletionCandidate[0]); } - private String[] getTableNames() { - List tableNameList = new ArrayList(); - for(Table table: databaseDescriptor.getSchema().getTables()) { - String tableName = table.getName(); - tableNameList.add(tableName); - } - Collections.sort(tableNameList, String.CASE_INSENSITIVE_ORDER); - return tableNameList.toArray(new String[0]); - } - - private String[] getTableColumnNames() { - Set columnNameSet = new HashSet(); - for(Table table: databaseDescriptor.getSchema().getTables()) { - for(Field field: table.getFields()) { - String columnName = field.getName(); - columnNameSet.add(columnName); - } - } - String[] columnNames = columnNameSet.toArray(new String[0]); - Arrays.sort(columnNames, String.CASE_INSENSITIVE_ORDER); - return columnNames; - } - public void adjustDefaultFocus() { editorTextArea.requestFocusInWindow(); } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/LoggerPane.java b/jOOQ-console/src/main/java/org/jooq/debug/console/LoggerPane.java index 080bc3525e..5845e1d048 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/LoggerPane.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/LoggerPane.java @@ -97,9 +97,9 @@ import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import org.jooq.debug.Debugger; -import org.jooq.debug.DebuggerData; -import org.jooq.debug.DebuggerRegistry; -import org.jooq.debug.DebuggerResultSetData; +import org.jooq.debug.LoggingListener; +import org.jooq.debug.QueryLoggingData; +import org.jooq.debug.ResultSetLoggingData; import org.jooq.debug.SqlQueryType; import org.jooq.debug.console.misc.JTableX; import org.jooq.debug.console.misc.RichTextTransferable; @@ -135,7 +135,7 @@ public class LoggerPane extends JPanel { private final ImageIcon OTHER_ICON = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlOther16.png")); private final ImageIcon SELECT_ICON = new ImageIcon(getClass().getResource("/org/jooq/debug/console/resources/SqlSelect16.png")); - private Debugger sqlQueryDebugger; + private Debugger debugger; private JTableX table; private SqlTextArea textArea; private JLabel loggerStatusLabel; @@ -147,8 +147,9 @@ public class LoggerPane extends JPanel { private boolean isOtherQueryTypeDisplayed = true; private boolean isScrollLocked; - public LoggerPane() { + public LoggerPane(Debugger debugger) { super(new BorderLayout()); + this.debugger = debugger; setOpaque(false); JPanel loggerHeaderPanel = new JPanel(new BorderLayout()); loggerHeaderPanel.setOpaque(false); @@ -359,15 +360,15 @@ public class LoggerPane extends JPanel { return duration < 0? null: duration; } case COLUMN_RS_LIFETIME: { - DebuggerResultSetData rsData = queryDebuggingInfo.getSqlQueryDebuggerResultSetData(); + ResultSetLoggingData rsData = queryDebuggingInfo.getResultSetLoggingData(); return rsData == null? null: rsData.getLifeTime(); } case COLUMN_RS_READ: { - DebuggerResultSetData rsData = queryDebuggingInfo.getSqlQueryDebuggerResultSetData(); + ResultSetLoggingData rsData = queryDebuggingInfo.getResultSetLoggingData(); return rsData == null? null: rsData.getReadCount(); } case COLUMN_RS_READ_ROWS: { - DebuggerResultSetData rsData = queryDebuggingInfo.getSqlQueryDebuggerResultSetData(); + ResultSetLoggingData rsData = queryDebuggingInfo.getResultSetLoggingData(); return rsData == null? null: rsData.getReadRows(); } case COLUMN_DUPLICATION_COUNT: { @@ -732,36 +733,36 @@ public class LoggerPane extends JPanel { private static class QueryDebuggingInfo { private long timestamp; - private DebuggerData sqlQueryDebuggerData; + private QueryLoggingData queryLoggingData; private Throwable throwable; private int duplicationCount; - public QueryDebuggingInfo(long timestamp, DebuggerData sqlQueryDebuggerData) { + public QueryDebuggingInfo(long timestamp, QueryLoggingData queryLoggingData) { this.timestamp = timestamp; - this.sqlQueryDebuggerData = sqlQueryDebuggerData; + this.queryLoggingData = queryLoggingData; this.throwable = new Exception("Statement Stack trace"); - throwable.setStackTrace(sqlQueryDebuggerData.getCallerStackTraceElements()); + throwable.setStackTrace(queryLoggingData.getCallerStackTraceElements()); } public long getTimestamp() { return timestamp; } - public DebuggerData getSqlQueryDebuggerData() { - return sqlQueryDebuggerData; + public QueryLoggingData getQueryLoggingData() { + return queryLoggingData; } public Long getPrepardeStatementPreparationDuration() { - return sqlQueryDebuggerData.getPreparedStatementPreparationDuration(); + return queryLoggingData.getPreparedStatementPreparationDuration(); } public Long getPrepardeStatementBindingDuration() { - return sqlQueryDebuggerData.getPreparedStatementBindingDuration(); + return queryLoggingData.getPreparedStatementBindingDuration(); } public long getExecutionDuration() { - return sqlQueryDebuggerData.getExecutionDuration(); + return queryLoggingData.getExecutionDuration(); } public SqlQueryType getQueryType() { - return sqlQueryDebuggerData.getQueryType(); + return queryLoggingData.getQueryType(); } public String[] getQueries() { - String parameterDescription = sqlQueryDebuggerData.getParameterDescription(); - String[] queries = sqlQueryDebuggerData.getQueries(); + String parameterDescription = queryLoggingData.getParameterDescription(); + String[] queries = queryLoggingData.getQueries(); if(parameterDescription != null) { return new String[] {queries[0] + " -> " + parameterDescription}; } @@ -771,10 +772,10 @@ public class LoggerPane extends JPanel { return throwable; } public String getThreadName() { - return sqlQueryDebuggerData.getThreadName(); + return queryLoggingData.getThreadName(); } public long getThreadId() { - return sqlQueryDebuggerData.getThreadID(); + return queryLoggingData.getThreadID(); } public void setDuplicationCount(int duplicationCount) { this.duplicationCount = duplicationCount; @@ -782,12 +783,12 @@ public class LoggerPane extends JPanel { public int getDuplicationCount() { return duplicationCount; } - private DebuggerResultSetData sqlQueryDebuggerResultSetData; - public void setSqlQueryDebuggerResultSetData(DebuggerResultSetData sqlQueryDebuggerResultSetData) { - this.sqlQueryDebuggerResultSetData = sqlQueryDebuggerResultSetData; + private ResultSetLoggingData resultSetLoggingData; + public void setResultSetLoggingData(ResultSetLoggingData resultSetLoggingData) { + this.resultSetLoggingData = resultSetLoggingData; } - public DebuggerResultSetData getSqlQueryDebuggerResultSetData() { - return sqlQueryDebuggerResultSetData; + public ResultSetLoggingData getResultSetLoggingData() { + return resultSetLoggingData; } private int displayedRow = -1; public int getDisplayedRow() { @@ -807,15 +808,11 @@ public class LoggerPane extends JPanel { this.isLogging = isLogging; loggerOnButton.setVisible(!isLogging); loggerOffButton.setVisible(isLogging); - if(sqlQueryDebugger != null) { - DebuggerRegistry.removeSqlQueryDebugger(sqlQueryDebugger); - sqlQueryDebugger = null; - } if(isLogging) { - sqlQueryDebugger = new Debugger() { + LoggingListener loggingListener = new LoggingListener() { @Override - public void debugQueries(DebuggerData sqlQueryDebuggerData) { - debugQueries(new QueryDebuggingInfo(System.currentTimeMillis(), sqlQueryDebuggerData)); + public void logQueries(QueryLoggingData queryLoggingData) { + debugQueries(new QueryDebuggingInfo(System.currentTimeMillis(), queryLoggingData)); } public void debugQueries(final QueryDebuggingInfo queryDebuggingInfo) { if(!SwingUtilities.isEventDispatchThread()) { @@ -830,20 +827,20 @@ public class LoggerPane extends JPanel { addRow(queryDebuggingInfo); } @Override - public void debugResultSet(final int sqlQueryDebuggerDataID, final DebuggerResultSetData sqlQueryDebuggerResultSetData) { + public void logResultSet(final int queryLoggingDataID, final ResultSetLoggingData resultSetLoggingData) { if(!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - debugResultSet(sqlQueryDebuggerDataID, sqlQueryDebuggerResultSetData); + logResultSet(queryLoggingDataID, resultSetLoggingData); } }); return; } for(int i=queryDebuggingInfoList.size()-1; i>=0; i--) { QueryDebuggingInfo queryDebuggingInfo = queryDebuggingInfoList.get(i); - if(queryDebuggingInfo.getSqlQueryDebuggerData().getID() == sqlQueryDebuggerDataID) { - queryDebuggingInfo.setSqlQueryDebuggerResultSetData(sqlQueryDebuggerResultSetData); + if(queryDebuggingInfo.getQueryLoggingData().getID() == queryLoggingDataID) { + queryDebuggingInfo.setResultSetLoggingData(resultSetLoggingData); XTableColumnModel columnModel = (XTableColumnModel)table.getColumnModel(); boolean isResultSetDataShown = columnModel.isColumnVisible(columnModel.getColumnByModelIndex(COLUMN_RS_LIFETIME)); if(isResultSetDataShown) { @@ -854,7 +851,9 @@ public class LoggerPane extends JPanel { } } }; - DebuggerRegistry.addSqlQueryDebugger(sqlQueryDebugger); + debugger.setLoggingListener(loggingListener); + } else { + debugger.setLoggingListener(null); } } @@ -909,7 +908,7 @@ public class LoggerPane extends JPanel { "Stack trace" + "\n"); for(QueryDebuggingInfo queryDebuggingInfo: queryDebuggingInfos) { - DebuggerResultSetData resultSetData = queryDebuggingInfo.getSqlQueryDebuggerResultSetData(); + ResultSetLoggingData resultSetData = queryDebuggingInfo.getResultSetLoggingData(); htmlSB.append("\n"); htmlSB.append(""); htmlSB.append(queryDebuggingInfo.getQueryType()); diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugQueriesMessage.java b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugQueriesMessage.java index 53ba103cdf..6a28798666 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugQueriesMessage.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugQueriesMessage.java @@ -36,7 +36,7 @@ */ package org.jooq.debug.console.remote; -import org.jooq.debug.DebuggerData; +import org.jooq.debug.QueryLoggingData; /** * @author Christopher Deckers @@ -44,13 +44,13 @@ import org.jooq.debug.DebuggerData; @SuppressWarnings("serial") public class ClientDebugQueriesMessage implements Message { - private DebuggerData sqlQueryDebuggerData; + private QueryLoggingData sqlQueryDebuggerData; - public ClientDebugQueriesMessage(DebuggerData sqlQueryDebuggerData) { + public ClientDebugQueriesMessage(QueryLoggingData sqlQueryDebuggerData) { this.sqlQueryDebuggerData = sqlQueryDebuggerData; } - public DebuggerData getSqlQueryDebuggerData() { + public QueryLoggingData getSqlQueryDebuggerData() { return sqlQueryDebuggerData; } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugResultSetMessage.java b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugResultSetMessage.java index 200e89022f..1fc49cc6bf 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugResultSetMessage.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientDebugResultSetMessage.java @@ -36,7 +36,7 @@ */ package org.jooq.debug.console.remote; -import org.jooq.debug.DebuggerResultSetData; +import org.jooq.debug.ResultSetLoggingData; /** * @author Christopher Deckers @@ -45,9 +45,9 @@ import org.jooq.debug.DebuggerResultSetData; public class ClientDebugResultSetMessage implements Message { private int sqlQueryDebuggerDataID; - private DebuggerResultSetData sqlQueryDebuggerResultSetData; + private ResultSetLoggingData sqlQueryDebuggerResultSetData; - public ClientDebugResultSetMessage(int sqlQueryDebuggerDataID, DebuggerResultSetData sqlQueryDebuggerData) { + public ClientDebugResultSetMessage(int sqlQueryDebuggerDataID, ResultSetLoggingData sqlQueryDebuggerData) { this.sqlQueryDebuggerDataID = sqlQueryDebuggerDataID; this.sqlQueryDebuggerResultSetData = sqlQueryDebuggerData; } @@ -56,7 +56,7 @@ public class ClientDebugResultSetMessage implements Message { return sqlQueryDebuggerDataID; } - public DebuggerResultSetData getSqlQueryDebuggerResultSetData() { + public ResultSetLoggingData getSqlQueryDebuggerResultSetData() { return sqlQueryDebuggerResultSetData; } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerClient.java b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerClient.java index 363ac1b91b..01c03475dd 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerClient.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerClient.java @@ -44,61 +44,45 @@ import java.io.ObjectOutputStream; import java.net.Socket; import org.jooq.debug.Debugger; -import org.jooq.debug.DebuggerData; -import org.jooq.debug.DebuggerRegistry; -import org.jooq.debug.DebuggerRegistryListener; -import org.jooq.debug.DebuggerResultSetData; +import org.jooq.debug.LoggingListener; +import org.jooq.debug.QueryLoggingData; +import org.jooq.debug.ResultSetLoggingData; +import org.jooq.debug.StatementExecutor; /** * @author Christopher Deckers */ -public class RemoteDebuggerClient { +public class RemoteDebuggerClient implements Debugger { private Socket socket; + private ObjectOutputStream out; + private final Object LOCK = new Object(); public RemoteDebuggerClient(String ip, int port) throws Exception { socket = new Socket(ip, port); + out = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); Thread thread = new Thread("SQL Remote Debugger Client on port " + port) { @Override public void run() { - DebuggerRegistryListener debuggerRegisterListener = null; - try { - final ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); - debuggerRegisterListener = new DebuggerRegistryListener() { - @Override - public void notifyDebuggerListenersModified() { - try { - boolean isLogging = !DebuggerRegistry.getDebuggerList().isEmpty(); - out.writeObject(new ServerLoggingActivationMessage(isLogging)); - out.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; - DebuggerRegistry.addDebuggerRegisterListener(debuggerRegisterListener); - ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); - for(Message o; (o=(Message)in.readObject()) != null; ) { - if(o instanceof ClientDebugQueriesMessage) { - DebuggerData sqlQueryDebuggerData = ((ClientDebugQueriesMessage) o).getSqlQueryDebuggerData(); - for(Debugger debugger: DebuggerRegistry.getDebuggerList()) { - debugger.debugQueries(sqlQueryDebuggerData); - } - } else if(o instanceof ClientDebugResultSetMessage) { - ClientDebugResultSetMessage m = (ClientDebugResultSetMessage) o; - int sqlQueryDebuggerDataID = m.getSqlQueryDebuggerDataID(); - DebuggerResultSetData clientDebugResultSetData = m.getSqlQueryDebuggerResultSetData(); - for(Debugger debugger: DebuggerRegistry.getDebuggerList()) { - debugger.debugResultSet(sqlQueryDebuggerDataID, clientDebugResultSetData); - } - } - } + try { + ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); + for(Message o; (o=(Message)in.readObject()) != null; ) { + if(o instanceof ClientDebugQueriesMessage) { + if(loggingListener != null) { + QueryLoggingData sqlQueryDebuggerData = ((ClientDebugQueriesMessage) o).getSqlQueryDebuggerData(); + loggingListener.logQueries(sqlQueryDebuggerData); + } + } else if(o instanceof ClientDebugResultSetMessage) { + if(loggingListener != null) { + ClientDebugResultSetMessage m = (ClientDebugResultSetMessage) o; + int sqlQueryDebuggerDataID = m.getSqlQueryDebuggerDataID(); + ResultSetLoggingData clientDebugResultSetData = m.getSqlQueryDebuggerResultSetData(); + loggingListener.logResultSet(sqlQueryDebuggerDataID, clientDebugResultSetData); + } + } + } } catch(Exception e) { e.printStackTrace(); - } finally { - if(debuggerRegisterListener != null) { - DebuggerRegistry.removeDebuggerRegisterListener(debuggerRegisterListener); - } } } }; @@ -106,4 +90,54 @@ public class RemoteDebuggerClient { thread.start(); } + private LoggingListener loggingListener; + + @Override + public void setLoggingListener(LoggingListener loggingListener) { + this.loggingListener = loggingListener; + try { + synchronized (LOCK) { + out.writeObject(new ServerLoggingActivationMessage(loggingListener != null)); + out.flush(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public LoggingListener getLoggingListener() { + return loggingListener; + } + + @Override + public boolean isExecutionSupported() { + // TODO: implement + return false; + } + + @Override + public StatementExecutor createStatementExecutor(String sql, int maxRSRowsParsing, int retainParsedRSDataRowCountThreshold) { + // TODO: implement + return null; + } + + @Override + public String[] getTableNames() { + // TODO: implement + return null; + } + + @Override + public String[] getTableColumnNames() { + // TODO: implement + return null; + } + + @Override + public boolean isReadOnly() { + // TODO: implement + return false; + } + } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerServer.java b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerServer.java index 72f65243ed..d0d1416c5d 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerServer.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/RemoteDebuggerServer.java @@ -45,9 +45,11 @@ import java.net.ServerSocket; import java.net.Socket; import org.jooq.debug.Debugger; -import org.jooq.debug.DebuggerData; import org.jooq.debug.DebuggerRegistry; -import org.jooq.debug.DebuggerResultSetData; +import org.jooq.debug.LocalDebugger; +import org.jooq.debug.LoggingListener; +import org.jooq.debug.QueryLoggingData; +import org.jooq.debug.ResultSetLoggingData; /** * @author Christopher Deckers @@ -88,38 +90,40 @@ public class RemoteDebuggerServer { Thread clientThread = new Thread("SQL Remote Debugger Server on port " + port) { @Override public void run() { - Debugger sqlQueryDebugger = null; + Debugger debugger = null; boolean isLogging = false; try { ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(socket.getInputStream())); final ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(socket.getOutputStream())); - sqlQueryDebugger = new Debugger() { - @Override - public synchronized void debugQueries(DebuggerData sqlQueryDebuggerData) { - try { - out.writeObject(new ClientDebugQueriesMessage(sqlQueryDebuggerData)); - out.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - @Override - public synchronized void debugResultSet(int sqlQueryDebuggerDataID, DebuggerResultSetData sqlQueryDebuggerResultSetData) { - try { - out.writeObject(new ClientDebugResultSetMessage(sqlQueryDebuggerDataID, sqlQueryDebuggerResultSetData)); - out.flush(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; + // TODO: find how to pass a database descriptor for remote edition. + debugger = new LocalDebugger(null); + DebuggerRegistry.addSqlQueryDebugger(debugger); for(Message o; (o=(Message)in.readObject()) != null; ) { if(o instanceof ServerLoggingActivationMessage) { isLogging = ((ServerLoggingActivationMessage) o).isLogging(); if(isLogging) { - DebuggerRegistry.addSqlQueryDebugger(sqlQueryDebugger); + debugger.setLoggingListener(new LoggingListener() { + @Override + public void logQueries(QueryLoggingData queryLoggingData) { + try { + out.writeObject(new ClientDebugQueriesMessage(queryLoggingData)); + out.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + @Override + public void logResultSet(int sqlQueryDebuggerDataID, ResultSetLoggingData resultSetLoggingData) { + try { + out.writeObject(new ClientDebugResultSetMessage(sqlQueryDebuggerDataID, resultSetLoggingData)); + out.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); } else { - DebuggerRegistry.removeSqlQueryDebugger(sqlQueryDebugger); + debugger.setLoggingListener(null); } } } @@ -128,8 +132,8 @@ public class RemoteDebuggerServer { e.printStackTrace(); } } finally { - if(sqlQueryDebugger != null) { - DebuggerRegistry.removeSqlQueryDebugger(sqlQueryDebugger); + if(debugger != null) { + DebuggerRegistry.removeSqlQueryDebugger(debugger); } } }