package net.sourceforge.phpeclipse.xdebug.core; import java.io.ByteArrayInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.IOException; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.UnknownHostException; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import net.sourceforge.phpeclipse.xdebug.php.model.XDebugLineBreakpoint; import net.sourceforge.phpeclipse.xdebug.php.model.XDebugTarget; import net.sourceforge.phpeclipse.xdebug.php.model.XDebugThread; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; 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.IThread; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.SAXException; public class DebugConnection { private ServerSocket fDebugServerSocket; private DebugResponse lastResponse; private Socket fDebugSocket; private OutputStreamWriter fDebugWriter; private DataInputStream fDebugReader; private ResponseListenerJob fResponseListener; // Settings for Debug Process private int fTransactionID=0; private String fileuri = ""; private String appID=""; private String lastCommand = ""; private XDebugTarget fDebugTarget; private HashMap ResponseList; public class DebugResponse { private Node parentNode; private int fTransactionID=-1; private String fCommand=""; private String fStatus; private String fReason; private String fName; private boolean fError; public synchronized void setParentNode (String xmlInput){ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder=null; Document doc=null; try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } ByteArrayInputStream InputXMLStream = new ByteArrayInputStream(xmlInput.getBytes()); try { doc = builder.parse(InputXMLStream); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } parentNode=doc.getFirstChild(); fName=parentNode.getNodeName(); String idStr = getAttributeValue("transaction_id"); if (idStr!="") fTransactionID = Integer.parseInt(idStr); fCommand = getAttributeValue("command"); fStatus = getAttributeValue("status"); fReason = getAttributeValue("reason"); // notifyAll(); } public String getAttributeValue (String AttributeName) { String strValue = ""; if (parentNode.hasAttributes()) { NamedNodeMap listAttribute = parentNode.getAttributes(); Node attribute = listAttribute.getNamedItem(AttributeName); if (attribute !=null) strValue = attribute.getNodeValue(); } return strValue; } public synchronized Node getParentNode(){ return parentNode; } public synchronized String getCommand() { return fCommand; } public synchronized String getName() { return fName; } DebugResponse () { fTransactionID = -1; fCommand = ""; fStatus = ""; fReason = ""; fName= ""; } DebugResponse (String XMLInput) { setParentNode(XMLInput); } public synchronized String getReason() { return fReason; } public synchronized String getStatus() { return fStatus; } public synchronized int getTransactionID() { return fTransactionID; } private synchronized DebugResponse waitforTransactionID(int id) { while (fTransactionID!= id) { try { wait(); } catch (InterruptedException e) { } } // XDebugCorePlugin.log(IStatus.INFO,"got TransID: "+id); return this; } public boolean isError() { return fError; } public void setError(boolean error) { fError = error; } protected synchronized void notifyWait() { notifyAll(); } } /** * Listens to events from the XDebug and fires corresponding * debug events. */ class ResponseListenerJob extends Job { public ResponseListenerJob() { super("XDebug Event Dispatch"); setSystem(true); } private void checkResponse(DebugResponse response) { Node node=response.getParentNode(); if (node.hasChildNodes()) { Node child=node.getFirstChild(); if (child.getNodeName().equals("error")) { int code = Integer.parseInt(PHPDebugUtils.getAttributeValue(child, "code")); String text=(child.getFirstChild()).getNodeValue(); XDebugCorePlugin.log(IStatus.ERROR,lastCommand+" ERROR "+code+": "+text); lastResponse.setError(true); return; } } lastResponse.setError(false); if (response.getStatus().equals("stopped")) terminated(); else if (response.getStatus().equals("break") && response.getReason().equals("ok")){ if (response.getCommand().equals("run")) { // breakpoint hit int id=-1; try { id=sendRequest("stack_get"); } catch (DebugException e) { // TODO Auto-generated catch block e.printStackTrace(); } String InputXML = readData(); if (InputXML != null) { XDebugCorePlugin.log(IStatus.INFO, InputXML); lastResponse.setParentNode(InputXML); fDebugTarget.breakpointHit(lastResponse.getParentNode()); } } else if (response.getCommand().equals("step_into")) { // step_into fDebugTarget.suspended(DebugEvent.STEP_END); // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent"); } else if (response.getCommand().equals("step_over")) { // step_over fDebugTarget.suspended(DebugEvent.STEP_END); // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent"); } else if (response.getCommand().equals("step_out")) { // step_over fDebugTarget.suspended(DebugEvent.STEP_END); // XDebugCorePlugin.log(IStatus.INFO,response.getCommand()+" STEP_END sent"); } } else if (response.getCommand().equals("breakpoint_set")) { // step_over String idStr=response.getAttributeValue("id"); if (idStr!="") { int targetID=response.getTransactionID(); BreakpointResponseData ResponseData= new BreakpointResponseData(response.getTransactionID(),Integer.parseInt(idStr)); fDebugTarget.fireDebugResponseEvent(ResponseData); IBreakpoint[] breakpoints=XDebugCorePlugin.getBreakpoints(); for(int i=0;i< breakpoints.length;i++) { XDebugLineBreakpoint breakpoint=(XDebugLineBreakpoint)breakpoints[i]; try { if (breakpoint.getID()==targetID) breakpoint.setID(Integer.parseInt(idStr)); } catch (NumberFormatException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (CoreException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } } /* (non-Javadoc) * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) */ protected IStatus run(IProgressMonitor monitor) { String InputXML = ""; while (!fDebugTarget.isTerminated() && (InputXML != null)) { InputXML = readData(); if (InputXML != null) { XDebugCorePlugin.log(IStatus.INFO, InputXML); lastResponse.setParentNode(InputXML); if (lastResponse.getName() == "init") { Node myNode=lastResponse.getParentNode(); appID = PHPDebugUtils.getAttributeValue(myNode, "appid"); fileuri = PHPDebugUtils.getAttributeValue(myNode, "fileuri"); // fDebugTarget.started(); fDebugTarget.fireCreationEvent(); } else if (lastResponse.getName() == "response") { checkResponse(lastResponse); } ResponseList.put(new Integer(lastResponse.getTransactionID()),lastResponse); lastResponse.notifyWait(); } } return Status.OK_STATUS; } } public DebugConnection (XDebugTarget debugTarget,int debugPort) { fDebugTarget=debugTarget; lastResponse=new DebugResponse(); ResponseList= new HashMap(); try { fDebugServerSocket = new ServerSocket(debugPort); fDebugSocket = fDebugServerSocket.accept(); fDebugWriter = new OutputStreamWriter(fDebugSocket.getOutputStream(), "UTF8"); fDebugReader = new DataInputStream(fDebugSocket.getInputStream()); // fDebugReader = new BufferedReader(new InputStreamReader(fDebugSocket.getInputStream())); } catch (UnknownHostException e) { // abort("Unable to connect to PHP Debuger", e); } catch (IOException e) { // abort("Unable to connect to PHP Debuger", e); } fResponseListener = new ResponseListenerJob(); fResponseListener.schedule(); } private void terminated() { try { fDebugReader.close(); fDebugWriter.close(); fDebugSocket.close(); fDebugServerSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } fDebugTarget.terminated(); } private String readData() { byte byteBuffer[]=null,b; int count=0; try { while ( (b =fDebugReader.readByte())!=0) { count=count*10+b-'0'; // count=count*10+Integer.parseInt(b); } // System.out.println(count); byteBuffer = new byte[count]; int readCount=0; int attempts=0; while ((count >0) && (attempts <5)) { int rc=fDebugReader.read(byteBuffer,readCount,count); count-=rc; readCount+=rc; attempts++; } if((b= fDebugReader.readByte())!=0) // reads the NULL Byte at the end; System.out.println("NULL-Byte missing!!"); } catch (IOException e) { // TODO Auto-generated catch block if (e instanceof EOFException==false) e.printStackTrace(); return null; } return new String(byteBuffer); } /** * Sends a request to the Debugengine and waits for an OK. * * @param command debug command * @throws DebugException if the request fails */ public int sendRequest(String command) throws DebugException { return sendRequest(command,""); } /** * Sends a request to the Debugengine and waits for an OK. * * @param command debug command * @arguments arguments for the command * @throws DebugException if the request fails */ public synchronized int sendRequest(String command, String arguments) throws DebugException { // System.out.println(command+" -i "+transactionID+" "+arguments); XDebugCorePlugin.log(IStatus.INFO,command+" -i "+fTransactionID+" "+arguments); synchronized (fDebugSocket) { try { fDebugWriter.write(command); fDebugWriter.write(" -i " + fTransactionID); if (arguments!="") fDebugWriter.write(" " + arguments); fDebugWriter.write(0); fDebugWriter.flush(); } catch (IOException e) { e.printStackTrace(); } } return fTransactionID++; } public DebugResponse waitforTransID(int id) { if (ResponseList.containsKey(new Integer(id))) { // return (DebugResponse)ResponseList.get(new Integer(id)); return (DebugResponse)ResponseList.remove(new Integer(id)); } else return lastResponse.waitforTransactionID(id); } public DebugResponse getResponse(int id) { if (ResponseList.containsKey(new Integer(id))) return (DebugResponse)ResponseList.get(new Integer(id)); else return waitforTransID(id); } }