LoggerAdminLoggerI.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.Ice;

import java.util.ArrayDeque;
import java.util.Calendar;
import java.util.Deque;
import java.util.List;

final class LoggerAdminLoggerI implements LoggerAdminLogger, Runnable {
    @Override
    public void print(String message) {
        LogMessage logMessage = new LogMessage(LogMessageType.PrintMessage, now(), "", message);
        _localLogger.print(message);
        log(logMessage);
    }

    @Override
    public void trace(String category, String message) {
        LogMessage logMessage =
            new LogMessage(LogMessageType.TraceMessage, now(), category, message);
        _localLogger.trace(category, message);
        log(logMessage);
    }

    @Override
    public void warning(String message) {
        LogMessage logMessage = new LogMessage(LogMessageType.WarningMessage, now(), "", message);
        _localLogger.warning(message);
        log(logMessage);
    }

    @Override
    public void error(String message) {
        LogMessage logMessage = new LogMessage(LogMessageType.ErrorMessage, now(), "", message);
        _localLogger.error(message);
        log(logMessage);
    }

    @Override
    public String getPrefix() {
        return _localLogger.getPrefix();
    }

    @Override
    public Logger cloneWithPrefix(String prefix) {
        return _localLogger.cloneWithPrefix(prefix);
    }

    @Override
    public Object getFacet() {
        return _loggerAdmin;
    }

    @Override
    public void destroy() {
        Thread thread = null;
        synchronized (this) {
            if (_sendLogThread != null) {
                thread = _sendLogThread;
                _sendLogThread = null;
                _destroyed = true;
                notifyAll();
            }
        }

        if (thread != null) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                synchronized (this) {
                    _sendLogThread = thread;
                }
                throw new OperationInterruptedException(e);
            }
        }

        _loggerAdmin.destroy();
    }

    @Override
    public void run() {
        if (_loggerAdmin.getTraceLevel() > 1) {
            _localLogger.trace(_traceCategory, "send log thread started");
        }

        for (; ; ) {
            Job job = null;
            synchronized (this) {
                while (!_destroyed && _jobQueue.isEmpty()) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // Ignored, this should never occur
                    }
                }

                if (_destroyed) {
                    break; // for(;;)
                }

                assert (!_jobQueue.isEmpty());
                job = _jobQueue.removeFirst();
            }

            for (RemoteLoggerPrx p : job.remoteLoggers) {
                if (_loggerAdmin.getTraceLevel() > 1) {
                    _localLogger.trace(
                        _traceCategory, "sending log message to `" + p.toString() + "'");
                }

                try {
                    //
                    // p is a proxy associated with the _sendLogCommunicator
                    //
                    p.logAsync(job.logMessage)
                        .whenComplete(
                            (Void v, Throwable ex) -> {
                                if (ex != null) {
                                    if (ex instanceof CommunicatorDestroyedException) {
                                        // Expected if there are outstanding calls during
                                        // communicator destruction.
                                    } else if (ex instanceof LocalException) {
                                        _loggerAdmin.deadRemoteLogger(
                                            p,
                                            _localLogger,
                                            (LocalException) ex,
                                            "log");
                                    } else {
                                        _loggerAdmin.deadRemoteLogger(
                                            p,
                                            _localLogger,
                                            new UnknownException(ex),
                                            "log");
                                    }
                                } else {
                                    if (_loggerAdmin.getTraceLevel() > 1) {
                                        _localLogger.trace(
                                            _traceCategory,
                                            "log on `"
                                                + p.toString()
                                                + "' completed successfully");
                                    }
                                }
                            });
                } catch (LocalException ex) {
                    _loggerAdmin.deadRemoteLogger(p, _localLogger, ex, "log");
                }
            }
        }

        if (_loggerAdmin.getTraceLevel() > 1) {
            _localLogger.trace(_traceCategory, "send log thread completed");
        }
    }

    LoggerAdminLoggerI(Properties props, Logger localLogger) {
        if (localLogger instanceof LoggerAdminLoggerI) {
            _localLogger = ((LoggerAdminLoggerI) localLogger).getLocalLogger();
        } else {
            _localLogger = localLogger;
        }

        _loggerAdmin = new LoggerAdminI(props, this);
    }

    Logger getLocalLogger() {
        return _localLogger;
    }

    void log(LogMessage logMessage) {
        List<RemoteLoggerPrx> remoteLoggers = _loggerAdmin.log(logMessage);

        if (remoteLoggers != null) {
            assert (!remoteLoggers.isEmpty());

            synchronized (this) {
                if (_sendLogThread == null) {
                    _sendLogThread = new Thread(this, "Ice.SendLogThread");
                    _sendLogThread.start();
                }

                _jobQueue.addLast(new Job(remoteLoggers, logMessage));
                notifyAll();
            }
        }
    }

    private static long now() {
        return Calendar.getInstance().getTimeInMillis() * 1000;
    }

    private static class Job {
        Job(List<RemoteLoggerPrx> r, LogMessage l) {
            remoteLoggers = r;
            logMessage = l;
        }

        final List<RemoteLoggerPrx> remoteLoggers;
        final LogMessage logMessage;
    }

    private final Logger _localLogger;
    private final LoggerAdminI _loggerAdmin;
    private boolean _destroyed;
    private Thread _sendLogThread;
    private final Deque<Job> _jobQueue = new ArrayDeque<>();

    private static final String _traceCategory = "Admin.Logger";
}