You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tika.apache.org by ta...@apache.org on 2018/09/24 18:19:55 UTC

[tika] branch branch_1x updated (932ff38 -> f75ba63)

This is an automated email from the ASF dual-hosted git repository.

tallison pushed a change to branch branch_1x
in repository https://gitbox.apache.org/repos/asf/tika.git.


    from 932ff38  TIKA-2638 -- allow multiple languages in config for OCR parser
     new 88bb6ab  TIKA-2732 -- allow configuration of XMLReaderUtils via TikaConfig
     new f75ba63  TIKA-2733 -- improve oom unit test and error/logging when the child process can't start in tika-server

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/apache/tika/config/TikaConfig.java    |  37 +++++--
 .../java/org/apache/tika/utils/XMLReaderUtils.java | 116 +++++++++++++++++----
 .../org/apache/tika/config/TikaConfigTest.java     |  41 +++++++-
 .../tika/config/TIKA-2732-xmlreaderutils-exc.xml   |   2 +-
 .../tika/config/TIKA-2732-xmlreaderutils.xml       |   2 +-
 .../java/org/apache/tika/server/TikaServerCli.java |   2 +-
 .../org/apache/tika/server/TikaServerWatchDog.java |  19 +++-
 .../tika/server/TikaServerIntegrationTest.java     | 105 ++++++++++++++++---
 8 files changed, 273 insertions(+), 51 deletions(-)
 copy tika-app/src/test/resources/test-data/TIKA-2389-ignore-init-problems.xml => tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils-exc.xml (91%)
 copy tika-app/src/test/resources/test-data/TIKA-2389-ignore-init-problems.xml => tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils.xml (93%)


[tika] 01/02: TIKA-2732 -- allow configuration of XMLReaderUtils via TikaConfig

Posted by ta...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch branch_1x
in repository https://gitbox.apache.org/repos/asf/tika.git

commit 88bb6abebdd059d9ce3fd8ec6ebdeac79f6fd58f
Author: TALLISON <ta...@apache.org>
AuthorDate: Mon Sep 24 12:49:10 2018 -0400

    TIKA-2732 -- allow configuration of XMLReaderUtils via TikaConfig
    
    # Conflicts:
    #	tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java
---
 .../java/org/apache/tika/config/TikaConfig.java    |  37 +++++--
 .../java/org/apache/tika/utils/XMLReaderUtils.java | 116 +++++++++++++++++----
 .../org/apache/tika/config/TikaConfigTest.java     |  41 +++++++-
 .../tika/config/TIKA-2732-xmlreaderutils-exc.xml   |  20 ++++
 .../tika/config/TIKA-2732-xmlreaderutils.xml       |  20 ++++
 5 files changed, 202 insertions(+), 32 deletions(-)

diff --git a/tika-core/src/main/java/org/apache/tika/config/TikaConfig.java b/tika-core/src/main/java/org/apache/tika/config/TikaConfig.java
index 5b1b1ab..a6fed6c 100644
--- a/tika-core/src/main/java/org/apache/tika/config/TikaConfig.java
+++ b/tika-core/src/main/java/org/apache/tika/config/TikaConfig.java
@@ -125,21 +125,21 @@ public class TikaConfig {
 
     public TikaConfig(Path path)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(path.toFile()));
+        this(XMLReaderUtils.buildDOM(path));
     }
     public TikaConfig(Path path, ServiceLoader loader)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(path.toFile()), loader);
+        this(XMLReaderUtils.buildDOM(path), loader);
     }
 
     public TikaConfig(File file)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(file));
+        this(XMLReaderUtils.buildDOM(file.toPath()));
     }
 
     public TikaConfig(File file, ServiceLoader loader)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(file), loader);
+        this(XMLReaderUtils.buildDOM(file.toPath()), loader);
     }
 
     public TikaConfig(URL url)
@@ -148,16 +148,16 @@ public class TikaConfig {
     }
     public TikaConfig(URL url, ClassLoader loader)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(url.toString()).getDocumentElement(), loader);
+        this(XMLReaderUtils.buildDOM(url.toString()).getDocumentElement(), loader);
     }
     public TikaConfig(URL url, ServiceLoader loader)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(url.toString()).getDocumentElement(), loader);
+        this(XMLReaderUtils.buildDOM(url.toString()).getDocumentElement(), loader);
     }
 
     public TikaConfig(InputStream stream)
             throws TikaException, IOException, SAXException {
-        this(XMLReaderUtils.getDocumentBuilder().parse(stream));
+        this(XMLReaderUtils.buildDOM(stream));
     }
 
     public TikaConfig(Document document) throws TikaException, IOException {
@@ -182,6 +182,7 @@ public class TikaConfig {
         TranslatorXmlLoader translatorLoader = new TranslatorXmlLoader();
         ExecutorServiceXmlLoader executorLoader = new ExecutorServiceXmlLoader();
         EncodingDetectorXmlLoader encodingDetectorXmlLoader = new EncodingDetectorXmlLoader();
+        updateXMLReaderUtils(element);
         this.mimeTypes = typesFromDomElement(element);
         this.detector = detectorLoader.loadOverall(element, mimeTypes, loader);
         this.encodingDetector = encodingDetectorXmlLoader.loadOverall(element, mimeTypes, loader);
@@ -253,7 +254,8 @@ public class TikaConfig {
         } else {
             ServiceLoader tmpServiceLoader = new ServiceLoader();
             try (InputStream stream = getConfigInputStream(config, tmpServiceLoader)) {
-                Element element = XMLReaderUtils.getDocumentBuilder().parse(stream).getDocumentElement();
+                Element element = XMLReaderUtils.buildDOM(stream).getDocumentElement();
+                updateXMLReaderUtils(element);
                 serviceLoader = serviceLoaderFromDomElement(element, tmpServiceLoader.getLoader());
                 DetectorXmlLoader detectorLoader = new DetectorXmlLoader();
                 EncodingDetectorXmlLoader encodingDetectorLoader = new EncodingDetectorXmlLoader();
@@ -278,6 +280,25 @@ public class TikaConfig {
         TIMES_INSTANTIATED.incrementAndGet();
     }
 
+    private void updateXMLReaderUtils(Element element) throws TikaException {
+
+        Element child = getChild(element, "xml-reader-utils");
+        if (child == null) {
+            return;
+        }
+        String attr = child.getAttribute("maxEntityExpansions");
+        if (attr != null) {
+            XMLReaderUtils.setMaxEntityExpansions(Integer.parseInt(attr));
+        }
+
+        //make sure to call this after set entity expansions
+        attr = child.getAttribute("poolSize");
+        if (attr != null) {
+            XMLReaderUtils.setPoolSize(Integer.parseInt(attr));
+        }
+
+    }
+
     private static InputStream getConfigInputStream(String config, ServiceLoader serviceLoader)
             throws TikaException, IOException {
         InputStream stream = null;
diff --git a/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java b/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java
index 4ccdad4..71e1f84 100644
--- a/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java
+++ b/tika-core/src/main/java/org/apache/tika/utils/XMLReaderUtils.java
@@ -19,6 +19,7 @@ package org.apache.tika.utils;
 
 import org.apache.tika.exception.TikaException;
 import org.apache.tika.parser.ParseContext;
+import org.apache.tika.sax.OfflineContentHandler;
 import org.w3c.dom.Document;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.InputSource;
@@ -46,7 +47,8 @@ import java.io.InputStream;
 import java.io.Serializable;
 import java.io.StringReader;
 import java.lang.reflect.Method;
-import java.util.Properties;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
@@ -55,7 +57,7 @@ import java.util.logging.Logger;
 
 /**
  * Utility functions for reading XML.  If you are doing SAX parsing, make sure
- * to use the {@link org.apache.tika.sax.OfflineContentHandler} to guard against
+ * to use the {@link OfflineContentHandler} to guard against
  * XML External Entity attacks.
  */
 public class XMLReaderUtils implements Serializable {
@@ -68,16 +70,22 @@ public class XMLReaderUtils implements Serializable {
     private static final Logger LOG = Logger.getLogger(XMLReaderUtils.class.getName());
 
     /**
+     * Default size for the pool of SAX Parsers
+     * and the pool of DOM builders
+     */
+    public static final int DEFAULT_POOL_SIZE = 10;
+
+    /**
      * Parser pool size
      */
-    private static int POOL_SIZE = 10;
+    private static int POOL_SIZE = DEFAULT_POOL_SIZE;
 
     private static long LAST_LOG = -1;
 
     private static final String JAXP_ENTITY_EXPANSION_LIMIT_KEY = "jdk.xml.entityExpansionLimit";
-    private static final int DEFAULT_MAX_ENTITY_EXPANSIONS = 20;
+    public static final int DEFAULT_MAX_ENTITY_EXPANSIONS = 20;
 
-    private static int MAX_ENTITY_EXPANSIONS = determineMaxEntityExpansions();
+    private static volatile int MAX_ENTITY_EXPANSIONS = determineMaxEntityExpansions();
 
     private static int determineMaxEntityExpansions() {
         String expansionLimit = System.getProperty(JAXP_ENTITY_EXPANSION_LIMIT_KEY);
@@ -127,8 +135,12 @@ public class XMLReaderUtils implements Serializable {
      * Set the maximum number of entity expansions allowable in SAX/DOM/StAX parsing.
      * <b>NOTE:</b>A value less than or equal to zero indicates no limit.
      * This will override the system property {@link #JAXP_ENTITY_EXPANSION_LIMIT_KEY}
-     * and the {@link #DEFAULT_MAX_ENTITY_EXPANSIONS} value for pa
-     *
+     * and the {@link #DEFAULT_MAX_ENTITY_EXPANSIONS} value for allowable entity expansions
+     *<p>
+     *<b>NOTE:</b> To trigger a rebuild of the pool of parsers with this setting,
+     * the client must call {@link #setPoolSize(int)} to rebuild the SAX and DOM parsers
+     * with this setting.
+     *</p>
      * @param maxEntityExpansions -- maximum number of allowable entity expansions
      * @since Apache Tika 1.19
      */
@@ -162,7 +174,7 @@ public class XMLReaderUtils implements Serializable {
      * is not explicitly specified, then one is created using the specified
      * or the default SAX parser factory.
      * <p>
-     * Make sure to wrap your handler in the {@link org.apache.tika.sax.OfflineContentHandler} to
+     * Make sure to wrap your handler in the {@link OfflineContentHandler} to
      * prevent XML External Entity attacks
      * </p>
      *
@@ -190,7 +202,7 @@ public class XMLReaderUtils implements Serializable {
      * configured to be namespace-aware, not validating, and to use
      * {@link XMLConstants#FEATURE_SECURE_PROCESSING secure XML processing}.
      * <p>
-     * Make sure to wrap your handler in the {@link org.apache.tika.sax.OfflineContentHandler} to
+     * Make sure to wrap your handler in the {@link OfflineContentHandler} to
      * prevent XML External Entity attacks
      * </p>
      *
@@ -350,6 +362,60 @@ public class XMLReaderUtils implements Serializable {
     }
 
     /**
+     * Builds a Document with a DocumentBuilder from the pool
+     *
+     * @since Apache Tika 1.19.1
+     * @param path path to parse
+     * @return a document
+     * @throws TikaException
+     * @throws IOException
+     * @throws SAXException
+     */
+    public static Document buildDOM(Path path) throws TikaException, IOException, SAXException {
+        try (InputStream is = Files.newInputStream(path)){
+            return buildDOM(is);
+        }
+    }
+
+    /**
+     * Builds a Document with a DocumentBuilder from the pool
+     *
+     * @since Apache Tika 1.19.1
+     * @param uriString uriString to process
+     * @return a document
+     * @throws TikaException
+     * @throws IOException
+     * @throws SAXException
+     */
+    public static Document buildDOM(String uriString) throws TikaException, IOException, SAXException {
+        DocumentBuilder builder = acquireDOMBuilder();
+        try {
+            return builder.parse(uriString);
+        } finally {
+            releaseDOMBuilder(builder);
+        }
+    }
+
+    /**
+     * Builds a Document with a DocumentBuilder from the pool
+     *
+     * @since Apache Tika 1.19.1
+     *
+     * @return a document
+     * @throws TikaException
+     * @throws IOException
+     * @throws SAXException
+     */
+    public static Document buildDOM(InputStream is) throws TikaException, IOException, SAXException {
+        DocumentBuilder builder = acquireDOMBuilder();
+        try {
+            return builder.parse(is);
+        } finally {
+            releaseDOMBuilder(builder);
+        }
+    }
+
+    /**
      * This checks context for a user specified {@link SAXParser}.
      * If one is not found, this reuses a SAXParser from the pool.
      *
@@ -501,7 +567,9 @@ public class XMLReaderUtils implements Serializable {
     }
 
     /**
-     * Set the pool size for cached XML parsers.
+     * Set the pool size for cached XML parsers.  This has a side
+     * effect of locking the pool, and rebuilding the pool from
+     * scratch with the most recent settings, such as {@link #MAX_ENTITY_EXPANSIONS}
      *
      * @since Apache Tika 1.19
      * @param poolSize
@@ -515,23 +583,21 @@ public class XMLReaderUtils implements Serializable {
             //parsers have already started.  We could have an NPE on SAX_PARSERS
             //if we didn't lock.
             SAX_READ_WRITE_LOCK.writeLock().lock();
-            if (SAX_PARSERS.size() != poolSize) {
-                SAX_PARSERS = new ArrayBlockingQueue<>(poolSize);
-                for (int i = 0; i < poolSize; i++) {
-                    SAX_PARSERS.offer(getSAXParser());
-                }
+            SAX_PARSERS.clear();
+            SAX_PARSERS = new ArrayBlockingQueue<>(poolSize);
+            for (int i = 0; i < poolSize; i++) {
+                SAX_PARSERS.offer(getSAXParser());
             }
+
         } finally {
             SAX_READ_WRITE_LOCK.writeLock().unlock();
         }
         try {
             DOM_READ_WRITE_LOCK.writeLock().lock();
-
-            if (DOM_BUILDERS.size() != poolSize) {
-                DOM_BUILDERS = new ArrayBlockingQueue<>(poolSize);
-                for (int i = 0; i < poolSize; i++) {
-                    DOM_BUILDERS.offer(getDocumentBuilder());
-                }
+            DOM_BUILDERS.clear();
+            DOM_BUILDERS = new ArrayBlockingQueue<>(poolSize);
+            for (int i = 0; i < poolSize; i++) {
+                DOM_BUILDERS.offer(getDocumentBuilder());
             }
         } finally {
             DOM_READ_WRITE_LOCK.writeLock().unlock();
@@ -624,4 +690,12 @@ public class XMLReaderUtils implements Serializable {
             }
         }
     }
+
+    public static int getPoolSize() {
+        return POOL_SIZE;
+    }
+
+    public static int getMaxEntityExpansions() {
+        return MAX_ENTITY_EXPANSIONS;
+    }
 }
diff --git a/tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java b/tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java
index aa10923..5c406cd 100644
--- a/tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java
+++ b/tika-core/src/test/java/org/apache/tika/config/TikaConfigTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.tika.config;
 
+import java.io.InputStream;
 import java.net.URI;
 import java.net.URL;
 import java.nio.file.Path;
@@ -25,11 +26,11 @@ import java.util.Map;
 import java.util.concurrent.ThreadPoolExecutor;
 
 import org.apache.tika.ResourceLoggingClassLoader;
-import org.apache.tika.config.DummyExecutor;
-import org.apache.tika.config.TikaConfig;
-import org.apache.tika.config.TikaConfigTest;
 import org.apache.tika.exception.TikaConfigException;
 import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.mime.MimeDetectionTest;
 import org.apache.tika.parser.AutoDetectParser;
 import org.apache.tika.parser.CompositeParser;
 import org.apache.tika.parser.DefaultParser;
@@ -37,6 +38,7 @@ import org.apache.tika.parser.EmptyParser;
 import org.apache.tika.parser.ErrorParser;
 import org.apache.tika.parser.Parser;
 import org.apache.tika.parser.ParserDecorator;
+import org.apache.tika.utils.XMLReaderUtils;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
@@ -292,4 +294,37 @@ public class TikaConfigTest extends AbstractTikaConfigTest {
         TikaConfig config = getConfig("TIKA-2389-warn-per-parser.xml");
     }
 
+
+    @Test
+    public void testXMLReaderUtils() throws Exception {
+        //pool size may have been reset already by an
+        //earlier test.  Can't test for default here.
+        assertEquals(XMLReaderUtils.DEFAULT_MAX_ENTITY_EXPANSIONS, XMLReaderUtils.getMaxEntityExpansions());
+        //make sure that detection on this file actually works with
+        //default expansions
+        assertEquals("application/rdf+xml", detect("test-difficult-rdf1.xml", TikaConfig.getDefaultConfig()).toString());
+
+        TikaConfig tikaConfig = getConfig("TIKA-2732-xmlreaderutils.xml");
+        try {
+            assertEquals(33, XMLReaderUtils.getPoolSize());
+            assertEquals(5, XMLReaderUtils.getMaxEntityExpansions());
+            //make sure that there's actually a change in behavior
+            assertEquals("text/plain", detect("test-difficult-rdf1.xml", tikaConfig).toString());
+        } finally {
+            XMLReaderUtils.setMaxEntityExpansions(XMLReaderUtils.DEFAULT_MAX_ENTITY_EXPANSIONS);
+            XMLReaderUtils.setPoolSize(XMLReaderUtils.DEFAULT_POOL_SIZE);
+        }
+    }
+
+    private MediaType detect(String testFileName, TikaConfig tikaConfig) throws Exception {
+        try (InputStream is = MimeDetectionTest.class.getResourceAsStream(testFileName)) {
+            return tikaConfig.getDetector().detect(is, new Metadata());
+        }
+    }
+
+    @Test(expected = NumberFormatException.class)
+    public void testXMLReaderUtilsException() throws Exception {
+        getConfig("TIKA-2732-xmlreaderutils-exc.xml");
+    }
+
 }
\ No newline at end of file
diff --git a/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils-exc.xml b/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils-exc.xml
new file mode 100644
index 0000000..5c93ad4
--- /dev/null
+++ b/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils-exc.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<properties>
+    <xml-reader-utils maxEntityExpansions="notAnInteger" poolSize="notAnInteger"/>
+</properties>
diff --git a/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils.xml b/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils.xml
new file mode 100644
index 0000000..890e11c
--- /dev/null
+++ b/tika-core/src/test/resources/org/apache/tika/config/TIKA-2732-xmlreaderutils.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+-->
+<properties>
+    <xml-reader-utils maxEntityExpansions="5" poolSize="33"/>
+</properties>


[tika] 02/02: TIKA-2733 -- improve oom unit test and error/logging when the child process can't start in tika-server

Posted by ta...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

tallison pushed a commit to branch branch_1x
in repository https://gitbox.apache.org/repos/asf/tika.git

commit f75ba632e02d26ca2f3471135e31db54cc43d069
Author: TALLISON <ta...@apache.org>
AuthorDate: Mon Sep 24 14:13:19 2018 -0400

    TIKA-2733 -- improve oom unit test and error/logging when the child process can't start in tika-server
---
 .../java/org/apache/tika/server/TikaServerCli.java |   2 +-
 .../org/apache/tika/server/TikaServerWatchDog.java |  19 +++-
 .../tika/server/TikaServerIntegrationTest.java     | 105 ++++++++++++++++++---
 3 files changed, 109 insertions(+), 17 deletions(-)

diff --git a/tika-server/src/main/java/org/apache/tika/server/TikaServerCli.java b/tika-server/src/main/java/org/apache/tika/server/TikaServerCli.java
index e3242d1..35ee7b5 100644
--- a/tika-server/src/main/java/org/apache/tika/server/TikaServerCli.java
+++ b/tika-server/src/main/java/org/apache/tika/server/TikaServerCli.java
@@ -117,7 +117,7 @@ public class TikaServerCli {
             execute(args);
         } catch (Exception e) {
             e.printStackTrace();
-            LOG.error("Can't start", e);
+            LOG.error("Can't start: ", e);
             System.exit(-1);
         }
     }
diff --git a/tika-server/src/main/java/org/apache/tika/server/TikaServerWatchDog.java b/tika-server/src/main/java/org/apache/tika/server/TikaServerWatchDog.java
index 8c780cd..abd5663 100644
--- a/tika-server/src/main/java/org/apache/tika/server/TikaServerWatchDog.java
+++ b/tika-server/src/main/java/org/apache/tika/server/TikaServerWatchDog.java
@@ -17,12 +17,14 @@
 
 package org.apache.tika.server;
 
+import org.apache.commons.io.IOUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.ArrayList;
@@ -161,8 +163,21 @@ public class TikaServerWatchDog {
             this.toChild = new DataOutputStream(process.getOutputStream());
             byte status = fromChild.readByte();
             if (status != ServerStatus.STATUS.OPERATING.getByte()) {
-                throw new IOException("bad status from child process: "+
-                        ServerStatus.STATUS.lookup(status));
+                try {
+                    ServerStatus.STATUS currStatus = ServerStatus.STATUS.lookup(status);
+                    throw new IOException("bad status from child process: "+
+                             currStatus);
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    //swallow
+                }
+                int len = process.getInputStream().available();
+                byte[] msg = new byte[len+1];
+                msg[0] = status;
+                process.getInputStream().read(msg, 1, len);
+
+                throw new IOException(
+                        "Unrecognized status code; message:\n"+new String(msg, StandardCharsets.UTF_8));
+
             }
             lastPing = Instant.now();
         }
diff --git a/tika-server/src/test/java/org/apache/tika/server/TikaServerIntegrationTest.java b/tika-server/src/test/java/org/apache/tika/server/TikaServerIntegrationTest.java
index d328711..8f3a2aa 100644
--- a/tika-server/src/test/java/org/apache/tika/server/TikaServerIntegrationTest.java
+++ b/tika-server/src/test/java/org/apache/tika/server/TikaServerIntegrationTest.java
@@ -18,19 +18,22 @@ package org.apache.tika.server;
 
 import org.apache.cxf.jaxrs.client.WebClient;
 import org.apache.tika.TikaTest;
-import org.apache.tika.io.IOUtils;
 import org.apache.tika.metadata.Metadata;
 import org.apache.tika.metadata.OfficeOpenXMLExtended;
 import org.apache.tika.metadata.serialization.JsonMetadataList;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 import javax.ws.rs.core.Response;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.security.Permission;
 import java.time.Duration;
 import java.time.Instant;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.junit.Assert.assertEquals;
@@ -51,6 +54,43 @@ public class TikaServerIntegrationTest extends TikaTest {
     protected static final String endPoint =
             "http://localhost:" + INTEGRATION_TEST_PORT;
 
+    private SecurityManager existingSecurityManager = null;
+
+    private static class MyExitException extends RuntimeException {
+        private final int status;
+        MyExitException(int status) {
+            this.status = status;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+    }
+    @Before
+    public void setUp() throws Exception {
+        SecurityManager existingSecurityManager = System.getSecurityManager();
+        System.setSecurityManager(new SecurityManager() {
+            @Override
+            public void checkExit(int status) {
+                super.checkExit(status);
+                throw new MyExitException(status);
+            }
+            @Override
+            public void checkPermission(Permission perm) {
+                // all ok
+            }
+            @Override
+            public void checkPermission(Permission perm, Object context) {
+                // all ok
+            }
+        });
+    }
+
+    @After
+    public void tearDown() {
+        System.setSecurityManager(existingSecurityManager);
+    }
+
     @Test
     public void testBasic() throws Exception {
 
@@ -93,18 +133,26 @@ public class TikaServerIntegrationTest extends TikaTest {
             public void run() {
                 TikaServerCli.main(
                         new String[]{
-                                "-spawnChild", "-JXmx512m",
-                                "-p", INTEGRATION_TEST_PORT
+                                "-spawnChild", "-JXmx256m",
+                                "-p", INTEGRATION_TEST_PORT,
+                                "-pingPulseMillis", "100"
                         });
             }
         };
         serverThread.start();
         awaitServerStartup();
-        Response response = WebClient
-                .create(endPoint + META_PATH)
-                .accept("application/json")
-                .put(ClassLoader
-                        .getSystemResourceAsStream(TEST_OOM));
+
+        Response response = null;
+        try {
+            response = WebClient
+                    .create(endPoint + META_PATH)
+                    .accept("application/json")
+                    .put(ClassLoader
+                            .getSystemResourceAsStream(TEST_OOM));
+        } catch (Exception e) {
+            //oom may or may not cause an exception depending
+            //on the timing
+        }
         //give some time for the server to crash/kill itself
         Thread.sleep(2000);
         awaitServerStartup();
@@ -186,11 +234,16 @@ public class TikaServerIntegrationTest extends TikaTest {
         };
         serverThread.start();
         awaitServerStartup();
-        Response response = WebClient
-                .create(endPoint + META_PATH)
-                .accept("application/json")
-                .put(ClassLoader
-                        .getSystemResourceAsStream(TEST_HEAVY_HANG_SHORT));
+        Response response = null;
+        try {
+            response = WebClient
+                    .create(endPoint + META_PATH)
+                    .accept("application/json")
+                    .put(ClassLoader
+                            .getSystemResourceAsStream(TEST_HEAVY_HANG_SHORT));
+        } catch (Exception e) {
+            //potential exception depending on timing
+        }
         awaitServerStartup();
 
         response = WebClient
@@ -253,6 +306,30 @@ public class TikaServerIntegrationTest extends TikaTest {
 
     }
 
+    @Test
+    public void testBadJVMArgs() throws Exception {
+        final AtomicInteger i = new AtomicInteger();
+        Thread serverThread = new Thread() {
+            @Override
+            public void run() {
+                TikaServerCli.main(
+                        new String[]{
+                                "-spawnChild", "-JXms20m", "-JXmx10m",
+                                "-p", INTEGRATION_TEST_PORT
+                        });
+            }
+        };
+        serverThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+            @Override
+            public void uncaughtException(Thread t, Throwable e) {
+                i.set(((MyExitException) e).getStatus());
+            }
+        });
+        serverThread.start();
+        serverThread.join(30000);
+
+        assertEquals(-1, i.get());
+    }
     private void awaitServerStartup() throws Exception {
 
         Instant started = Instant.now();
@@ -268,7 +345,7 @@ public class TikaServerIntegrationTest extends TikaTest {
                 }
             } catch (javax.ws.rs.ProcessingException e) {
             }
-            Thread.sleep(1000);
+            Thread.sleep(100);
             elapsed = Duration.between(started, Instant.now()).toMillis();
         }