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 }