com.mirth.connect.donkey.server.channel.Statistics.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.donkey.server.channel.Statistics.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.donkey.server.channel;

import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.lang3.ArrayUtils;

import com.mirth.connect.donkey.model.event.MessageEventType;
import com.mirth.connect.donkey.model.message.Status;
import com.mirth.connect.donkey.server.Donkey;
import com.mirth.connect.donkey.server.event.EventDispatcher;
import com.mirth.connect.donkey.server.event.MessageEvent;

public class Statistics {

    public static final Status[] TRACKED_STATUSES = new Status[] { Status.RECEIVED, Status.FILTERED, Status.SENT,
            Status.ERROR };

    private Map<String, Map<Integer, Map<Status, AtomicLong>>> stats = new ConcurrentHashMap<String, Map<Integer, Map<Status, AtomicLong>>>();
    private EventDispatcher eventDispatcher;
    private boolean sendEvents;

    public Statistics(boolean sendEvents) {
        this.sendEvents = sendEvents;
    }

    public Map<String, Map<Integer, Map<Status, Long>>> getStats() {
        Map<String, Map<Integer, Map<Status, Long>>> stats = new HashMap<String, Map<Integer, Map<Status, Long>>>();

        for (Entry<String, Map<Integer, Map<Status, AtomicLong>>> channelEntry : this.stats.entrySet()) {
            Map<Integer, Map<Status, Long>> channelMap = new LinkedHashMap<Integer, Map<Status, Long>>();

            for (Entry<Integer, Map<Status, AtomicLong>> metaDataEntry : channelEntry.getValue().entrySet()) {
                Map<Status, Long> statusMap = new LinkedHashMap<Status, Long>();

                for (Entry<Status, AtomicLong> statusEntry : metaDataEntry.getValue().entrySet()) {
                    statusMap.put(statusEntry.getKey(), statusEntry.getValue().get());
                }

                channelMap.put(metaDataEntry.getKey(), statusMap);
            }

            stats.put(channelEntry.getKey(), channelMap);
        }

        return stats;
    }

    public Map<Integer, Map<Status, Long>> getChannelStats(String channelId) {
        Map<Integer, Map<Status, Long>> channelStats = new LinkedHashMap<Integer, Map<Status, Long>>();

        for (Entry<Integer, Map<Status, AtomicLong>> metaDataEntry : getChannelStatsMap(channelId).entrySet()) {
            Map<Status, Long> statusMap = new LinkedHashMap<Status, Long>();

            for (Entry<Status, AtomicLong> statusEntry : metaDataEntry.getValue().entrySet()) {
                statusMap.put(statusEntry.getKey(), statusEntry.getValue().get());
            }

            channelStats.put(metaDataEntry.getKey(), statusMap);
        }

        return channelStats;
    }

    public Map<Status, Long> getConnectorStats(String channelId, Integer metaDataId) {
        Map<Status, Long> statusMap = new LinkedHashMap<Status, Long>();

        for (Entry<Status, AtomicLong> statusEntry : getConnectorStatsMap(getChannelStatsMap(channelId), metaDataId)
                .entrySet()) {
            statusMap.put(statusEntry.getKey(), statusEntry.getValue().get());
        }

        return statusMap;
    }

    public boolean isEmpty() {
        if (!stats.isEmpty()) {
            for (Map<Integer, Map<Status, AtomicLong>> channelMap : stats.values()) {
                for (Map<Status, AtomicLong> statusMap : channelMap.values()) {
                    for (AtomicLong diff : statusMap.values()) {
                        if (diff.get() != 0) {
                            return false;
                        }
                    }
                }
            }
        }

        return true;
    }

    public void update(Map<String, Map<Integer, Map<Status, Long>>> stats) {
        for (Entry<String, Map<Integer, Map<Status, Long>>> channelEntry : stats.entrySet()) {
            for (Entry<Integer, Map<Status, Long>> connectorEntry : channelEntry.getValue().entrySet()) {
                Integer metaDataId = connectorEntry.getKey();

                if (metaDataId != null) {
                    update(channelEntry.getKey(), metaDataId, connectorEntry.getValue());
                }
            }
        }
    }

    public void update(String channelId, int metaDataId, Status incrementStatus, Status decrementStatus) {
        // If no net change will be done then return immediately
        if (incrementStatus == decrementStatus) {
            return;
        }

        Map<Status, Long> statsDiff = new LinkedHashMap<Status, Long>();
        statsDiff.put(incrementStatus, 1L);

        if (decrementStatus != null) {
            statsDiff.put(decrementStatus, -1L);
        }

        update(channelId, metaDataId, statsDiff);
    }

    public void update(String channelId, int metaDataId, Map<Status, Long> statsDiff) {
        Map<Integer, Map<Status, AtomicLong>> channelStats = getChannelStatsMap(channelId);
        Map<Status, AtomicLong> aggregateStats = getConnectorStatsMap(channelStats, null);
        Map<Status, AtomicLong> connectorStats = getConnectorStatsMap(channelStats, metaDataId);

        for (Entry<Status, Long> statsEntry : statsDiff.entrySet()) {
            Long diff = statsEntry.getValue();

            if (ArrayUtils.contains(TRACKED_STATUSES, statsEntry.getKey()) && diff != 0) {
                Status status = statsEntry.getKey();

                // update the connector statistics
                Long connectorCount = connectorStats.get(status).addAndGet(diff);

                // update the channel statistics
                switch (status) {
                // update the following statuses based on the source connector
                case RECEIVED:
                    if (metaDataId == 0) {
                        aggregateStats.get(status).addAndGet(diff);
                    }
                    break;

                // update the following statuses based on the source and destination connectors
                case FILTERED:
                case ERROR:
                    aggregateStats.get(status).addAndGet(diff);
                    break;

                // update the following statuses based on the destination connectors
                case SENT:
                    if (metaDataId > 0) {
                        aggregateStats.get(status).addAndGet(diff);
                    }
                    break;

                default:
                    break;
                }

                if (sendEvents) {
                    MessageEventType type = MessageEventType.fromStatus(status);
                    if (type != null) {
                        // Dispatch a message event if the the status is in MessageEventType and the connector stat was updated
                        if (eventDispatcher == null) {
                            eventDispatcher = Donkey.getInstance().getEventDispatcher();
                        }
                        eventDispatcher.dispatchEvent(
                                new MessageEvent(channelId, metaDataId, type, connectorCount, diff <= 0));
                    }
                }
            }
        }
    }

    public void overwrite(String channelId, Integer metaDataId, Map<Status, Long> stats) {
        Map<Status, AtomicLong> connectorStats = getConnectorStatsMap(getChannelStatsMap(channelId), metaDataId);
        for (Entry<Status, Long> entry : stats.entrySet()) {
            connectorStats.get(entry.getKey()).set(entry.getValue());
        }
    }

    /**
     * Updates (increments/decrements) values from another Statistics object
     */
    public void update(Statistics statistics) {
        for (Entry<String, Map<Integer, Map<Status, Long>>> entry : statistics.getStats().entrySet()) {
            for (Entry<Integer, Map<Status, Long>> connectorEntry : entry.getValue().entrySet()) {
                Integer metaDataId = connectorEntry.getKey();

                if (metaDataId != null) {
                    update(entry.getKey(), metaDataId, connectorEntry.getValue());
                }
            }
        }
    }

    public void resetStats(String channelId, Integer metaDataId, Set<Status> statuses) {
        for (Status status : statuses) {
            if (ArrayUtils.contains(TRACKED_STATUSES, status)) {
                Map<Status, AtomicLong> connectorStats = getConnectorStatsMap(getChannelStatsMap(channelId),
                        metaDataId);
                connectorStats.get(status).set(0L);

                if (sendEvents && metaDataId != null) {
                    MessageEventType type = MessageEventType.fromStatus(status);

                    if (type != null) {
                        // Dispatch a message event if the the status is in MessageEventType and the connector stat was updated
                        if (eventDispatcher == null) {
                            eventDispatcher = Donkey.getInstance().getEventDispatcher();
                        }

                        eventDispatcher.dispatchEvent(new MessageEvent(channelId, metaDataId, type, 0L, true));
                    }
                }
            }
        }
    }

    public void remove(String channelId) {
        synchronized (stats) {
            Map<Integer, Map<Status, AtomicLong>> channelStats = stats.get(channelId);

            if (channelStats != null) {
                synchronized (channelStats) {
                    stats.remove(channelId);
                }
            }
        }
    }

    public void clear() {
        synchronized (stats) {
            stats.clear();
        }
    }

    private Map<Integer, Map<Status, AtomicLong>> getChannelStatsMap(String channelId) {
        Map<Integer, Map<Status, AtomicLong>> channelStats = stats.get(channelId);

        if (channelStats == null) {
            synchronized (stats) {
                channelStats = stats.get(channelId);

                if (channelStats == null) {
                    channelStats = new LinkedHashMap<Integer, Map<Status, AtomicLong>>();
                    stats.put(channelId, channelStats);
                }
            }
        }

        return channelStats;
    }

    private Map<Status, AtomicLong> getConnectorStatsMap(Map<Integer, Map<Status, AtomicLong>> channelStats,
            Integer metaDataId) {
        Map<Status, AtomicLong> connectorStats = channelStats.get(metaDataId);

        if (connectorStats == null) {
            synchronized (channelStats) {
                connectorStats = channelStats.get(metaDataId);

                if (connectorStats == null) {
                    connectorStats = new LinkedHashMap<Status, AtomicLong>();
                    connectorStats.put(Status.RECEIVED, new AtomicLong(0L));
                    connectorStats.put(Status.FILTERED, new AtomicLong(0L));
                    connectorStats.put(Status.SENT, new AtomicLong(0L));
                    connectorStats.put(Status.ERROR, new AtomicLong(0L));

                    channelStats.put(metaDataId, connectorStats);
                }
            }
        }

        return connectorStats;
    }

    public static Set<Status> getTrackedStatuses() {
        return new HashSet<Status>(Arrays.asList(TRACKED_STATUSES));
    }
}