/*
* #%~
* org.overture.ide.debug
* %%
* Copyright (C) 2008 - 2014 Overture
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #~%
*/
package org.overture.ide.debug.core.dbgp.internal.packets;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedList;
import org.overture.ide.debug.core.dbgp.internal.DbgpRawPacket;
import org.overture.ide.debug.core.dbgp.internal.DbgpWorkingThread;
import org.overture.ide.debug.core.dbgp.internal.utils.DbgpXmlPacketParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
public class DbgpPacketReceiver extends DbgpWorkingThread
{
private static class ResponcePacketWaiter
{
// private static final int MIN_TIMEOUT = 5;
private final HashMap<Integer, DbgpResponsePacket> map;
private boolean terminated;
public ResponcePacketWaiter()
{
map = new HashMap<Integer, DbgpResponsePacket>();
terminated = false;
}
public synchronized void put(DbgpResponsePacket packet)
{
int id = packet.getTransactionId();
map.put(new Integer(id), packet);
notifyAll();
}
public synchronized DbgpResponsePacket waitPacket(int id, int timeout)
throws InterruptedException
{
Integer key = new Integer(id);
long endTime = 0;
if (timeout > 0)
{
endTime = System.currentTimeMillis() + timeout;
}
while (!terminated && !map.containsKey(key))
{
long current = System.currentTimeMillis();
if (endTime != 0 && current >= endTime)
{
break;
}
if (endTime == 0)
{
wait();
} else
{
wait(endTime - current);
}
}
if (map.containsKey(key))
{
return (DbgpResponsePacket) map.remove(key);
}
if (terminated)
{
System.out.println("failed to get response for packet: " + id);
throw new InterruptedException("responsePacketWaiterTerminated");
}
System.out.println("failed to get response for packet: " + id);
return null;
}
public synchronized void terminate()
{
terminated = true;
notifyAll();
}
}
private static class PacketWaiter
{
private final LinkedList<DbgpPacket> queue;
private boolean terminated;
public PacketWaiter()
{
terminated = false;
this.queue = new LinkedList<DbgpPacket>();
}
public synchronized void put(DbgpPacket obj)
{
queue.addLast(obj);
notifyAll();
}
public synchronized DbgpPacket waitPacket() throws InterruptedException
{
while (!terminated && queue.isEmpty())
{
wait();
}
if (terminated)
{
throw new InterruptedException("packetWaiterTerminated");
}
return (DbgpPacket) queue.removeFirst();
}
public synchronized void terminate()
{
terminated = true;
notifyAll();
}
}
private static final String INIT_TAG = "init"; //$NON-NLS-1$
private static final String RESPONSE_TAG = "response"; //$NON-NLS-1$
private static final String STREAM_TAG = "stream"; //$NON-NLS-1$
private static final String NOTIFY_TAG = "notify"; //$NON-NLS-1$
private final ResponcePacketWaiter responseWaiter;
private final PacketWaiter notifyWaiter;
private final PacketWaiter streamWaiter;
private final InputStream input;
private IDbgpRawLogger logger;
protected void workingCycle() throws Exception
{
try
{
while (!Thread.interrupted())
{
DbgpRawPacket packet = DbgpRawPacket.readPacket(input);
if (logger != null)
{
logger.log(packet);
}
addDocument(packet.getParsedXml());
}
} finally
{
responseWaiter.terminate();
notifyWaiter.terminate();
streamWaiter.terminate();
}
}
protected void addDocument(Document doc)
{
Element element = (Element) doc.getFirstChild();
String tag = element.getTagName();
// TODO: correct init tag handling without this hack
if (tag.equals(INIT_TAG))
{
responseWaiter.put(new DbgpResponsePacket(element, -1));
} else if (tag.equals(RESPONSE_TAG))
{
DbgpResponsePacket packet = DbgpXmlPacketParser.parseResponsePacket(element);
responseWaiter.put(packet);
} else if (tag.equals(STREAM_TAG))
{
streamWaiter.put(DbgpXmlPacketParser.parseStreamPacket(element));
} else if (tag.equals(NOTIFY_TAG))
{
notifyWaiter.put(DbgpXmlPacketParser.parseNotifyPacket(element));
}
}
public DbgpNotifyPacket getNotifyPacket() throws InterruptedException
{
return (DbgpNotifyPacket) notifyWaiter.waitPacket();
}
public DbgpStreamPacket getStreamPacket() throws InterruptedException
{
return (DbgpStreamPacket) streamWaiter.waitPacket();
}
public DbgpResponsePacket getResponsePacket(int transactionId, int timeout)
throws InterruptedException
{
return responseWaiter.waitPacket(transactionId, timeout);
}
public DbgpPacketReceiver(InputStream input)
{
super("DBGP - Packet receiver"); //$NON-NLS-1$
if (input == null)
{
throw new IllegalArgumentException();
}
this.input = input;
this.notifyWaiter = new PacketWaiter();
this.streamWaiter = new PacketWaiter();
this.responseWaiter = new ResponcePacketWaiter();
}
public void setLogger(IDbgpRawLogger logger)
{
this.logger = logger;
}
}