public void parse(InputStream stream, ContentHandler handler, Metadata metadata,
                      ParseContext context) throws IOException, SAXException, TikaException {
        if (stream == null) {
            throw new NullPointerException("null stream");
        }

        Throwable t;

        boolean alive = false;
        ForkClient client = acquireClient();
        try {
            ContentHandler tee =
                    (handler instanceof AbstractRecursiveParserWrapperHandler) ? handler :
                            new TeeContentHandler(handler, new MetadataContentHandler(metadata));

            t = client.call("parse", stream, tee, metadata, context);
            alive = true;
        } catch (TikaException te) {
            // Problem occurred on our side
            alive = true;
            throw te;
        } catch (IOException e) {
            // Problem occurred on the other side
            throw new TikaException("Failed to communicate with a forked parser process." +
                    " The process has most likely crashed due to some error" +
                    " like running out of memory. A new process will be" +
                    " started for the next parsing request.", e);
        } finally {
            releaseClient(client, alive);
        }

        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);
        }
    }
@Test
    public void testToFileHandler() throws Exception {
        //test that a server-side write-to-file works without proxying back the
        //AbstractContentHandlerFactory
        Path target = Files.createTempFile(tempDir, "fork-to-file-handler-", ".txt");
        try (InputStream is = getResourceAsStream("/test-documents/basic_embedded.xml")) {
            RecursiveParserWrapper wrapper = new RecursiveParserWrapper(new AutoDetectParser());
            ToFileHandler toFileHandler =
                    new ToFileHandler(new SBContentHandlerFactory(), target.toFile());
            try (ForkParser forkParser = new ForkParser(ForkParserTest.class.getClassLoader(),
                    wrapper)) {
                Metadata m = new Metadata();
                ParseContext context = new ParseContext();
                forkParser.parse(is, toFileHandler, m, context);
            }
        }

        String contents = null;
        try (Reader reader = Files.newBufferedReader(target, StandardCharsets.UTF_8)) {
            contents = IOUtils.toString(reader);
        }
        assertContainsCount(TikaCoreProperties.TIKA_PARSED_BY.getName() +
                " : org.apache.tika.parser.DefaultParser", contents, 2);
        assertContainsCount(TikaCoreProperties.TIKA_PARSED_BY.getName() +
                " : org.apache.tika.parser.mock.MockParser", contents, 2);
        assertContains("Nikolai Lobachevsky", contents);
        assertContains("embeddedAuthor", contents);
        assertContains("main_content", contents);
        assertContains("some_embedded_content", contents);
        assertContains("X-TIKA:embedded_resource_path : /embed1.xml", contents);

    }