/******************************************************************************* * Copyright (c) 2004, 2016 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.ant.internal.launching.debug.model; import java.util.ArrayList; import java.util.List; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.IBreakpoint; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; import org.eclipse.debug.core.model.IVariable; /** * An Ant build thread. */ public class AntThread extends AntDebugElement implements IThread { static final IBreakpoint[] NO_BREAKPOINTS = new IBreakpoint[0]; /** * Breakpoints this thread is suspended at or <code>null</code> if none. */ private IBreakpoint[] fBreakpoints; /** * The stackframes associated with this thread */ private List<AntStackFrame> fFrames = new ArrayList<>(1); /** * The stackframes to be reused on suspension */ private List<AntStackFrame> fOldFrames; /** * Whether this thread is stepping */ private boolean fStepping = false; private boolean fRefreshProperties = true; /** * The user properties associated with this thread */ private AntProperties fUserProperties; /** * The system properties associated with this thread */ private AntProperties fSystemProperties; /** * The properties set during the build associated with this thread */ private AntProperties fRuntimeProperties; private Object fPropertiesLock = new Object(); /** * Constructs a new thread for the given target * * @param target * the Ant Build */ public AntThread(AntDebugTarget target) { super(target); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#getStackFrames() */ @Override public synchronized IStackFrame[] getStackFrames() throws DebugException { if (isSuspended()) { if (fFrames.size() == 0) { getStackFrames0(); } } return fFrames.toArray(new IStackFrame[fFrames.size()]); } /** * Retrieves the current stack frames in the thread possibly waiting until the frames are populated * */ private void getStackFrames0() throws DebugException { synchronized (fFrames) { getAntDebugTarget().getStackFrames(); if (fFrames.size() > 0) { // frames set..no need to wait return; } int attempts = 0; try { while (fFrames.size() == 0 && !isTerminated()) { fFrames.wait(50); if (attempts == 20 && fFrames.size() == 0 && !isTerminated()) { throwDebugException(DebugModelMessages.AntThread_3); } attempts++; } } catch (InterruptedException e) { // do nothing } } } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#hasStackFrames() */ @Override public boolean hasStackFrames() throws DebugException { return isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#getPriority() */ @Override public int getPriority() throws DebugException { return 0; } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#getTopStackFrame() */ @Override public synchronized IStackFrame getTopStackFrame() throws DebugException { if (isSuspended()) { if (fFrames.size() == 0) { getStackFrames0(); } if (fFrames.size() > 0) { return fFrames.get(0); } } return null; } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#getName() */ @Override public String getName() { return "Thread [Ant Build]"; //$NON-NLS-1$ } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IThread#getBreakpoints() */ @Override public IBreakpoint[] getBreakpoints() { if (fBreakpoints == null) { return NO_BREAKPOINTS; } return fBreakpoints; } /** * Sets the breakpoints this thread is suspended at, or <code>null</code> if none. * * @param breakpoints * the breakpoints this thread is suspended at, or <code>null</code> if none */ protected void setBreakpoints(IBreakpoint[] breakpoints) { fBreakpoints = breakpoints; } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ISuspendResume#canResume() */ @Override public boolean canResume() { return isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend() */ @Override public boolean canSuspend() { return !isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended() */ @Override public boolean isSuspended() { return getDebugTarget().isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ISuspendResume#resume() */ @Override public synchronized void resume() throws DebugException { aboutToResume(DebugEvent.CLIENT_REQUEST, false); getDebugTarget().resume(); } /** * Call-back when the target is resumed * * @since 1.0 */ void resumedByTarget() { aboutToResume(DebugEvent.CLIENT_REQUEST, false); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ISuspendResume#suspend() */ @Override public synchronized void suspend() throws DebugException { getDebugTarget().suspend(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#canStepInto() */ @Override public boolean canStepInto() { return isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#canStepOver() */ @Override public boolean canStepOver() { return isSuspended(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#canStepReturn() */ @Override public boolean canStepReturn() { return false; } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#isStepping() */ @Override public boolean isStepping() { return fStepping; } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#stepInto() */ @Override public synchronized void stepInto() throws DebugException { aboutToResume(DebugEvent.STEP_INTO, true); ((AntDebugTarget) getDebugTarget()).stepInto(); } private void aboutToResume(int detail, boolean stepping) { fRefreshProperties = true; fOldFrames = new ArrayList<>(fFrames); fFrames.clear(); setPropertiesValid(false); setStepping(stepping); setBreakpoints(null); fireResumeEvent(detail); } private void setPropertiesValid(boolean valid) { if (fUserProperties != null) { fUserProperties.setValid(valid); fSystemProperties.setValid(valid); fRuntimeProperties.setValid(valid); } } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#stepOver() */ @Override public synchronized void stepOver() throws DebugException { aboutToResume(DebugEvent.STEP_OVER, true); ((AntDebugTarget) getDebugTarget()).stepOver(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.IStep#stepReturn() */ @Override public synchronized void stepReturn() throws DebugException { // do nothing } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ITerminate#canTerminate() */ @Override public boolean canTerminate() { return !isTerminated(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ITerminate#isTerminated() */ @Override public boolean isTerminated() { return getDebugTarget().isTerminated(); } /* * (non-Javadoc) * * @see org.eclipse.debug.core.model.ITerminate#terminate() */ @Override public void terminate() throws DebugException { fFrames.clear(); getDebugTarget().terminate(); } /** * Sets whether this thread is stepping * * @param stepping * whether stepping */ protected void setStepping(boolean stepping) { fStepping = stepping; } public void buildStack(String data) { synchronized (fFrames) { String[] strings = data.split(DebugMessageIds.MESSAGE_DELIMITER); // 0 STACK message // 1 targetName // 2 taskName // 3 filePath // 4 lineNumber // 5 ... if (fOldFrames != null && (strings.length - 1) / 4 != fOldFrames.size()) { fOldFrames = null; // stack size changed..do not preserve } StringBuffer name; String filePath; int lineNumber; int stackFrameId = 0; String taskName; for (int i = 1; i < strings.length; i++) { if (strings[i].length() > 0) { name = new StringBuffer(strings[i]); taskName = strings[++i]; if (taskName.length() > 0) { name.append(": "); //$NON-NLS-1$ name.append(taskName); } } else { name = new StringBuffer(strings[++i]); } filePath = strings[++i]; lineNumber = Integer.parseInt(strings[++i]); addFrame(stackFrameId++, name.toString(), filePath, lineNumber); } // wake up the call from getStackFrames fFrames.notifyAll(); } } private void addFrame(int stackFrameId, String name, String filePath, int lineNumber) { AntStackFrame frame = getOldFrame(); if (frame == null || !frame.getFilePath().equals(filePath)) { frame = new AntStackFrame(this, stackFrameId, name, filePath, lineNumber); } else { frame.setFilePath(filePath); frame.setId(stackFrameId); frame.setLineNumber(lineNumber); frame.setName(name); } fFrames.add(frame); } private AntStackFrame getOldFrame() { if (fOldFrames == null) { return null; } AntStackFrame frame = fOldFrames.remove(0); if (fOldFrames.isEmpty()) { fOldFrames = null; } return frame; } public void newProperties(String data) { synchronized (fPropertiesLock) { try { String[] datum = data.split(DebugMessageIds.MESSAGE_DELIMITER); if (fUserProperties == null) { initializePropertyGroups(); } List<AntProperty> userProperties = ((AntPropertiesValue) fUserProperties.getLastValue()).getProperties(); List<AntProperty> systemProperties = ((AntPropertiesValue) fSystemProperties.getLastValue()).getProperties(); List<AntProperty> runtimeProperties = ((AntPropertiesValue) fRuntimeProperties.getLastValue()).getProperties(); // 0 PROPERTIES message // 1 propertyName length // 2 propertyName // 3 propertyValue length // 3 propertyValue // 4 propertyType // 5 ... if (datum.length > 1) { // new properties StringBuffer propertyName; StringBuffer propertyValue; int propertyNameLength; int propertyValueLength; for (int i = 1; i < datum.length; i++) { propertyNameLength = Integer.parseInt(datum[i]); propertyName = new StringBuffer(datum[++i]); while (propertyName.length() != propertyNameLength) { propertyName.append(DebugMessageIds.MESSAGE_DELIMITER); propertyName.append(datum[++i]); } propertyName = getAntDebugTarget().getAntDebugController().unescapeString(propertyName); propertyValueLength = Integer.parseInt(datum[++i]); if (propertyValueLength == 0 && i + 1 == datum.length) { // bug 81299 propertyValue = new StringBuffer(""); //$NON-NLS-1$ } else { propertyValue = new StringBuffer(datum[++i]); } while (propertyValue.length() != propertyValueLength) { propertyValue.append(DebugMessageIds.MESSAGE_DELIMITER); propertyValue.append(datum[++i]); } propertyValue = getAntDebugTarget().getAntDebugController().unescapeString(propertyValue); int propertyType = Integer.parseInt(datum[++i]); addProperty(userProperties, systemProperties, runtimeProperties, propertyName.toString(), propertyValue.toString(), propertyType); } } } finally { fRefreshProperties = false; setPropertiesValid(true); // wake up the call from getVariables fPropertiesLock.notifyAll(); } } } private void addProperty(List<AntProperty> userProperties, List<AntProperty> systemProperties, List<AntProperty> runtimeProperties, String propertyName, String propertyValue, int propertyType) { AntProperty property = new AntProperty((AntDebugTarget) getDebugTarget(), propertyName, propertyValue); switch (propertyType) { case DebugMessageIds.PROPERTY_SYSTEM: systemProperties.add(property); break; case DebugMessageIds.PROPERTY_USER: userProperties.add(property); break; case DebugMessageIds.PROPERTY_RUNTIME: runtimeProperties.add(property); break; default: break; } } private void initializePropertyGroups() { AntDebugTarget target = getAntDebugTarget(); fUserProperties = new AntProperties(target, DebugModelMessages.AntThread_0); fUserProperties.setValue(new AntPropertiesValue(target)); fSystemProperties = new AntProperties(target, DebugModelMessages.AntThread_1); fSystemProperties.setValue(new AntPropertiesValue(target)); fRuntimeProperties = new AntProperties(target, DebugModelMessages.AntThread_2); fRuntimeProperties.setValue(new AntPropertiesValue(target)); } protected IVariable[] getVariables() throws DebugException { synchronized (fPropertiesLock) { if (fRefreshProperties) { getAntDebugTarget().getProperties(); if (fRefreshProperties) { // properties have not been set; need to wait try { int attempts = 0; while (fRefreshProperties && !isTerminated()) { fPropertiesLock.wait(50); if (attempts == 20 && fRefreshProperties && !isTerminated()) { throwDebugException(DebugModelMessages.AntThread_4); } attempts++; } } catch (InterruptedException ie) { // do nothing } } } if (fSystemProperties == null) { return new IVariable[0]; } return new IVariable[] { fSystemProperties, fUserProperties, fRuntimeProperties }; } } }