/* * Copyright 2007-2010 Sun Microsystems, Inc. * * This file is part of Project Darkstar Server. * * Project Darkstar Server is free software: you can redistribute it * and/or modify it under the terms of the GNU General Public License * version 2 as published by the Free Software Foundation and * distributed hereunder to you. * * Project Darkstar Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * -- */ package com.sun.sgs.impl.profile; import com.sun.sgs.auth.Identity; import com.sun.sgs.kernel.KernelRunnable; import com.sun.sgs.profile.AccessedObjectsDetail; import com.sun.sgs.profile.ProfileParticipantDetail; import com.sun.sgs.profile.ProfileReport; import com.sun.sgs.profile.TransactionListenerDetail; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * Package-private implementation of <code>ProfileReport</code>. */ class ProfileReportImpl implements ProfileReport { /** * An empty map for returning when no profile counters have been * updated. */ private static final Map<String, Long> EMPTY_COUNTER_MAP = Collections.emptyMap(); /** * An empty list for returning when no operations have been updated. */ private static final List<String> EMPTY_OPS = Collections.emptyList(); /** * An empty map for returning when no profile samples have been * updated. We need this map as well because typing issues * prevent us from using {@link Collections#emptyMap()}. */ private static final Map<String, List<Long>> EMPTY_SAMPLE_MAP = Collections.unmodifiableMap(new HashMap<String, List<Long>>()); // the final fields, set by the constructor final KernelRunnable task; final Identity owner; final long scheduledStartTime; final int readyCount; final long actualStartTime; // the other fields, set directly by the ProfileCollectorImpl byte [] transactionId = null; boolean succeeded = false; long runningTime = 0; int tryCount = 0; Throwable throwable = null; private AccessedObjectsDetail accessedObjectsDetail = null; private Set<ProfileParticipantDetail> participants; private Set<TransactionListenerDetail> txnListeners; // counters that are updated through methods on this class private Map<String, Long> taskCounters; // a list of operations performed, which is updated through // methods on this class private List<String> ops; // samples that are added through methods on this class private Map<String, List<Long>> taskSamples; /** * Creates an instance of <code>ProfileReportImpl</code> with the * actual starting time being set to the current time. * * @param task the <code>KernelRunnable</code> being reported on * @param owner the <code>Identity</code> that owns the task * @param scheduledStartTime the time the task was scheduled to run * @param readyCount the number of tasks in the scheduler, ready to run, * that are associated with the same context as the task */ ProfileReportImpl(KernelRunnable task, Identity owner, long scheduledStartTime, int readyCount) { this.task = task; this.owner = owner; this.scheduledStartTime = scheduledStartTime; this.readyCount = readyCount; this.actualStartTime = System.currentTimeMillis(); participants = new HashSet<ProfileParticipantDetail>(); txnListeners = new HashSet<TransactionListenerDetail>(); taskCounters = null; ops = null; taskSamples = null; } /** * Package-private method used to increment task-local counters * that were changed during this task. If this counter hasn't had a * value reported yet for this task, then the provided value is * set as the current value for the counter. * * @param counter the name of the counter * @param value the amount to increment the counter */ void incrementTaskCounter(String counter, long value) { long currentValue = 0; if (taskCounters == null) { taskCounters = new HashMap<String, Long>(); } else { if (taskCounters.containsKey(counter)) { currentValue = taskCounters.get(counter); } } taskCounters.put(counter, currentValue + value); } /** * Package-private method used to add to a task-local sample. If * this sample hasn't had a value reported yet for this task, then * a new list is made and the the provided value is added to it. * * @param sampleName the name of the sample * @param value the latest value for the sample */ void addTaskSample(String sampleName, long value) { List<Long> samples; if (taskSamples == null) { taskSamples = new HashMap<String, List<Long>>(); samples = new LinkedList<Long>(); taskSamples.put(sampleName, samples); } else { if (taskSamples.containsKey(sampleName)) { samples = taskSamples.get(sampleName); } else { samples = new LinkedList<Long>(); taskSamples.put(sampleName, samples); } } samples.add(value); } /** * Package-private method used to add an operation. * * @param operationName name of the operation */ void addOperation(String operationName) { if (ops == null) { ops = new ArrayList<String>(); } ops.add(operationName); } void addParticipant(ProfileParticipantDetail participantDetail) { participants.add(participantDetail); } void addListener(TransactionListenerDetail listenerDetail) { txnListeners.add(listenerDetail); } void setAccessedObjectsDetail(AccessedObjectsDetail detail) { accessedObjectsDetail = detail; } /** * {@inheritDoc} */ public KernelRunnable getTask() { return task; } /** * {@inheritDoc} */ public Identity getTaskOwner() { return owner; } /** * {@inheritDoc} */ public boolean wasTaskTransactional() { return transactionId != null; } /** * {@inheritDoc} */ public byte [] getTransactionId() { return transactionId; } /** * {@inheritDoc} */ public Set<ProfileParticipantDetail> getParticipantDetails() { return participants; } /** * {@inheritDoc} */ public Set<TransactionListenerDetail> getListenerDetails() { return txnListeners; } /** * {@inheritDoc} */ public boolean wasTaskSuccessful() { return succeeded; } /** * {@inheritDoc} */ public long getScheduledStartTime() { return scheduledStartTime; } /** * {@inheritDoc} */ public long getActualStartTime() { return actualStartTime; } /** * {@inheritDoc} */ public long getRunningTime() { return runningTime; } /** * {@inheritDoc} */ public int getRetryCount() { return tryCount; } /** * {@inheritDoc} */ public List<String> getReportedOperations() { return (ops == null) ? EMPTY_OPS : ops; } /** * {@inheritDoc} */ public Map<String, Long> getUpdatedTaskCounters() { return (taskCounters == null) ? EMPTY_COUNTER_MAP : taskCounters; } /** * {@inheritDoc} */ public Map<String, List<Long>> getUpdatedTaskSamples() { return (taskSamples == null) ? EMPTY_SAMPLE_MAP : taskSamples; } /** * {@inheritDoc} */ public AccessedObjectsDetail getAccessedObjectsDetail() { return accessedObjectsDetail; } /** * {@inheritDoc} */ public int getReadyCount() { return readyCount; } /** * {@inheritDoc} */ public Throwable getFailureCause() { return throwable; } /** * Package-private method used to merge the state of one report into * another. This is typically used when a nested, profiled task * completes and needs to share its data with its parent. */ void merge(ProfileReportImpl report) { // for each of the child task counters and samples, we first // check whether the task recorded any data. If so, then we // copy the data to this report. if (report.taskCounters != null) { if (taskCounters == null) { taskCounters = new HashMap<String, Long>(report.taskCounters); } else { for (Map.Entry<String, Long> e : report.taskCounters.entrySet()) { Long curCount = taskCounters.get(e.getKey()); taskCounters.put(e.getKey(), (curCount == null) ? e.getValue() : curCount + e.getValue()); } } } if (report.taskSamples != null) { if (taskSamples == null) { taskSamples = new HashMap<String, List<Long>>(); for (Map.Entry<String, List<Long>> e : report.taskSamples.entrySet()) { // make a copy of the child task's samples List<Long> samples = new LinkedList<Long>(e.getValue()); taskSamples.put(e.getKey(), samples); } } else { for (Map.Entry<String, List<Long>> e : report.taskSamples.entrySet()) { List<Long> samples = taskSamples.get(e.getKey()); if (samples == null) { // make a copy of the child task's samples taskSamples.put(e.getKey(), new LinkedList<Long>(e.getValue())); } else { samples.addAll(e.getValue()); } } } } if (report.ops != null) { if (ops == null) { ops = new LinkedList<String>(report.ops); } else { ops.addAll(report.ops); } } // NOTE: we do not include the the participant or listener detail // since this is specific to a task and not to its // children. } /** * Package-private method to note that a task has finished running. * All collections are modified to be immutable. */ void finish() { if (ops != null) { ops = Collections.unmodifiableList(ops); } if (taskCounters != null) { taskCounters = Collections.unmodifiableMap(taskCounters); } if (taskSamples != null) { taskSamples = Collections.unmodifiableMap(taskSamples); } participants = Collections.unmodifiableSet(participants); txnListeners = Collections.unmodifiableSet(txnListeners); } }