This is an automated email from the ASF dual-hosted git repository.
tballison 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 ff2d687d69 TIKA-4839 - simplify signature (#3081)
ff2d687d69 is described below
commit ff2d687d692c311dfeac88ebc8b034c6ce52da65
Author: Tim Allison <[email protected]>
AuthorDate: Thu Aug 27 14:42:48 2026 -0400
TIKA-4839 - simplify signature (#3081)
---
.../tika/pipes/core/PerClientServerManager.java | 43 ++++--
.../org/apache/tika/pipes/core/PipesClient.java | 10 ++
.../org/apache/tika/pipes/core/ServerManager.java | 64 ++-------
.../tika/pipes/core/SharedServerManager.java | 22 ---
.../pipes/core/PipesClientClosedManagerTest.java | 64 +++++++++
.../tika/pipes/core/PipesClientInterruptTest.java | 6 +
.../pipes/core/PipesClientPayloadLimitTest.java | 4 +
.../tika/pipes/core/SentinelServerManager.java | 23 +++-
.../pipes/core/ServerManagerMarkContractTest.java | 150 ---------------------
.../apache/tika/pipes/core/PipesClientTest.java | 20 +++
10 files changed, 172 insertions(+), 234 deletions(-)
diff --git
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PerClientServerManager.java
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PerClientServerManager.java
index b381251d8c..103cb397a3 100644
---
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PerClientServerManager.java
+++
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PerClientServerManager.java
@@ -179,6 +179,7 @@ public class PerClientServerManager implements
ServerManager {
private volatile Path tmpDir;
private volatile int port = -1;
private long filesProcessed = 0;
+ private volatile long generation;
private volatile boolean pendingRestart = false;
private final RestartCounter restarts = new RestartCounter();
// Set once by shutdown()/close(); guards a request thread from starting a
fresh
@@ -291,18 +292,31 @@ public class PerClientServerManager implements
ServerManager {
return pendingRestart;
}
+ /**
+ * One client owns one manager here, so {@code generation} carries no
information a sibling
+ * could invalidate and is accepted only to satisfy the single {@link
ServerManager} spelling.
+ * Shared mode is where staleness is real.
+ */
@Override
- public void markServerForRestart() {
- markServerForRestart(RestartReason.CRASH);
+ public void markServerForRestart(RestartReason reason, long
ignoredGeneration) {
+ LOG.info("clientId={}: marking server for restart ({})", clientId,
reason);
+ markForRestart(reason);
}
+ /** Counts forks so {@code PipesParser.getGeneration()} is meaningful in
per-client mode too. */
@Override
- public void markServerForRestart(RestartReason reason) {
- LOG.info("clientId={}: marking server for restart ({})", clientId,
reason);
- markForRestart(reason);
+ public long getGeneration() {
+ return generation;
}
- private void markForRestart(RestartReason reason) {
+ /**
+ * Takes the same monitor as {@link #ensureRunning()}, which consumes the
mark: recording the
+ * reason and raising the flag must not straddle a restart, or a reason
lands against a
+ * restart that has already been counted. Correctness here previously
rested on an
+ * undocumented one-client-per-manager invariant; shared mode, where
siblings are the norm,
+ * already locked for this and TIKA-4844 is what a stale mark costs.
+ */
+ private synchronized void markForRestart(RestartReason reason) {
restarts.mark(reason);
pendingRestart = true;
}
@@ -319,7 +333,7 @@ public class PerClientServerManager implements
ServerManager {
}
@Override
- public int handleCrashAndGetExitCode() {
+ public int handleCrashAndGetExitCode(long generation) {
// Not marked: RestartCounter attributes by exit code; the caller
refines OOM/TIMEOUT.
pendingRestart = true;
if (process != null) {
@@ -475,6 +489,7 @@ public class PerClientServerManager implements
ServerManager {
try {
process = pb.start();
+ generation++;
} catch (Exception e) {
deleteDir(tmpDir);
tmpDir = null;
@@ -531,11 +546,17 @@ public class PerClientServerManager implements
ServerManager {
private void destroyProcess() throws InterruptedException {
if (process != null) {
process.destroyForcibly();
- process.waitFor(WAIT_ON_DESTROY_MS, TimeUnit.MILLISECONDS);
- if (process.isAlive()) {
- LOG.error("clientId={}: process still alive after {}ms",
clientId, WAIT_ON_DESTROY_MS);
+ try {
+ process.waitFor(WAIT_ON_DESTROY_MS, TimeUnit.MILLISECONDS);
+ if (process.isAlive()) {
+ LOG.error("clientId={}: process still alive after {}ms",
clientId, WAIT_ON_DESTROY_MS);
+ }
+ } finally {
+ // An interrupt here must not leave the field pointing at a
SIGKILLed process:
+ // ensureRunning would then see process == previous and skip
counting the restart,
+ // startServer() would try to reap it again, and tmpDir would
never be deleted.
+ process = null;
}
- process = null;
}
}
diff --git
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesClient.java
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesClient.java
index 77339145f5..340fbf25ef 100644
---
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesClient.java
+++
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/PipesClient.java
@@ -235,6 +235,16 @@ public class PipesClient implements Closeable {
closeConnection();
return buildFatalResult(t.getId(), t.getEmitKey(),
PipesResult.RESULT_STATUS.FAILED_TO_INITIALIZE,
intermediateResult.get());
+ } catch (IllegalStateException e) {
+ // Typically the manager was closed underneath us: a request
thread racing PipesParser.close()
+ // or AsyncProcessor.close(), which interrupts workers without
awaiting them. Nothing
+ // to restart and nothing to recover -- but report it rather than
letting an unchecked
+ // exception escape PipesParser.parse() to a caller that cannot
act on it.
+ LOG.warn("clientId={}: server manager rejected initialization of
{}", pipesClientId,
+ t.getId(), e);
+ closeConnection();
+ return buildFatalResult(t.getId(), t.getEmitKey(),
PipesResult.RESULT_STATUS.FAILED_TO_INITIALIZE,
+ intermediateResult.get(), e.getMessage());
}
try {
diff --git
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/ServerManager.java
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/ServerManager.java
index f620cda3c0..faa0138807 100644
---
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/ServerManager.java
+++
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/ServerManager.java
@@ -95,61 +95,28 @@ public interface ServerManager extends Closeable {
*/
java.nio.file.Path getTempDirectory();
- /**
- * Marks the server for restart due to a fatal error (OOM, timeout, etc.).
- * <p>
- * This is called by clients when they receive a fatal error status from
the server.
- * It signals that the server process is stopping, even if {@link
#isRunning()}
- * might still return true briefly. The next call to {@link
#ensureRunning()} will
- * wait for the process to fully exit and then restart.
- * <p>
- * The reason form below defaults to this one, so this must NOT default to
the reason form:
- * an implementation overriding neither would recurse until the stack
blew. Concrete managers
- * in tika-pipes override both, so callers of either spelling reach a real
implementation.
- */
- default void markServerForRestart() {
- // Default no-op: preserves implementations written before
RestartReason existed.
- }
-
- /** As {@link #markServerForRestart()}, attributing the restart to {@code
reason}. Override this one. */
- default void markServerForRestart(RestartReason reason) {
- markServerForRestart();
- }
-
/**
* The generation of the currently running process: a counter incremented
every time this
* manager forks a replacement. A client captures it when it connects and
hands it back with
* every report, so a report about a process that has already been
replaced can be recognised
* and dropped rather than being applied to its healthy successor.
*/
- default long getGeneration() {
- return 0;
- }
-
- /**
- * As {@link #markServerForRestart(RestartReason)}, but only if {@code
generation} is still
- * current. Reports about a superseded process are dropped.
- */
- default void markServerForRestart(RestartReason reason, long generation) {
- markServerForRestart(reason);
- }
+ long getGeneration();
/**
- * The reasonless spelling of the above, kept for callers that cannot
attribute the failure.
- * Routed through the reason form rather than the bare no-arg default:
that default exists
- * only to keep pre-RestartReason implementations working, and delegating
here would leave
- * this silently inert for any implementation that overrides only the
reason form.
- */
- default void markServerForRestart(long generation) {
- markServerForRestart(RestartReason.CRASH, generation);
- }
-
- /**
- * As {@link #handleCrashAndGetExitCode()}, but only if {@code generation}
is still current.
+ * Marks the server for restart due to a fatal error, attributed to {@code
reason}, but only
+ * if {@code generation} is still current -- reports about a superseded
process are dropped.
+ * <p>
+ * Called by a client that received a fatal status: the process is
stopping even if
+ * {@link #isRunning()} still says otherwise, and the next {@link
#ensureRunning()} waits for
+ * it to exit and restarts it.
+ * <p>
+ * Deliberately the only spelling, and deliberately abstract. Earlier
revisions offered a
+ * no-arg and a reasonless form defaulting to one another; an
implementation that overrode
+ * only one left the others silently inert, which is how a worker known to
be poisoned kept
+ * being handed documents.
*/
- default int handleCrashAndGetExitCode(long generation) {
- return handleCrashAndGetExitCode();
- }
+ void markServerForRestart(RestartReason reason, long generation);
/** Restarts performed so far for {@code reason}; monotonic, never reset.
*/
default long getRestartCount(RestartReason reason) {
@@ -205,9 +172,6 @@ public interface ServerManager extends Closeable {
*
* @return the exit code if available, or -1 if the process is still
running or unavailable
*/
- default int handleCrashAndGetExitCode() {
- markServerForRestart(RestartReason.CRASH);
- return -1;
- }
+ int handleCrashAndGetExitCode(long generation);
}
diff --git
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/SharedServerManager.java
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/SharedServerManager.java
index 17cdf4f6ff..a4a0478ae9 100644
---
a/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/SharedServerManager.java
+++
b/tika-pipes/tika-pipes-core/src/main/java/org/apache/tika/pipes/core/SharedServerManager.java
@@ -169,19 +169,6 @@ public class SharedServerManager implements ServerManager {
* Called by a client that received OOM or TIMEOUT: the process is exiting
even if
* isRunning() still says otherwise; the next ensureRunning() restarts it.
*/
- @Override
- public void markServerForRestart() {
- markServerForRestart(RestartReason.CRASH);
- }
-
- @Override
- public void markServerForRestart(RestartReason reason) {
- synchronized (lock) {
- LOG.debug("Server marked for restart ({}) - will restart on next
ensureRunning()", reason);
- markForRestart(reason);
- }
- }
-
@Override
public void markServerForRestart(RestartReason reason, long generation) {
synchronized (lock) {
@@ -228,15 +215,6 @@ public class SharedServerManager implements ServerManager {
}
/** Another client may already have attributed this crash (OOM/TIMEOUT);
don't overwrite it. */
- @Override
- public int handleCrashAndGetExitCode() {
- synchronized (lock) {
- restarts.markIfUnmarked(RestartReason.CRASH);
- pendingRestart = true;
- }
- return -1;
- }
-
@Override
public int handleCrashAndGetExitCode(long generation) {
synchronized (lock) {
diff --git
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientClosedManagerTest.java
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientClosedManagerTest.java
new file mode 100644
index 0000000000..61802b5aa9
--- /dev/null
+++
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientClosedManagerTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.ServerSocket;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.pipes.api.FetchEmitTuple;
+import org.apache.tika.pipes.api.PipesResult;
+import org.apache.tika.pipes.api.emitter.EmitKey;
+import org.apache.tika.pipes.api.fetcher.FetchKey;
+
+public class PipesClientClosedManagerTest {
+
+ /**
+ * A request that reaches initialization after its manager was closed (a
parse racing
+ * PipesParser.close()/AsyncProcessor.close()) must come back as
FAILED_TO_INITIALIZE
+ * rather than escaping as an unchecked IllegalStateException -- and must
not mark a
+ * worker for restart, since there is nothing left to restart.
+ */
+ @Test
+ @Timeout(30)
+ public void closedManagerDuringInitReturnsFailedToInitialize() throws
Exception {
+ try (ServerSocket serverSocket = new ServerSocket(0)) {
+ SentinelServerManager manager = new
SentinelServerManager(serverSocket.getLocalPort());
+ manager.closed = true;
+ try (PipesClient client = new PipesClient(new PipesConfig(),
manager)) {
+ PipesResult result = client.process(new
FetchEmitTuple("closed-manager-test",
+ new FetchKey("fetcher", "key"), new EmitKey(), new
Metadata(),
+ new ParseContext(),
FetchEmitTuple.ON_PARSE_EXCEPTION.SKIP));
+
+ assertEquals(PipesResult.RESULT_STATUS.FAILED_TO_INITIALIZE,
result.status(),
+ "got: " + result.status() + " / " + result.message());
+ assertTrue(result.message().contains("closed"),
+ "message should carry the manager's reason, got: " +
result.message());
+ assertNull(manager.marked, "nothing to restart on a closed
manager");
+ assertFalse(manager.abandoned, "no connection was established
to abandon");
+ }
+ }
+ }
+}
diff --git
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientInterruptTest.java
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientInterruptTest.java
index 48a7336934..46fc2a1210 100644
---
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientInterruptTest.java
+++
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientInterruptTest.java
@@ -16,6 +16,7 @@
*/
package org.apache.tika.pipes.core;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.DataInputStream;
@@ -96,6 +97,11 @@ public class PipesClientInterruptTest {
assertTrue(manager.abandoned,
"the manager was not told; a per-client worker never
dials back, so the "
+ "next connect() would wait out the accept
timeout for nothing");
+ // Recycling on an abandoned connection travels
connectionAbandoned(), which the
+ // real managers attribute to CONNECTION_ABANDONED. Marking
here too would double
+ // count the restart and overwrite that reason with a less
specific one.
+ assertNull(manager.marked,
+ "an interrupt must recycle via connectionAbandoned(),
not by marking");
}
}
}
diff --git
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientPayloadLimitTest.java
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientPayloadLimitTest.java
index 67b62a50f3..6440a4a087 100644
---
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientPayloadLimitTest.java
+++
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/PipesClientPayloadLimitTest.java
@@ -18,6 +18,7 @@ package org.apache.tika.pipes.core;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.DataInputStream;
@@ -72,6 +73,9 @@ public class PipesClientPayloadLimitTest {
assertTrue(result.message().contains("maxIpcPayloadBytes"),
"message should name the limit, got: " +
result.message());
assertFalse(manager.abandoned, "nothing was sent; no reason to
abandon");
+ assertNull(manager.marked,
+ "the request was refused before anything was written;
the worker is "
+ + "healthy and must not be recycled");
assertFalse(connectionClosed.await(300, TimeUnit.MILLISECONDS),
"nothing was sent; the connection must stay usable");
}
diff --git
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/SentinelServerManager.java
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/SentinelServerManager.java
index 7007072b51..49a6813ed6 100644
---
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/SentinelServerManager.java
+++
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/SentinelServerManager.java
@@ -26,6 +26,9 @@ import java.nio.file.Path;
final class SentinelServerManager implements ServerManager {
private final int port;
volatile boolean abandoned;
+ volatile RestartReason marked;
+ /** When set, {@link #ensureRunning()} throws like a real manager that has
been closed. */
+ volatile boolean closed;
SentinelServerManager(int port) {
this.port = port;
@@ -43,6 +46,9 @@ final class SentinelServerManager implements ServerManager {
@Override
public void ensureRunning() {
+ if (closed) {
+ throw new IllegalStateException("sentinel server manager is
closed");
+ }
// the scripted server is already listening
}
@@ -68,8 +74,23 @@ final class SentinelServerManager implements ServerManager {
return null;
}
+ @Override
+ public long getGeneration() {
+ return 0;
+ }
+
+ @Override
+ public void markServerForRestart(RestartReason reason, long generation) {
+ marked = reason;
+ }
+
+ @Override
+ public int handleCrashAndGetExitCode(long generation) {
+ return -1;
+ }
+
@Override
public void close() {
- // nothing to close
+ closed = true;
}
}
diff --git
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/ServerManagerMarkContractTest.java
b/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/ServerManagerMarkContractTest.java
deleted file mode 100644
index 72bd63e7df..0000000000
---
a/tika-pipes/tika-pipes-core/src/test/java/org/apache/tika/pipes/core/ServerManagerMarkContractTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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.core;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.net.Socket;
-import java.nio.file.Path;
-
-import org.junit.jupiter.api.Test;
-
-/**
- * {@link ServerManager} offers two spellings of "recycle this worker" and
every in-repo caller
- * uses the reason form, so a regression in the no-arg form is invisible to
every other test.
- * These pin the contract from the <em>caller's</em> side: whichever spelling
a downstream
- * integration picked up, the worker must actually be marked.
- */
-public class ServerManagerMarkContractTest {
-
- /** Overrides only the no-arg form -- what a pre-RestartReason
implementation would have. */
- private static class NoArgOnly implements ServerManager {
- private int marks;
-
- @Override
- public void markServerForRestart() {
- marks++;
- }
-
- @Override
- public int getPort() {
- return -1;
- }
-
- @Override
- public void ensureRunning() {
- }
-
- @Override
- public Socket connect(int socketTimeoutMillis) {
- return null;
- }
-
- @Override
- public void shutdown() {
- }
-
- @Override
- public boolean isRunning() {
- return false;
- }
-
- @Override
- public Path getTempDirectory() {
- return null;
- }
-
- @Override
- public void close() {
- }
- }
-
- private static PipesConfig config() {
- return new PipesConfig();
- }
-
- @Test
- public void testPerClientHonoursBothSpellings() {
- PerClientServerManager sm = new PerClientServerManager(config(), null,
0);
- sm.markServerForRestart();
- assertTrue(sm.needsRestart(), "no-arg markServerForRestart() must
recycle the worker");
-
- PerClientServerManager other = new PerClientServerManager(config(),
null, 1);
- other.markServerForRestart(RestartReason.OOM);
- assertTrue(other.needsRestart(), "reason form must recycle the
worker");
- }
-
- @Test
- public void testSharedHonoursBothSpellings() {
- SharedServerManager sm = new SharedServerManager(config(), null, 2);
- sm.markServerForRestart();
- assertTrue(sm.needsRestart(), "no-arg markServerForRestart() must
recycle the worker");
-
- SharedServerManager other = new SharedServerManager(config(), null, 2);
- other.markServerForRestart(RestartReason.OOM);
- assertTrue(other.needsRestart(), "reason form must recycle the
worker");
- }
-
- @Test
- public void testReasonFormReachesANoArgOnlyImplementation() {
- NoArgOnly sm = new NoArgOnly();
- sm.markServerForRestart(RestartReason.OOM);
- assertEquals(1, sm.marks, "reason form must fall back to an older
no-arg implementation");
- }
-
- @Test
- public void testDefaultsDoNotRecurse() {
- // markServerForRestart(reason) defaults to the no-arg form, so the
no-arg form must not
- // default back to it: an implementation overriding neither would blow
the stack.
- ServerManager sm = new ServerManager() {
- @Override
- public int getPort() {
- return -1;
- }
-
- @Override
- public void ensureRunning() {
- }
-
- @Override
- public Socket connect(int socketTimeoutMillis) {
- return null;
- }
-
- @Override
- public void shutdown() {
- }
-
- @Override
- public boolean isRunning() {
- return false;
- }
-
- @Override
- public Path getTempDirectory() {
- return null;
- }
-
- @Override
- public void close() {
- }
- };
- sm.markServerForRestart();
- sm.markServerForRestart(RestartReason.CRASH);
- }
-}
diff --git
a/tika-pipes/tika-pipes-integration-tests/src/test/java/org/apache/tika/pipes/core/PipesClientTest.java
b/tika-pipes/tika-pipes-integration-tests/src/test/java/org/apache/tika/pipes/core/PipesClientTest.java
index ad3e7e2840..e343efd060 100644
---
a/tika-pipes/tika-pipes-integration-tests/src/test/java/org/apache/tika/pipes/core/PipesClientTest.java
+++
b/tika-pipes/tika-pipes-integration-tests/src/test/java/org/apache/tika/pipes/core/PipesClientTest.java
@@ -966,6 +966,10 @@ public class PipesClientTest {
assertTrue(elapsed < 10000,
"client backstop should fire within a few seconds of its
~1800ms " +
"deadline, not wait for the 60s default SO_TIMEOUT
(took " + elapsed + "ms)");
+ // Returning TIMEOUT is only half the job: the worker is still
wedged on this
+ // request, so it must also be marked, or the pool hands it the
next document.
+ assertEquals(RestartReason.TIMEOUT, fakeServerManager.marked,
+ "a worker that blew the client backstop must be marked for
restart");
}
}
@@ -979,6 +983,7 @@ public class PipesClientTest {
private static class ChattyNeverFinishingServerManager implements
ServerManager {
private final ServerSocket serverSocket;
private volatile boolean running = true;
+ volatile RestartReason marked;
ChattyNeverFinishingServerManager() throws IOException {
serverSocket = new ServerSocket(0, 50,
InetAddress.getLoopbackAddress());
@@ -1048,6 +1053,21 @@ public class PipesClientTest {
return null;
}
+ @Override
+ public long getGeneration() {
+ return 0;
+ }
+
+ @Override
+ public void markServerForRestart(RestartReason reason, long
generation) {
+ marked = reason;
+ }
+
+ @Override
+ public int handleCrashAndGetExitCode(long generation) {
+ return -1;
+ }
+
@Override
public void close() throws IOException {
running = false;