e28b56b8c1642af4ea8d4edcdfb9852373095af7
[phpeclipse.git] / net.sourceforge.phpeclipse.xdebug.core / src / net / sourceforge / phpeclipse / xdebug / core / DebugConnection.java
1 package net.sourceforge.phpeclipse.xdebug.core;
2
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;
12
13 import javax.xml.parsers.DocumentBuilder;
14 import javax.xml.parsers.DocumentBuilderFactory;
15 import javax.xml.parsers.ParserConfigurationException;
16
17 import net.sourceforge.phpeclipse.xdebug.php.model.XDebugLineBreakpoint;
18 import net.sourceforge.phpeclipse.xdebug.php.model.XDebugTarget;
19
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;
32
33 public class DebugConnection {
34
35         private ServerSocket fDebugServerSocket;
36
37         private DebugResponse lastResponse;
38
39         private Socket fDebugSocket;
40
41         private OutputStreamWriter fDebugWriter;
42
43         private DataInputStream fDebugReader;
44
45         private ResponseListenerJob fResponseListener;
46
47         // Settings for Debug Process
48         private int fTransactionID = 0;
49
50         private String fileuri = "";
51
52         private String appID = "";
53
54         private String lastCommand = "";
55
56         private XDebugTarget fDebugTarget;
57
58         private HashMap ResponseList;
59
60         public class DebugResponse {
61
62                 private Node parentNode;
63
64                 private int fTransactionID = -1;
65
66                 private String fCommand = "";
67
68                 private String fStatus;
69
70                 private String fReason;
71
72                 private String fName;
73
74                 private boolean fError;
75
76                 public synchronized void setParentNode(String xmlInput) {
77                         DocumentBuilderFactory factory = DocumentBuilderFactory
78                                         .newInstance();
79                         DocumentBuilder builder = null;
80                         Document doc = null;
81                         try {
82                                 builder = factory.newDocumentBuilder();
83                         } catch (ParserConfigurationException e) {
84                                 // TODO Auto-generated catch block
85                                 e.printStackTrace();
86                         }
87                         ByteArrayInputStream InputXMLStream = new ByteArrayInputStream(
88                                         xmlInput.getBytes());
89
90                         try {
91                                 doc = builder.parse(InputXMLStream);
92                         } catch (SAXException e) {
93                                 // TODO Auto-generated catch block
94                                 e.printStackTrace();
95                         } catch (IOException e) {
96                                 // TODO Auto-generated catch block
97                                 e.printStackTrace();
98                         }
99                         parentNode = doc.getFirstChild();
100                         fName = parentNode.getNodeName();
101                         String idStr = getAttributeValue("transaction_id");
102                         if (idStr != "")
103                                 fTransactionID = Integer.parseInt(idStr);
104                         fCommand = getAttributeValue("command");
105                         fStatus = getAttributeValue("status");
106                         fReason = getAttributeValue("reason");
107                         // notifyAll();
108                 }
109
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();
117                         }
118                         return strValue;
119                 }
120
121                 public synchronized Node getParentNode() {
122                         return parentNode;
123                 }
124
125                 public synchronized String getCommand() {
126                         return fCommand;
127                 }
128
129                 public synchronized String getName() {
130                         return fName;
131                 }
132
133                 DebugResponse() {
134                         fTransactionID = -1;
135                         fCommand = "";
136                         fStatus = "";
137                         fReason = "";
138                         fName = "";
139                 }
140
141                 DebugResponse(String XMLInput) {
142                         setParentNode(XMLInput);
143                 }
144
145                 public synchronized String getReason() {
146                         return fReason;
147                 }
148
149                 public synchronized String getStatus() {
150                         return fStatus;
151                 }
152
153                 public synchronized int getTransactionID() {
154                         return fTransactionID;
155                 }
156
157                 private synchronized DebugResponse waitforTransactionID(int id) {
158                         while (fTransactionID != id) {
159                                 try {
160                                         wait();
161                                 } catch (InterruptedException e) {
162                                 }
163                         }
164                         // XDebugCorePlugin.log(IStatus.INFO,"got TransID: "+id);
165
166                         return this;
167                 }
168
169                 public boolean isError() {
170                         return fError;
171                 }
172
173                 public void setError(boolean error) {
174                         fError = error;
175                 }
176
177                 protected synchronized void notifyWait() {
178                         notifyAll();
179                 }
180         }
181
182         /**
183          * Listens to events from the XDebug and fires corresponding debug events.
184          */
185         class ResponseListenerJob extends Job {
186
187                 public ResponseListenerJob() {
188                         super("XDebug Event Dispatch");
189                         setSystem(true);
190
191                 }
192
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);
204                                         return;
205                                 }
206                         }
207                         lastResponse.setError(false);
208                         if (response.getStatus().equals("stopped"))
209                                 terminated();
210                         else if (response.getStatus().equals("break")
211                                         && response.getReason().equals("ok")) {
212                                 if (response.getCommand().equals("run")) { // breakpoint hit
213                                         int id = -1;
214                                         try {
215                                                 id = sendRequest("stack_get");
216                                         } catch (DebugException e) {
217                                                 // TODO Auto-generated catch block
218                                                 e.printStackTrace();
219                                         }
220                                         String InputXML = readData();
221                                         if (InputXML != null) {
222                                                 XDebugCorePlugin.log(IStatus.INFO, InputXML);
223                                                 lastResponse.setParentNode(InputXML);
224                                                 fDebugTarget
225                                                                 .breakpointHit(lastResponse.getParentNode());
226                                         }
227
228                                 } else if (response.getCommand().equals("step_into")) { // step_into
229                                         fDebugTarget.suspended(DebugEvent.STEP_END);
230                                         // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
231                                         // STEP_END sent");
232                                 } else if (response.getCommand().equals("step_over")) { // step_over
233                                         fDebugTarget.suspended(DebugEvent.STEP_END);
234                                         // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
235                                         // STEP_END sent");
236                                 } else if (response.getCommand().equals("step_out")) { // step_over
237                                         fDebugTarget.suspended(DebugEvent.STEP_END);
238                                         // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+"
239                                         // STEP_END sent");
240                                 }
241                         } else if (response.getCommand().equals("breakpoint_set")) { // step_over
242                                 String idStr = response.getAttributeValue("id");
243                                 if (idStr != "") {
244                                         int targetID = response.getTransactionID();
245                                         BreakpointResponseData ResponseData = new BreakpointResponseData(
246                                                         response.getTransactionID(), Integer
247                                                                         .parseInt(idStr));
248
249                                         fDebugTarget.fireDebugResponseEvent(ResponseData);
250                                         IBreakpoint[] breakpoints = XDebugCorePlugin
251                                                         .getBreakpoints();
252                                         for (int i = 0; i < breakpoints.length; i++) {
253                                                 XDebugLineBreakpoint breakpoint = (XDebugLineBreakpoint) breakpoints[i];
254                                                 try {
255                                                         if (breakpoint.getID() == targetID)
256                                                                 breakpoint.setID(Integer.parseInt(idStr));
257                                                 } catch (NumberFormatException e) {
258                                                         // TODO Auto-generated catch block
259                                                         e.printStackTrace();
260                                                 } catch (CoreException e) {
261                                                         // TODO Auto-generated catch block
262                                                         e.printStackTrace();
263                                                 }
264                                         }
265                                 }
266                         }
267
268                 }
269
270                 /*
271                  * (non-Javadoc)
272                  * 
273                  * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
274                  */
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,
287                                                                 "fileuri");
288                                                 // fDebugTarget.started();
289                                                 fDebugTarget.fireCreationEvent();
290                                         } else if (lastResponse.getName() == "response") {
291                                                 checkResponse(lastResponse);
292                                         }
293                                         ResponseList.put(new Integer(lastResponse
294                                                         .getTransactionID()), lastResponse);
295                                         lastResponse.notifyWait();
296                                 }
297                         }
298                         return Status.OK_STATUS;
299                 }
300
301         }
302
303         public DebugConnection(XDebugTarget debugTarget, int debugPort) {
304                 fDebugTarget = debugTarget;
305                 lastResponse = new DebugResponse();
306                 ResponseList = new HashMap();
307
308                 try {
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()));
316
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);
321                 }
322                 fResponseListener = new ResponseListenerJob();
323                 fResponseListener.schedule();
324
325         }
326
327         private void terminated() {
328                 try {
329                         fDebugReader.close();
330                         fDebugWriter.close();
331                         fDebugSocket.close();
332                         fDebugServerSocket.close();
333                 } catch (IOException e) {
334                         // TODO Auto-generated catch block
335                         e.printStackTrace();
336                 }
337                 fDebugTarget.terminated();
338
339         }
340
341         private String readData() {
342                 byte byteBuffer[] = null, b;
343                 int count = 0;
344
345                 try {
346                         while ((b = fDebugReader.readByte()) != 0) {
347                                 count = count * 10 + b - '0';
348                                 // count=count*10+Integer.parseInt(b);
349                         }
350                         // System.out.println(count);
351                         byteBuffer = new byte[count];
352                         int readCount = 0;
353                         int attempts = 0;
354                         while ((count > 0) && (attempts < 5)) {
355                                 int rc = fDebugReader.read(byteBuffer, readCount, count);
356                                 count -= rc;
357                                 readCount += rc;
358                                 attempts++;
359                         }
360                         if ((b = fDebugReader.readByte()) != 0) // reads the NULL Byte at
361                                                                                                         // the end;
362                                 System.out.println("NULL-Byte missing!!");
363                 } catch (IOException e) {
364                         // TODO Auto-generated catch block
365                         if (e instanceof EOFException == false)
366                                 e.printStackTrace();
367                         return null;
368                 }
369                 return new String(byteBuffer);
370         }
371
372         /**
373          * Sends a request to the Debugengine and waits for an OK.
374          * 
375          * @param command
376          *            debug command
377          * @throws DebugException
378          *             if the request fails
379          */
380
381         public int sendRequest(String command) throws DebugException {
382                 return sendRequest(command, "");
383         }
384
385         /**
386          * Sends a request to the Debugengine and waits for an OK.
387          * 
388          * @param command
389          *            debug command
390          * @arguments arguments for the command
391          * @throws DebugException
392          *             if the request fails
393          */
394
395         public synchronized int sendRequest(String command, String arguments)
396                         throws DebugException {
397
398                 // System.out.println(command+" -i "+transactionID+" "+arguments);
399                 XDebugCorePlugin.log(IStatus.INFO, command + " -i " + fTransactionID
400                                 + " " + arguments);
401                 synchronized (fDebugSocket) {
402                         try {
403                                 fDebugWriter.write(command);
404                                 fDebugWriter.write(" -i " + fTransactionID);
405                                 if (arguments != "")
406                                         fDebugWriter.write(" " + arguments);
407                                 fDebugWriter.write(0);
408                                 fDebugWriter.flush();
409                         } catch (IOException e) {
410                                 e.printStackTrace();
411                         }
412                 }
413                 return fTransactionID++;
414         }
415
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));
420                 } else
421                         return lastResponse.waitforTransactionID(id);
422         }
423
424         public DebugResponse getResponse(int id) {
425                 if (ResponseList.containsKey(new Integer(id)))
426                         return (DebugResponse) ResponseList.get(new Integer(id));
427                 else
428                         return waitforTransID(id);
429         }
430
431 }