class TapeDriveWorker implements Runnable {

    @Override
    public void run() {
        try {
            StopWatch exceptionStopWatch = null;
            final StopWatch loopStopWatch = StopWatch.createStarted();
            final StopWatch inProgressWorkerStopWatch = StopWatch.createStarted();
            while (!stop.get()) {
                LOGGER.debug(msgPrefix + "Start take readWriteOrder from queue ");

                ReadWriteOrder readWriteOrder = null;
                loopStopWatch.reset();
                loopStopWatch.start();

                try {
                    Optional<? extends ReadWriteOrder> order = receiver.consume(this);

                    if (order.isPresent()) {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug(msgPrefix + "Process write order :" + JsonHandler.unprettyPrint(order.get()));
                        }
                        readWriteOrder = order.get();
                    }

                } catch (QueueException e) {
                    if (null == exceptionStopWatch) {
                        LOGGER.error(e);
                        exceptionStopWatch = StopWatch.createStarted();

                    } else {
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug(msgPrefix + "Sleep " + sleepTime + " ms because of exception : ", e);
                        }
                        TimeUnit.MILLISECONDS.sleep(sleepTime);

                        // Log every one minute
                        if (exceptionStopWatch.getTime(TimeUnit.MINUTES) >= 1) {
                            LOGGER.error(e);
                            exceptionStopWatch.reset();
                            exceptionStopWatch.start();
                        }
                    }
                }

                if (readWriteOrder != null) {

                    // Reset StopWatch
                    exceptionStopWatch = null;

                    TapeCatalog
                        currentTape =
                        (readWriteResult != null) ? readWriteResult.getCurrentTape() : null;

                    ReadWriteTask readWriteTask =
                        new ReadWriteTask(readWriteOrder, currentTape, tapeLibraryService,
                            tapeCatalogService, archiveReferentialRepository, readRequestReferentialRepository,
                            inputTarPath,
                            forceOverrideNonEmptyCartridges, archiveOutputRetentionPolicy);
                    readWriteResult = readWriteTask.get();

                    currentTape = readWriteResult.getCurrentTape();

                    QueueState orderState = readWriteResult.getOrderState();
                    final String orderId = readWriteOrder.getId();

                    switch (orderState) {
                        case ERROR:
                            // Mark order as error state
                            retryable().exec(() -> receiver.getQueue().markError(orderId));
                            break;

                        case READY:
                            // Re-enqueue order
                            retryable().exec(() -> receiver.getQueue().markReady(orderId));
                            break;

                        case COMPLETED:
                            // Remove order from queue
                            retryable().exec(() -> receiver.getQueue().remove(orderId));
                            break;

                        default:
                            throw new IllegalStateException(
                                msgPrefix + "Order should have state Completed, Ready or Error");
                    }

                    PerformanceLogger
                        .getInstance().log("STP_Offer_Tape", ((QueueMessageEntity) readWriteOrder).getId(),
                        readWriteOrder.isWriteOrder() ? "WRITE_TO_TAPE" : "READ_FROM_TAPE",
                        loopStopWatch.getTime(TimeUnit.MILLISECONDS));

                    if (StatusCode.FATAL.equals(readWriteResult.getStatus())) {
                        LOGGER.error(String.format(
                            "[Library] : %s, [Drive] : %s, [Tape]: %s, is paused because of FATAL status when executing order: %s",
                            tapeRobotPool.getLibraryIdentifier(),
                            tapeDriveService.getTapeDriveConf().getIndex(),
                            currentTape == null ? "No active tape" : currentTape.getCode(),
                            JsonHandler.unprettyPrint(readWriteOrder)));

                        // Pause worker for maintenance
                        fatalPause();
                    }

                    interceptPauseRequest();

                } else {
                    // Log every
                    if (inProgressWorkerStopWatch.getTime(TimeUnit.MILLISECONDS) >=
                        intervalDelayLogInProgressWorker) {
                        inProgressWorkerStopWatch.reset();
                        inProgressWorkerStopWatch.start();

                        LOGGER.warn(
                            msgPrefix + "No read/write to tape order found. waiting (" + sleepTime + ") ms ...");
                    }
                    TimeUnit.MILLISECONDS.sleep(sleepTime);
                }

            }
        } catch (Exception e) {
            throw new VitamRuntimeException(e);
        } finally {
            this.shutdownSignal.countDown();
            this.pauseSignal.countDown();
        }
    }

    @VisibleForTesting public  TapeDriveWorker(
        TapeRobotPool tapeRobotPool,
        TapeDriveService tapeDriveService,
        TapeCatalogService tapeCatalogService,
        TapeDriveOrderConsumer receiver,
        ArchiveReferentialRepository archiveReferentialRepository,
        ReadRequestReferentialRepository readRequestReferentialRepository,
        TapeCatalog currentTape,
        String inputTarPath, long sleepTime, boolean forceOverrideNonEmptyCartridges,
        ArchiveOutputRetentionPolicy archiveOutputRetentionPolicy);
    public  TapeDriveWorker(
        TapeRobotPool tapeRobotPool,
        TapeDriveService tapeDriveService,
        TapeCatalogService tapeCatalogService,
        TapeDriveOrderConsumer receiver,
        ArchiveReferentialRepository archiveReferentialRepository,
        ReadRequestReferentialRepository readRequestReferentialRepository,
        TapeCatalog currentTape,
        String inputTarPath, boolean forceOverrideNonEmptyCartridges,
        ArchiveOutputRetentionPolicy archiveOutputRetentionPolicy);

}