| /* |
| * 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.jclouds.logging.internal; |
| |
| import com.google.common.io.ByteStreams; |
| import com.google.common.io.FileBackedOutputStream; |
| import org.jclouds.io.MutableContentMetadata; |
| import org.jclouds.io.Payload; |
| import org.jclouds.io.PayloadEnclosing; |
| import org.jclouds.logging.Logger; |
| |
| import javax.annotation.Resource; |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FilterInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static org.jclouds.io.Payloads.newPayload; |
| import static org.jclouds.util.Closeables2.closeQuietly; |
| |
| /** |
| * Logs data to the wire LOG, similar to {@code org.apache.HttpWire.impl.conn.Wire} |
| */ |
| public abstract class Wire { |
| |
| @Resource |
| protected Logger logger = Logger.NULL; |
| |
| protected abstract Logger getWireLog(); |
| |
| protected boolean isLogSensitiveInformation() { |
| return false; |
| } |
| |
| private void wire(String header, InputStream instream) { |
| StringBuilder buffer = new StringBuilder(); |
| int ch; |
| try { |
| while ((ch = instream.read()) != -1) { |
| if (ch == 13) { |
| buffer.append("[\\r]"); |
| } else if (ch == 10) { |
| buffer.append("[\\n]\""); |
| buffer.insert(0, "\""); |
| buffer.insert(0, header); |
| getWireLog().debug(buffer.toString()); |
| buffer.setLength(0); |
| } else if ((ch < 32) || (ch > 127)) { |
| buffer.append("[0x"); |
| buffer.append(Integer.toHexString(ch)); |
| buffer.append("]"); |
| } else { |
| buffer.append((char) ch); |
| } |
| } |
| if (buffer.length() > 0) { |
| buffer.append('\"'); |
| buffer.insert(0, '\"'); |
| buffer.insert(0, header); |
| getWireLog().debug(buffer.toString()); |
| } |
| } catch (IOException e) { |
| logger.error(e, "Error tapping line"); |
| } |
| } |
| |
| public boolean enabled() { |
| return getWireLog().isDebugEnabled(); |
| } |
| |
| public InputStream copy(final String header, InputStream instream) { |
| int limit = 256 * 1024; |
| final FileBackedOutputStream out = new FileBackedOutputStream(limit); |
| try { |
| long bytesRead = ByteStreams.copy(instream, out); |
| if (bytesRead >= limit) |
| logger.debug("over limit %d/%d: wrote temp file", bytesRead, limit); |
| InputStream is = out.asByteSource().openStream(); |
| try { |
| wire(header, is); |
| } finally { |
| is.close(); |
| } |
| // we must call FileBackedOutputStream.reset to remove temporary file |
| return new FilterInputStream(out.asByteSource().openStream()) { |
| @Override |
| public void close() throws IOException { |
| super.close(); |
| out.reset(); |
| } |
| }; |
| } catch (IOException e) { |
| throw new RuntimeException("Error tapping line", e); |
| } finally { |
| closeQuietly(out); |
| closeQuietly(instream); |
| } |
| } |
| |
| public InputStream input(InputStream instream) { |
| return copy("<< ", checkNotNull(instream, "input")); |
| } |
| |
| public void input(PayloadEnclosing request) { |
| Payload oldContent = request.getPayload(); |
| Payload wiredPayload; |
| if (!oldContent.isSensitive() || isLogSensitiveInformation()) { |
| wiredPayload = newPayload(input(oldContent.getInput())); |
| } else { |
| wiredPayload = newPayload(oldContent.getInput()); |
| } |
| wiredPayload.setSensitive(oldContent.isSensitive()); |
| copyPayloadMetadata(oldContent, wiredPayload); |
| request.setPayload(wiredPayload); |
| } |
| |
| public void output(PayloadEnclosing request) { |
| Payload oldContent = request.getPayload(); |
| Payload wiredPayload; |
| if (!oldContent.isSensitive() || isLogSensitiveInformation()) { |
| try { |
| wiredPayload = newPayload(output(oldContent.getRawContent())); |
| } catch (UnsupportedOperationException e) { |
| wiredPayload = newPayload(output(oldContent.getInput())); |
| } |
| } else { |
| try { |
| wiredPayload = newPayload(oldContent.getRawContent()); |
| } catch (UnsupportedOperationException e) { |
| wiredPayload = newPayload(oldContent.getInput()); |
| } |
| output("Sensitive data in payload, use PROPERTY_LOGGER_WIRE_LOG_SENSITIVE_INFO override to enable logging this data."); |
| } |
| wiredPayload.setSensitive(oldContent.isSensitive()); |
| copyPayloadMetadata(oldContent, wiredPayload); |
| request.setPayload(wiredPayload); |
| } |
| |
| private void copyPayloadMetadata(Payload oldContent, Payload wiredPayload) { |
| MutableContentMetadata oldMd = oldContent.getContentMetadata(); |
| MutableContentMetadata wiredMd = wiredPayload.getContentMetadata(); |
| if (oldMd.getContentLength() != null) |
| wiredMd.setContentLength(oldMd.getContentLength()); |
| wiredMd.setContentType(oldMd.getContentType()); |
| wiredMd.setContentMD5(oldMd.getContentMD5()); |
| wiredMd.setContentDisposition(oldMd.getContentDisposition()); |
| wiredMd.setContentEncoding(oldMd.getContentEncoding()); |
| wiredMd.setContentLanguage(oldMd.getContentLanguage()); |
| wiredMd.setExpires(oldMd.getExpires()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public <T> T output(T data) { |
| checkNotNull(data, "data"); |
| if (data instanceof InputStream) { |
| return (T) copy(">> ", (InputStream) data); |
| } else if (data instanceof byte[]) { |
| output((byte[]) data); |
| return data; |
| } else if (data instanceof String) { |
| output((String) data); |
| return data; |
| } else if (data instanceof File) { |
| output((File) data); |
| return data; |
| } |
| throw new UnsupportedOperationException("Content not supported " + data.getClass()); |
| } |
| |
| private void output(final File out) { |
| checkNotNull(out, "output"); |
| InputStream in = null; |
| try { |
| in = new FileInputStream(out); |
| wire(">> ", in); |
| } catch (FileNotFoundException e) { |
| logger.error(e, "Error tapping file: %s", out); |
| } finally { |
| closeQuietly(in); |
| } |
| } |
| |
| private void output(byte[] b) { |
| wire(">> ", new ByteArrayInputStream(checkNotNull(b, "output"))); |
| } |
| |
| private void output(final String s) { |
| output(checkNotNull(s, "output").getBytes()); |
| } |
| |
| } |