[#1249] Add breakpoint capability to jOOQ Console - Initial breakpoint

code structure.
This commit is contained in:
Chrriis 2012-05-09 23:25:33 +02:00
parent d11e54a33e
commit b97d23fd0f
40 changed files with 1896 additions and 89 deletions

View File

@ -0,0 +1,92 @@
/**
* 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.Serializable;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class Breakpoint implements Serializable {
private int id;
private StatementMatcher statementMatcher;
// private Integer hitCount;
private boolean isBreaking;
private StatementProcessor beforeExecutionProcessor;
private StatementProcessor replacementExecutionProcessor;
private StatementProcessor afterExecutionProcessor;
public Breakpoint(int id, StatementMatcher statementMatcher, boolean isBreaking, StatementProcessor beforeExecutionProcessor, StatementProcessor replacementExecutionProcessor, StatementProcessor afterExecutionProcessor) {
this.id = id;
this.statementMatcher = statementMatcher;
this.isBreaking = isBreaking;
this.beforeExecutionProcessor = beforeExecutionProcessor;
this.replacementExecutionProcessor = replacementExecutionProcessor;
this.afterExecutionProcessor = afterExecutionProcessor;
}
public int getID() {
return id;
}
public StatementMatcher getStatementMatcher() {
return statementMatcher;
}
public boolean matches(StatementInfo statementInfo) {
return statementMatcher != null && statementMatcher.matches(statementInfo);
}
public boolean isBreaking() {
return isBreaking;
}
public StatementProcessor getBeforeExecutionProcessor() {
return beforeExecutionProcessor;
}
public StatementProcessor getReplacementExecutionProcessor() {
return replacementExecutionProcessor;
}
public StatementProcessor getAfterExecutionProcessor() {
return afterExecutionProcessor;
}
}

View File

@ -0,0 +1,63 @@
/**
* 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.Serializable;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class BreakpointAfterExecutionHit implements Serializable {
private int breakpointID;
private String sql;
public BreakpointAfterExecutionHit(int breakpointID, String sql) {
this.breakpointID = breakpointID;
this.sql = sql;
}
public int getBreakpointID() {
return breakpointID;
}
public String getSql() {
return sql;
}
}

View File

@ -0,0 +1,84 @@
/**
* 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.Serializable;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class BreakpointBeforeExecutionHit implements Serializable {
public static enum ExecutionType {
STEP_THROUGH,
RUN_OVER,
RUN,
}
private Integer breakpointID;
private String sql;
public BreakpointBeforeExecutionHit(int breakpointID, String sql) {
this.breakpointID = breakpointID;
this.sql = sql;
}
/**
* @return null if the breakpoint was processed and contains an execution type.
*/
public Integer getBreakpointID() {
return breakpointID;
}
public String getSql() {
return sql;
}
private ExecutionType executionType = ExecutionType.RUN;
public void setExecutionType(ExecutionType executionType, String sql) {
this.breakpointID = null;
this.executionType = executionType;
this.sql = sql;
}
public ExecutionType getExecutionType() {
return executionType;
}
}

View File

@ -0,0 +1,48 @@
/**
* 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 BreakpointHitHandler {
public void processBreakpointBeforeExecutionHit(BreakpointBeforeExecutionHit breakpointHit);
public void processBreakpointAfterExecutionHit(BreakpointAfterExecutionHit breakpointHit);
}

View File

@ -39,20 +39,26 @@ package org.jooq.debug;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import org.jooq.ExecuteContext;
import org.jooq.ExecuteType;
import org.jooq.debug.BreakpointBeforeExecutionHit.ExecutionType;
import org.jooq.impl.DefaultExecuteListener;
import org.jooq.impl.Factory;
/**
* @author Christopher Deckers
*/
public class DebugListener extends DefaultExecuteListener {
private boolean isLogging;
private boolean hasDebuggers;
@Override
public void renderStart(ExecuteContext ctx) {
isLogging = !DebuggerRegistry.getDebuggerList().isEmpty();
hasDebuggers = !DebuggerRegistry.getDebuggerList().isEmpty();
startPreparationTime = 0;
aggregatedPreparationDuration = 0;
startBindTime = 0;
@ -66,7 +72,7 @@ public class DebugListener extends DefaultExecuteListener {
@Override
public void prepareStart(ExecuteContext ctx) {
if(!isLogging) {
if(!hasDebuggers) {
return;
}
startPreparationTime = System.currentTimeMillis();
@ -74,7 +80,7 @@ public class DebugListener extends DefaultExecuteListener {
@Override
public void prepareEnd(ExecuteContext ctx) {
if(!isLogging) {
if(!hasDebuggers) {
return;
}
aggregatedPreparationDuration += System.currentTimeMillis() - startPreparationTime;
@ -91,7 +97,7 @@ public class DebugListener extends DefaultExecuteListener {
@Override
public void bindStart(ExecuteContext ctx) {
if(!isLogging) {
if(!hasDebuggers) {
return;
}
startBindTime = System.currentTimeMillis();
@ -99,7 +105,7 @@ public class DebugListener extends DefaultExecuteListener {
@Override
public void bindEnd(ExecuteContext ctx) {
if(!isLogging) {
if(!hasDebuggers) {
return;
}
endBindTime = System.currentTimeMillis();
@ -107,80 +113,241 @@ public class DebugListener extends DefaultExecuteListener {
private long startExecutionTime;
private long endExecutionTime;
private String matchingSQL;
private String effectiveSQL;
private Breakpoint matchingBreakpoint;
private BreakpointHitHandler matchingBreakpointHitHandler;
@Override
public void executeStart(ExecuteContext ctx) {
if(!isLogging) {
return;
}
BreakpointHitHandler breakpointHitHandler = null;
List<Debugger> debuggerList = DebuggerRegistry.getDebuggerList();
if(!debuggerList.isEmpty()) {
StatementInfo statementInfo = null;
bp: for(Debugger debugger: debuggerList) {
Breakpoint[] breakpoints = debugger.getBreakpoints();
if(breakpoints != null) {
for(Breakpoint breakpoint: breakpoints) {
if(statementInfo == null) {
String[] sql = ctx.batchSQL();
SqlQueryType sqlQueryType = SqlQueryType.detectType(sql[0]);
String parameterDescription = null;
if(sql.length == 1) {
matchingSQL = sql[0];
PreparedStatement statement = ctx.statement();
if(statement instanceof UsageTrackingPreparedStatement) {
parameterDescription = ((UsageTrackingPreparedStatement) statement).getParameterDescription();
}
} else {
StringBuilder sb = new StringBuilder();
for(int i=0; i<sql.length; i++) {
if(i > 0) {
sb.append('\n');
}
sb.append(sql[i]);
}
matchingSQL = sb.toString();
}
statementInfo = new StatementInfo(sqlQueryType, sql, parameterDescription);
}
if(breakpoint.matches(statementInfo)) {
matchingBreakpoint = breakpoint;
if(breakpoint.isBreaking()) {
breakpointHitHandler = debugger.getBreakpointHitHandler();
}
break bp;
}
}
}
}
}
// We consider raw SQL (not the parameters). If we want to match on parameters, this should be a separate matcher.
// For batched execution of in-lined statements, we aggregate the statements as a multiple-line one for matching purposes.
if(matchingBreakpoint != null) {
StatementProcessor beforeExecutionProcessor = matchingBreakpoint.getBeforeExecutionProcessor();
if(beforeExecutionProcessor != null) {
String sql = beforeExecutionProcessor.processSQL(matchingSQL);
long subStartExecutionTime = System.currentTimeMillis();
executeSQL(ctx, sql);
long subEndExecutionTime = System.currentTimeMillis();
// Log result of pre-processing.
for(Debugger listener: debuggerList) {
LoggingListener loggingListener = listener.getLoggingListener();
if(loggingListener != null) {
SqlQueryType sqlQueryType = SqlQueryType.detectType(sql);
QueryLoggingData queryLoggingData = new QueryLoggingData(sqlQueryType, new String[] {sql}, null, null, null, subEndExecutionTime - subStartExecutionTime);
StatementMatcher[] loggingStatementMatchers = listener.getLoggingStatementMatchers();
if(loggingStatementMatchers == null) {
loggingListener.logQueries(queryLoggingData);
} else for(StatementMatcher statementMatcher: loggingStatementMatchers) {
if(statementMatcher.matches(queryLoggingData)) {
loggingListener.logQueries(queryLoggingData);
break;
}
}
}
}
}
String mainSQL = null;
StatementProcessor replacementExecutionProcessor = matchingBreakpoint.getReplacementExecutionProcessor();
if(replacementExecutionProcessor != null) {
mainSQL = replacementExecutionProcessor.processSQL(matchingSQL);
try {
ctx.statement().close();
ctx.sql(mainSQL);
ctx.statement(ctx.getConnection().prepareStatement(mainSQL));
} catch(Exception e) {
// TODO: how to process properly breakpoint errors??
throw new RuntimeException(e);
}
}
ExecutionType executionType = BreakpointBeforeExecutionHit.ExecutionType.RUN;
if(breakpointHitHandler != null) {
effectiveSQL = mainSQL != null? mainSQL: matchingSQL;
// TODO: find a way for the handler to replace the statement (not just step over).
BreakpointBeforeExecutionHit breakpointBeforeExecutionHit = new BreakpointBeforeExecutionHit(matchingBreakpoint.getID(), effectiveSQL);
breakpointHitHandler.processBreakpointBeforeExecutionHit(breakpointBeforeExecutionHit);
executionType = breakpointBeforeExecutionHit.getExecutionType();
}
switch(executionType) {
case STEP_THROUGH: {
matchingBreakpointHitHandler = breakpointHitHandler;
break;
}
case RUN_OVER: {
try {
ctx.statement().close();
// Better return possibility? Based on originating query?
String sql = new Factory(ctx.getDialect()).selectZero().where("1 = 2").getSQL();
ctx.sql(sql);
ctx.statement(ctx.getConnection().prepareStatement(sql));
} catch(Exception e) {
// TODO: how to process properly breakpoint errors??
throw new RuntimeException(e);
}
break;
}
}
}
if(!hasDebuggers) {
return;
}
startExecutionTime = System.currentTimeMillis();
}
private void executeSQL(ExecuteContext ctx, String sql) {
Statement statement = null;
try {
statement = ctx.getConnection().createStatement();
statement.execute(sql);
} catch(Exception e) {
// TODO: how to process properly breakpoint errors??
throw new RuntimeException(e);
} finally {
if(statement != null) {
try {
statement.close();
} catch(Exception e) {
// No error for closing problems.
e.printStackTrace();
}
}
}
}
@Override
public void executeEnd(ExecuteContext ctx) {
if(!isLogging) {
if(!hasDebuggers) {
return;
}
endExecutionTime = System.currentTimeMillis();
List<Debugger> debuggerList = DebuggerRegistry.getDebuggerList();
if(debuggerList.isEmpty()) {
return;
if(matchingBreakpointHitHandler != null) {
matchingBreakpointHitHandler.processBreakpointAfterExecutionHit(new BreakpointAfterExecutionHit(matchingBreakpoint.getID(), effectiveSQL));
}
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]);
String parameterDescription = null;
if(sql.length == 1) {
PreparedStatement statement = ctx.statement();
if(statement instanceof UsageTrackingPreparedStatement) {
parameterDescription = ((UsageTrackingPreparedStatement) statement).getParameterDescription();
List<Debugger> debuggerList = DebuggerRegistry.getDebuggerList();
if(!debuggerList.isEmpty()) {
boolean hasListener = false;
for(Debugger debugger: debuggerList) {
LoggingListener loggingListener = debugger.getLoggingListener();
if(loggingListener != null) {
hasListener = true;
break;
}
}
}
QueryLoggingData queryLoggingData = new QueryLoggingData(sqlQueryType, sql, parameterDescription, startPreparationTime == 0? null: aggregatedPreparationDuration, startBindTime == 0? null: endBindTime - startBindTime, endExecutionTime - startExecutionTime);
final List<LoggingListener> loggingListenerList = new ArrayList<LoggingListener>(debuggerList.size());
for(Debugger listener: debuggerList) {
LoggingListener loggingListener = listener.getLoggingListener();
if(loggingListener != null) {
StatementMatcher[] loggingStatementMatchers = listener.getLoggingStatementMatchers();
if(loggingStatementMatchers == null) {
loggingListenerList.add(loggingListener);
loggingListener.logQueries(queryLoggingData);
} else for(StatementMatcher statementMatcher: loggingStatementMatchers) {
if(statementMatcher.matches(queryLoggingData)) {
loggingListenerList.add(loggingListener);
loggingListener.logQueries(queryLoggingData);
break;
if(hasListener) {
String[] sql = ctx.batchSQL();
SqlQueryType sqlQueryType = SqlQueryType.detectType(sql[0]);
String parameterDescription = null;
if(sql.length == 1) {
PreparedStatement statement = ctx.statement();
if(statement instanceof UsageTrackingPreparedStatement) {
parameterDescription = ((UsageTrackingPreparedStatement) statement).getParameterDescription();
}
}
QueryLoggingData queryLoggingData = new QueryLoggingData(sqlQueryType, sql, parameterDescription, startPreparationTime == 0? null: aggregatedPreparationDuration, startBindTime == 0? null: endBindTime - startBindTime, endExecutionTime - startExecutionTime);
final List<LoggingListener> loggingListenerList = new ArrayList<LoggingListener>(debuggerList.size());
for(Debugger listener: debuggerList) {
LoggingListener loggingListener = listener.getLoggingListener();
if(loggingListener != null) {
StatementMatcher[] loggingStatementMatchers = listener.getLoggingStatementMatchers();
if(loggingStatementMatchers == null) {
loggingListenerList.add(loggingListener);
loggingListener.logQueries(queryLoggingData);
} else for(StatementMatcher statementMatcher: loggingStatementMatchers) {
if(statementMatcher.matches(queryLoggingData)) {
loggingListenerList.add(loggingListener);
loggingListener.logQueries(queryLoggingData);
break;
}
}
}
}
ResultSet resultSet = ctx.resultSet();
if(resultSet != null && !loggingListenerList.isEmpty()) {
final int queryLoggingDataID = queryLoggingData.getID();
ResultSet newResultSet = new UsageTrackingResultSet(resultSet) {
@Override
protected void notifyData(long lifeTime, int readRows, int readCount, int writeCount) {
ResultSetLoggingData resultSetLoggingData = null;
for(LoggingListener loggingListener: loggingListenerList) {
if(resultSetLoggingData == null) {
resultSetLoggingData = new ResultSetLoggingData(lifeTime, readRows, readCount, writeCount);
}
loggingListener.logResultSet(queryLoggingDataID, resultSetLoggingData);
}
}
};
ctx.resultSet(newResultSet);
}
}
}
if(resultSet != null && !loggingListenerList.isEmpty()) {
final int queryLoggingDataID = queryLoggingData.getID();
ResultSet newResultSet = new UsageTrackingResultSet(resultSet) {
@Override
protected void notifyData(long lifeTime, int readRows, int readCount, int writeCount) {
ResultSetLoggingData resultSetLoggingData = null;
for(LoggingListener loggingListener: loggingListenerList) {
if(resultSetLoggingData == null) {
resultSetLoggingData = new ResultSetLoggingData(lifeTime, readRows, readCount, writeCount);
}
loggingListener.logResultSet(queryLoggingDataID, resultSetLoggingData);
}
}
};
ctx.resultSet(newResultSet);
}
if(matchingBreakpoint != null) {
StatementProcessor afterExecutionProcessor = matchingBreakpoint.getAfterExecutionProcessor();
matchingBreakpoint = null;
if(afterExecutionProcessor != null) {
String sql = afterExecutionProcessor.processSQL(matchingSQL);
long subStartExecutionTime = System.currentTimeMillis();
executeSQL(ctx, sql);
long subEndExecutionTime = System.currentTimeMillis();
// Log result of pre-processing.
for(Debugger listener: debuggerList) {
LoggingListener loggingListener = listener.getLoggingListener();
if(loggingListener != null) {
SqlQueryType sqlQueryType = SqlQueryType.detectType(sql);
QueryLoggingData queryLoggingData = new QueryLoggingData(sqlQueryType, new String[] {sql}, null, null, null, subEndExecutionTime - subStartExecutionTime);
StatementMatcher[] loggingStatementMatchers = listener.getLoggingStatementMatchers();
if(loggingStatementMatchers == null) {
loggingListener.logQueries(queryLoggingData);
} else for(StatementMatcher statementMatcher: loggingStatementMatchers) {
if(statementMatcher.matches(queryLoggingData)) {
loggingListener.logQueries(queryLoggingData);
break;
}
}
}
}
}
}
}
// private long startFetchTime;

View File

@ -37,7 +37,9 @@
package org.jooq.debug;
/**
* @author Christopher Deckers
*/
public interface Debugger {
/**
@ -51,6 +53,14 @@ public interface Debugger {
public StatementMatcher[] getLoggingStatementMatchers();
public void setBreakpoints(Breakpoint[] breakpoints);
public void setBreakpointHitHandler(BreakpointHitHandler breakpointHitHandler);
public BreakpointHitHandler getBreakpointHitHandler();
public Breakpoint[] getBreakpoints();
public boolean isExecutionSupported();
public StatementExecutor createStatementExecutor();

View File

@ -39,6 +39,9 @@ package org.jooq.debug;
import org.jooq.debug.console.DatabaseDescriptor;
/**
* @author Christopher Deckers
*/
public class LocalDebugger implements Debugger {
private DatabaseDescriptor databaseDescriptor;
@ -48,27 +51,74 @@ public class LocalDebugger implements Debugger {
}
private LoggingListener loggingListener;
private final Object LOGGING_LISTENER_LOCK = new Object();
@Override
public void setLoggingListener(LoggingListener loggingListener) {
this.loggingListener = loggingListener;
synchronized (LOGGING_LISTENER_LOCK) {
this.loggingListener = loggingListener;
}
}
@Override
public LoggingListener getLoggingListener() {
return loggingListener;
synchronized (LOGGING_LISTENER_LOCK) {
return loggingListener;
}
}
private StatementMatcher[] loggingStatementMatchers;
private final Object LOGGING_STATEMENT_MATCHERS_LOCK = new Object();
@Override
public void setLoggingStatementMatchers(StatementMatcher[] loggingStatementMatchers) {
this.loggingStatementMatchers = loggingStatementMatchers;
synchronized (LOGGING_STATEMENT_MATCHERS_LOCK) {
this.loggingStatementMatchers = loggingStatementMatchers;
}
}
@Override
public StatementMatcher[] getLoggingStatementMatchers() {
return loggingStatementMatchers;
synchronized (LOGGING_STATEMENT_MATCHERS_LOCK) {
return loggingStatementMatchers;
}
}
private Breakpoint[] breakpoints;
private final Object BREAKPOINT_LOCK = new Object();
@Override
public void setBreakpoints(Breakpoint[] breakpoints) {
if(breakpoints != null && breakpoints.length == 0) {
breakpoints = null;
}
synchronized (BREAKPOINT_LOCK) {
this.breakpoints = breakpoints;
}
}
@Override
public Breakpoint[] getBreakpoints() {
synchronized (BREAKPOINT_LOCK) {
return breakpoints;
}
}
private BreakpointHitHandler breakpointHitHandler;
private final Object BREAKPOINT_HIT_HANDLER_LOCK = new Object();
@Override
public void setBreakpointHitHandler(BreakpointHitHandler breakpointHitHandler) {
synchronized (BREAKPOINT_HIT_HANDLER_LOCK) {
this.breakpointHitHandler = breakpointHitHandler;
}
}
@Override
public BreakpointHitHandler getBreakpointHitHandler() {
synchronized (BREAKPOINT_HIT_HANDLER_LOCK) {
return breakpointHitHandler;
}
}
@Override

View File

@ -68,6 +68,9 @@ import org.jooq.debug.StatementExecutionResultSetResult.TypeInfo;
import org.jooq.debug.console.DatabaseDescriptor;
import org.jooq.debug.console.misc.Utils;
/**
* @author Christopher Deckers
*/
public class LocalStatementExecutor implements StatementExecutor {
private DatabaseDescriptor databaseDescriptor;

View File

@ -37,6 +37,9 @@
package org.jooq.debug;
/**
* @author Christopher Deckers
*/
public interface LoggingListener {
public void logQueries(QueryLoggingData queryLoggingData);

View File

@ -39,7 +39,6 @@ package org.jooq.debug;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author Christopher Deckers
*/

View File

@ -37,7 +37,9 @@
package org.jooq.debug;
/**
* @author Christopher Deckers
*/
public class StatementExecution {
private long executionDuration;

View File

@ -39,6 +39,9 @@ package org.jooq.debug;
import java.io.PrintWriter;
import java.io.StringWriter;
/**
* @author Christopher Deckers
*/
public class StatementExecutionMessageResult implements StatementExecutionResult {
private String message;

View File

@ -37,6 +37,9 @@
package org.jooq.debug;
/**
* @author Christopher Deckers
*/
public interface StatementExecutionResult {
}

View File

@ -42,6 +42,9 @@ import java.sql.SQLException;
import org.jooq.debug.console.misc.Utils;
/**
* @author Christopher Deckers
*/
public class StatementExecutionResultSetResult implements StatementExecutionResult {
public static class TypeInfo {

View File

@ -37,6 +37,9 @@
package org.jooq.debug;
/**
* @author Christopher Deckers
*/
public interface StatementExecutor {
public StatementExecution execute(String sql, int maxRSRowsParsing, int retainParsedRSDataRowCountThreshold);

View File

@ -38,6 +38,9 @@ package org.jooq.debug;
import java.io.Serializable;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementInfo implements Serializable {

View File

@ -42,6 +42,9 @@ import java.util.Set;
import org.jooq.debug.console.misc.TextMatcher;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementMatcher implements Serializable {

View File

@ -0,0 +1,87 @@
/**
* 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.Serializable;
import org.jooq.debug.console.misc.Utils;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementProcessor implements Serializable {
public static enum ProcessorExecutionType {
STATIC("Static SQL"),
SED_LIKE_REG_EXP("Sed-like Reg. Exp."),
;
private String name;
private ProcessorExecutionType(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}
private ProcessorExecutionType type;
private String text;
public StatementProcessor(ProcessorExecutionType type, String text) {
this.type = type;
this.text = text;
}
public ProcessorExecutionType getType() {
return type;
}
public String getText() {
return text;
}
public String processSQL(String sql) {
switch(type) {
case STATIC: return text;
case SED_LIKE_REG_EXP: return Utils.applySedRegularExpression(sql, text);
}
throw new IllegalStateException();
}
}

View File

@ -58,6 +58,9 @@ import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Map;
/**
* @author Christopher Deckers
*/
abstract class UsageTrackingResultSet implements ResultSet {
private ResultSet resultSet;

View File

@ -0,0 +1,311 @@
/**
* 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.console;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import org.jooq.debug.Breakpoint;
import org.jooq.debug.SqlQueryType;
import org.jooq.debug.StatementMatcher;
import org.jooq.debug.StatementProcessor;
import org.jooq.debug.console.misc.TextMatcher;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class BreakpointEditor extends JPanel {
private static final String BREAK = "Break on match";
private static final String PROCESS = "Process on match";
private int id;
private JCheckBox threadNameTextMatcherCheckBox;
private TextMatcherPane threadNameTextMatcherPane;
private JCheckBox statementTextMatcherCheckBox;
private TextMatcherPane statementTextMatcherPane;
private JCheckBox statementTypeCheckBox;
private JCheckBox statementTypeSelectCheckBox;
private JCheckBox statementTypeUpdateCheckBox;
private JCheckBox statementTypeInsertCheckBox;
private JCheckBox statementTypeDeleteCheckBox;
private JCheckBox statementTypeOtherCheckBox;
private JComboBox breakpointTypeComboBox;
private JPanel processorPane;
private JCheckBox beforeExecutionCheckBox;
private StatementProcessorPane beforeExecutionProcessorPane;
private JRadioButton executeRadioButton;
// private JRadioButton doNotExecuteRadioButton;
private JRadioButton replaceExecutionRadioButton;
private StatementProcessorPane replacementExecutionProcessorPane;
private JCheckBox afterExecutionCheckBox;
private StatementProcessorPane afterExecutionProcessorPane;
public BreakpointEditor(final DebuggerPane debuggerPane, Breakpoint breakpoint) {
super(new GridBagLayout());
StatementMatcher statementMatcher = breakpoint.getStatementMatcher();
id = breakpoint.getID();
if(statementMatcher == null) {
statementMatcher = new StatementMatcher(null, null, null, true);
}
int y = 0;
TextMatcher statementTextMatcher = statementMatcher.getStatementTextMatcher();
statementTextMatcherCheckBox = new JCheckBox("Statement", statementTextMatcher != null);
statementTextMatcherCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
add(statementTextMatcherCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
statementTextMatcherPane = new TextMatcherPane(statementTextMatcher);
add(statementTextMatcherPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
y++;
Set<SqlQueryType> queryTypeSet = statementMatcher.getQueryTypeSet();
statementTypeCheckBox = new JCheckBox("Type", queryTypeSet != null);
statementTypeCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
add(statementTypeCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
JPanel typesPane = new JPanel(new GridBagLayout());
statementTypeSelectCheckBox = new JCheckBox("SELECT", queryTypeSet != null && queryTypeSet.contains(SqlQueryType.SELECT));
typesPane.add(statementTypeSelectCheckBox, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
statementTypeUpdateCheckBox = new JCheckBox("UPDATE", queryTypeSet != null && queryTypeSet.contains(SqlQueryType.UPDATE));
typesPane.add(statementTypeUpdateCheckBox, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
statementTypeInsertCheckBox = new JCheckBox("INSERT", queryTypeSet != null && queryTypeSet.contains(SqlQueryType.INSERT));
typesPane.add(statementTypeInsertCheckBox, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
statementTypeDeleteCheckBox = new JCheckBox("DELETE", queryTypeSet != null && queryTypeSet.contains(SqlQueryType.DELETE));
typesPane.add(statementTypeDeleteCheckBox, new GridBagConstraints(3, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
statementTypeOtherCheckBox = new JCheckBox("OTHER", queryTypeSet != null && queryTypeSet.contains(SqlQueryType.OTHER));
typesPane.add(statementTypeOtherCheckBox, new GridBagConstraints(4, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
add(typesPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
y++;
TextMatcher threadNameTextMatcher = statementMatcher.getThreadNameTextMatcher();
threadNameTextMatcherCheckBox = new JCheckBox("Thread name", threadNameTextMatcher != null);
threadNameTextMatcherCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
add(threadNameTextMatcherCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
threadNameTextMatcherPane = new TextMatcherPane(threadNameTextMatcher);
add(threadNameTextMatcherPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
y++;
breakpointTypeComboBox = new JComboBox(new Object[] {BREAK, PROCESS});
breakpointTypeComboBox.setSelectedItem(breakpoint.isBreaking()? BREAK: PROCESS);
breakpointTypeComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
add(breakpointTypeComboBox, new GridBagConstraints(0, y, 2, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
y++;
processorPane = new JPanel(new GridBagLayout());
populateProcessorPane(breakpoint);
add(processorPane, new GridBagConstraints(0, y, 2, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 20, 0, 0), 0, 0));
y++;
JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
buttonPane.setBorder(BorderFactory.createEmptyBorder(20, 5, 5, 5));
JButton applyButton = new JButton("Apply changes");
applyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
debuggerPane.modifyBreakpoint(getBreakpoint());
}
});
buttonPane.add(applyButton);
add(buttonPane, new GridBagConstraints(0, y, 2, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 20, 0, 0), 0, 0));
add(Box.createGlue(), new GridBagConstraints(0, Short.MAX_VALUE, 1, 1, 0, 1, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
adjustStates();
}
private void populateProcessorPane(Breakpoint breakpoint) {
int y = 0;
StatementProcessor beforeExecutionProcessor = breakpoint.getBeforeExecutionProcessor();
beforeExecutionCheckBox = new JCheckBox("Execute before: ");
beforeExecutionCheckBox.setSelected(beforeExecutionProcessor != null);
beforeExecutionCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
processorPane.add(beforeExecutionCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
beforeExecutionProcessorPane = new StatementProcessorPane(beforeExecutionProcessor);
processorPane.add(beforeExecutionProcessorPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
y++;
ButtonGroup executionButtonGroup = new ButtonGroup();
StatementProcessor replacementExecutionProcessor = breakpoint.getReplacementExecutionProcessor();
executeRadioButton = new JRadioButton("Execute");
executionButtonGroup.add(executeRadioButton);
processorPane.add(executeRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
y++;
// doNotExecuteRadioButton = new JRadioButton("Do not execute");
// executionButtonGroup.add(doNotExecuteRadioButton);
// processorPane.add(doNotExecuteRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
// y++;
replaceExecutionRadioButton = new JRadioButton("Replace with: ");
executionButtonGroup.add(replaceExecutionRadioButton);
if(replacementExecutionProcessor != null) {
replaceExecutionRadioButton.setSelected(true);
} else {
executeRadioButton.setSelected(true);
}
processorPane.add(replaceExecutionRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
replacementExecutionProcessorPane = new StatementProcessorPane(breakpoint.getReplacementExecutionProcessor());
processorPane.add(replacementExecutionProcessorPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
executeRadioButton.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
replaceExecutionRadioButton.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
y++;
StatementProcessor afterExecutionProcessor = breakpoint.getAfterExecutionProcessor();
afterExecutionCheckBox = new JCheckBox("Execute after: ");
afterExecutionCheckBox.setSelected(afterExecutionProcessor != null);
afterExecutionCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
adjustStates();
}
});
processorPane.add(afterExecutionCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
afterExecutionProcessorPane = new StatementProcessorPane(afterExecutionProcessor);
processorPane.add(afterExecutionProcessorPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
}
private void adjustStates() {
boolean isActive = true;
statementTextMatcherCheckBox.setEnabled(isActive);
statementTypeCheckBox.setEnabled(isActive);
threadNameTextMatcherCheckBox.setEnabled(isActive);
statementTextMatcherPane.setLocked(!isActive || !statementTextMatcherCheckBox.isSelected());
statementTypeSelectCheckBox.setEnabled(isActive && statementTypeCheckBox.isSelected());
statementTypeUpdateCheckBox.setEnabled(isActive && statementTypeCheckBox.isSelected());
statementTypeInsertCheckBox.setEnabled(isActive && statementTypeCheckBox.isSelected());
statementTypeDeleteCheckBox.setEnabled(isActive && statementTypeCheckBox.isSelected());
statementTypeOtherCheckBox.setEnabled(isActive && statementTypeCheckBox.isSelected());
threadNameTextMatcherPane.setLocked(!isActive || !threadNameTextMatcherCheckBox.isSelected());
breakpointTypeComboBox.setEnabled(isActive && (statementTextMatcherCheckBox.isSelected() || statementTypeCheckBox.isSelected() || threadNameTextMatcherCheckBox.isSelected()));
processorPane.setVisible(isActive && breakpointTypeComboBox.getSelectedItem() == PROCESS);
beforeExecutionCheckBox.setEnabled(isActive);
beforeExecutionProcessorPane.setLocked(!isActive || !beforeExecutionCheckBox.isSelected());
executeRadioButton.setEnabled(isActive);
replaceExecutionRadioButton.setEnabled(isActive);
// doNotExecuteRadioButton;
replacementExecutionProcessorPane.setLocked(!isActive || !replaceExecutionRadioButton.isSelected());
afterExecutionCheckBox.setEnabled(isActive);
afterExecutionProcessorPane.setLocked(!isActive || !afterExecutionCheckBox.isSelected());
}
public StatementMatcher getStatementMatcher() {
boolean isActive = true;//activeCheckBox.isSelected();
TextMatcher threadNameTextMatcher = threadNameTextMatcherCheckBox.isSelected()? threadNameTextMatcherPane.getTextMatcher(): null;
TextMatcher statementTextMatcher = statementTextMatcherCheckBox.isSelected()? statementTextMatcherPane.getTextMatcher(): null;
Set<SqlQueryType> queryTypeSet;
if(statementTypeCheckBox.isSelected()) {
List<SqlQueryType> typeList = new ArrayList<SqlQueryType>();
if(statementTypeSelectCheckBox.isSelected()) {
typeList.add(SqlQueryType.SELECT);
}
if(statementTypeUpdateCheckBox.isSelected()) {
typeList.add(SqlQueryType.UPDATE);
}
if(statementTypeInsertCheckBox.isSelected()) {
typeList.add(SqlQueryType.INSERT);
}
if(statementTypeDeleteCheckBox.isSelected()) {
typeList.add(SqlQueryType.DELETE);
}
if(statementTypeOtherCheckBox.isSelected()) {
typeList.add(SqlQueryType.OTHER);
}
queryTypeSet = EnumSet.copyOf(typeList);
} else {
queryTypeSet = null;
}
return new StatementMatcher(threadNameTextMatcher, statementTextMatcher, queryTypeSet, isActive);
}
public Breakpoint getBreakpoint() {
StatementMatcher statementMatcher = getStatementMatcher();
// Integer hitCount;
boolean isBreaking = breakpointTypeComboBox.getSelectedItem() == BREAK;
StatementProcessor beforeExecutionProcessor = null;
StatementProcessor replacementExecutionProcessor = null;
StatementProcessor afterExecutionProcessor = null;
if(!isBreaking) {
beforeExecutionProcessor = beforeExecutionCheckBox.isSelected()? beforeExecutionProcessorPane.getStatementProcessor(): null;
replacementExecutionProcessor = replaceExecutionRadioButton.isSelected()? replacementExecutionProcessorPane.getStatementProcessor(): null;
afterExecutionProcessor = afterExecutionCheckBox.isSelected()? afterExecutionProcessorPane.getStatementProcessor(): null;
}
return new Breakpoint(id, statementMatcher, isBreaking, beforeExecutionProcessor, replacementExecutionProcessor, afterExecutionProcessor);
}
}

View File

@ -103,7 +103,7 @@ public class Console extends JFrame {
private JTabbedPane mainTabbedPane;
private JTabbedPane editorTabbedPane;
public Console(DatabaseDescriptor editorDatabaseDescriptor, boolean isShowingLoggingTab) {
public Console(DatabaseDescriptor editorDatabaseDescriptor, boolean isShowingLoggingTab, boolean isShowingDebugger) {
debugger = new LocalDebugger(editorDatabaseDescriptor);
// Local debugger registration is managed by the console since it hides the debugger.
addWindowListener(new WindowAdapter() {
@ -116,20 +116,16 @@ public class Console extends JFrame {
DebuggerRegistry.removeSqlQueryDebugger(debugger);
}
});
init(isShowingLoggingTab);
init(isShowingLoggingTab, isShowingDebugger);
}
public Console(final Debugger debugger, boolean isShowingLoggingTab) {
public Console(final Debugger debugger, boolean isShowingLoggingTab, boolean isShowingDebugger) {
this.debugger = debugger;
// Local debugger registration is handled externally if needed (e.g.: remote client must not be registered).
init(isShowingLoggingTab);
init(isShowingLoggingTab, isShowingDebugger);
}
/**
* @param debugger
* @param isShowingLoggingTab
*/
private void init(boolean isShowingLoggingTab) {
private void init(boolean isShowingLoggingTab, boolean isShowingDebugger) {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
@ -233,6 +229,9 @@ public class Console extends JFrame {
if(isShowingLoggingTab) {
addLoggerTab();
}
if(isShowingDebugger) {
addDebuggerTab();
}
getContentPane().add(mainTabbedPane, BorderLayout.CENTER);
setLocationByPlatform(true);
setSize(800, 600);
@ -263,6 +262,14 @@ public class Console extends JFrame {
}
}
private DebuggerPane sqlDebuggerPane;
private void addDebuggerTab() {
sqlDebuggerPane = new DebuggerPane(debugger);
mainTabbedPane.addTab("Debugger", sqlDebuggerPane);
}
private LoggerPane sqlLoggerPane;
private void addLoggerTab() {
@ -407,7 +414,7 @@ public class Console extends JFrame {
}
public static void openConsole(DatabaseDescriptor databaseDescriptor, boolean isLoggingActive) {
Console sqlConsoleFrame = new Console(databaseDescriptor, true);
Console sqlConsoleFrame = new Console(databaseDescriptor, true, true);
sqlConsoleFrame.setLoggingActive(isLoggingActive);
sqlConsoleFrame.setVisible(true);
}
@ -506,7 +513,7 @@ public class Console extends JFrame {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Console sqlConsoleFrame = new Console(debugger, true);
Console sqlConsoleFrame = new Console(debugger, true, true);
sqlConsoleFrame.setDefaultCloseOperation(EXIT_ON_CLOSE);
sqlConsoleFrame.setVisible(true);
}

View File

@ -0,0 +1,240 @@
/**
* 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.console;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.jooq.debug.Breakpoint;
import org.jooq.debug.Debugger;
import org.jooq.debug.console.misc.CheckBoxNode;
import org.jooq.debug.console.misc.CheckBoxNodeEditor;
import org.jooq.debug.console.misc.CheckBoxNodeRenderer;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class DebuggerPane extends JPanel {
private Debugger debugger;
private DefaultMutableTreeNode rootNode;
private JTree breakpointTree;
private DefaultTreeModel breakpointTreeModel;
private JPanel eastPane;
public DebuggerPane(Debugger debugger) {
super(new BorderLayout());
this.debugger = debugger;
JPanel westPane = new JPanel(new BorderLayout());
JPanel breakpointAddPane = new JPanel(new GridBagLayout());
final JTextField addBreakpointTextField = new JTextField(7);
breakpointAddPane.add(addBreakpointTextField, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
final JButton addBreakpointButton = new JButton("Add");
addBreakpointButton.setEnabled(false);
breakpointAddPane.add(addBreakpointButton, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0));
addBreakpointTextField.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
adjustStates();
}
@Override
public void insertUpdate(DocumentEvent e) {
adjustStates();
}
@Override
public void changedUpdate(DocumentEvent e) {
adjustStates();
}
private void adjustStates() {
// TODO: restrict to unique names?
boolean isEnabled = addBreakpointTextField.getText().length() > 0;
addBreakpointButton.setEnabled(isEnabled);
}
});
ActionListener addBreakpointActionListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String name = addBreakpointTextField.getText();
addBreakpointTextField.setText("");
addBreakpoint(name);
}
};
addBreakpointTextField.addActionListener(addBreakpointActionListener);
addBreakpointButton.addActionListener(addBreakpointActionListener);
westPane.add(breakpointAddPane, BorderLayout.NORTH);
rootNode = new DefaultMutableTreeNode();
breakpointTreeModel = new DefaultTreeModel(rootNode) {
@Override
public void valueForPathChanged(TreePath path, Object newValue) {
if(newValue instanceof CheckBoxNode) {
CheckBoxNode node = (CheckBoxNode)path.getLastPathComponent();
node.setSelected(((CheckBoxNode) newValue).isSelected());
super.valueForPathChanged(path, node.getUserObject());
}
}
};
breakpointTree = new JTree(breakpointTreeModel);
breakpointTree.setRootVisible(false);
breakpointTree.setCellRenderer(new CheckBoxNodeRenderer());
breakpointTree.setCellEditor(new CheckBoxNodeEditor(breakpointTree));
breakpointTree.setEditable(true);
westPane.add(new JScrollPane(breakpointTree), BorderLayout.CENTER);
JPanel breakpointRemovePane = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
final JButton removeBreakpointButton = new JButton("Remove");
removeBreakpointButton.setEnabled(false);
removeBreakpointButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
TreePath[] paths = breakpointTree.getSelectionPaths();
boolean isValid = paths != null && paths.length > 0;
if(isValid) {
for(int i=0; i<paths.length; i++) {
if(!(paths[i].getLastPathComponent() instanceof CheckBoxNode)) {
isValid = false;
break;
}
}
}
if(isValid) {
breakpointTree.cancelEditing();
for(int i=0; i<paths.length; i++) {
CheckBoxNode childNode = (CheckBoxNode)paths[i].getLastPathComponent();
int index = rootNode.getIndex(childNode);
rootNode.remove(index);
breakpointTreeModel.nodesWereRemoved(rootNode, new int[] {index}, new Object[] {childNode});
}
commitBreakpoints();
}
}
});
breakpointTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
TreePath[] paths = breakpointTree.getSelectionPaths();
boolean isValid = paths != null && paths.length > 0;
if(isValid) {
for(int i=0; i<paths.length; i++) {
if(!(paths[i].getLastPathComponent() instanceof CheckBoxNode)) {
isValid = false;
break;
}
}
}
removeBreakpointButton.setEnabled(isValid);
processTreeSelection();
}
});
breakpointRemovePane.add(removeBreakpointButton);
westPane.add(breakpointRemovePane, BorderLayout.SOUTH);
eastPane = new JPanel(new BorderLayout());
add(new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, westPane, eastPane), BorderLayout.CENTER);
}
private static int nextID = 1;
private void addBreakpoint(String name) {
breakpointTree.cancelEditing();
Breakpoint breakpoint = new Breakpoint(nextID++, null, true, null, null, null);
CheckBoxNode breakpointNode = new CheckBoxNode(breakpoint, name, true);
rootNode.add(breakpointNode);
breakpointTreeModel.nodesWereInserted(rootNode, new int[] {rootNode.getIndex(breakpointNode)});
breakpointTree.expandPath(new TreePath(rootNode));
commitBreakpoints();
}
void modifyBreakpoint(Breakpoint breakpoint) {
int childCount = rootNode.getChildCount();
for(int i=0; i<childCount; i++) {
CheckBoxNode checkBoxNode = (CheckBoxNode)rootNode.getChildAt(i);
Breakpoint breakpoint_ = (Breakpoint)checkBoxNode.getUserObject();
if(breakpoint_.getID() == breakpoint.getID()) {
checkBoxNode.setUserObject(breakpoint);
commitBreakpoints();
break;
}
}
}
private void commitBreakpoints() {
List<Breakpoint> breakpointList = new ArrayList<Breakpoint>();
int childCount = rootNode.getChildCount();
for(int i=0; i<childCount; i++) {
CheckBoxNode node = (CheckBoxNode)rootNode.getChildAt(i);
if(node.isSelected()) {
breakpointList.add((Breakpoint)node.getUserObject());
}
}
debugger.setBreakpoints(breakpointList.toArray(new Breakpoint[0]));
}
private void processTreeSelection() {
TreePath[] paths = breakpointTree.getSelectionPaths();
// TODO: commit last value.
eastPane.removeAll();
if(paths != null && paths.length == 1) {
Object o = ((DefaultMutableTreeNode)paths[0].getLastPathComponent()).getUserObject();
if(o instanceof Breakpoint) {
eastPane.add(new BreakpointEditor(this, (Breakpoint)o));
}
}
eastPane.revalidate();
eastPane.repaint();
}
}

View File

@ -59,6 +59,9 @@ import org.jooq.debug.SqlQueryType;
import org.jooq.debug.StatementMatcher;
import org.jooq.debug.console.misc.TextMatcher;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementMatcherPane extends JPanel {
@ -114,7 +117,7 @@ public class StatementMatcherPane extends JPanel {
});
add(statementTextMatcherCheckBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
statementTextMatcherPane = new TextMatcherPane(statementTextMatcher);
add(statementTextMatcherPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
add(statementTextMatcherPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
y++;
Set<SqlQueryType> queryTypeSet = statementMatcher.getQueryTypeSet();
statementTypeCheckBox = new JCheckBox("Type", queryTypeSet != null);

View File

@ -53,6 +53,9 @@ import javax.swing.SwingUtilities;
import org.jooq.debug.Debugger;
import org.jooq.debug.StatementMatcher;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementMatchersDialogBox extends JDialog {

View File

@ -51,6 +51,9 @@ import javax.swing.Scrollable;
import org.jooq.debug.StatementMatcher;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementMatchersPane extends JPanel {

View File

@ -0,0 +1,79 @@
/**
* 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.console;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jooq.debug.StatementProcessor;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class StatementProcessorPane extends JPanel {
private JComboBox processorTypeComboBox;
private JTextField processorTextField;
public StatementProcessorPane(StatementProcessor statementProcessor) {
super(new GridBagLayout());
if(statementProcessor == null) {
statementProcessor = new StatementProcessor(StatementProcessor.ProcessorExecutionType.STATIC, "");
}
processorTypeComboBox = new JComboBox(StatementProcessor.ProcessorExecutionType.values());
processorTypeComboBox.setSelectedItem(statementProcessor.getType());
add(processorTypeComboBox, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
processorTextField = new JTextField(statementProcessor.getText(), 14);
add(processorTextField, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0));
}
public StatementProcessor getStatementProcessor() {
return new StatementProcessor((StatementProcessor.ProcessorExecutionType)processorTypeComboBox.getSelectedItem(), processorTextField.getText());
}
public void setLocked(boolean isLocked) {
processorTypeComboBox.setEnabled(!isLocked);
processorTextField.setEnabled(!isLocked);
}
}

View File

@ -49,6 +49,9 @@ import org.jooq.debug.console.misc.TextMatcher;
import org.jooq.debug.console.misc.TextMatcher.TextMatchingType;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class TextMatcherPane extends JPanel {
@ -61,7 +64,7 @@ public class TextMatcherPane extends JPanel {
matcherTypeComboBox = new JComboBox(TextMatchingType.values());
add(matcherTypeComboBox, new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
textField = new JTextField(14);
add(textField, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
add(textField, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0));
caseSensitiveCheckBox = new JCheckBox("Case sensitive");
add(caseSensitiveCheckBox, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 2, 0, 0), 0, 0));
if(textMatcher != null) {

View File

@ -0,0 +1,32 @@
package org.jooq.debug.console.misc;
import javax.swing.tree.DefaultMutableTreeNode;
/**
* @author Christopher Deckers
*/
public class CheckBoxNode extends DefaultMutableTreeNode {
private String text;
private boolean selected;
public CheckBoxNode(Object o, String text, boolean selected) {
super(o);
this.text = text;
this.selected = selected;
}
public String getText() {
return text;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean newValue) {
selected = newValue;
}
}

View File

@ -0,0 +1,63 @@
package org.jooq.debug.console.misc;
import java.awt.Component;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.util.EventObject;
import javax.swing.AbstractCellEditor;
import javax.swing.JCheckBox;
import javax.swing.JTree;
import javax.swing.tree.TreeCellEditor;
import javax.swing.tree.TreePath;
/**
* @author Christopher Deckers
*/
public class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
private CheckBoxNodeRenderer renderer = new CheckBoxNodeRenderer();
private JTree tree;
public CheckBoxNodeEditor(JTree tree) {
this.tree = tree;
}
@Override
public Object getCellEditorValue() {
JCheckBox checkbox = renderer.getCheckBoxRenderer();
return new CheckBoxNode(null, checkbox.getText(), checkbox.isSelected());
}
@Override
public boolean isCellEditable(EventObject event) {
boolean returnValue = false;
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent) event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path != null) {
Object node = path.getLastPathComponent();
returnValue = node instanceof CheckBoxNode;
}
}
return returnValue;
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row) {
Component editor = renderer.getTreeCellRendererComponent(tree, value, true, expanded, leaf, row, true);
// editor always selected / focused
if (editor instanceof JCheckBox) {
((JCheckBox) editor).addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent itemEvent) {
if (stopCellEditing()) {
fireEditingStopped();
}
}
});
}
return editor;
}
}

View File

@ -0,0 +1,78 @@
package org.jooq.debug.console.misc;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.TreeCellRenderer;
/**
* @author Christopher Deckers
*/
public class CheckBoxNodeRenderer implements TreeCellRenderer {
private JCheckBox checkBoxRenderer;
private DefaultTreeCellRenderer nonCheckBoxRenderer = new DefaultTreeCellRenderer();
Color selectionBorderColor, selectionForeground, selectionBackground, textForeground, textBackground;
protected JCheckBox getCheckBoxRenderer() {
return checkBoxRenderer;
}
public CheckBoxNodeRenderer() {
checkBoxRenderer = new JCheckBox();
checkBoxRenderer.setMargin(new Insets(0, 0, 0, 0));
Font fontValue = UIManager.getFont("Tree.font");
if (fontValue != null) {
checkBoxRenderer.setFont(fontValue);
}
Boolean booleanValue = (Boolean) UIManager.get("Tree.drawsFocusBorderAroundIcon");
checkBoxRenderer.setFocusPainted((booleanValue != null) && (booleanValue.booleanValue()));
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
Object usedValue = value;// stringValue = tree.convertValueToText(value, selected, expanded, leaf, row, false);
if (value instanceof CheckBoxNode) {
usedValue = ((CheckBoxNode) value).getText();
}
Component c = nonCheckBoxRenderer.getTreeCellRendererComponent(tree, usedValue, selected, expanded, leaf, row, hasFocus);
if (value instanceof CheckBoxNode) {
// checkBoxRenderer.setText(stringValue);
checkBoxRenderer.setSelected(false);
checkBoxRenderer.setEnabled(tree.isEnabled());
checkBoxRenderer.setOpaque(false);
// if (selected) {
// checkBoxRenderer.setForeground(selectionForeground);
// checkBoxRenderer.setBackground(selectionBackground);
// } else {
// checkBoxRenderer.setForeground(textForeground);
// checkBoxRenderer.setBackground(textBackground);
// }
CheckBoxNode node = (CheckBoxNode) value;
// checkBoxRenderer.setText(node.getText());
checkBoxRenderer.setSelected(node.isSelected());
JPanel pane = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
pane.setOpaque(false);
pane.add(checkBoxRenderer);
pane.add(c);
c = pane;
}
return c;
}
}

View File

@ -0,0 +1,98 @@
/**
* 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.console.misc;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class JSedRegExBuilder extends JPanel {
public JSedRegExBuilder(String patternSummary, String sampleText) {
setLayout(new GridBagLayout());
JPanel summaryPanel = new JPanel(new GridBagLayout());
summaryPanel.add(new JLabel("Pattern summary: "), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
final JTextField summaryTextField = new JTextField(7);
final JTextArea sampleTextTextArea = new JTextArea(sampleText);
final JTextArea outputTextArea = new JTextArea();
outputTextArea.setEditable(false);
DocumentListener documentListener = new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
update();
}
@Override
public void insertUpdate(DocumentEvent e) {
update();
}
@Override
public void changedUpdate(DocumentEvent e) {
update();
}
private void update() {
try {
outputTextArea.setText(Utils.applySedRegularExpression(sampleTextTextArea.getText(), summaryTextField.getText()));
} catch(Exception e) {
outputTextArea.setText(e.getMessage());
outputTextArea.setCaretPosition(0);
}
}
};
summaryTextField.getDocument().addDocumentListener(documentListener);
summaryPanel.add(summaryTextField, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
add(summaryPanel, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
add(new JLabel("Sample text:"), new GridBagConstraints(0, 1, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
add(new JScrollPane(sampleTextTextArea), new GridBagConstraints(0, 2, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
add(new JLabel("Output:"), new GridBagConstraints(0, 3, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0));
add(new JScrollPane(outputTextArea), new GridBagConstraints(0, 4, 1, 1, 1, 1, GridBagConstraints.WEST, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
summaryTextField.setText(patternSummary);
sampleTextTextArea.getDocument().addDocumentListener(documentListener);
}
}

View File

@ -39,6 +39,9 @@ package org.jooq.debug.console.misc;
import java.io.Serializable;
import java.util.regex.Pattern;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
public class TextMatcher implements Serializable {

View File

@ -43,6 +43,8 @@ import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Christopher Deckers & others
@ -287,4 +289,116 @@ public class Utils {
return keywordSet.contains(s.substring(0, index));
}
/**
* A "sed -e" like reg exp, of the form:<br/>
* - /regexp/flags: find and output the matches.<br/>
* - /regexp/replacement/flags: replace the matches and output the resulting string.<br/>
* Flags can be left empty or any combinations of the characters 'gidmsux' (g perfoms a replace all instead of just the first match. For other flags, refer to the Javadoc of Pattern).
* It is also possible to chain the output using ';' to perform multiple replacements.<br/>
* If the regexp contains capturing groups, a find operation would only retain those; for a replace operation, the replacement string can refer to capturing groups with a syntax like '$1'.
*/
public static String applySedRegularExpression(String text, String regex) {
String originalRegEx = regex;
if(!regex.startsWith("/")) {
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
regex = regex.substring(1);
StringBuilder sb = new StringBuilder();
char[] chars = regex.toCharArray();
int index1 = -1;
int index2 = -1;
for(int i=0; i<chars.length; i++) {
char c = chars[i];
switch(c) {
case ';':
text = applySedRegularExpression(sb.toString(), text, originalRegEx, index1, index2);
index1 = -1;
index2 = -1;
sb = new StringBuilder();
i++;
if(i >= chars.length || chars[i] != '/') {
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
break;
case '\\':
i++;
if(i >= chars.length) {
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
switch(chars[i]) {
case '/': sb.append('/'); break;
case ';': sb.append(';'); break;
default: sb.append('\\').append(chars[i]); break;
}
break;
case '/':
if(index1 == -1) {
index1 = sb.length();
} else if(index2 == -1) {
index2 = sb.length();
} else {
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
break;
default: sb.append(c); break;
}
}
if(index1 == -1) {
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
return applySedRegularExpression(sb.toString(), text, originalRegEx, index1, index2);
}
private static String applySedRegularExpression(String s, String text, String originalRegEx, int index1, int index2) {
StringBuilder sb;
String toFind = s.substring(0, index1);
String replacement = index2 == -1? null: s.substring(index1, index2);
String modifiers = index2 == -1? s.substring(index1): s.substring(index2);
boolean isGlobal = false;
int flags = 0;
for(int i=0; i<modifiers.length(); i++) {
char c = modifiers.charAt(i);
switch(c) {
case 'g': isGlobal = true; break;
case 'i': flags |= Pattern.CASE_INSENSITIVE; break;
case 'd': flags |= Pattern.UNIX_LINES; break;
case 'm': flags |= Pattern.MULTILINE; break;
case 's': flags |= Pattern.DOTALL; break;
case 'u': flags |= Pattern.UNICODE_CASE; break;
case 'x': flags |= Pattern.COMMENTS; break;
default:
throw new IllegalArgumentException("Invalid expression format: " + originalRegEx);
}
}
Matcher matcher = Pattern.compile(toFind, flags).matcher(text);
if(replacement == null) {
// Just returning the matches, no replacement
int groupCount = matcher.groupCount();
sb = new StringBuilder();
while(matcher.find()) {
if(groupCount > 0) {
for(int i=0; i<groupCount; i++) {
String group = matcher.group(i + 1);
if(group != null) {
sb.append(group);
}
}
} else {
String group = matcher.group();
if(group != null) {
sb.append(group);
}
}
if(!isGlobal) {
break;
}
}
return sb.toString();
}
if(isGlobal) {
return matcher.replaceAll(replacement);
}
return matcher.replaceFirst(replacement);
}
}

View File

@ -36,6 +36,10 @@
*/
package org.jooq.debug.console.remote;
import org.jooq.debug.Breakpoint;
import org.jooq.debug.BreakpointAfterExecutionHit;
import org.jooq.debug.BreakpointBeforeExecutionHit;
import org.jooq.debug.BreakpointHitHandler;
import org.jooq.debug.Debugger;
import org.jooq.debug.LoggingListener;
import org.jooq.debug.QueryLoggingData;
@ -62,29 +66,78 @@ public class ClientDebugger implements Debugger {
}
private LoggingListener loggingListener;
private final Object LOGGING_LISTENER_LOCK = new Object();
@Override
public void setLoggingListener(LoggingListener loggingListener) {
this.loggingListener = loggingListener;
synchronized (LOGGING_LISTENER_LOCK) {
this.loggingListener = loggingListener;
}
new ServerDebugger.CMS_setLoggingActive().asyncExec(communicationInterface, loggingListener != null);
}
@Override
public LoggingListener getLoggingListener() {
return loggingListener;
synchronized (LOGGING_LISTENER_LOCK) {
return loggingListener;
}
}
private StatementMatcher[] loggingStatementMatchers;
private final Object LOGGING_STATEMENT_MATCHERS_LOCK = new Object();
@Override
public void setLoggingStatementMatchers(StatementMatcher[] loggingStatementMatchers) {
this.loggingStatementMatchers = loggingStatementMatchers;
synchronized (LOGGING_STATEMENT_MATCHERS_LOCK) {
this.loggingStatementMatchers = loggingStatementMatchers;
}
new ServerDebugger.CMS_setLoggingStatementMatchers().asyncExec(communicationInterface, (Object)loggingStatementMatchers);
}
@Override
public StatementMatcher[] getLoggingStatementMatchers() {
return loggingStatementMatchers;
synchronized (LOGGING_STATEMENT_MATCHERS_LOCK) {
return loggingStatementMatchers;
}
}
private Breakpoint[] breakpoints;
private final Object BREAKPOINT_LOCK = new Object();
@Override
public void setBreakpoints(Breakpoint[] breakpoints) {
if(breakpoints != null && breakpoints.length == 0) {
breakpoints = null;
}
synchronized (BREAKPOINT_LOCK) {
this.breakpoints = breakpoints;
}
new ServerDebugger.CMS_setBreakpoints().asyncExec(communicationInterface, (Object)breakpoints);
}
@Override
public Breakpoint[] getBreakpoints() {
synchronized (BREAKPOINT_LOCK) {
return breakpoints;
}
}
private BreakpointHitHandler breakpointHitHandler;
private final Object BREAKPOINT_HIT_HANDLER_LOCK = new Object();
@Override
public void setBreakpointHitHandler(BreakpointHitHandler breakpointHitHandler) {
synchronized (BREAKPOINT_HIT_HANDLER_LOCK) {
this.breakpointHitHandler = breakpointHitHandler;
}
new ServerDebugger.CMS_setBreakpointHitHandlerActive().asyncExec(communicationInterface, breakpointHitHandler != null);
}
@Override
public BreakpointHitHandler getBreakpointHitHandler() {
synchronized (BREAKPOINT_HIT_HANDLER_LOCK) {
return breakpointHitHandler;
}
}
@Override
@ -107,7 +160,7 @@ public class ClientDebugger implements Debugger {
if(loggingListener != null) {
loggingListener.logQueries((QueryLoggingData)args[0]);
}
return 1;
return null;
}
}
@ -123,4 +176,35 @@ public class ClientDebugger implements Debugger {
}
}
@SuppressWarnings("serial")
static class CMC_processBreakpointBeforeExecutionHit extends ClientDebuggerCommandMessage {
@Override
public Object run(Object[] args) {
BreakpointHitHandler breakpointHitHandler = getDebugger().getBreakpointHitHandler();
if(breakpointHitHandler != null) {
BreakpointBeforeExecutionHit breakpointHit = (BreakpointBeforeExecutionHit)args[0];
breakpointHitHandler.processBreakpointBeforeExecutionHit(breakpointHit);
if(breakpointHit.getBreakpointID() != null) {
// The breakpoint was not processed, so we process it here.
breakpointHit.setExecutionType(BreakpointBeforeExecutionHit.ExecutionType.RUN, null);
}
return breakpointHit;
}
return null;
}
}
@SuppressWarnings("serial")
static class CMC_processBreakpointAfterExecutionHit extends ClientDebuggerCommandMessage {
@Override
public Object run(Object[] args) {
BreakpointHitHandler breakpointHitHandler = getDebugger().getBreakpointHitHandler();
if(breakpointHitHandler != null) {
BreakpointAfterExecutionHit breakpointHit = (BreakpointAfterExecutionHit)args[0];
breakpointHitHandler.processBreakpointAfterExecutionHit(breakpointHit);
}
return null;
}
}
}

View File

@ -38,6 +38,9 @@ package org.jooq.debug.console.remote;
import org.jooq.debug.console.remote.messaging.CommandMessage;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
abstract class ClientDebuggerCommandMessage extends CommandMessage {

View File

@ -39,6 +39,9 @@ package org.jooq.debug.console.remote;
import org.jooq.debug.Debugger;
import org.jooq.debug.console.remote.messaging.CommunicationInterface;
/**
* @author Christopher Deckers
*/
class DebuggerCommmunicationInterface extends CommunicationInterface {
private Debugger debugger;

View File

@ -36,6 +36,10 @@
*/
package org.jooq.debug.console.remote;
import org.jooq.debug.Breakpoint;
import org.jooq.debug.BreakpointAfterExecutionHit;
import org.jooq.debug.BreakpointBeforeExecutionHit;
import org.jooq.debug.BreakpointHitHandler;
import org.jooq.debug.LocalDebugger;
import org.jooq.debug.LoggingListener;
import org.jooq.debug.QueryLoggingData;
@ -44,6 +48,9 @@ import org.jooq.debug.StatementMatcher;
import org.jooq.debug.console.DatabaseDescriptor;
import org.jooq.debug.console.remote.messaging.CommunicationInterface;
/**
* @author Christopher Deckers
*/
class ServerDebugger extends LocalDebugger {
public ServerDebugger(DatabaseDescriptor databaseDescriptor) {
@ -82,6 +89,26 @@ class ServerDebugger extends LocalDebugger {
}
}
private void setBreakpointHitHandlerActive(boolean isActive) {
if(isActive) {
setBreakpointHitHandler(new BreakpointHitHandler() {
@Override
public void processBreakpointBeforeExecutionHit(BreakpointBeforeExecutionHit breakpointHit) {
BreakpointBeforeExecutionHit modifiedBreakpointHit = (BreakpointBeforeExecutionHit)new ClientDebugger.CMC_processBreakpointBeforeExecutionHit().syncExec(communicationInterface, breakpointHit);
if(modifiedBreakpointHit != null) {
breakpointHit.setExecutionType(modifiedBreakpointHit.getExecutionType(), modifiedBreakpointHit.getSql());
}
}
@Override
public void processBreakpointAfterExecutionHit(BreakpointAfterExecutionHit breakpointHit) {
new ClientDebugger.CMC_processBreakpointAfterExecutionHit().syncExec(communicationInterface, breakpointHit);
}
});
} else {
setBreakpointHitHandler(null);
}
}
@SuppressWarnings("serial")
static class CMS_setLoggingStatementMatchers extends ServerDebuggerCommandMessage {
@Override
@ -91,4 +118,22 @@ class ServerDebugger extends LocalDebugger {
}
}
@SuppressWarnings("serial")
static class CMS_setBreakpoints extends ServerDebuggerCommandMessage {
@Override
public Object run(Object[] args) {
getDebugger().setBreakpoints((Breakpoint[])args[0]);
return null;
}
}
@SuppressWarnings("serial")
static class CMS_setBreakpointHitHandlerActive extends ServerDebuggerCommandMessage {
@Override
public Object run(Object[] args) {
getDebugger().setBreakpointHitHandlerActive((Boolean)args[0]);
return null;
}
}
}

View File

@ -38,6 +38,9 @@ package org.jooq.debug.console.remote;
import org.jooq.debug.console.remote.messaging.CommandMessage;
/**
* @author Christopher Deckers
*/
@SuppressWarnings("serial")
abstract class ServerDebuggerCommandMessage extends CommandMessage {

View File

@ -42,7 +42,7 @@ import java.net.ServerSocket;
import java.net.Socket;
/**
* The native interface, which establishes the link between a peer VM and this local side.
* The communication interface, which establishes the link between a peer VM and this local side.
* @author Christopher Deckers
*/
public class CommunicationInterface {

View File

@ -37,6 +37,9 @@
package org.jooq.debug.console.remote.messaging;
/**
* @author Christopher Deckers
*/
public interface CommunicationInterfaceFactory {
public CommunicationInterface createCommunicationInterface(int port);