com.mgmtp.perfload.core.console.status.StatusHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.mgmtp.perfload.core.console.status.StatusHandler.java

Source

/*
 * Copyright (c) 2002-2014 mgm technology partners GmbH
 *
 * Licensed 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 com.mgmtp.perfload.core.console.status;

import static com.google.common.collect.Maps.filterKeys;
import static com.google.common.collect.Maps.newHashMap;

import java.util.Calendar;
import java.util.Collections;
import java.util.Deque;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingDeque;

import org.apache.commons.lang3.time.FastDateFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.MapMaker;
import com.mgmtp.perfload.core.common.util.StatusInfo;
import com.mgmtp.perfload.core.common.util.StatusInfoType;

/**
 * {@link Runnable} implementation that takes {@link StatusInfo} objects from a queue transforming
 * them to a map of {@link StatusInfo} objects.
 * 
 * @author rnaegele
 */
public class StatusHandler {
    private static final FastDateFormat TIMESTAMP_FORMAT = FastDateFormat
            .getInstance("yyyy-MM-dd'T'HH:mm:ss.SSSZZ");

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final ConcurrentMap<StatusInfoKey, StatusInfo> statusInfoMap = new MapMaker().makeMap();
    private final ConcurrentMap<StatusInfoKey, Deque<ThreadActivity>> threadActivities = new MapMaker().makeMap();

    /**
     * 
     * @param statusInfo
     *            The status info object
     */
    public void addStatusInfo(final StatusInfo statusInfo) {
        StatusInfoKey key = new StatusInfoKey(statusInfo.getDaemonId(), statusInfo.getProcessId(),
                statusInfo.getThreadId());
        StatusInfo si = statusInfoMap.get(key);

        if (si == null && statusInfo.getType() != StatusInfoType.PROCESS_FINISHED) {
            log.debug("Adding status info: {}", statusInfo);
            si = statusInfoMap.putIfAbsent(key, statusInfo);
            if (si != null) {
                // Another thread was faster and we already have an entry for this key.
                // So we recursively call this method to update the existing entry.
                log.debug("Recursively updating status info: {}", statusInfo);
                addStatusInfo(statusInfo);
            } else {
                addThreadActivity(statusInfo.getDaemonId(), statusInfo.getProcessId(),
                        statusInfo.getActiveThreads());
            }
        } else {
            log.debug("Updating status info {}:" + statusInfo);

            if (statusInfo.getType() == StatusInfoType.PROCESS_FINISHED) {
                final Integer daemonId = statusInfo.getDaemonId();
                final Integer processId = statusInfo.getProcessId();

                // Filter by daemonId and processId to get map of all thread entries for those
                Map<StatusInfoKey, StatusInfo> filteredMap = filterKeys(statusInfoMap,
                        new Predicate<StatusInfoKey>() {
                            @Override
                            public boolean apply(final StatusInfoKey sik) {
                                return sik.getDaemonId().equals(daemonId) && sik.getProcessId().equals(processId);
                            }
                        });

                // Create updates for filtered entries with final error status of the process
                Map<StatusInfoKey, StatusInfo> updates = newHashMap();
                for (Entry<StatusInfoKey, StatusInfo> entry : filteredMap.entrySet()) {
                    StatusInfo siNew = new StatusInfo.Builder(entry.getValue())
                            .stackTrace(statusInfo.getStackTrace()).error(statusInfo.getError()).build();
                    updates.put(entry.getKey(), siNew);

                    addThreadActivity(siNew.getDaemonId(), siNew.getProcessId(), 0);
                }

                statusInfoMap.putAll(updates);
            } else {
                // Create new builder based on existing status info object and update necessary fields
                StatusInfo.Builder builder = new StatusInfo.Builder(si).stackTrace(statusInfo.getStackTrace())
                        .finished(statusInfo.getFinished());

                si = builder.build();
                statusInfoMap.put(key, si);
            }
        }
    }

    private void addThreadActivity(final Integer daemonId, final Integer processId, final int activeThreads) {
        StatusInfoKey key = new StatusInfoKey(daemonId, processId, null);

        Deque<ThreadActivity> taDeque = threadActivities.get(key);
        if (taDeque == null) {
            Deque<ThreadActivity> newDeque = new LinkedBlockingDeque<>();
            taDeque = threadActivities.putIfAbsent(key, newDeque);
            if (taDeque == null) {
                taDeque = newDeque;
            }
        }
        String timestamp = TIMESTAMP_FORMAT.format(Calendar.getInstance());
        taDeque.offerLast(new ThreadActivity(daemonId, processId, activeThreads, timestamp));
    }

    public Map<StatusInfoKey, Deque<ThreadActivity>> getThreadActivitiesMap() {
        return Collections.unmodifiableMap(threadActivities);
    }

    /**
     * Creates a snapshot of the status information collected so far. The status information is
     * aggregated by daemon, process, and thread.
     * 
     * @return a list of {@link ThreadStatus} objects representing a snapshot of the current test
     *         status
     */
    public Map<StatusInfoKey, StatusInfo> getStatusInfoMap() {
        return Collections.unmodifiableMap(statusInfoMap);
    }
}