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