You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ju...@apache.org on 2011/01/21 15:59:18 UTC

svn commit: r1061834 - in /tika/trunk/tika-core/src/main/java/org/apache/tika/fork: ForkClient.java ForkObjectInputStream.java ForkParser.java ForkServer.java

Author: jukka
Date: Fri Jan 21 14:59:18 2011
New Revision: 1061834

URL: http://svn.apache.org/viewvc?rev=1061834&view=rev
Log:
TIKA-416: Out-of-process text extraction

Add better error handling to forked processes

Modified:
    tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkClient.java
    tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkObjectInputStream.java
    tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkParser.java
    tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkServer.java

Modified: tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkClient.java
URL: http://svn.apache.org/viewvc/tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkClient.java?rev=1061834&r1=1061833&r2=1061834&view=diff
==============================================================================
--- tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkClient.java (original)
+++ tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkClient.java Fri Jan 21 14:59:18 2011
@@ -16,14 +16,12 @@
  */
 package org.apache.tika.fork;
 
-import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.ObjectOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -31,6 +29,7 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 import java.util.zip.ZipEntry;
 
+import org.apache.tika.io.IOExceptionWithCause;
 import org.apache.tika.io.IOUtils;
 import org.xml.sax.ContentHandler;
 
@@ -38,6 +37,8 @@ class ForkClient {
 
     private final List<ForkResource> resources = new ArrayList<ForkResource>();
 
+    private final ClassLoader loader;
+
     private final File jar;
 
     private final Process process;
@@ -52,6 +53,7 @@ class ForkClient {
             throws IOException {
         boolean ok = false;
         try {
+            this.loader = loader;
             this.jar = createBootstrapJar();
 
             ProcessBuilder builder = new ProcessBuilder();
@@ -77,7 +79,7 @@ class ForkClient {
         }
     }
 
-    public synchronized void call(String method, Object... args)
+    public synchronized Throwable call(String method, Object... args)
             throws IOException {
         List<ForkResource> r = new ArrayList<ForkResource>(resources);
         output.writeByte(ForkServer.CALL);
@@ -85,7 +87,7 @@ class ForkClient {
         for (int i = 0; i < args.length; i++) {
             sendObject(args[i], r);
         }
-        waitForResponse(r);
+        return waitForResponse(r);
     }
 
     /**
@@ -110,14 +112,7 @@ class ForkClient {
             object = new ClassLoaderProxy(n);
         }
 
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        ObjectOutputStream serializer = new ObjectOutputStream(buffer);
-        serializer.writeObject(object);
-        serializer.close();
-
-        byte[] data = buffer.toByteArray();
-        output.writeInt(data.length);
-        output.write(data);
+        ForkObjectInputStream.sendObject(object, output);
 
         waitForResponse(resources);
     }
@@ -143,7 +138,7 @@ class ForkClient {
         }
     }
 
-    private byte waitForResponse(List<ForkResource> resources)
+    private Throwable waitForResponse(List<ForkResource> resources)
             throws IOException {
         output.flush();
         while (true) {
@@ -157,8 +152,16 @@ class ForkClient {
                 ForkResource resource =
                     resources.get(input.readUnsignedByte());
                 resource.process(input, output);
+            } else if (type == ForkServer.ERROR) {
+                try {
+                    return (Throwable) ForkObjectInputStream.readObject(
+                            input, loader);
+                } catch (ClassNotFoundException e) {
+                    throw new IOExceptionWithCause(
+                            "Unable to deserialize an exception", e);
+                }
             } else {
-                return (byte) type;
+                return null;
             }
         }
     }

Modified: tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkObjectInputStream.java
URL: http://svn.apache.org/viewvc/tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkObjectInputStream.java?rev=1061834&r1=1061833&r2=1061834&view=diff
==============================================================================
--- tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkObjectInputStream.java (original)
+++ tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkObjectInputStream.java Fri Jan 21 14:59:18 2011
@@ -16,9 +16,14 @@
  */
 package org.apache.tika.fork;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 
 /**
@@ -64,4 +69,45 @@ class ForkObjectInputStream extends Obje
         return Class.forName(desc.getName(), false, loader);
     }
 
+    /**
+     * Serializes the object first into an in-memory buffer and then
+     * writes it to the output stream with a preceding size integer.
+     *
+     * @param object object to be serialized
+     * @param output output stream
+     * @throws IOException if the object could not be serialized
+     */
+    public static void sendObject(Object object, DataOutputStream output)
+            throws IOException {
+        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+        ObjectOutputStream serializer = new ObjectOutputStream(buffer);
+        serializer.writeObject(object);
+        serializer.close();
+
+        byte[] data = buffer.toByteArray();
+        output.writeInt(data.length);
+        output.write(data);
+    }
+
+    /**
+     * Deserializes an object from the given stream. The serialized object
+     * is expected to be preceded by a size integer, that is used for reading
+     * the entire serialization into a memory before deserializing it.
+     *
+     * @param input input stream from which the serialized object is read
+     * @param loader class loader to be used for loading referenced classes
+     * @throws IOException if the object could not be deserialized
+     * @throws ClassNotFoundException if a referenced class is not found
+     */
+    public static Object readObject(DataInputStream input, ClassLoader loader)
+            throws IOException, ClassNotFoundException {
+        int n = input.readInt();
+        byte[] data = new byte[n];
+        input.readFully(data);
+
+        ObjectInputStream deserializer =
+            new ForkObjectInputStream(new ByteArrayInputStream(data), loader);
+        return deserializer.readObject();
+    }
+
 }

Modified: tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkParser.java
URL: http://svn.apache.org/viewvc/tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkParser.java?rev=1061834&r1=1061833&r2=1061834&view=diff
==============================================================================
--- tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkParser.java (original)
+++ tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkParser.java Fri Jan 21 14:59:18 2011
@@ -111,7 +111,18 @@ public class ForkParser implements Parse
             throws IOException, SAXException, TikaException {
         ForkClient client = acquireClient();
         try {
-            client.call("parse", stream, handler, metadata, context);
+            Throwable t = client.call(
+                    "parse", stream, handler, metadata, context);
+            if (t instanceof IOException) {
+                throw (IOException) t;
+            } else if (t instanceof SAXException) {
+                throw (SAXException) t;
+            } else if (t instanceof TikaException) {
+                throw (TikaException) t;
+            } else if (t != null) {
+                throw new TikaException(
+                        "Unexpected error in forked server process", t);
+            }
         } finally {
             releaseClient(client);
         }

Modified: tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkServer.java
URL: http://svn.apache.org/viewvc/tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkServer.java?rev=1061834&r1=1061833&r2=1061834&view=diff
==============================================================================
--- tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkServer.java (original)
+++ tika/trunk/tika-core/src/main/java/org/apache/tika/fork/ForkServer.java Fri Jan 21 14:59:18 2011
@@ -21,8 +21,8 @@ import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.ObjectInputStream;
 import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
 import java.util.zip.CheckedInputStream;
@@ -31,6 +31,8 @@ import java.util.zip.Checksum;
 
 class ForkServer implements Runnable, Checksum {
 
+    public static final byte ERROR = -1;
+
     public static final byte DONE = 0;
 
     public static final byte CALL = 1;
@@ -109,18 +111,11 @@ class ForkServer implements Runnable, Ch
                 if (request == -1) {
                     break;
                 } else if (request == CALL) {
-                    Method method = getMethod(object, input.readUTF());
-                    Object[] args =
-                        new Object[method.getParameterTypes().length];
-                    for (int i = 0; i < args.length; i++) {
-                        args[i] = readObject(loader);
-                    }
-                    method.invoke(object, args);
-                    output.write(DONE);
-                    output.flush();
+                    call(loader, object);
                 } else {
                     throw new IllegalStateException("Unexpected request");
                 }
+                output.flush();
             }
         } catch (Throwable t) {
             t.printStackTrace();
@@ -128,6 +123,22 @@ class ForkServer implements Runnable, Ch
         System.err.flush();
     }
 
+    private void call(ClassLoader loader, Object object) throws Exception {
+        Method method = getMethod(object, input.readUTF());
+        Object[] args =
+            new Object[method.getParameterTypes().length];
+        for (int i = 0; i < args.length; i++) {
+            args[i] = readObject(loader);
+        }
+        try {
+            method.invoke(object, args);
+            output.write(DONE);
+        } catch (InvocationTargetException e) {
+            output.write(ERROR);
+            ForkObjectInputStream.sendObject(e.getCause(), output);
+        }
+    }
+
     private Method getMethod(Object object, String name) {
         Class<?> klass = object.getClass();
         for (Class<?> iface : klass.getInterfaces()) {
@@ -152,13 +163,7 @@ class ForkServer implements Runnable, Ch
      */
     private Object readObject(ClassLoader loader)
             throws IOException, ClassNotFoundException {
-        int n = input.readInt();
-        byte[] data = new byte[n];
-        input.readFully(data);
-
-        ObjectInputStream deserializer =
-            new ForkObjectInputStream(new ByteArrayInputStream(data), loader);
-        Object object = deserializer.readObject();
+        Object object = ForkObjectInputStream.readObject(input, loader);
         if (object instanceof ForkProxy) {
             ((ForkProxy) object).init(input, output);
         }