You are viewing a plain text version of this content. The canonical link for it is here.
Posted to common-commits@hadoop.apache.org by st...@apache.org on 2009/03/30 14:54:06 UTC

svn commit: r759951 - in /hadoop/core/branches/HADOOP-3628/src: core/org/apache/hadoop/io/ThrowableWritable.java test/org/apache/hadoop/io/TestThrowableWritable.java

Author: stevel
Date: Mon Mar 30 12:54:06 2009
New Revision: 759951

URL: http://svn.apache.org/viewvc?rev=759951&view=rev
Log:
adding a throwable writable and a test for it. This is not yet in use,  but the ping operation would benefit from switching to it.

Added:
    hadoop/core/branches/HADOOP-3628/src/core/org/apache/hadoop/io/ThrowableWritable.java
    hadoop/core/branches/HADOOP-3628/src/test/org/apache/hadoop/io/TestThrowableWritable.java

Added: hadoop/core/branches/HADOOP-3628/src/core/org/apache/hadoop/io/ThrowableWritable.java
URL: http://svn.apache.org/viewvc/hadoop/core/branches/HADOOP-3628/src/core/org/apache/hadoop/io/ThrowableWritable.java?rev=759951&view=auto
==============================================================================
--- hadoop/core/branches/HADOOP-3628/src/core/org/apache/hadoop/io/ThrowableWritable.java (added)
+++ hadoop/core/branches/HADOOP-3628/src/core/org/apache/hadoop/io/ThrowableWritable.java Mon Mar 30 12:54:06 2009
@@ -0,0 +1,278 @@
+/**
+ * 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.hadoop.io;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/** This writable component builds from a throwable */
+
+public final class ThrowableWritable implements Writable {
+
+  /** throwable classname */
+  private String classname;
+
+  /** throwable message */
+
+  private String message;
+  /** cause: may be null */
+  private ThrowableWritable cause;
+
+  /**
+   * Stack trace as string; will be null when an empty element is created,
+   * otherwise it will be an array of length zero.
+   */
+  private String[] stack;
+
+
+  /**
+   * Empty constructor.
+   * Only use this when you are planning to deserialize data, as the object is
+   * otherwise incomplete.
+   */
+  public ThrowableWritable() {
+  }
+
+
+  /**
+   * Construct a lightweight throwable writeable with no stack trace; and the
+   * message passed in
+   *
+   * @param message message to use
+   */
+  public ThrowableWritable(String message) {
+    this.message = message;
+    stack = new String[0];
+    classname = "";
+  }
+
+  /**
+   * recursively construct from a throwable chain.
+   *
+   * @param thrown The throwable chain to build this writeable from.
+   */
+  public ThrowableWritable(Throwable thrown) {
+    classname = thrown.getClass().getName();
+    message = thrown.getMessage();
+
+    StackTraceElement[] st = thrown.getStackTrace();
+    if (st != null) {
+      int sl = st.length;
+      stack = new String[sl];
+      for (int i = 0; i < sl; i++) {
+        stack[i] = st[i].toString();
+      }
+    } else {
+      stack = new String[0];
+    }
+    Throwable rootCause = thrown.getCause();
+    if (rootCause != null && rootCause != thrown) {
+      cause = new ThrowableWritable(rootCause);
+    }
+  }
+
+  /**
+   * Copy constructor.
+   *
+   * @param that the original instance to copy
+   */
+  public ThrowableWritable(ThrowableWritable that) {
+    classname = that.classname;
+    message = that.message;
+    //copy stack trace
+    if (that.stack == null) {
+      stack = new String[0];
+    } else {
+      int l = that.stack.length;
+      stack = new String[l];
+      System.arraycopy(that.stack, 0, stack, 0, l);
+    }
+    //copy any nested cause
+    if (that.cause != null) {
+      cause = new ThrowableWritable(that.cause);
+    }
+  }
+
+
+  /**
+   * Get the classname of the underlying throwable
+   *
+   * @return the classname of the original throwable
+   */
+  public String getClassname() {
+    return classname;
+  }
+
+  /**
+   * Get the text string this instance was constructed with
+   *
+   * @return the message of the underlying throwable
+   */
+  public String getMessage() {
+    return message;
+  }
+
+  /**
+   * Get any nested cause of the exception
+   *
+   * @return any nested cause as another ThrowableWritable -or null
+   */
+  public ThrowableWritable getCause() {
+    return cause;
+  }
+
+  /**
+   * Get the stack trace of the original throwable. It may be of size 0.
+   *
+   * @return the stack trace converted to strings
+   */
+  public String[] getStack() {
+    return stack;
+  }
+
+
+  /**
+   * determine (recursively) the depth of this Throwable chain
+   *
+   * @return a number equal to or greater than 1
+   */
+  public int getDepth() {
+    return 1 + (cause == null ? 0 : cause.getDepth());
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @param out <code>DataOutput</code> to serialize this object into.
+   *
+   * @throws IOException IO trouble
+   */
+  public void write(DataOutput out) throws IOException {
+    out.writeUTF(classname);
+    out.writeUTF(message);
+    if (stack != null) {
+      out.writeInt(stack.length);
+      for (String call : stack) {
+        out.writeUTF(call);
+      }
+    } else {
+      out.writeInt(0);
+    }
+    //look for a cause
+    boolean hasCause = cause != null;
+    out.writeBoolean(hasCause);
+    if (hasCause) {
+      //recursively write it
+      cause.write(out);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @param in <code>DataInput</code> to deseriablize this object from.
+   *
+   * @throws IOException IO trouble
+   */
+  public void readFields(DataInput in) throws IOException {
+    classname = in.readUTF();
+    message = in.readUTF();
+    int stackLength = in.readInt();
+    if (stack == null || stack.length != stackLength) {
+      //create a new stack array
+      stack = new String[stackLength];
+    }
+    //read in the stack
+    for (int i = 0; i < stackLength; i++) {
+      stack[i] = in.readUTF();
+    }
+    //look for any nested cause
+    boolean hasCause = in.readBoolean();
+    if (hasCause) {
+      if (cause == null) {
+        cause = new ThrowableWritable();
+      }
+      cause.readFields(in);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * @throws CloneNotSupportedException this should not happen
+   */
+  @SuppressWarnings({"CloneDoesntCallSuperClone"})
+  @Override
+  protected Object clone() throws CloneNotSupportedException {
+    return new ThrowableWritable(this);
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * The classname and message are used for equality
+   */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+
+    ThrowableWritable that = (ThrowableWritable) o;
+
+    if (classname != null ? !classname.equals(that.classname) : that.classname != null)
+      return false;
+    return !(message != null ? !message.equals(that.message) : that.message != null);
+
+  }
+
+  /**
+   * {@inheritDoc}
+   *
+   * The classname and message are used in the hash
+   */
+  @Override
+  public int hashCode() {
+    int result = classname != null ? classname.hashCode() : 0;
+    result = 31 * result + (message != null ? message.hashCode() : 0);
+    return result;
+  }
+
+  /**
+   * Return the classname and message in the format classname: message The
+   * output is designed to resemble that of {@link Throwable#toString()} if the
+   * message and classname are both set. If only the message is set, only that
+   * is printed.
+   *
+   * @return a string representation of the object.
+   */
+  @Override
+  public String toString() {
+    StringBuilder builder = new StringBuilder();
+    if (classname != null) {
+      builder.append(classname);
+      if (!classname.isEmpty()) {
+        builder.append(": ");
+      }
+    }
+    if (message != null) {
+      builder.append(message);
+    }
+    return builder.toString();
+  }
+}

Added: hadoop/core/branches/HADOOP-3628/src/test/org/apache/hadoop/io/TestThrowableWritable.java
URL: http://svn.apache.org/viewvc/hadoop/core/branches/HADOOP-3628/src/test/org/apache/hadoop/io/TestThrowableWritable.java?rev=759951&view=auto
==============================================================================
--- hadoop/core/branches/HADOOP-3628/src/test/org/apache/hadoop/io/TestThrowableWritable.java (added)
+++ hadoop/core/branches/HADOOP-3628/src/test/org/apache/hadoop/io/TestThrowableWritable.java Mon Mar 30 12:54:06 2009
@@ -0,0 +1,154 @@
+/**
+ * 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.hadoop.io;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+
+public class TestThrowableWritable extends TestCase {
+
+  ThrowableWritable simple, messageOnly, chained, empty;
+  private static final String SIMPLE = "simple";
+  private static final String MESSAGE_ONLY = "messageOnly";
+  private static final String OUTER = "outer";
+  private static final String INNER = "inner";
+
+  public TestThrowableWritable() {
+  }
+
+  public TestThrowableWritable(String s) {
+    super(s);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected void setUp() throws Exception {
+    super.setUp();
+
+    simple = new ThrowableWritable(new Throwable(SIMPLE));
+    messageOnly = new ThrowableWritable(MESSAGE_ONLY);
+    empty = new ThrowableWritable();
+    chained = new ThrowableWritable(new Throwable(OUTER,
+        new IOException(INNER)));
+  }
+
+  private void assertEmptyStack(ThrowableWritable throwableWritable) {
+    assertEquals(0, throwableWritable.getStack().length);
+  }
+
+  private void assertCopyWorks(ThrowableWritable instance) throws CloneNotSupportedException {
+    Object cloned = instance.clone();
+    ThrowableWritable copy = new ThrowableWritable(instance);
+    assertEquals(cloned, copy);
+    assertEquals(instance, copy);
+    assertEquals(instance.hashCode(), copy.hashCode());
+    assertEquals(instance.getDepth(), copy.getDepth());
+  }
+
+  private void assertStackSetUp(ThrowableWritable instance) {
+    assertTrue(instance.getStack().length > 0);
+    String topEntry = instance.getStack()[0];
+    assertTrue("No stack in "+topEntry,
+        topEntry.contains("TestThrowableWritable"));
+  }
+
+  private void assertMessageEquals(String message, ThrowableWritable instance) {
+    assertEquals(message,instance.getMessage());
+  }
+
+  private void assertDepth(int depth, ThrowableWritable instance) {
+    assertEquals(depth, instance.getDepth());
+  }
+
+  private void assertClassnameContains(String classname, ThrowableWritable instance) {
+    assertNotNull(instance.getClassname());
+    assertContains(classname, instance.getClassname());
+  }
+
+  private void assertContains(String expected, String source) {
+    assertNotNull(source);
+    assertTrue("Did not find "+expected+ " in "+source,source.contains(expected));
+  }
+
+  private void close(java.io.Closeable c) throws IOException {
+    if(c!=null) {
+      c.close();
+    }
+  }
+
+  private void assertRoundTrips(ThrowableWritable source) throws IOException {
+    DataOutputBuffer out = null;
+    DataInputBuffer in = null;
+    ThrowableWritable dest;
+    try {
+      out = new DataOutputBuffer();
+      in = new DataInputBuffer();
+      out.reset();
+      source.write(out);
+      in.reset(out.getData(), out.getLength());
+      dest = new ThrowableWritable();
+      dest.readFields(in);
+    } finally {
+      close(in);
+      close(out);
+    }
+    assertEquals(source, dest);
+  }
+
+  public void testEmptyInstance() throws Throwable {
+    assertNotNull(empty.toString());
+    assertNull(empty.getClassname());
+    assertEquals(empty, empty);
+    assertNull(empty.getMessage());
+    assertCopyWorks(empty);
+    assertDepth(1, empty);
+  }
+
+  public void testSimple() throws Throwable {
+    assertMessageEquals(SIMPLE, simple);
+    assertClassnameContains("Throwable", simple);
+    assertStackSetUp(simple);
+    assertDepth(1, simple);
+    assertCopyWorks(simple);
+    assertRoundTrips(simple);
+  }
+
+  public void testMessageOnly() throws Throwable {
+    assertMessageEquals(MESSAGE_ONLY, messageOnly);
+    assertEmptyStack(messageOnly);
+    assertDepth(1, messageOnly);
+    assertCopyWorks(messageOnly);
+    assertRoundTrips(messageOnly);
+  }
+
+  public void testChained() throws Throwable {
+    assertContains(OUTER, chained.toString());
+    assertClassnameContains("Throwable", chained);
+    assertStackSetUp(chained);
+    assertDepth(2, chained);
+    assertCopyWorks(chained);
+    ThrowableWritable cause = chained.getCause();
+    assertContains(INNER, cause.toString());
+    assertClassnameContains("IOException", cause);
+    assertRoundTrips(chained);
+  }
+
+
+}