| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.apache.cxf.feature.transform; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.Writer; |
| import java.util.logging.Logger; |
| |
| import javax.xml.stream.XMLStreamException; |
| import javax.xml.stream.XMLStreamWriter; |
| import javax.xml.transform.Templates; |
| import javax.xml.transform.stream.StreamSource; |
| |
| import org.apache.cxf.common.logging.LogUtils; |
| import org.apache.cxf.helpers.IOUtils; |
| import org.apache.cxf.interceptor.AbstractOutDatabindingInterceptor; |
| import org.apache.cxf.interceptor.Fault; |
| import org.apache.cxf.interceptor.StaxOutInterceptor; |
| import org.apache.cxf.io.CachedOutputStream; |
| import org.apache.cxf.io.CachedOutputStreamCallback; |
| import org.apache.cxf.io.CachedWriter; |
| import org.apache.cxf.message.Message; |
| import org.apache.cxf.phase.Phase; |
| import org.apache.cxf.staxutils.DelegatingXMLStreamWriter; |
| import org.apache.cxf.staxutils.StaxUtils; |
| |
| /** Class provides XSLT transformation of outgoing message. |
| * Actually it breaks streaming (can be fixed in further versions when XSLT engine supports XML stream) |
| */ |
| public class XSLTOutInterceptor extends AbstractXSLTInterceptor { |
| private static final Logger LOG = LogUtils.getL7dLogger(XSLTOutInterceptor.class); |
| |
| public XSLTOutInterceptor(String xsltPath) { |
| super(Phase.PRE_STREAM, StaxOutInterceptor.class, null, xsltPath); |
| } |
| |
| public XSLTOutInterceptor(String phase, Class<?> before, Class<?> after, String xsltPath) { |
| super(phase, before, after, xsltPath); |
| } |
| |
| @Override |
| public void handleMessage(Message message) { |
| if (checkContextProperty(message)) { |
| return; |
| } |
| |
| // 1. Try to get and transform XMLStreamWriter message content |
| XMLStreamWriter xWriter = message.getContent(XMLStreamWriter.class); |
| if (xWriter != null) { |
| transformXWriter(message, xWriter); |
| } else { |
| // 2. Try to get and transform OutputStream message content |
| OutputStream out = message.getContent(OutputStream.class); |
| if (out != null) { |
| transformOS(message, out); |
| } else { |
| // 3. Try to get and transform Writer message content (actually used for JMS TextMessage) |
| Writer writer = message.getContent(Writer.class); |
| if (writer != null) { |
| transformWriter(message, writer); |
| } |
| } |
| } |
| } |
| |
| protected void transformXWriter(Message message, XMLStreamWriter xWriter) { |
| CachedWriter writer = new CachedWriter(); |
| XMLStreamWriter delegate = StaxUtils.createXMLStreamWriter(writer); |
| XSLTStreamWriter wrapper = new XSLTStreamWriter(getXSLTTemplate(), writer, delegate, xWriter); |
| message.setContent(XMLStreamWriter.class, wrapper); |
| message.put(AbstractOutDatabindingInterceptor.DISABLE_OUTPUTSTREAM_OPTIMIZATION, |
| Boolean.TRUE); |
| } |
| |
| protected void transformOS(Message message, OutputStream out) { |
| CachedOutputStream wrapper = new CachedOutputStream(); |
| CachedOutputStreamCallback callback = new XSLTCachedOutputStreamCallback(getXSLTTemplate(), out); |
| wrapper.registerCallback(callback); |
| message.setContent(OutputStream.class, wrapper); |
| } |
| |
| protected void transformWriter(Message message, Writer writer) { |
| XSLTCachedWriter wrapper = new XSLTCachedWriter(getXSLTTemplate(), writer); |
| message.setContent(Writer.class, wrapper); |
| } |
| |
| |
| public static class XSLTStreamWriter extends DelegatingXMLStreamWriter { |
| private final Templates xsltTemplate; |
| private final CachedWriter cachedWriter; |
| private final XMLStreamWriter origXWriter; |
| |
| public XSLTStreamWriter(Templates xsltTemplate, CachedWriter cachedWriter, |
| XMLStreamWriter delegateXWriter, XMLStreamWriter origXWriter) { |
| super(delegateXWriter); |
| this.xsltTemplate = xsltTemplate; |
| this.cachedWriter = cachedWriter; |
| this.origXWriter = origXWriter; |
| } |
| |
| @Override |
| public void close() { |
| Reader transformedReader = null; |
| try { |
| super.flush(); |
| transformedReader = XSLTUtils.transform(xsltTemplate, cachedWriter.getReader()); |
| StaxUtils.copy(new StreamSource(transformedReader), origXWriter); |
| } catch (XMLStreamException e) { |
| throw new Fault("STAX_COPY", LOG, e, e.getMessage()); |
| } catch (IOException e) { |
| throw new Fault("GET_CACHED_INPUT_STREAM", LOG, e, e.getMessage()); |
| } finally { |
| try { |
| if (transformedReader != null) { |
| transformedReader.close(); |
| } |
| cachedWriter.close(); |
| StaxUtils.close(origXWriter); |
| super.close(); |
| } catch (Exception e) { |
| LOG.warning("Cannot close stream after transformation: " + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| public static class XSLTCachedOutputStreamCallback implements CachedOutputStreamCallback { |
| private final Templates xsltTemplate; |
| private final OutputStream origStream; |
| |
| public XSLTCachedOutputStreamCallback(Templates xsltTemplate, OutputStream origStream) { |
| this.xsltTemplate = xsltTemplate; |
| this.origStream = origStream; |
| } |
| |
| @Override |
| public void onFlush(CachedOutputStream wrapper) { |
| } |
| |
| @Override |
| public void onClose(CachedOutputStream wrapper) { |
| InputStream transformedStream; |
| Exception exceptionOnClose = null; |
| try { |
| transformedStream = XSLTUtils.transform(xsltTemplate, wrapper.getInputStream()); |
| IOUtils.copyAndCloseInput(transformedStream, origStream); |
| } catch (IOException e) { |
| throw new Fault("STREAM_COPY", LOG, e, e.getMessage()); |
| } finally { |
| try { |
| origStream.close(); |
| } catch (Exception e) { |
| exceptionOnClose = e; |
| } |
| } |
| |
| if (exceptionOnClose == null) { |
| return; |
| } |
| throw new Fault(exceptionOnClose); |
| } |
| } |
| |
| public static class XSLTCachedWriter extends CachedWriter { |
| private final Templates xsltTemplate; |
| private final Writer origWriter; |
| |
| public XSLTCachedWriter(Templates xsltTemplate, Writer origWriter) { |
| this.xsltTemplate = xsltTemplate; |
| this.origWriter = origWriter; |
| } |
| |
| @Override |
| protected void doClose() { |
| try { |
| final Reader transformedReader = XSLTUtils.transform(xsltTemplate, getReader()); |
| IOUtils.copyAndCloseInput(transformedReader, origWriter, IOUtils.DEFAULT_BUFFER_SIZE); |
| } catch (IOException e) { |
| throw new Fault("READER_COPY", LOG, e, e.getMessage()); |
| } finally { |
| try { |
| origWriter.close(); |
| } catch (IOException e) { |
| LOG.warning("Cannot close stream after transformation: " + e.getMessage()); |
| } |
| } |
| } |
| } |
| |
| } |