public void start() throws Exception {
        HealthStatusManager healthStatusManager = new HealthStatusManager();
        ServerCredentials creds;
        if (secure) {
            TlsServerCredentials.Builder channelCredBuilder = TlsServerCredentials.newBuilder();
            channelCredBuilder.keyManager(certChain, privateKey, privateKeyPassword);
            if (trustCertCollection != null && trustCertCollection.exists()) {
                channelCredBuilder.trustManager(trustCertCollection);
                if (clientAuthRequired) {
                    channelCredBuilder.clientAuth(TlsServerCredentials.ClientAuth.REQUIRE);
                }
            }
            creds = channelCredBuilder.build();
        } else {
            creds = InsecureServerCredentials.create();
        }
        if (tikaConfigXml == null) {
            // Create a default tika config
            tikaConfigXml = File.createTempFile("tika-config", ".xml");
            try (FileWriter fw = new FileWriter(tikaConfigXml, StandardCharsets.UTF_8)) {
                TikaConfigSerializer.serialize(new TikaConfig(), TikaConfigSerializer.Mode.STATIC_FULL, fw, StandardCharsets.UTF_8);
            }
        }
        File tikaConfigFile = new File(tikaConfigXml.getAbsolutePath());
        healthStatusManager.setStatus(TikaGrpcServer.class.getSimpleName(), ServingStatus.SERVING);
        server = Grpc
                .newServerBuilderForPort(port, creds)
                .addService(new TikaGrpcServerImpl(tikaConfigFile.getAbsolutePath()))
                .addService(healthStatusManager.getHealthService())
                .addService(ProtoReflectionService.newInstance())
                .build()
                .start();
        LOGGER.info("Server started, listening on " + port);
        Runtime
                .getRuntime()
                .addShutdownHook(new Thread(() -> {
                    // Use stderr here since the logger may have been reset by its JVM shutdown hook.
                    System.err.println("*** shutting down gRPC server since JVM is shutting down");
                    healthStatusManager.clearStatus(TikaGrpcServer.class.getSimpleName());
                    try {
                        TikaGrpcServer.this.stop();
                    } catch (InterruptedException e) {
                        e.printStackTrace(System.err);
                    }
                    System.err.println("*** server shut down");
                }));
    }
@Test
    public void testFetcherCrud(Resources resources) throws Exception {
        Assertions.assertTrue(tikaConfigXml.setWritable(false));
        String serverName = InProcessServerBuilder.generateName();

        Server server = InProcessServerBuilder
                .forName(serverName)
                .directExecutor()
                .addService(new TikaGrpcServerImpl(tikaConfigXml.getAbsolutePath()))
                .build()
                .start();
        resources.register(server, Duration.ofSeconds(10));

        ManagedChannel channel = InProcessChannelBuilder
                .forName(serverName)
                .directExecutor()
                .build();
        resources.register(channel, Duration.ofSeconds(10));
        TikaGrpc.TikaBlockingStub blockingStub = TikaGrpc.newBlockingStub(channel);

        String targetFolder = new File("target").getAbsolutePath();
        // create fetchers
        for (int i = 0; i < NUM_FETCHERS_TO_CREATE; ++i) {
            String fetcherId = createFetcherId(i);
            SaveFetcherReply reply = blockingStub.saveFetcher(SaveFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .setFetcherClass(FileSystemFetcher.class.getName())
                    .setFetcherConfigJson(OBJECT_MAPPER.writeValueAsString(ImmutableMap
                            .builder()
                            .put("basePath", targetFolder)
                            .put("extractFileSystemMetadata", true)
                            .build()))
                    .build());
            assertEquals(fetcherId, reply.getFetcherId());
        }
        // update fetchers
        for (int i = 0; i < NUM_FETCHERS_TO_CREATE; ++i) {
            String fetcherId = createFetcherId(i);
            SaveFetcherReply reply = blockingStub.saveFetcher(SaveFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .setFetcherClass(FileSystemFetcher.class.getName())
                    .setFetcherConfigJson(OBJECT_MAPPER.writeValueAsString(ImmutableMap
                            .builder()
                            .put("basePath", targetFolder)
                            .put("extractFileSystemMetadata", false)
                            .build()))
                    .build());
            assertEquals(fetcherId, reply.getFetcherId());
            GetFetcherReply getFetcherReply = blockingStub.getFetcher(GetFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .build());
            assertEquals("false", getFetcherReply
                    .getParamsMap()
                    .get("extractFileSystemMetadata"));
        }

        // get fetchers
        for (int i = 0; i < NUM_FETCHERS_TO_CREATE; ++i) {
            String fetcherId = createFetcherId(i);
            GetFetcherReply getFetcherReply = blockingStub.getFetcher(GetFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .build());
            assertEquals(fetcherId, getFetcherReply.getFetcherId());
            assertEquals(FileSystemFetcher.class.getName(), getFetcherReply.getFetcherClass());
        }

        // delete fetchers
        for (int i = 0; i < NUM_FETCHERS_TO_CREATE; ++i) {
            String fetcherId = createFetcherId(i);
            DeleteFetcherReply deleteFetcherReply = blockingStub.deleteFetcher(DeleteFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .build());
            Assertions.assertTrue(deleteFetcherReply.getSuccess());
            StatusRuntimeException statusRuntimeException = Assertions.assertThrows(StatusRuntimeException.class, () -> blockingStub.getFetcher(GetFetcherRequest
                    .newBuilder()
                    .setFetcherId(fetcherId)
                    .build()));
            Assertions.assertEquals(Status.NOT_FOUND
                    .getCode()
                    .value(), statusRuntimeException
                    .getStatus()
                    .getCode()
                    .value());
        }
    }