/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.translator.ws; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; import java.sql.SQLXML; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.activation.DataSource; import javax.xml.ws.Dispatch; import javax.xml.ws.Service.Mode; import javax.xml.ws.WebServiceException; import javax.xml.ws.handler.MessageContext; import javax.xml.ws.http.HTTPBinding; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import org.teiid.connector.DataPlugin; import org.teiid.core.types.BlobImpl; import org.teiid.core.types.BlobType; import org.teiid.core.types.ClobImpl; import org.teiid.core.types.InputStreamFactory; import org.teiid.language.Argument; import org.teiid.language.Argument.Direction; import org.teiid.language.Call; import org.teiid.metadata.ProcedureParameter.Type; import org.teiid.metadata.RuntimeMetadata; import org.teiid.translator.DataNotAvailableException; import org.teiid.translator.ExecutionContext; import org.teiid.translator.ProcedureExecution; import org.teiid.translator.TranslatorException; import org.teiid.translator.WSConnection; /** * http handler */ public class BinaryWSProcedureExecution implements ProcedureExecution { public static final class StreamingBlob extends BlobImpl { InputStream is; public StreamingBlob(InputStream is) { this.is = is; } @Override public InputStream getBinaryStream() throws SQLException { if (this.is == null) { throw new SQLException(DataPlugin.Util.gs(DataPlugin.Event.TEIID60019)); } InputStream result = this.is; this.is = null; return result; } } RuntimeMetadata metadata; ExecutionContext context; private Call procedure; private DataSource returnValue; private WSConnection conn; WSExecutionFactory executionFactory; Map<String, List<String>> customHeaders; Map<String, Object> responseContext = Collections.emptyMap(); int responseCode = 200; private boolean useResponseContext; /** * @param env */ public BinaryWSProcedureExecution(Call procedure, RuntimeMetadata metadata, ExecutionContext context, WSExecutionFactory executionFactory, WSConnection conn) { this.metadata = metadata; this.context = context; this.procedure = procedure; this.conn = conn; this.executionFactory = executionFactory; } public void setUseResponseContext(boolean useResponseContext) { this.useResponseContext = useResponseContext; } public void execute() throws TranslatorException { List<Argument> arguments = this.procedure.getArguments(); String method = (String)arguments.get(0).getArgumentValue().getValue(); Object payload = arguments.get(1).getArgumentValue().getValue(); String endpoint = (String)arguments.get(2).getArgumentValue().getValue(); try { Dispatch<DataSource> dispatch = this.conn.createDispatch(HTTPBinding.HTTP_BINDING, endpoint, DataSource.class, Mode.MESSAGE); if (method == null) { method = "POST"; //$NON-NLS-1$ } dispatch.getRequestContext().put(MessageContext.HTTP_REQUEST_METHOD, method); if (payload != null && !"POST".equalsIgnoreCase(method) && !"PUT".equalsIgnoreCase(method) && !"PATCH".equalsIgnoreCase(method)) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ throw new WebServiceException(WSExecutionFactory.UTIL.getString("http_usage_error")); //$NON-NLS-1$ } Map<String, List<String>> httpHeaders = (Map<String, List<String>>)dispatch.getRequestContext().get(MessageContext.HTTP_REQUEST_HEADERS); if (customHeaders != null) { httpHeaders.putAll(customHeaders); } if (arguments.size() > 5 //designer modeled the return value as an out, which will add an argument in the 5th position that is an out && this.procedure.getMetadataObject() != null && this.procedure.getMetadataObject().getParameters().get(0).getType() == Type.ReturnValue) { Clob headers = (Clob)arguments.get(5).getArgumentValue().getValue(); if (headers != null) { parseHeader(httpHeaders, headers); } } dispatch.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, httpHeaders); DataSource ds = null; if (payload instanceof String) { ds = new InputStreamFactory.ClobInputStreamFactory(new ClobImpl((String)payload)); } else if (payload instanceof SQLXML) { ds = new InputStreamFactory.SQLXMLInputStreamFactory((SQLXML)payload); } else if (payload instanceof Clob) { ds = new InputStreamFactory.ClobInputStreamFactory((Clob)payload); } else if (payload instanceof Blob) { ds = new InputStreamFactory.BlobInputStreamFactory((Blob)payload); } this.returnValue = dispatch.invoke(ds); Map<String, Object> rc = dispatch.getResponseContext(); this.responseCode = (Integer)rc.get(WSConnection.STATUS_CODE); if (this.useResponseContext) { //it's presumed that the caller will handle the response codes this.responseContext = rc; } else { //TODO: may need to add logic around some 200/300 codes - cxf should at least be logging this if (this.responseCode >= 400) { String message = conn.getStatusMessage(this.responseCode); throw new TranslatorException(WSExecutionFactory.Event.TEIID15005, WSExecutionFactory.UTIL.gs(WSExecutionFactory.Event.TEIID15005, this.responseCode, message)); } } } catch (WebServiceException e) { throw new TranslatorException(e); } catch (ParseException e) { throw new TranslatorException(e); } catch (IOException e) { throw new TranslatorException(e); } catch (SQLException e) { throw new TranslatorException(e); } } static void parseHeader(Map<String, List<String>> httpHeaders, Clob headers) throws ParseException, TranslatorException, IOException, SQLException { SimpleContentHandler sch = new SimpleContentHandler(); JSONParser parser = new JSONParser(); Reader characterStream = headers.getCharacterStream(); try { parser.parse(characterStream, sch); Object result = sch.getResult(); if (!(result instanceof Map)) { throw new TranslatorException(WSExecutionFactory.Event.TEIID15006, WSExecutionFactory.UTIL.gs(WSExecutionFactory.Event.TEIID15006)); } Map<String, Object> values = (Map)result; for (Map.Entry<String, Object> entry : values.entrySet()) { if ((entry.getValue() instanceof Map) || entry.getValue() == null) { throw new TranslatorException(WSExecutionFactory.Event.TEIID15006, WSExecutionFactory.UTIL.gs(WSExecutionFactory.Event.TEIID15006)); } if (!(entry.getValue() instanceof List)) { entry.setValue(Arrays.asList(entry.getValue().toString())); continue; } List<Object> list = (List)entry.getValue(); for (int i = 0; i < list.size(); i++) { Object value = list.get(i); if (value instanceof Map || value instanceof List) { throw new TranslatorException(WSExecutionFactory.Event.TEIID15006, WSExecutionFactory.UTIL.gs(WSExecutionFactory.Event.TEIID15006)); } if (!(value instanceof String)) { list.set(i, value.toString()); } } } httpHeaders.putAll((Map) values); } finally { characterStream.close(); } } @Override public List<?> next() throws TranslatorException, DataNotAvailableException { return null; } @Override public List<?> getOutputParameterValues() throws TranslatorException { Object result = this.returnValue; if (returnValue != null && procedure.getArguments().size() > 4 && procedure.getArguments().get(3).getDirection() == Direction.IN && Boolean.TRUE.equals(procedure.getArguments().get(3).getArgumentValue().getValue())) { try { result = new BlobType(new StreamingBlob(this.returnValue.getInputStream())); } catch (IOException e) { throw new TranslatorException(e); } } return Arrays.asList(result, this.returnValue.getContentType()); } public void close() { } public void cancel() throws TranslatorException { // no-op } public void setCustomHeaders(Map<String, List<String>> customHeaders) { this.customHeaders = customHeaders; } public Object getResponseHeader(String name){ return this.responseContext.get(name); } public Map<String, Object> getResponseHeaders(){ return new HashMap<String, Object>(this.responseContext) ; } public int getResponseCode() { return this.responseCode; } }