ServerManager.java
/*
* 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 java.io.Closeable;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.TimeoutException;
/**
* Manages the lifecycle of a PipesServer process and client connections.
* <p>
* Implementations handle starting, monitoring, and restarting the server process.
* In per-client mode (default), each PipesClient has its own ServerManager.
* In shared mode, multiple PipesClients share a single ServerManager.
*
* @see PerClientServerManager
* @see SharedServerManager
*/
public interface ServerManager extends Closeable {
/**
* Returns the port number the server is listening on.
* May return -1 if the server hasn't been started yet.
*
* @return the server port, or -1 if not started
*/
int getPort();
/**
* Ensures the server is running, starting or restarting it if necessary.
* <p>
* In shared mode, this method is synchronized to prevent multiple clients
* from attempting to restart the server simultaneously.
*
* @throws IOException if the server cannot be started
* @throws InterruptedException if interrupted while waiting for server startup
* @throws TimeoutException if the server doesn't start within the configured timeout
* @throws ServerInitializationException if the server fails to initialize
*/
void ensureRunning() throws IOException, InterruptedException, TimeoutException, ServerInitializationException;
/**
* Establishes a connection to the server and returns a connected Socket.
* <p>
* The behavior differs by implementation:
* <ul>
* <li>Per-client mode: Accepts incoming connection from the dedicated server</li>
* <li>Shared mode: Connects out to the shared server</li>
* </ul>
* <p>
* This method should be called after {@link #ensureRunning()}.
*
* @param socketTimeoutMs the socket timeout in milliseconds
* @return a connected Socket ready for communication
* @throws IOException if connection fails
* @throws ServerInitializationException if the server died before connecting
*/
Socket connect(int socketTimeoutMs) throws IOException, ServerInitializationException;
/**
* Shuts down the server process and cleans up resources.
* After calling this method, {@link #ensureRunning()} can be called to restart.
*
* @throws InterruptedException if interrupted while waiting for shutdown
*/
void shutdown() throws InterruptedException;
/**
* Checks if the server process is currently running.
*
* @return true if the server process is running
*/
boolean isRunning();
/**
* Returns the path to the temporary directory used by the server.
* May return null if the server hasn't been started yet.
*
* @return the temp directory path, or null if not started
*/
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>
* In per-client mode, this is typically a no-op since the client owns the server.
* In shared mode, this is important for coordinating restarts among multiple clients.
*/
default void markServerForRestart() {
// Default no-op for backward compatibility
}
/**
* Increments the count of files processed and marks for restart if limit reached.
* <p>
* This tracks progress toward the maxFilesProcessedPerProcess limit. When the limit
* is reached, {@link #needsRestart()} will return true and the next call to
* {@link #ensureRunning()} will restart the server.
*
* @param maxFilesPerProcess the maximum files before restart (0 means unlimited)
*/
default void incrementFilesProcessed(long maxFilesPerProcess) {
// Default no-op for backward compatibility
}
/**
* Checks if the server has been marked for restart.
* <p>
* This allows clients to detect that a restart is pending before attempting
* to use an existing connection that might be stale.
*
* @return true if the server has been marked for restart
*/
default boolean needsRestart() {
return false;
}
/**
* Handles a crash by checking the process exit code and marking for restart.
* <p>
* In per-client mode, waits briefly for the process to exit and checks the
* exit code to determine if this was an OOM or TIMEOUT.
* In shared mode, just marks for restart (exit code checking is not reliable
* since multiple clients share the process).
*
* @return the exit code if available, or -1 if the process is still running or unavailable
*/
default int handleCrashAndGetExitCode() {
markServerForRestart();
return -1;
}
}