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 2022/09/29 19:00:09 UTC

[tika] branch main updated: TIKA-3867 -- add a pipes reporter that writes summary info to disk

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

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


The following commit(s) were added to refs/heads/main by this push:
     new 812f78716 TIKA-3867 -- add a pipes reporter that writes summary info to disk
812f78716 is described below

commit 812f787162aaa739ee453adb25439c5a7f9039ff
Author: tallison <ta...@apache.org>
AuthorDate: Thu Sep 29 14:59:55 2022 -0400

    TIKA-3867 -- add a pipes reporter that writes summary info to disk
---
 tika-pipes/tika-pipes-reporters/pom.xml            |   1 +
 .../tika-pipes-reporter-fs-status/pom.xml          | 115 ++++++++++++++++++++
 .../reporters/fs/FileSystemStatusReporter.java     | 117 +++++++++++++++++++++
 .../reporters/fs/TestFileSystemStatusReporter.java | 101 ++++++++++++++++++
 4 files changed, 334 insertions(+)

diff --git a/tika-pipes/tika-pipes-reporters/pom.xml b/tika-pipes/tika-pipes-reporters/pom.xml
index 0710efbeb..7ab8f73f8 100644
--- a/tika-pipes/tika-pipes-reporters/pom.xml
+++ b/tika-pipes/tika-pipes-reporters/pom.xml
@@ -33,6 +33,7 @@
 
   <modules>
     <module>tika-pipes-reporter-opensearch</module>
+    <module>tika-pipes-reporter-fs-status</module>
   </modules>
 
   <scm>
diff --git a/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/pom.xml b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/pom.xml
new file mode 100644
index 000000000..e6658f855
--- /dev/null
+++ b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/pom.xml
@@ -0,0 +1,115 @@
+<?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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <parent>
+    <groupId>org.apache.tika</groupId>
+    <artifactId>tika-pipes-reporters</artifactId>
+    <version>2.5.0-SNAPSHOT</version>
+    <relativePath>../pom.xml</relativePath>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+
+  <artifactId>tika-pipes-reporter-fs-status</artifactId>
+
+  <name>Apache Tika Pipes Reporter - OpenSearch</name>
+  <url>https://tika.apache.org/</url>
+
+  <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>tika-core</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Automatic-Module-Name>org.apache.tika.pipes.reporters.fs.status</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>${maven.shade.version}</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <createDependencyReducedPom>
+                false
+              </createDependencyReducedPom>
+              <!-- <filters> -->
+              <filters>
+                <filter>
+                  <artifact>*:*</artifact>
+                  <excludes>
+                    <exclude>META-INF/*</exclude>
+                    <exclude>LICENSE.txt</exclude>
+                    <exclude>NOTICE.txt</exclude>
+                  </excludes>
+                </filter>
+              </filters>
+              <transformers>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
+                  <resource>META-INF/LICENSE</resource>
+                  <file>target/classes/META-INF/LICENSE</file>
+                </transformer>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
+                  <resource>META-INF/NOTICE</resource>
+                  <file>target/classes/META-INF/NOTICE</file>
+                </transformer>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.IncludeResourceTransformer">
+                  <resource>META-INF/DEPENDENCIES</resource>
+                  <file>target/classes/META-INF/DEPENDENCIES</file>
+                </transformer>
+              </transformers>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+    </plugins>
+  </build>
+
+  <scm>
+    <tag>2.2.1-rc2</tag>
+  </scm>
+</project>
diff --git a/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/main/java/org/apache/tika/pipes/reporters/fs/FileSystemStatusReporter.java b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/main/java/org/apache/tika/pipes/reporters/fs/FileSystemStatusReporter.java
new file mode 100644
index 000000000..fbe8196ea
--- /dev/null
+++ b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/main/java/org/apache/tika/pipes/reporters/fs/FileSystemStatusReporter.java
@@ -0,0 +1,117 @@
+/*
+ * 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.tika.pipes.reporters.fs;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.LongAdder;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.tika.config.Field;
+import org.apache.tika.config.Initializable;
+import org.apache.tika.config.InitializableProblemHandler;
+import org.apache.tika.config.Param;
+import org.apache.tika.exception.TikaConfigException;
+import org.apache.tika.pipes.FetchEmitTuple;
+import org.apache.tika.pipes.PipesReporter;
+import org.apache.tika.pipes.PipesResult;
+
+/**
+ * This is intended to write summary statistics to disk
+ * periodically.
+ */
+public class FileSystemStatusReporter extends PipesReporter
+        implements Initializable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(FileSystemStatusReporter.class);
+
+    ObjectMapper objectMapper = new ObjectMapper();
+    private Path statusFile;
+
+    private long reportUpdateMillis = 1000;
+
+    Thread reporterThread;
+    private ConcurrentHashMap<PipesResult.STATUS, LongAdder> counts = new ConcurrentHashMap<>();
+
+    @Field
+    public void setStatusFile(String path) {
+        this.statusFile = Paths.get(path);
+    }
+
+    @Field
+    public void setReportUpdateMillis(long millis) {
+        this.reportUpdateMillis = millis;
+    }
+
+    @Override
+    public void initialize(Map<String, Param> params) throws TikaConfigException {
+        reporterThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    while (true) {
+                        Thread.sleep(reportUpdateMillis);
+                        report(statusFile, objectMapper, counts);
+                    }
+                } catch (InterruptedException e) {
+                    //no op
+                }
+
+            }
+        });
+        reporterThread.start();
+    }
+
+    private static void report(Path statusFile, ObjectMapper objectMapper,
+                               ConcurrentHashMap<PipesResult.STATUS, LongAdder> counts) {
+        try (Writer writer = Files.newBufferedWriter(statusFile, StandardCharsets.UTF_8)) {
+            objectMapper.writeValue(writer, counts);
+        } catch (IOException e) {
+            LOG.warn("couldn't write report", e);
+        }
+    }
+
+    @Override
+    public void checkInitialization(InitializableProblemHandler problemHandler)
+            throws TikaConfigException {
+        if (statusFile == null) {
+            throw new TikaConfigException("must initialize 'statusFile'");
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        LOG.debug("finishing and writing last report");
+        report(statusFile, objectMapper, counts);
+    }
+
+    @Override
+    public void report(FetchEmitTuple t, PipesResult result, long elapsed) {
+        counts.computeIfAbsent(result.getStatus(),
+                k -> new LongAdder()).increment();
+    }
+
+}
diff --git a/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/test/java/org/apache/tika/pipes/reporters/fs/TestFileSystemStatusReporter.java b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/test/java/org/apache/tika/pipes/reporters/fs/TestFileSystemStatusReporter.java
new file mode 100644
index 000000000..4d1082c61
--- /dev/null
+++ b/tika-pipes/tika-pipes-reporters/tika-pipes-reporter-fs-status/src/test/java/org/apache/tika/pipes/reporters/fs/TestFileSystemStatusReporter.java
@@ -0,0 +1,101 @@
+/*
+ * 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.tika.pipes.reporters.fs;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.LongAdder;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import org.apache.tika.pipes.PipesResult;
+import org.apache.tika.pipes.pipesiterator.PipesIterator;
+
+public class TestFileSystemStatusReporter {
+
+    @Test
+    public void testBasic(@TempDir Path tmpDir) throws Exception {
+        FileSystemStatusReporter reporter = new FileSystemStatusReporter();
+        Path path = Files.createTempFile(tmpDir, "tika-fssr-", ".xml");
+        reporter.setStatusFile(path.toAbsolutePath().toString());
+        reporter.setReportUpdateMillis(100);
+        reporter.initialize(new HashMap<>());
+        final ObjectMapper objectMapper = new ObjectMapper();
+        Thread readerThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                while (true) {
+                    try {
+                        Map<PipesResult.STATUS, LongAdder> map =
+                                objectMapper.readValue(path.toFile(), Map.class);
+                    } catch (IOException e) {
+                        //there will be problems reading from the file
+                        //before it is originally written, etc.  Ignore these
+                        //problems
+                    }
+                    try {
+                        Thread.sleep(50);
+                    } catch (InterruptedException e) {
+                        //throw new RuntimeException(e);
+                    }
+                }
+            }
+        });
+        readerThread.start();
+        PipesResult.STATUS[] statuses = PipesResult.STATUS.values();
+        Map<PipesResult.STATUS, Long> written = new HashMap<>();
+        Random random = new Random();
+        for (int i = 0; i < 1000; i++) {
+            PipesResult.STATUS status = statuses[random.nextInt(statuses.length)];
+            PipesResult pipesResult = new PipesResult(status);
+            reporter.report(PipesIterator.COMPLETED_SEMAPHORE, pipesResult, 100l);
+            Long cnt = written.get(status);
+            if (cnt == null) {
+                written.put(status, new Long(1));
+            } else {
+                cnt++;
+                written.put(status, cnt);
+            }
+            if (i % 100 == 0) {
+                Thread.sleep(94);
+            }
+        }
+        reporter.close();
+        readerThread.interrupt();
+        TypeReference<HashMap<PipesResult.STATUS, Long>> typeRef
+                = new TypeReference<HashMap<PipesResult.STATUS, Long>>() {};
+        Map<PipesResult.STATUS, Long> map = objectMapper.readValue(path.toFile(), typeRef);
+
+
+        assertEquals(written.size(), map.size());
+        for (Map.Entry<PipesResult.STATUS, Long> e : written.entrySet()) {
+            assertTrue(map.containsKey(e.getKey()), e.getKey().toString());
+            assertEquals(e.getValue(), map.get(e.getKey()), e.getKey().toString());
+        }
+    }
+
+}