001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    package org.apache.camel.component.exec;
018    
019    import java.io.ByteArrayInputStream;
020    import java.io.FileInputStream;
021    import java.io.FileNotFoundException;
022    import java.io.IOException;
023    import java.io.InputStream;
024    
025    import org.w3c.dom.Document;
026    
027    import org.apache.camel.Converter;
028    import org.apache.camel.Exchange;
029    import org.apache.commons.io.IOUtils;
030    import org.slf4j.Logger;
031    import org.slf4j.LoggerFactory;
032    
033    /**
034     * Default converters for {@link ExecResult}. For details how to extend the
035     * converters check out <a
036     * href="http://camel.apache.org/type-converter.html">the Camel docs for type
037     * converters.</a>
038     */
039    @Converter
040    public final class ExecResultConverter {
041    
042        private static final Logger LOG = LoggerFactory.getLogger(ExecResultConverter.class);
043    
044        private ExecResultConverter() {
045        }
046    
047        @Converter
048        public static InputStream convertToInputStream(ExecResult result) throws FileNotFoundException {
049            return toInputStream(result);
050        }
051    
052        @Converter
053        public static byte[] convertToByteArray(ExecResult result, Exchange exchange) throws FileNotFoundException, IOException {
054            InputStream stream = toInputStream(result);
055            try {
056                return IOUtils.toByteArray(stream);
057            } finally {
058                IOUtils.closeQuietly(stream);
059            }
060        }
061    
062        @Converter
063        public static String convertToString(ExecResult result, Exchange exchange) throws FileNotFoundException {
064            // special for string, as we want an empty string if no output from stdin / stderr
065            InputStream is = toInputStream(result);
066            if (is != null) {
067                return exchange.getContext().getTypeConverter().convertTo(String.class, exchange, is);
068            } else {
069                // no stdin/stdout, so return an empty string
070                return "";
071            }
072        }
073    
074        @Converter
075        public static Document convertToDocument(ExecResult result, Exchange exchange) throws FileNotFoundException {
076            return convertTo(Document.class, exchange, result);
077        }
078    
079        /**
080         * Converts <code>ExecResult</code> to the type <code>T</code>.
081         * 
082         * @param <T> The type to convert to
083         * @param type Class instance of the type to which to convert
084         * @param exchange a Camel exchange. If exchange is <code>null</code>, no
085         *            conversion will be made
086         * @param result the exec result
087         * @return the converted {@link ExecResult}
088         * @throws FileNotFoundException if there is a file in the execResult, and
089         *             the file can not be found
090         */
091        @SuppressWarnings("unchecked")
092        public static <T> T convertTo(Class<T> type, Exchange exchange, ExecResult result) throws FileNotFoundException {
093            InputStream is = toInputStream(result);
094            if (is != null) {
095                return exchange.getContext().getTypeConverter().convertTo(type, exchange, is);
096            } else {
097                // use Void to indicate we cannot convert it
098                // (prevents Camel from using a fallback converter which may convert a String from the instance name)  
099                return (T) Void.TYPE;
100            }
101        }
102    
103        /**
104         * Returns <code>InputStream</code> object with the <i>output</i> of the
105         * executable. If there is {@link ExecCommand#getOutFile()}, its content is
106         * preferred to {@link ExecResult#getStdout()}. If no out file is set, and
107         * the stdout of the exec result is <code>null</code> returns the stderr of
108         * the exec result. <br>
109         * If the output stream is of type <code>ByteArrayInputStream</code>, its
110         * <code>reset()</code> method is called.
111         * 
112         * @param execResult ExecResult object to convert to InputStream.
113         * @return InputStream object with the <i>output</i> of the executable.
114         *         Returns <code>null</code> if both {@link ExecResult#getStdout()}
115         *         and {@link ExecResult#getStderr()} are <code>null</code> , or if
116         *         the <code>execResult</code> is <code>null</code>.
117         * @throws FileNotFoundException if the {@link ExecCommand#getOutFile()} can
118         *             not be opened. In this case the out file must have had a not
119         *             <code>null</code> value
120         */
121        public static InputStream toInputStream(ExecResult execResult) throws FileNotFoundException {
122            if (execResult == null) {
123                LOG.warn("Received a null ExecResult instance to convert!");
124                return null;
125            }
126            // prefer the out file for output
127            InputStream result;
128            if (execResult.getCommand().getOutFile() != null) {
129                result = new FileInputStream(execResult.getCommand().getOutFile());
130            } else {
131                // if the stdout is null, return the stderr.
132                if (execResult.getStdout() == null && execResult.getCommand().isUseStderrOnEmptyStdout()) {
133                    LOG.warn("ExecResult has no stdout, will fallback to use stderr.");
134                    result = execResult.getStderr();
135                } else {
136                    result = execResult.getStdout() != null ? execResult.getStdout() : null;
137                }
138            }
139            // reset the stream if it was already read.
140            resetIfByteArrayInputStream(result);
141            return result;
142        }
143    
144        /**
145         * Resets the stream, only if it's a ByteArrayInputStream.
146         */
147        private static void resetIfByteArrayInputStream(InputStream stream) {
148            if (stream != null && stream instanceof ByteArrayInputStream) {
149                try {
150                    stream.reset();
151                } catch (IOException ioe) {
152                    LOG.error("Unable to reset the stream ", ioe);
153                }
154            }
155        }
156    }