Update of /cvsroot/e-p-i-c/org.epic.debug/src/org/epic/debug/cgi In directory sc8-pr-cvs5.sourceforge.net:/tmp/cvs-serv11289/src/org/epic/debug/cgi Added Files: CGIDebugTarget.java CGILaunchConfigurationDelegate.java CGIProxy.java CGIConsoleColorProvider.java CGIBrowser.java Removed Files: CustomBrowser.java StringReaderThread.java StreamForwarder.java run.cmd CGIConfig.java ProcessExecutor.java run.sh EpicCgiHandler.java ProcessOutput.java Log Message: - Global refactoring and clean-up, focused on PerlDB and launch configuration delegates. - New class DebuggerInterface for low-level communication with "perl -d", separate from the former gigantic PerlDB. - Created new packages to better reflect different types of launch configurations and related code dependencies. - Made "Perl Remote" launch configurations actually work (at least on simple examples). - Hopefully fixed bug [ 1602375 ] Debugger hangs under Windows. --- NEW FILE: CGIBrowser.java --- package org.epic.debug.cgi; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.help.browser.IBrowser; import org.eclipse.help.internal.browser.BrowserDescriptor; import org.eclipse.help.internal.browser.BrowserManager; import org.eclipse.swt.widgets.Shell; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PartInitException; import org.epic.core.views.browser.BrowserView; import org.epic.debug.PerlDebugPlugin; import org.epic.debug.PerlLaunchConfigurationConstants; import org.epic.debug.cgi.server.CustomBrowser; /** * Represents a web browser instance launched to request a CGI script * during execution of a "Perl CGI" launch configuration. Encapsulates * the code for opening and closing the browser and for reporting failure * to user. */ public class CGIBrowser { private final ILaunch mLaunch; private final int mWebserverPort; private final String mHtmlRootFileRel; private IBrowser mBrowser; /** * Creates an instance which will be used to request the given * relative URI from a web server running on the given port as * part of the given launch. */ public CGIBrowser(ILaunch launch, String relURL, int port) { mLaunch = launch; mWebserverPort = port; mHtmlRootFileRel = relURL; } /** * Closes the web browser opened by a call to {@link #open}. * If open has not been called or has failed, this method has no effect. */ public void close() { if (mBrowser != null) mBrowser.close(); } /** * Opens the browser with the URL configured at construction time. * If an appropriate web browser is already running, it may be reused. * In case of misconfiguration, the method may fail and display * an error dialog instead. */ public void open() { DebugPlugin.getDefault().asyncExec(new Runnable() { public void run() { try { startBrowserImpl(); } catch (Exception e) { PerlDebugPlugin.getDefault().logError( "Could not start browser for CGI debugging", e); } } }); } private String getLaunchAttribute(String attrName) throws CoreException { return mLaunch.getLaunchConfiguration().getAttribute( attrName, (String) null); } private void startBrowserImpl() throws Exception { String browserID = getLaunchAttribute( PerlLaunchConfigurationConstants.ATTR_BROWSER_ID); String browserPath = getLaunchAttribute( PerlLaunchConfigurationConstants.ATTR_CUSTOM_BROWSER_PATH); if (browserID.equals(BrowserView.ID_BROWSER)) { showBrowserView(); return; } else if (CustomBrowser.isCustomBrowserID(browserID)) { mBrowser = new CustomBrowser(browserPath); } else { BrowserDescriptor[] browserDescr = getBrowserDescriptor(); for (int i = 0; i < browserDescr.length; i++) { BrowserDescriptor descr = browserDescr[i]; if (descr.getID().equals(browserID)) { mBrowser = descr.getFactory().createBrowser(); break; } } } if (mBrowser != null) { mBrowser.displayURL( "http://localhost:" + mWebserverPort + "/"+ mHtmlRootFileRel); } else { PerlDebugPlugin.getDefault().logError( "Could not find browser for CGI debugging."); } } private BrowserDescriptor[] getBrowserDescriptor() { Shell shell = PerlDebugPlugin.getActiveWorkbenchShell(); if (shell == null) return new BrowserDescriptor[0]; final Object[] ret = new Object[1]; shell.getDisplay().syncExec(new Runnable() { public void run() { ret[0] = BrowserManager.getInstance().getBrowserDescriptors(); } }); return (BrowserDescriptor[]) ret[0]; } private void showBrowserView() throws PartInitException { Shell shell = PerlDebugPlugin.getActiveWorkbenchShell(); if (shell == null) return; shell.getDisplay().syncExec(new Runnable() { public void run() { try { IWorkbenchPage activePage = PerlDebugPlugin.getWorkbenchWindow().getActivePage(); BrowserView view = (BrowserView) activePage.showView(BrowserView.ID_BROWSER); view.setUrl("http://localhost:" + mWebserverPort + "/"); } catch (PartInitException e) { PerlDebugPlugin.getDefault().logError( "Could not open browser view.", e); } } }); } } --- NEW FILE: CGIProxy.java --- /* * Created on 03.04.2004 * * To change the template for this generated file go to * Window>Preferences>Java>Code Generation>Code and Comments */ package org.epic.debug.cgi; import java.io.IOException; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.PlatformObject; import org.eclipse.debug.core.*; import org.eclipse.debug.core.model.*; import org.epic.debug.PerlDebugPlugin; import org.epic.debug.util.OutputStreamMonitor; import org.epic.debug.util.RemotePort; /** * @author ST * * To change the template for this generated type comment go to * Window>Preferences>Java>Code Generation>Code and Comments */ public class CGIProxy extends PlatformObject implements IProcess, ITerminate { private volatile boolean mIsConnected; private Thread mWaitThread; private OutputStreamMonitor mMonitorError; private OutputStreamMonitor mMonitorOut; private OutputStreamMonitor mMonitorIn; private ILaunch mLaunch; private String mLabel; private RemotePort mInStream; private RemotePort mOutStream; private RemotePort mErrorStream; private boolean mIsTerminated; private IStreamsProxy mStreamsProxy; public CGIProxy(ILaunch fLaunch, String fLabel) { mLaunch = fLaunch; ((IProcess) this).setAttribute(ATTR_PROCESS_TYPE, "EpicCGIProxy"); mIsConnected = false; mIsTerminated = false; mLabel = fLabel; mInStream = new RemotePort("CGIProxy.mInStream"); mInStream.startConnect(); mOutStream = new RemotePort("CGIProxy.mOutStream"); mOutStream.startConnect(); mErrorStream = new RemotePort("CGIProxy.mErrorStream"); mErrorStream.startConnect(); mWaitThread = new Thread("EPIC-Debugger:CGIProxy") { public void run() { try { int ret; ret = mInStream.waitForConnect(false); if (ret == RemotePort.WAIT_ERROR) PerlDebugPlugin.getDefault().logError( "Could not connect to CGI-Console"); if (ret == RemotePort.WAIT_OK) { ret = mOutStream.waitForConnect(true); if (ret == RemotePort.WAIT_OK) ret = mErrorStream.waitForConnect(true); } if (ret != RemotePort.WAIT_OK) { if (ret == RemotePort.WAIT_ERROR) PerlDebugPlugin.getDefault().logError( "Could not connect to CGI-Console"); terminate(); return; } } catch (DebugException e) { PerlDebugPlugin.getDefault().logError( "Could not connect to CGI-Console", e); } mMonitorIn = new OutputStreamMonitor(mInStream.getInStream()); mMonitorOut = new OutputStreamMonitor(mOutStream.getInStream()); mMonitorError = new OutputStreamMonitor(mErrorStream.getInStream()); mMonitorIn.startMonitoring(); mMonitorOut.startMonitoring(); mMonitorError.startMonitoring(); mStreamsProxy = new IStreamsProxy() { public IStreamMonitor getErrorStreamMonitor() { return mMonitorError; } public IStreamMonitor getOutputStreamMonitor() { return mMonitorOut; } public void write(String input) throws IOException { // we can't provide input to a CGI process // through console } }; mIsConnected = true; fireCreationEvent(); } }; mWaitThread.start(); } public boolean isConnected() { return mIsConnected; } public int getErrorPort() { return mErrorStream.getServerPort(); } public int getInPort() { return mInStream.getServerPort(); } public int getOutPort() { return mOutStream.getServerPort(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getLabel() */ public String getLabel() { return mLabel; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getLaunch() */ public ILaunch getLaunch() { return mLaunch; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getStreamsProxy() */ public IStreamsProxy getStreamsProxy() { return mStreamsProxy; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#setAttribute(java.lang.String, java.lang.String) */ public void setAttribute(String key, String value) { ILaunchConfigurationWorkingCopy workingcopy; try { workingcopy = mLaunch.getLaunchConfiguration().getWorkingCopy(); workingcopy.setAttribute(key, value); workingcopy.doSave(); } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getAttribute(java.lang.String) */ public String getAttribute(String key) { try { return mLaunch.getLaunchConfiguration().getAttribute( key, (String) null); } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IProcess#getExitValue() */ public int getExitValue() throws DebugException { return 0; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ public Object getAdapter(Class adapter) { if (adapter.equals(IProcess.class)) { return this; } if (adapter.equals(IDebugTarget.class)) { ILaunch launch = getLaunch(); IDebugTarget[] targets = launch.getDebugTargets(); for (int i = 0; i < targets.length; i++) { if (this.equals(targets[i].getProcess())) { return targets[i]; } } return null; } return super.getAdapter(adapter); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */ public boolean canTerminate() { return !isTerminated(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */ public boolean isTerminated() { return mIsTerminated; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.ITerminate#terminate() */ public void terminate() throws DebugException { shutdown(); } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStreamsProxy#getErrorStreamMonitor() */ public IStreamMonitor getErrorStreamMonitor() { return mMonitorError; } /* (non-Javadoc) * @see org.eclipse.debug.core.model.IStreamsProxy#getOutputStreamMonitor() */ public IStreamMonitor getOutputStreamMonitor() { return mMonitorOut; } public IStreamMonitor getInputStreamMonitor() { return mMonitorIn; } /** * Fire a debug event marking the creation of this element. */ private void fireCreationEvent() { fireEvent(new DebugEvent(this, DebugEvent.CREATE)); } /** * Fire a debug event */ private void fireEvent(DebugEvent event) { DebugPlugin manager = DebugPlugin.getDefault(); if (manager != null) { manager.fireDebugEventSet(new DebugEvent[] { event }); } } /** * Fire a debug event marking the termination of this process. */ private void fireTerminateEvent() { fireEvent(new DebugEvent(this, DebugEvent.TERMINATE)); } void shutdown() { mMonitorError.kill(); mMonitorOut.kill(); mMonitorIn.kill(); mInStream.shutdown(); mOutStream.shutdown(); mErrorStream.shutdown(); mIsTerminated = true; fireTerminateEvent(); } /** * @return */ public Thread getWaitThread() { return mWaitThread; } public void waitForConnect() { try { mWaitThread.join(30000); } catch (InterruptedException e) { } } } --- ProcessExecutor.java DELETED --- --- run.sh DELETED --- --- NEW FILE: CGIConsoleColorProvider.java --- /* * Created on 17.04.2004 * * To change the template for this generated file go to * Window>Preferences>Java>Code Generation>Code and Comments */ package org.epic.debug.cgi; /******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.debug.ui.console.IConsole; import org.eclipse.debug.ui.console.IConsoleColorProvider; import org.eclipse.swt.graphics.Color; /** * Default console color provider for a processs. Colors output to standard * out, in, and error, as specified by user preferences. * <p> * Clients implementing a console color provider should subclass this class. * </p> * @since 2.1 */ public class CGIConsoleColorProvider implements IConsoleColorProvider { private IProcess fProcess; private IConsole fConsole; /** * @see IConsoleColorProvider#connect(IProcess, IConsole) */ public void connect(IProcess process, IConsole console) { fProcess = process; fConsole = console; console.connect(((CGIProxy)process).getErrorStreamMonitor(),IDebugUIConstants.ID_STANDARD_ERROR_STREAM); console.connect(((CGIProxy)process).getInputStreamMonitor(),IDebugUIConstants.ID_STANDARD_INPUT_STREAM); console.connect(((CGIProxy)process).getOutputStreamMonitor(),IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM); } /** * @see IConsoleColorProvider#disconnect() */ public void disconnect() { fConsole = null; fProcess = null; } /** * @see IConsoleColorProvider#isReadOnly() */ public boolean isReadOnly() { return true; //return fProcess == null || fProcess.isTerminated(); } /** * @see IConsoleColorProvider#getColor(String) */ public Color getColor(String streamIdentifer) { if (IDebugUIConstants.ID_STANDARD_OUTPUT_STREAM.equals(streamIdentifer)) { return DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_SYS_OUT_COLOR); } if (IDebugUIConstants.ID_STANDARD_ERROR_STREAM.equals(streamIdentifer)) { return DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_SYS_ERR_COLOR); } if (IDebugUIConstants.ID_STANDARD_INPUT_STREAM.equals(streamIdentifer)) { return DebugUIPlugin.getPreferenceColor(IDebugPreferenceConstants.CONSOLE_SYS_IN_COLOR); } return null; } /** * Returns the process this color provider is providing color for, or * <code>null</code> if none. * * @return the process this color provider is providing color for, or * <code>null</code> if none */ protected IProcess getProcess() { return fProcess; } /** * Returns the consonle this color provider is connected to, or * <code>null</code> if none. * * @return IConsole the consonle this color provider is connected to, or * <code>null</code> if none */ protected IConsole getConsole() { return fConsole; } } --- CGIConfig.java DELETED --- --- NEW FILE: CGILaunchConfigurationDelegate.java --- package org.epic.debug.cgi; import java.io.*; import java.net.URL; import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.debug.core.*; import org.eclipse.debug.core.model.IProcess; import org.epic.core.PerlCore; import org.epic.core.PerlProject; import org.epic.core.util.PerlExecutableUtilities; import org.epic.debug.*; import org.epic.debug.util.RemotePort; import org.osgi.framework.Bundle; /** * Executes launch configurations of type "Perl CGI". */ public class CGILaunchConfigurationDelegate extends LaunchConfigurationDelegate { protected void doLaunch( ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor monitor) throws CoreException { RemotePort debugPort = createDebugPort(launch); try { CGIProxy cgiProxy = new CGIProxy(launch, "CGI Process"); int brazilPort = RemotePort.findFreePort(); IProcess process = startBrazil( launch, cgiProxy, brazilPort, debugPort); cgiProxy.waitForConnect(); if (!cgiProxy.isConnected()) { PerlDebugPlugin.getDefault().logError( "(CGI-Target) Could not connect to CGI-Proxy"); launch.terminate(); return; } launch.addProcess(cgiProxy); openBrowser(launch, brazilPort); if (debugPort != null) createCGIDebugTarget(launch, process, debugPort); } catch (CoreException e) { if (debugPort != null) debugPort.shutdown(); launch.terminate(); throw e; } } private BrazilProps createBrazilProps( ILaunch launch, CGIProxy cgiProxy, int brazilPort, int debugPort) throws CoreException { String htmlRootDir = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_HTML_ROOT_DIR, true); String cgiRootDir = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_CGI_ROOT_DIR, true); String cgiFileExtension = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_CGI_FILE_EXTENSION, false); String perlParams = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_PERL_PARAMETERS, false); if (perlParams == null) perlParams = ""; perlParams = perlParams.replaceAll("[\\n\\r]", " "); PerlProject project = PerlCore.create(getProject(launch)); String perlPath = PerlExecutableUtilities.getPerlInterpreterPath(); if (perlPath == null) perlPath = ""; // TODO report an error? BrazilProps props = new BrazilProps(); props.add("cgi.InPort", cgiProxy.getInPort()); props.add("cgi.OutPort", cgiProxy.getOutPort()); props.add("cgi.ErrorPort", cgiProxy.getErrorPort()); props.add("cgi.Debug", isDebugMode(launch)); props.add("root", htmlRootDir); props.add("port", brazilPort); props.add("cgi.root", cgiRootDir); props.add("cgi.executable", perlPath); props.add("cgi.suffix", cgiFileExtension); props.add("cgi.PerlParams", perlParams); props.add("cgi.DebugInclude", " -I" + PerlDebugPlugin.getDefault().getInternalDebugInc()); props.add("cgi.RunInclude", PerlExecutableUtilities.getPerlIncArgs(project)); String[] env = PerlDebugPlugin.getDebugEnv(launch, debugPort); for (int i = 0; i < env.length; i++) props.add("cgi.ENV_" + env[i]); return props; } private void createCGIDebugTarget( ILaunch launch, IProcess process, RemotePort debugPort) throws CoreException { if (debugPort.waitForConnect(true) != RemotePort.WAIT_OK) { PerlDebugPlugin.errorDialog("Could not connect to debug port!"); debugPort.shutdown(); launch.terminate(); return; } else { CGIDebugTarget target = new CGIDebugTarget( launch, process, debugPort, getLocalWorkingDir(launch)); launch.addDebugTarget(target); } } private RemotePort createDebugPort(ILaunch launch) { if (!isDebugMode(launch)) return null; RemotePort debugPort = new RemotePort("DebugTarget.mDebugPort"); debugPort.startConnect(); return debugPort; } /** * @return a List of Files representing entries of the classpath * passed to the Brazil (web server) JVM */ private List getBrazilJVMClasspath() throws CoreException { try { List cp = new ArrayList(); URL brazilUrl = Platform.resolve(Platform.getBundle("org.epic.lib") .getEntry("/lib/brazil_mini.jar")); assert "file".equalsIgnoreCase(brazilUrl.getProtocol()) : "brazil_mini.jar must reside in the file system"; cp.add(urlToFile(brazilUrl)); Bundle bundle = PerlDebugPlugin.getDefault().getBundle(); URL binUrl = bundle.getEntry("/bin"); if (binUrl != null) { binUrl = Platform.resolve(binUrl); assert binUrl.getProtocol().equalsIgnoreCase("file"); // 'bin' folder exists = we're running inside of // a hosted workbench cp.add(urlToFile(binUrl)); } else { URL dirUrl = Platform.resolve(bundle.getEntry("/")); if (dirUrl.getProtocol().equalsIgnoreCase("jar")) { // org.epic.debug was deployed as a jar; add this jar // to the classpath String path = dirUrl.getPath(); assert path.startsWith("file:"); assert path.endsWith(".jar!/"); URL jarUrl = new URL(path.substring(0, path.length()-2)); cp.add(urlToFile(jarUrl)); } else { assert dirUrl.getProtocol().equalsIgnoreCase("file"); // org.epic.debug was deployed as a directory: // add this directory to the classpath cp.add(urlToFile(dirUrl)); } } return cp; } catch (Exception e) { throw new CoreException(new Status( IStatus.ERROR, PerlDebugPlugin.getUniqueIdentifier(), IStatus.OK, "getBrazilJVMClasspath failed", e)); } } private String getLaunchAttribute( ILaunch launch, String attrName, boolean isPath) throws CoreException { String attrValue = launch.getLaunchConfiguration().getAttribute( attrName, (String) null); if (attrValue == null) return null; else return new Path(attrValue).toString(); } private IPath getLocalWorkingDir(ILaunch launch) throws CoreException { String path = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_CGI_ROOT_DIR, false); assert path != null; return new Path(path); } private String getRelativeURL(ILaunch launch) throws CoreException { String htmlRootFile = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_HTML_ROOT_FILE, true); String htmlRootDir = getLaunchAttribute(launch, PerlLaunchConfigurationConstants.ATTR_HTML_ROOT_DIR, true); return new Path(htmlRootFile) .setDevice(null) .removeFirstSegments(new Path(htmlRootDir).segments().length) .toString(); } /** * @param path a list of File objects representing paths * @return a string with absolute paths separated by * the platform-specific path separator */ private static String makePathString(List path) { StringBuffer buf = new StringBuffer(); for (Iterator i = path.iterator(); i.hasNext();) { File entry = (File) i.next(); if (buf.length() > 0) buf.append(File.pathSeparator); buf.append(entry.getAbsolutePath()); } return buf.toString(); } private void openBrowser(ILaunch launch, int httpPort) throws CoreException { try { CGIBrowser browser = new CGIBrowser( launch, getRelativeURL(launch), httpPort); browser.open(); } catch (CoreException e) { PerlDebugPlugin.getDefault().logError( "Could not start web browser for CGI debugging.", e); throw e; } } private IProcess startBrazil( ILaunch launch, CGIProxy cgiProxy, int brazilPort, RemotePort debugPort) throws CoreException { try { createBrazilProps( launch, cgiProxy, brazilPort, debugPort != null ? debugPort.getServerPort() : -1 ).save(); } catch (CoreException e) { PerlDebugPlugin.getDefault().logError( "Could not read launch configuration attributes.", e); throw e; } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, PerlDebugPlugin.getUniqueIdentifier(), IStatus.OK, "Could not create configuration file for web server.", e)); } Process brazilProcess; try { brazilProcess = startBrazilProcess(); } catch (CoreException e) { PerlDebugPlugin.getDefault().logError( "Could not start web server", e); throw e; } return DebugPlugin.newProcess(launch, brazilProcess, "Web Server"); } private Process startBrazilProcess() throws CoreException { String javaExec = System.getProperty("java.home") + File.separator + "bin" + File.separator + "java"; File workingDir = PerlDebugPlugin.getDefault().getStateLocation().toFile(); String[] cmdParams = { javaExec, "-classpath", makePathString(getBrazilJVMClasspath()), "sunlabs.brazil.server.Main", "-c", "brazil.cfg" }; try { return Runtime.getRuntime().exec(cmdParams, null, workingDir); } catch (IOException e) { throw new CoreException(new Status( IStatus.ERROR, PerlDebugPlugin.getUniqueIdentifier(), IStatus.OK, "Could not start embedded web server: Runtime.exec failed", e)); } } private File urlToFile(URL url) { String urlString = url.toExternalForm(); if (urlString.matches("^file:/[A-Za-z]:/.*$")) { // Windows URL with volume letter: file:/C:/foo/bar/blah.txt return new File(urlString.substring(6)); } else { // Unix URLs look like this: file:/foo/bar/blah.txt assert urlString.matches("^file:/[^/].*$"); return new File(urlString.substring(5)); } } private static class BrazilProps { private StringBuffer props; public BrazilProps() { props = new StringBuffer(); } public void add(String name, boolean value) { add(name, String.valueOf(value)); } public void add(String name, int value) { add(name, String.valueOf(value)); } public void add(String name, List values) { int j = 0; for (Iterator i = values.iterator(); i.hasNext(); j++) add(name + "[" + j + "]", i.next().toString()); } public void add(String value) { props.append(value); props.append('\n'); } public void add(String name, String value) { props.append(name); props.append('='); props.append(value); props.append('\n'); } public void save() throws IOException { File propsFile = PerlDebugPlugin.getDefault().extractTempFile( "brazil_cgi_templ.cfg", "brazil.cfg"); // Append custom properties: OutputStream out = null; try { out = new FileOutputStream(propsFile, true); byte[] propsBytes = props.toString().getBytes(); out.write(propsBytes, 0, propsBytes.length); } finally { if (out != null) try { out.close(); } catch (Exception e) { } } } public String toString() { return props.toString(); } } } --- NEW FILE: CGIDebugTarget.java --- package org.epic.debug.cgi; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.debug.core.*; import org.eclipse.debug.core.model.IProcess; import org.epic.debug.DebugTarget; import org.epic.debug.PerlDebugPlugin; import org.epic.debug.util.RemotePort; /** * An implementation of IDebugTarget which tracks a Perl debugger * process launched by the web server and communicates with that * process using a TCP port. * <p> * Termination of the Perl debugger process normally results in * termination of the tracking DebugTarget instance. CGIDebugTarget * adds a little twist to this behavior: upon termination, it creates * a clone of itself, which reconnects to another (new) Perl debugger * process listening on the same port. If no Perl debugger responds, * reconnection attempts are repeated indefinitely. This strategy * allows multiple CGI scripts to be debugged (serially) within * the same debug session. */ public class CGIDebugTarget extends DebugTarget { private final IDebugEventSetListener listener = new IDebugEventSetListener() { public void handleDebugEvents(DebugEvent[] events) { for (int i = 0; i < events.length; i++) if (events[i].getKind() == DebugEvent.TERMINATE && getProcess().equals(events[i].getSource())) { shutdown(); // interrupt the acceptNewDebugger thread return; } } }; private CGIDebugTarget(CGIDebugTarget previous) throws CoreException { super( previous.getLaunch(), previous.getProcess(), previous.getRemotePort(), previous.getLocalWorkingDir()); DebugPlugin.getDefault().addDebugEventListener(listener); } public CGIDebugTarget( ILaunch launch, IProcess process, RemotePort debugPort, IPath workingDir) throws CoreException { super(launch, process, debugPort, workingDir); DebugPlugin.getDefault().addDebugEventListener(listener); } public String getName() throws DebugException { return "CGI Perl Debugger"; } public void debugSessionTerminated() { fireTerminateEvent(); getLaunch().removeDebugTarget(this); DebugPlugin.getDefault().asyncExec(new Runnable() { public void run() { if (acceptNewDebugger()) respawn(); } }); } private boolean acceptNewDebugger() { getRemotePort().startReconnect(); if (getRemotePort().waitForConnect(false) != RemotePort.WAIT_OK) { if (getProcess().isTerminated()) return false; PerlDebugPlugin.errorDialog("Could not connect to debug port!"); try { terminate(); } catch (DebugException e) { PerlDebugPlugin.log(e); } return false; } return true; } private void respawn() { try { DebugPlugin.getDefault().removeDebugEventListener(listener); getLaunch().addDebugTarget( new CGIDebugTarget(CGIDebugTarget.this)); } catch (CoreException e) { PerlDebugPlugin.log(e); try { terminate(); } catch (DebugException _e) { PerlDebugPlugin.log(_e); } } } } --- run.cmd DELETED --- --- StreamForwarder.java DELETED --- --- EpicCgiHandler.java DELETED --- --- ProcessOutput.java DELETED --- --- CustomBrowser.java DELETED --- --- StringReaderThread.java DELETED --- |