package org.jboss.seam.remoting;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.util.Iterator;
import javax.enterprise.context.ContextNotActiveException;
import javax.enterprise.context.Conversation;
import javax.enterprise.event.Event;
import javax.enterprise.inject.spi.BeanManager;
import javax.inject.Inject;
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.solder.logging.Logger;
import org.jboss.seam.remoting.wrapper.Wrapper;
/**
* Unmarshals the calls from an HttpServletRequest, executes them in order and
* marshals the responses.
*
* @author Shane Bryzak
*/
public class ExecutionHandler extends AbstractRequestHandler implements RequestHandler {
private static final Logger log = Logger.getLogger(ExecutionHandler.class);
@Inject BeanManager beanManager;
@Inject Conversation conversation;
@Inject Event<WriteHeaderEvent> writeHeaderEvent;
/**
* The entry point for handling a request.
*
* @param request HttpServletRequest
* @param response HttpServletResponse
* @throws Exception
*/
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");
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[256];
int read = request.getInputStream().read(buffer);
while (read != -1) {
out.write(buffer, 0, read);
read = request.getInputStream().read(buffer);
}
String requestData = new String(out.toByteArray());
log.debug("Processing remote request: " + requestData);
// Parse the incoming request as XML
SAXReader xmlReader = new SAXReader();
Document doc = xmlReader.read(new StringReader(requestData));
final Element env = doc.getRootElement();
final RequestContext ctx = new RequestContext(env.element("header"));
try {
activateConversationContext(request, ctx.getConversationId());
// Extract the calls from the request
Call call = unmarshalCall(env);
call.execute();
if (call.getException() != null) {
log.error("Error while executing call", call.getException());
}
// Store the conversation ID in the outgoing context
try {
ctx.setConversationId(conversation.isTransient() ? null : conversation.getId());
} catch (ContextNotActiveException ex) {
// No active conversation context, ignore
}
// Package up the response
marshalResponse(call, ctx, response.getOutputStream());
} finally {
deactivateConversationContext(request);
}
}
/**
* Unmarshal the request into a list of Calls.
*
* @param env Element
* @return Call
* @throws Exception for any error
*/
@SuppressWarnings("unchecked")
private Call unmarshalCall(Element env) throws Exception {
try {
Element callElement = env.element("body").element("call");
Element targetNode = callElement.element("target");
Element qualifiersNode = callElement.element("qualifiers");
Element methodNode = callElement.element("method");
Call call = new Call(beanManager, targetNode.getText(),
qualifiersNode != null ? qualifiersNode.getText() : null,
methodNode.getText());
// First reconstruct all the references
Element refsNode = callElement.element("refs");
Iterator<Element> iter = refsNode.elementIterator("ref");
while (iter.hasNext()) {
call.getContext().createWrapperFromElement(iter.next());
}
// Now unmarshal the ref values
for (Wrapper w : call.getContext().getInRefs().values()) {
w.unmarshal();
}
Element paramsNode = callElement.element("params");
// Then process the param values
iter = paramsNode.elementIterator("param");
while (iter.hasNext()) {
Element param = iter.next();
call.addParameter(call.getContext().createWrapperFromElement(
(Element) param.elementIterator().next()));
}
return call;
} catch (Exception ex) {
log.error("Error unmarshalling calls from request", ex);
throw ex;
}
}
/**
* Write the results to the output stream.
*
* @param call List The list of calls to write
* @param ctx The current Request Context
* @param out OutputStream The stream to write to
* @throws IOException for any I/O error
*/
private void marshalResponse(Call call, RequestContext ctx, OutputStream out)
throws IOException {
out.write(ENVELOPE_TAG_OPEN);
out.write(HEADER_OPEN);
writeHeaderEvent.fire(new WriteHeaderEvent(out));
out.write(CONTEXT_TAG_OPEN);
if (ctx.getConversationId() != null) {
out.write(CONVERSATION_ID_TAG_OPEN);
out.write(ctx.getConversationId().getBytes());
out.write(CONVERSATION_ID_TAG_CLOSE);
}
out.write(CALL_ID_TAG_OPEN);
out.write(ctx.getCallId().toString().getBytes());
out.write(CALL_ID_TAG_CLOSE);
out.write(CONTEXT_TAG_CLOSE);
out.write(HEADER_CLOSE);
out.write(BODY_TAG_OPEN);
MarshalUtils.marshalCallResult(call, out);
out.write(BODY_TAG_CLOSE);
out.write(ENVELOPE_TAG_CLOSE);
out.flush();
}
}