1 package net.sourceforge.phpeclipse.xdebug.core;
3 import java.io.ByteArrayInputStream;
4 import java.io.DataInputStream;
5 import java.io.EOFException;
6 import java.io.IOException;
7 import java.io.OutputStreamWriter;
8 import java.net.ServerSocket;
9 import java.net.Socket;
10 import java.net.UnknownHostException;
11 import java.util.HashMap;
13 import javax.xml.parsers.DocumentBuilder;
14 import javax.xml.parsers.DocumentBuilderFactory;
15 import javax.xml.parsers.ParserConfigurationException;
17 import net.sourceforge.phpeclipse.xdebug.php.model.XDebugLineBreakpoint;
18 import net.sourceforge.phpeclipse.xdebug.php.model.XDebugTarget;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.IProgressMonitor;
22 import org.eclipse.core.runtime.IStatus;
23 import org.eclipse.core.runtime.Status;
24 import org.eclipse.core.runtime.jobs.Job;
25 import org.eclipse.debug.core.DebugEvent;
26 import org.eclipse.debug.core.DebugException;
27 import org.eclipse.debug.core.model.IBreakpoint;
28 import org.w3c.dom.Document;
29 import org.w3c.dom.NamedNodeMap;
30 import org.w3c.dom.Node;
31 import org.xml.sax.SAXException;
33 public class DebugConnection {
35 private ServerSocket fDebugServerSocket;
37 private DebugResponse lastResponse;
39 private Socket fDebugSocket;
41 private OutputStreamWriter fDebugWriter;
43 private DataInputStream fDebugReader;
45 private ResponseListenerJob fResponseListener;
47 // Settings for Debug Process
48 private int fTransactionID = 0;
50 private String fileuri = "";
52 private String appID = "";
54 private String lastCommand = "";
56 private XDebugTarget fDebugTarget;
58 private HashMap ResponseList;
60 public class DebugResponse {
62 private Node parentNode;
64 private int fTransactionID = -1;
66 private String fCommand = "";
68 private String fStatus;
70 private String fReason;
74 private boolean fError;
76 public synchronized void setParentNode(String xmlInput) {
77 DocumentBuilderFactory factory = DocumentBuilderFactory
79 DocumentBuilder builder = null;
82 builder = factory.newDocumentBuilder();
83 } catch (ParserConfigurationException e) {
84 // TODO Auto-generated catch block
87 ByteArrayInputStream InputXMLStream = new ByteArrayInputStream(
91 doc = builder.parse(InputXMLStream);
92 } catch (SAXException e) {
93 // TODO Auto-generated catch block
95 } catch (IOException e) {
96 // TODO Auto-generated catch block
99 parentNode = doc.getFirstChild();
100 fName = parentNode.getNodeName();
101 String idStr = getAttributeValue("transaction_id");
103 fTransactionID = Integer.parseInt(idStr);
104 fCommand = getAttributeValue("command");
105 fStatus = getAttributeValue("status");
106 fReason = getAttributeValue("reason");
110 public String getAttributeValue(String AttributeName) {
111 String strValue = "";
112 if (parentNode.hasAttributes()) {
113 NamedNodeMap listAttribute = parentNode.getAttributes();
114 Node attribute = listAttribute.getNamedItem(AttributeName);
115 if (attribute != null)
116 strValue = attribute.getNodeValue();
121 public synchronized Node getParentNode() {
125 public synchronized String getCommand() {
129 public synchronized String getName() {
141 DebugResponse(String XMLInput) {
142 setParentNode(XMLInput);
145 public synchronized String getReason() {
149 public synchronized String getStatus() {
153 public synchronized int getTransactionID() {
154 return fTransactionID;
157 private synchronized DebugResponse waitforTransactionID(int id) {
158 while (fTransactionID != id) {
161 } catch (InterruptedException e) {
164 // XDebugCorePlugin.log(IStatus.INFO,"got TransID: "+id);
169 public boolean isError() {
173 public void setError(boolean error) {
177 protected synchronized void notifyWait() {
183 * Listens to events from the XDebug and fires corresponding debug events.
185 class ResponseListenerJob extends Job {
187 public ResponseListenerJob() {
188 super("XDebug Event Dispatch");
193 private void checkResponse(DebugResponse response) {
194 Node node = response.getParentNode();
195 if (node.hasChildNodes()) {
196 Node child = node.getFirstChild();
197 if (child.getNodeName().equals("error")) {
198 int code = Integer.parseInt(PHPDebugUtils
199 .getAttributeValue(child, "code"));
200 String text = (child.getFirstChild()).getNodeValue();
201 XDebugCorePlugin.log(IStatus.ERROR, lastCommand + " ERROR "
202 + code + ": " + text);
203 lastResponse.setError(true);
207 lastResponse.setError(false);
208 if (response.getStatus().equals("stopped"))
210 else if (response.getStatus().equals("break")
211 && response.getReason().equals("ok")) {
212 if (response.getCommand().equals("run")) { // breakpoint hit
215 id = sendRequest("stack_get");
216 } catch (DebugException e) {
217 // TODO Auto-generated catch block
220 String InputXML = readData();
221 if (InputXML != null) {
222 XDebugCorePlugin.log(IStatus.INFO, InputXML);
223 lastResponse.setParentNode(InputXML);
225 .breakpointHit(lastResponse.getParentNode());
228 } else if (response.getCommand().equals("step_into")) { // step_into
229 fDebugTarget.suspended(DebugEvent.STEP_END);
230 // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
232 } else if (response.getCommand().equals("step_over")) { // step_over
233 fDebugTarget.suspended(DebugEvent.STEP_END);
234 // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
236 } else if (response.getCommand().equals("step_out")) { // step_over
237 fDebugTarget.suspended(DebugEvent.STEP_END);
238 // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
241 } else if (response.getCommand().equals("breakpoint_set")) { // step_over
242 String idStr = response.getAttributeValue("id");
244 int targetID = response.getTransactionID();
245 BreakpointResponseData ResponseData = new BreakpointResponseData(
246 response.getTransactionID(), Integer
249 fDebugTarget.fireDebugResponseEvent(ResponseData);
250 IBreakpoint[] breakpoints = XDebugCorePlugin
252 for (int i = 0; i < breakpoints.length; i++) {
253 XDebugLineBreakpoint breakpoint = (XDebugLineBreakpoint) breakpoints[i];
255 if (breakpoint.getID() == targetID)
256 breakpoint.setID(Integer.parseInt(idStr));
257 } catch (NumberFormatException e) {
258 // TODO Auto-generated catch block
260 } catch (CoreException e) {
261 // TODO Auto-generated catch block
273 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
275 protected IStatus run(IProgressMonitor monitor) {
276 String InputXML = "";
277 while (!fDebugTarget.isTerminated() && (InputXML != null)) {
278 InputXML = readData();
279 if (InputXML != null) {
280 XDebugCorePlugin.log(IStatus.INFO, InputXML);
281 lastResponse.setParentNode(InputXML);
282 if (lastResponse.getName() == "init") {
283 Node myNode = lastResponse.getParentNode();
284 appID = PHPDebugUtils
285 .getAttributeValue(myNode, "appid");
286 fileuri = PHPDebugUtils.getAttributeValue(myNode,
288 // fDebugTarget.started();
289 fDebugTarget.fireCreationEvent();
290 } else if (lastResponse.getName() == "response") {
291 checkResponse(lastResponse);
293 ResponseList.put(new Integer(lastResponse
294 .getTransactionID()), lastResponse);
295 lastResponse.notifyWait();
298 return Status.OK_STATUS;
303 public DebugConnection(XDebugTarget debugTarget, int debugPort) {
304 fDebugTarget = debugTarget;
305 lastResponse = new DebugResponse();
306 ResponseList = new HashMap();
309 fDebugServerSocket = new ServerSocket(debugPort);
310 fDebugSocket = fDebugServerSocket.accept();
311 fDebugWriter = new OutputStreamWriter(fDebugSocket
312 .getOutputStream(), "UTF8");
313 fDebugReader = new DataInputStream(fDebugSocket.getInputStream());
314 // fDebugReader = new BufferedReader(new
315 // InputStreamReader(fDebugSocket.getInputStream()));
317 } catch (UnknownHostException e) {
318 // abort("Unable to connect to PHP Debuger", e);
319 } catch (IOException e) {
320 // abort("Unable to connect to PHP Debuger", e);
322 fResponseListener = new ResponseListenerJob();
323 fResponseListener.schedule();
327 private void terminated() {
329 fDebugReader.close();
330 fDebugWriter.close();
331 fDebugSocket.close();
332 fDebugServerSocket.close();
333 } catch (IOException e) {
334 // TODO Auto-generated catch block
337 fDebugTarget.terminated();
341 private String readData() {
342 byte byteBuffer[] = null, b;
346 while ((b = fDebugReader.readByte()) != 0) {
347 count = count * 10 + b - '0';
348 // count=count*10+Integer.parseInt(b);
350 // System.out.println(count);
351 byteBuffer = new byte[count];
354 while ((count > 0) && (attempts < 5)) {
355 int rc = fDebugReader.read(byteBuffer, readCount, count);
360 if ((b = fDebugReader.readByte()) != 0) // reads the NULL Byte at
362 System.out.println("NULL-Byte missing!!");
363 } catch (IOException e) {
364 // TODO Auto-generated catch block
365 if (e instanceof EOFException == false)
369 return new String(byteBuffer);
373 * Sends a request to the Debugengine and waits for an OK.
377 * @throws DebugException
378 * if the request fails
381 public int sendRequest(String command) throws DebugException {
382 return sendRequest(command, "");
386 * Sends a request to the Debugengine and waits for an OK.
390 * @arguments arguments for the command
391 * @throws DebugException
392 * if the request fails
395 public synchronized int sendRequest(String command, String arguments)
396 throws DebugException {
398 // System.out.println(command+" -i "+transactionID+" "+arguments);
399 XDebugCorePlugin.log(IStatus.INFO, command + " -i " + fTransactionID
401 synchronized (fDebugSocket) {
403 fDebugWriter.write(command);
404 fDebugWriter.write(" -i " + fTransactionID);
406 fDebugWriter.write(" " + arguments);
407 fDebugWriter.write(0);
408 fDebugWriter.flush();
409 } catch (IOException e) {
413 return fTransactionID++;
416 public DebugResponse waitforTransID(int id) {
417 if (ResponseList.containsKey(new Integer(id))) {
418 // return (DebugResponse)ResponseList.get(new Integer(id));
419 return (DebugResponse) ResponseList.remove(new Integer(id));
421 return lastResponse.waitforTransactionID(id);
424 public DebugResponse getResponse(int id) {
425 if (ResponseList.containsKey(new Integer(id)))
426 return (DebugResponse) ResponseList.get(new Integer(id));
428 return waitforTransID(id);