package org.jboss.seam.remoting;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.remoting.messaging.PollError;
import org.jboss.seam.remoting.messaging.PollRequest;
import org.jboss.seam.remoting.wrapper.Wrapper;
import org.jboss.seam.servlet.ContextualHttpServletRequest;
/**
* Handles JMS Message poll requests.
*
* @author Shane Bryzak
*/
public class PollHandler extends BaseRequestHandler implements RequestHandler
{
private static final LogProvider log = Logging.getLogProvider(SubscriptionHandler.class);
private static final byte[] ERRORS_TAG_OPEN_START = "<errors token=\"".getBytes();
private static final byte[] ERRORS_TAG_OPEN_END = "\">".getBytes();
private static final byte[] ERROR_TAG_OPEN_START = "<error code=\"".getBytes();
private static final byte[] ERROR_TAG_OPEN_END = "\">".getBytes();
private static final byte[] ERROR_TAG_CLOSE = "</error>".getBytes();
private static final byte[] MESSAGES_TAG_OPEN_START = "<messages token=\"".getBytes();
private static final byte[] MESSAGES_TAG_OPEN_END = "\">".getBytes();
private static final byte[] MESSAGES_TAG_CLOSE = "</messages>".getBytes();
private static final byte[] MESSAGE_TAG_OPEN_START = "<message type=\"".getBytes();
private static final byte[] MESSAGE_TAG_OPEN_END = "\">".getBytes();
private static final byte[] MESSAGE_TAG_CLOSE = "</message>".getBytes();
private static final byte[] VALUE_TAG_OPEN = "<value>".getBytes();
private static final byte[] VALUE_TAG_CLOSE = "</value>".getBytes();
public void handle(HttpServletRequest request, final HttpServletResponse response)
throws Exception
{
// We're sending an XML response, so set the response content type to text/xml
response.setContentType("text/xml");
// Parse the incoming request as XML
SAXReader xmlReader = new SAXReader();
Document doc = xmlReader.read(request.getInputStream());
Element env = doc.getRootElement();
final List<PollRequest> polls = unmarshalRequests(env);
new ContextualHttpServletRequest(request)
{
@Override
public void process() throws Exception
{
for (PollRequest req : polls)
{
req.poll();
}
// Package up the response
marshalResponse(polls, response.getOutputStream());
}
}.run();
}
private List<PollRequest> unmarshalRequests(Element env)
throws Exception
{
try
{
List<PollRequest> requests = new ArrayList<PollRequest>();
List<Element> requestElements = env.element("body").elements("poll");
for (Element e : requestElements)
{
requests.add(new PollRequest(e.attributeValue("token"),
Integer.parseInt(e.attributeValue("timeout"))));
}
return requests;
}
catch (Exception ex)
{
log.error("Error unmarshalling subscriptions from request", ex);
throw ex;
}
}
private void marshalResponse(List<PollRequest> reqs, OutputStream out)
throws IOException
{
out.write(ENVELOPE_TAG_OPEN);
out.write(BODY_TAG_OPEN);
for (PollRequest req : reqs)
{
if (req.getErrors() != null && req.getErrors().size() > 0)
{
out.write(ERRORS_TAG_OPEN_START);
out.write(req.getToken().getBytes());
out.write(ERRORS_TAG_OPEN_END);
for (PollError err : req.getErrors())
{
writeError(err, out);
}
}
else if (req.getMessages() != null && req.getMessages().size() > 0)
{
out.write(MESSAGES_TAG_OPEN_START);
out.write(req.getToken().getBytes());
out.write(MESSAGES_TAG_OPEN_END);
for (Message m : req.getMessages()) {
try {
writeMessage(m, out);
}
catch (JMSException ex) {
}
catch (IOException ex) {
}
}
out.write(MESSAGES_TAG_CLOSE);
}
}
out.write(BODY_TAG_CLOSE);
out.write(ENVELOPE_TAG_CLOSE);
out.flush();
}
private void writeMessage(Message m, OutputStream out)
throws IOException, JMSException
{
out.write(MESSAGE_TAG_OPEN_START);
// We need one of these to maintain a list of outbound references
CallContext ctx = new CallContext();
Object value = null;
if (m instanceof TextMessage)
{
out.write("text".getBytes());
value = ((TextMessage) m).getText();
}
else if (m instanceof ObjectMessage)
{
out.write("object".getBytes());
value = ((ObjectMessage) m).getObject();
}
out.write(MESSAGE_TAG_OPEN_END);
out.write(VALUE_TAG_OPEN);
ctx.createWrapperFromObject(value, "").marshal(out);
out.write(VALUE_TAG_CLOSE);
out.write(REFS_TAG_OPEN);
// Using a for-loop, because stuff can get added to outRefs as we recurse the object graph
for (int i = 0; i < ctx.getOutRefs().size(); i++)
{
Wrapper wrapper = ctx.getOutRefs().get(i);
out.write(REF_TAG_OPEN_START);
out.write(Integer.toString(i).getBytes());
out.write(REF_TAG_OPEN_END);
wrapper.serialize(out);
out.write(REF_TAG_CLOSE);
}
out.write(REFS_TAG_CLOSE);
out.write(MESSAGE_TAG_CLOSE);
}
private void writeError(PollError error, OutputStream out)
throws IOException
{
out.write(ERROR_TAG_OPEN_START);
out.write(error.getCode().getBytes());
out.write(ERROR_TAG_OPEN_END);
out.write(error.getMessage().getBytes());
out.write(ERROR_TAG_CLOSE);
}
}