Package com.dci.intellij.dbn.debugger

Source Code of com.dci.intellij.dbn.debugger.DBProgramDebugProcess$DebugOperationThread

package com.dci.intellij.dbn.debugger;

import com.dci.intellij.dbn.common.Constants;
import com.dci.intellij.dbn.common.thread.BackgroundTask;
import com.dci.intellij.dbn.common.thread.SimpleLaterInvocator;
import com.dci.intellij.dbn.common.util.DocumentUtil;
import com.dci.intellij.dbn.common.util.EditorUtil;
import com.dci.intellij.dbn.common.util.MessageUtil;
import com.dci.intellij.dbn.connection.ConnectionHandler;
import com.dci.intellij.dbn.database.DatabaseDebuggerInterface;
import com.dci.intellij.dbn.database.common.debug.BreakpointInfo;
import com.dci.intellij.dbn.database.common.debug.DebuggerRuntimeInfo;
import com.dci.intellij.dbn.database.common.debug.DebuggerSessionInfo;
import com.dci.intellij.dbn.database.common.debug.ExecutionBacktraceInfo;
import com.dci.intellij.dbn.debugger.breakpoint.DBProgramBreakpointHandler;
import com.dci.intellij.dbn.debugger.evaluation.DBProgramDebuggerEditorsProvider;
import com.dci.intellij.dbn.debugger.execution.DBProgramRunConfiguration;
import com.dci.intellij.dbn.debugger.frame.DBProgramDebugSuspendContext;
import com.dci.intellij.dbn.editor.code.SourceCodeEditor;
import com.dci.intellij.dbn.execution.method.MethodExecutionInput;
import com.dci.intellij.dbn.execution.method.MethodExecutionManager;
import com.dci.intellij.dbn.language.common.element.util.ElementTypeAttribute;
import com.dci.intellij.dbn.language.common.psi.BasePsiElement;
import com.dci.intellij.dbn.language.psql.PSQLFile;
import com.dci.intellij.dbn.object.DBMethod;
import com.dci.intellij.dbn.object.DBSchema;
import com.dci.intellij.dbn.object.common.DBSchemaObject;
import com.dci.intellij.dbn.vfs.DatabaseEditableObjectFile;
import com.dci.intellij.dbn.vfs.SourceCodeFile;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.LogicalPosition;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.fileEditor.FileEditor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.xdebugger.XDebugProcess;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XDebuggerManager;
import com.intellij.xdebugger.XDebuggerUtil;
import com.intellij.xdebugger.XSourcePosition;
import com.intellij.xdebugger.breakpoints.XBreakpointHandler;
import com.intellij.xdebugger.breakpoints.XBreakpointManager;
import com.intellij.xdebugger.breakpoints.XBreakpointType;
import com.intellij.xdebugger.breakpoints.XLineBreakpoint;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import org.jetbrains.annotations.NotNull;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collection;

public class DBProgramDebugProcess extends XDebugProcess {
    private Connection targetConnection;
    private Connection debugConnection;
    private ConnectionHandler connectionHandler;
    private DBProgramBreakpointHandler breakpointHandler;
    private DBProgramBreakpointHandler[] breakpointHandlers;
    private MethodExecutionInput executionInput;
    private BreakpointInfo defaultBreakpointInfo;
    private DBProgramDebugProcessStatus status = new DBProgramDebugProcessStatus();

    private transient DebuggerRuntimeInfo runtimeInfo;
    private transient ExecutionBacktraceInfo backtraceInfo;


    public DBProgramDebugProcess(@NotNull XDebugSession session, ConnectionHandler connectionHandler) {
        super(session);
        this.connectionHandler = connectionHandler;
        Project project = session.getProject();
        DatabaseDebuggerManager.getInstance(project).registerDebugSession(connectionHandler);

        DBProgramRunConfiguration runProfile = (DBProgramRunConfiguration) session.getRunProfile();
        executionInput = runProfile.getExecutionInput();

        breakpointHandler = new DBProgramBreakpointHandler(session, this);
        breakpointHandlers = new DBProgramBreakpointHandler[]{breakpointHandler};
    }

    public DBProgramDebugProcessStatus getStatus() {
        return status;
    }

    public Connection getTargetConnection() {
        return targetConnection;
    }

    public Connection getDebugConnection() {
        return debugConnection;
    }

    public ConnectionHandler getConnectionHandler() {
        return connectionHandler;
    }

    @NotNull
    @Override
    public XBreakpointHandler<?>[] getBreakpointHandlers() {
        return breakpointHandlers;
    }

    @NotNull
    @Override
    public XDebuggerEditorsProvider getEditorsProvider() {
        return DBProgramDebuggerEditorsProvider.INSTANCE;
    }

    @Override
    public void sessionInitialized() {
        final Project project = getSession().getProject();
        new BackgroundTask(project, "Initialize debug environment", true) {
            @Override
            public void execute(@NotNull ProgressIndicator progressIndicator) {
                try {
                    targetConnection = connectionHandler.getPoolConnection(executionInput.getExecutionSchema());
                    targetConnection.setAutoCommit(false);
                    debugConnection = connectionHandler.getPoolConnection();

                    DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                    progressIndicator.setText("Initializing debugger target session");
                    DebuggerSessionInfo sessionInfo = debuggerInterface.initializeSession(targetConnection);
                    debuggerInterface.enableDebugging(targetConnection);
                    debuggerInterface.attachSession(sessionInfo.getSessionId(), debugConnection);

                    synchronizeSession();
                    executeMethod();
                } catch (SQLException e) {
                    getSession().stop();
                    showErrorDialog(e);
                }
            }
        }.start();
    }

    private void synchronizeSession() {
        final Project project = getSession().getProject();
        new BackgroundTask(project, "Initialize debug environment", true) {

            public void execute(@NotNull ProgressIndicator progressIndicator) {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                if (getStatus().PROCESS_IS_TERMINATING) getSession().stop();
                try {
                    progressIndicator.setText("Synchronizing debug session");
                    runtimeInfo = debuggerInterface.synchronizeSession(debugConnection);

                    if (getStatus().TARGET_EXECUTION_TERMINATED) {
                        getSession().stop();
                    } else {
                        getStatus().CAN_SET_BREAKPOINTS = true;
                        progressIndicator.setText("Registering breakpoints");
                        registerBreakpoints();
                        runtimeInfo = debuggerInterface.stepOver(debugConnection);

                        progressIndicator.setText("Suspending session");
                        suspendSession();
                    }
                } catch (SQLException e) {
                    // typically a timeout
                    getSession().stop();
                    MessageUtil.showErrorDialog("Could not initialize debug environment on connection \"" + connectionHandler.getName() + "\". ", e);
                }
            }
        }.start();
    }

    private void executeMethod() {
        new DebugOperationThread("execute method") {
            public void executeOperation() throws SQLException {
                XDebugSession session = getSession();
                MethodExecutionManager executionManager = MethodExecutionManager.getInstance(session.getProject());
                if (getStatus().PROCESS_IS_TERMINATING) return;

                boolean success = executionManager.debugExecute(executionInput, targetConnection);

                if (!success) {
                    // if the method execution threw exception, the debugger-off statement is not reached,
                    // hence the session will hag as debuggable. To avoid this, disable debugging has
                    // to explicitly be called here
                    getDebuggerInterface().disableDebugging(targetConnection);
                }

                getStatus().TARGET_EXECUTION_TERMINATED = true;
                connectionHandler.freePoolConnection(targetConnection);
                targetConnection = null;
            }
        }.start();
    }

    /**
     * breakpoints need to be registered after the database session is started,
     * otherwise they do not get valid ids
     */
    private void registerBreakpoints() {
        Runnable readProcess = new Runnable() {
            public void run() {
                XBreakpointType localXBreakpointType = XDebuggerUtil.getInstance().findBreakpointType(breakpointHandler.getBreakpointTypeClass());
                Project project = getSession().getProject();
                XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager();
                Collection<XLineBreakpoint> breakpoints= breakpointManager.getBreakpoints(localXBreakpointType);

                for (XLineBreakpoint breakpoint : breakpoints) {
                    breakpointHandler.registerBreakpoint(breakpoint);
                }

                registerDefaultBreakpoint();
            }
        };
        ApplicationManager.getApplication().runReadAction(readProcess);
    }

    private void registerDefaultBreakpoint() {
        SourceCodeFile sourceCodeFile = (SourceCodeFile) getMainDatabaseFile().getMainContentFile();
        PSQLFile psqlFile = (PSQLFile) sourceCodeFile.getPsiFile();
        if (psqlFile != null) {
            DBMethod method = executionInput.getMethod();
            if (method != null) {
                BasePsiElement basePsiElement = psqlFile.lookupObjectDeclaration(method.getObjectType().getGenericType(), method.getName());
                if (basePsiElement != null) {
                    BasePsiElement subject = basePsiElement.lookupFirstPsiElement(ElementTypeAttribute.SUBJECT);
                    int offset = subject.getTextOffset();
                    Document document = DocumentUtil.getDocument(psqlFile);
                    int line = document.getLineNumber(offset);

                    DBSchemaObject schemaObject = getMainDatabaseObject();
                    try {
                        defaultBreakpointInfo = getDebuggerInterface().addBreakpoint(
                                method.getSchema().getName(),
                                schemaObject.getName(),
                                schemaObject.getObjectType().getName().toUpperCase(),
                                line,
                                debugConnection);
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * breakpoints need to be unregistered before closing the database session, otherwise they remain resident.
     */
    private void unregisterBreakpoints() {
        Runnable readProcess = new Runnable() {
            public void run() {
                XBreakpointType localXBreakpointType = XDebuggerUtil.getInstance().findBreakpointType(breakpointHandler.getBreakpointTypeClass());
                Project project = getSession().getProject();
                XBreakpointManager breakpointManager = XDebuggerManager.getInstance(project).getBreakpointManager();
                Collection<XLineBreakpoint> breakpoints= breakpointManager.getBreakpoints(localXBreakpointType);

                for (XLineBreakpoint breakpoint : breakpoints) {
                    breakpointHandler.unregisterBreakpoint(breakpoint, false);
                }

                try {
                    if (defaultBreakpointInfo != null) {
                        getDebuggerInterface().removeBreakpoint(defaultBreakpointInfo.getBreakpointId(), debugConnection);
                    }

                } catch (SQLException e) {
                }
            }
        };
        ApplicationManager.getApplication().runReadAction(readProcess);
    }

    @Override
    public void stop() {
        executionInput.setExecutionCancelled(!getStatus().PROCESS_STOPPED_NORMALLY);
        final Project project = getSession().getProject();

        if (getStatus().PROCESS_IS_TERMINATING) return;

        new BackgroundTask(project, "Stopping debugger", true) {
            public void execute(@NotNull ProgressIndicator progressIndicator) {
                progressIndicator.setText("Cancelling / resuming method execution.");
                try {
                    getStatus().PROCESS_IS_TERMINATING = true;
                    unregisterBreakpoints();
                    rollOutDebugger();
                    getStatus().CAN_SET_BREAKPOINTS = false;

                    if (debugConnection != null) {
                        DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                        runtimeInfo = debuggerInterface.stopExecution(debugConnection);
                        debuggerInterface.detachSession(debugConnection);

                        connectionHandler.freePoolConnection(debugConnection);
                        debugConnection = null;
                    }
                    getStatus().PROCESS_IS_TERMINATED = true;
                } catch (final SQLException e) {
                    showErrorDialog(e);
                } finally {
                    DatabaseDebuggerManager.getInstance(project).unregisterDebugSession(connectionHandler);
                }
            }
        }.start();
    }

    @Override
    public void startStepOver() {
        new DebugOperationThread("step over") {
            public void executeOperation() throws SQLException {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                runtimeInfo = debuggerInterface.stepOver(debugConnection);
                suspendSession();
            }
        }.start();
    }

    @Override
    public void startStepInto() {
        new DebugOperationThread("step into") {
            public void executeOperation() throws SQLException {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                runtimeInfo = debuggerInterface.stepInto(debugConnection);
                suspendSession();
            }
        }.start();
    }

    @Override
    public void startStepOut() {
        new DebugOperationThread("step out") {
            public void executeOperation() throws SQLException {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                runtimeInfo = debuggerInterface.stepOut(debugConnection);
                suspendSession();
            }
        }.start();
    }

    @Override
    public void resume() {
        new DebugOperationThread("resume execution") {
            public void executeOperation() throws SQLException {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                runtimeInfo = debuggerInterface.resumeExecution(debugConnection);
                suspendSession();
            }
        }.start();
    }

    @Override
    public void runToPosition(@NotNull final XSourcePosition position) {
        new DebugOperationThread("run to position") {
            public void executeOperation() throws SQLException {
                DBSchemaObject object = DBProgramDebugUtil.getObject(position);
                if (object != null) {
                    DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                    runtimeInfo = debuggerInterface.runToPosition(
                            object.getSchema().getName(),
                            object.getName(),
                            object.getObjectType().getName().toUpperCase(),
                            position.getLine(),
                            debugConnection);
                }

                suspendSession();
            }
        }.start();
    }

    @Override
    public void startPausing() {
        // NOT SUPPORTED!!!
        new DebugOperationThread("run to position") {
            public void executeOperation() throws SQLException {
                DatabaseDebuggerInterface debuggerInterface = getDebuggerInterface();
                runtimeInfo = debuggerInterface.synchronizeSession(debugConnection);
                suspendSession();
            }
        }.start();
    }

    private void showErrorDialog(SQLException e) {
        MessageUtil.showErrorDialog("Could not perform operation.", e);
    }

    private void suspendSession() {
        if (getStatus().PROCESS_IS_TERMINATING) return;

        if (runtimeInfo.getOwnerName() == null) {
            getStatus().PROCESS_STOPPED_NORMALLY = true;
            getSession().stop();
        } else {
            try {
                backtraceInfo = getDebuggerInterface().getExecutionBacktraceInfo(debugConnection);
            } catch (SQLException e) {
                showErrorDialog(e);
            }
            DatabaseEditableObjectFile databaseFile = getDatabaseFile(runtimeInfo);
            DBProgramDebugSuspendContext suspendContext = new DBProgramDebugSuspendContext(this);
            getSession().positionReached(suspendContext);
            navigateInEditor(databaseFile, runtimeInfo.getLineNumber());
        }
    }

    public DatabaseEditableObjectFile getDatabaseFile(DebuggerRuntimeInfo runtimeInfo) {
        DBSchemaObject schemaObject = getDatabaseObject(runtimeInfo);
        return schemaObject.getVirtualFile();
    }

    public DBSchemaObject getDatabaseObject(DebuggerRuntimeInfo runtimeInfo) {
        DBSchema schema = connectionHandler.getObjectBundle().getSchema(runtimeInfo.getOwnerName());
        DBSchemaObject schemaObject = schema.getProgram(runtimeInfo.getProgramName());
        if (schemaObject == null) schemaObject = schema.getMethod(runtimeInfo.getProgramName(), 0); // overload 0 is assuming debug is only supported in oracle (no schema method overloading)
        return schemaObject;
    }

    private DatabaseEditableObjectFile getMainDatabaseFile() {
        DBSchemaObject schemaObject = getMainDatabaseObject();
        return schemaObject.getVirtualFile();
    }

    private DBSchemaObject getMainDatabaseObject() {
        DBMethod method = executionInput.getMethod();
        return method.isProgramMethod() ? method.getProgram() : method;
    }

    private void rollOutDebugger() {
        try {
            while (runtimeInfo!= null && !runtimeInfo.isTerminated()) {
                runtimeInfo = getDebuggerInterface().stepOut(debugConnection);
            }
        } catch (SQLException e) {
            showErrorDialog(e);
        }
    }

    private void navigateInEditor(final DatabaseEditableObjectFile databaseFile, final int line) {
        new SimpleLaterInvocator() {
            public void execute() {
                // todo review this!!!
                SourceCodeEditor sourceCodeEditor = null;
                if (databaseFile.getContentFiles().size() > 1) {
                    FileEditorManager editorManager = FileEditorManager.getInstance(databaseFile.getProject());
                    FileEditor[] fileEditors = editorManager.getEditors(databaseFile);
                    if (fileEditors.length >= runtimeInfo.getNamespace()) {
                        FileEditor fileEditor = fileEditors[runtimeInfo.getNamespace() -1];
                        sourceCodeEditor = (SourceCodeEditor) fileEditor;
                        databaseFile.FAKE_DOCUMENT.set(sourceCodeEditor.getEditor().getDocument());
                    } else {
                        FileEditor fileEditor = EditorUtil.getFileEditor(databaseFile, databaseFile.getMainContentFile());
                        if (fileEditor != null && fileEditor instanceof SourceCodeEditor) {
                            sourceCodeEditor = (SourceCodeEditor) fileEditor;
                        }
                    }
                } else {
                    FileEditor fileEditor = EditorUtil.getFileEditor(databaseFile, databaseFile.getMainContentFile());
                    if (fileEditor != null && fileEditor instanceof SourceCodeEditor) {
                        sourceCodeEditor = (SourceCodeEditor) fileEditor;
                    }
                }
                LogicalPosition position = new LogicalPosition(line, 0);
                EditorUtil.selectEditor(databaseFile, sourceCodeEditor);
                if (sourceCodeEditor != null) sourceCodeEditor.getEditor().getScrollingModel().scrollTo(position, ScrollType.CENTER);
            }
        }.start();
    }

    public DatabaseDebuggerInterface getDebuggerInterface() {
        return connectionHandler.getInterfaceProvider().getDebuggerInterface();
    }

    public DebuggerRuntimeInfo getRuntimeInfo() {
        return runtimeInfo;
    }

    public ExecutionBacktraceInfo getBacktraceInfo() {
        return backtraceInfo;
    }

    abstract class DebugOperationThread extends Thread {
        private String operationName;
        protected DebugOperationThread(String operationName) {
            super(Constants.DBN_TITLE_PREFIX + "Debug (" + operationName + ")");
            this.operationName = operationName;
        }

        @Override
        public final void run() {
            try {
                executeOperation();
            } catch (final SQLException e) {
                MessageUtil.showErrorDialog("Could not perform debug operation (" + operationName + ").", e);
            }
        }
        public abstract void executeOperation() throws SQLException;
    }
}
TOP

Related Classes of com.dci.intellij.dbn.debugger.DBProgramDebugProcess$DebugOperationThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.