/*
* Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source 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 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source 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, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Emil Ong
*/
package com.caucho.soap.jaxws;
import com.caucho.soap.reflect.WebServiceIntrospector;
import com.caucho.soap.skeleton.Skeleton;
import com.caucho.util.L10N;
import com.caucho.util.ThreadPool;
import com.caucho.xml.stream.StaxUtil;
import com.caucho.xml.stream.XMLStreamReaderImpl;
import com.caucho.xml.stream.XMLStreamWriterImpl;
import javax.xml.bind.JAXBContext;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import static javax.xml.soap.SOAPConstants.*;
import javax.xml.ws.AsyncHandler;
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Response;
import javax.xml.ws.Service;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.handler.HandlerResolver;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.soap.SOAPBinding;
import javax.xml.ws.spi.ServiceDelegate;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.CharArrayReader;
import java.io.CharArrayWriter;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Proxy;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import java.util.logging.Level;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/**
* Dispatch
*/
public abstract class AbstractDispatch<T> implements Dispatch<T> {
private final static L10N L = new L10N(AbstractDispatch.class);
private final static Logger log
= Logger.getLogger(AbstractDispatch.class.getName());
protected final String _bindingId;
protected final Executor _executor;
protected Service.Mode _mode;
protected String _soapNamespace;
protected final Binding _binding;
protected final HashMap<String,Object> _requestContext
= new HashMap<String,Object>();
protected final HashMap<String,Object> _responseContext
= new HashMap<String,Object>();
public AbstractDispatch(String bindingId, Binding binding,
Service.Mode mode, Executor executor)
throws WebServiceException
{
_bindingId = bindingId;
_mode = mode;
_executor = executor;
_binding = binding;
if (bindingId.equals(SOAPBinding.SOAP11HTTP_BINDING) ||
bindingId.equals(SOAPBinding.SOAP11HTTP_MTOM_BINDING))
_soapNamespace = URI_NS_SOAP_1_1_ENVELOPE;
else if (bindingId.equals(SOAPBinding.SOAP12HTTP_BINDING) ||
bindingId.equals(SOAPBinding.SOAP12HTTP_MTOM_BINDING))
_soapNamespace = URI_NS_SOAP_1_2_ENVELOPE;
else if (bindingId.equals(HTTPBinding.HTTP_BINDING))
_mode = Service.Mode.MESSAGE;
else
throw new WebServiceException(L.l("Unknown binding id: {0}", bindingId));
}
//
// javax.xml.ws.Dispatch
//
public T invoke(T msg)
throws WebServiceException
{
if (log.isLoggable(Level.FINEST))
log.finest("AbstractDispatch.invoke(" + msg + ")");
ResponseImpl<T> response = new ResponseImpl<T>();
invokeNow(msg, response);
try {
return response.get();
}
catch (Exception e) {
throw new WebServiceException(e);
}
}
public Response<T> invokeAsync(T msg)
{
if (log.isLoggable(Level.FINEST))
log.finest("AbstractDispatch.invokeAsync(" + msg + ")");
ResponseImpl<T> response = new ResponseImpl<T>();
_executor.execute(new AsyncInvoker(msg, response));
return response;
}
public Future<?> invokeAsync(T msg, AsyncHandler<T> handler)
{
if (log.isLoggable(Level.FINEST))
log.finest("AbstractDispatch.invokeAsync(" + msg + "," + handler + ")");
ResponseImpl<T> response = new ResponseImpl<T>();
_executor.execute(new AsyncInvoker(msg, response, handler));
return response;
}
public void invokeOneWay(T msg)
throws WebServiceException
{
if (log.isLoggable(Level.FINEST))
log.finest("AbstractDispatch.invokeOneWay(" + msg + ")");
invokeNow(msg, null);
}
//
// javax.xml.ws.BindingProvider
//
public Binding getBinding()
{
return _binding;
}
public Map<String,Object> getRequestContext()
{
return _requestContext;
}
public Map<String,Object> getResponseContext()
{
return _responseContext;
}
protected abstract void writeRequest(T msg, OutputStream out)
throws WebServiceException;
protected abstract T formatResponse(byte[] response)
throws WebServiceException;
private void invokeNow(T msg, ResponseImpl<T> response)
throws WebServiceException
{
if (log.isLoggable(Level.FINEST))
log.finest("AbstractDispatch.invokeNow(" + msg + ")");
InputStream in = null;
OutputStream out = null;
try {
String urlSpec = (String) _requestContext.get(ENDPOINT_ADDRESS_PROPERTY);
if (urlSpec == null)
throw new WebServiceException(L.l("Endpoint address not set"));
URL url = new URL(urlSpec);
URLConnection connection = url.openConnection();
if (response != null)
connection.setDoInput(true);
connection.setDoOutput(true);
// send request
out = connection.getOutputStream();
OutputStreamWriter writer = null;
if (_mode == Service.Mode.PAYLOAD) {
writer = new OutputStreamWriter(out);
JAXWSUtil.writeStartSOAPEnvelope(writer, _soapNamespace);
}
writeRequest(msg, out);
if (_mode == Service.Mode.PAYLOAD)
JAXWSUtil.writeEndSOAPEnvelope(writer);
out.flush();
// read response
in = connection.getInputStream();
// XXX for some reason, it seems this is necessary to force the output
if (response == null) {
while (in.read() >= 0) {}
return;
}
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
if (_mode == Service.Mode.PAYLOAD) {
JAXWSUtil.extractSOAPBody(in, buffer);
}
else {
// XXX is a copy necessary here or should we expect the client to
// close the InputStream?
int ch = -1;
while ((ch = in.read()) != -1)
buffer.write(ch);
}
response.set(formatResponse(buffer.toByteArray()));
response.setContext(connection.getHeaderFields());
}
catch (WebServiceException e) {
throw e;
}
catch (Exception e) {
throw new WebServiceException(e);
}
finally {
try {
if (out != null)
out.close();
if (in != null)
in.close();
}
catch (IOException e) {
throw new WebServiceException(e);
}
}
}
protected class AsyncInvoker implements Runnable
{
private final T _msg;
private final ResponseImpl<T> _response;
private final AsyncHandler<T> _handler;
public AsyncInvoker(T msg, ResponseImpl<T> response)
{
this(msg, response, null);
}
public AsyncInvoker(T msg, ResponseImpl<T> response,
AsyncHandler<T> handler)
{
_msg = msg;
_response = response;
_handler = handler;
}
public void run()
{
try {
invokeNow(_msg, _response);
}
catch (Exception e) {
_response.setException(e);
}
if (_handler != null)
_handler.handleResponse(_response);
}
}
}