/* * Copyright (c) 2010-2015 Evolveum * * 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.evolveum.midpoint.schema.statistics; import com.evolveum.midpoint.prism.xml.XmlTypeConverter; import com.evolveum.midpoint.util.QNameUtil; import com.evolveum.midpoint.xml.ns._public.common.common_3.EnvironmentalPerformanceInformationType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingsStatisticsEntryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.MappingsStatisticsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.NotificationsStatisticsEntryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.NotificationsStatisticsType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningStatisticsEntryType; import com.evolveum.midpoint.xml.ns._public.common.common_3.ProvisioningStatisticsType; import org.apache.commons.lang.StringUtils; import javax.xml.datatype.DatatypeConstants; import javax.xml.namespace.QName; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @author Pavol Mederly */ public class EnvironmentalPerformanceInformation { /* * Thread safety: Instances of this class may be accessed from more than one thread at once. * Updates are invoked in the context of the thread executing the task. * Queries are invoked either from this thread, or from some observer (task manager or GUI thread). * * We ensure synchronization by making public methods synchronized. We don't expect much contention on this. */ private final EnvironmentalPerformanceInformationType startValue; // this object is concurrently read (that is thread-safe), not written private Map<ProvisioningStatisticsKey,ProvisioningStatisticsData> provisioningData = new HashMap<>(); private Map<NotificationsStatisticsKey,GenericStatisticsData> notificationsData = new HashMap<>(); private Map<MappingsStatisticsKey,GenericStatisticsData> mappingsData = new HashMap<>(); private static final int AGGREGATION_THRESHOLD = 50; private StatusMessage lastMessage; public EnvironmentalPerformanceInformation(EnvironmentalPerformanceInformationType value) { startValue = value; } public EnvironmentalPerformanceInformation() { this(null); } public EnvironmentalPerformanceInformationType getStartValue() { return startValue; } public synchronized EnvironmentalPerformanceInformationType getDeltaValue() { EnvironmentalPerformanceInformationType rv = toEnvironmentalPerformanceInformationType(); return rv; } public synchronized EnvironmentalPerformanceInformationType getAggregatedValue() { EnvironmentalPerformanceInformationType delta = toEnvironmentalPerformanceInformationType(); EnvironmentalPerformanceInformationType rv = aggregate(startValue, delta); return rv; } private EnvironmentalPerformanceInformationType toEnvironmentalPerformanceInformationType() { EnvironmentalPerformanceInformationType rv = new EnvironmentalPerformanceInformationType(); rv.setProvisioningStatistics(toProvisioningStatisticsType()); rv.setMappingsStatistics(toMappingsStatisticsType()); rv.setNotificationsStatistics(toNotificationsStatisticsType()); if (lastMessage != null) { rv.setLastMessageTimestamp(XmlTypeConverter.createXMLGregorianCalendar(lastMessage.getDate())); rv.setLastMessage(lastMessage.getMessage()); } return rv; } private NotificationsStatisticsType toNotificationsStatisticsType() { NotificationsStatisticsType rv = new NotificationsStatisticsType(); if (notificationsData == null) { return rv; } for (Map.Entry<NotificationsStatisticsKey, GenericStatisticsData> entry : notificationsData.entrySet()) { NotificationsStatisticsKey key = entry.getKey(); String transport = key.getTransport(); NotificationsStatisticsEntryType entryType = findNotificationsEntryType(rv.getEntry(), transport); if (entryType == null) { entryType = new NotificationsStatisticsEntryType(); entryType.setTransport(transport); rv.getEntry().add(entryType); } setValueNotifications(entryType, key.isSuccess(), entry.getValue().getCount(), entry.getValue().getMinDuration(), entry.getValue().getMaxDuration(), entry.getValue().getTotalDuration()); } return rv; } private MappingsStatisticsType toMappingsStatisticsType() { final MappingsStatisticsType rv = new MappingsStatisticsType(); if (mappingsData == null) { return rv; } final Map<String,Integer> entriesPerType = new HashMap<>(); for (MappingsStatisticsKey key: mappingsData.keySet()) { Integer current = entriesPerType.get(key.getObjectType()); entriesPerType.put(key.getObjectType(), current != null ? current+1 : 1); } for (Map.Entry<MappingsStatisticsKey, GenericStatisticsData> entry : mappingsData.entrySet()) { final MappingsStatisticsKey key = entry.getKey(); final String targetEntryName; if (entriesPerType.get(key.getObjectType()) < AGGREGATION_THRESHOLD) { targetEntryName = key.getObjectName(); } else { targetEntryName = key.getObjectType() + " (aggregated)"; } MappingsStatisticsEntryType entryType = findMappingsEntryType(rv.getEntry(), targetEntryName); if (entryType == null) { entryType = new MappingsStatisticsEntryType(); entryType.setObject(targetEntryName); rv.getEntry().add(entryType); } setValueMapping(entryType, entry.getValue().getCount(), entry.getValue().getMinDuration(), entry.getValue().getMaxDuration(), entry.getValue().getTotalDuration()); } return rv; } private ProvisioningStatisticsType toProvisioningStatisticsType() { ProvisioningStatisticsType rv = new ProvisioningStatisticsType(); if (provisioningData == null) { return rv; } for (Map.Entry<ProvisioningStatisticsKey, ProvisioningStatisticsData> entry : provisioningData.entrySet()) { ProvisioningStatisticsKey key = entry.getKey(); String resource = key.getResourceName(); QName oc = key.getObjectClass(); ProvisioningStatisticsEntryType entryType = findProvisioningEntryType(rv.getEntry(), resource, oc); if (entryType == null) { entryType = new ProvisioningStatisticsEntryType(); entryType.setResource(resource); entryType.setObjectClass(oc); rv.getEntry().add(entryType); } setValue(entryType, key.getOperation(), key.getStatusType(), entry.getValue().getCount(), entry.getValue().getMinDuration(), entry.getValue().getMaxDuration(), entry.getValue().getTotalDuration()); } return rv; } private static ProvisioningStatisticsEntryType findProvisioningEntryType(List<ProvisioningStatisticsEntryType> list, String resource, QName objectClass) { for (ProvisioningStatisticsEntryType entryType : list) { if (StringUtils.equals(entryType.getResource(), resource) && QNameUtil.match(entryType.getObjectClass(), objectClass)) { return entryType; } } return null; } private void setValue(ProvisioningStatisticsEntryType e, ProvisioningOperation operation, ProvisioningStatusType statusType, int count, long min, long max, long totalDuration) { switch (operation) { case ICF_GET: if (statusType == ProvisioningStatusType.SUCCESS) { e.setGetSuccess(e.getGetSuccess() + count); } else { e.setGetFailure(e.getGetFailure() + count); } break; case ICF_SEARCH: if (statusType == ProvisioningStatusType.SUCCESS) { e.setSearchSuccess(e.getSearchSuccess() + count); } else { e.setSearchFailure(e.getSearchFailure() + count); } break; case ICF_CREATE: if (statusType == ProvisioningStatusType.SUCCESS) { e.setCreateSuccess(e.getCreateSuccess() + count); } else { e.setCreateFailure(e.getCreateFailure() + count); } break; case ICF_UPDATE: if (statusType == ProvisioningStatusType.SUCCESS) { e.setUpdateSuccess(e.getUpdateSuccess() + count); } else { e.setUpdateFailure(e.getUpdateFailure() + count); } break; case ICF_DELETE: if (statusType == ProvisioningStatusType.SUCCESS) { e.setDeleteSuccess(e.getDeleteSuccess() + count); } else { e.setDeleteFailure(e.getDeleteFailure() + count); } break; case ICF_SYNC: if (statusType == ProvisioningStatusType.SUCCESS) { e.setSyncSuccess(e.getSyncSuccess() + count); } else { e.setSyncFailure(e.getSyncFailure() + count); } break; case ICF_SCRIPT: if (statusType == ProvisioningStatusType.SUCCESS) { e.setScriptSuccess(e.getScriptSuccess() + count); } else { e.setScriptFailure(e.getScriptFailure() + count); } break; case ICF_GET_LATEST_SYNC_TOKEN: case ICF_GET_SCHEMA: if (statusType == ProvisioningStatusType.SUCCESS) { e.setOtherSuccess(e.getOtherSuccess() + count); } else { e.setOtherFailure(e.getOtherFailure() + count); } break; default: throw new IllegalArgumentException("Illegal operation name: " + operation); } if (e.getMinTime() == null || min < e.getMinTime()) { e.setMinTime(min); } if (e.getMaxTime() == null || max > e.getMaxTime()) { e.setMaxTime(max); } e.setTotalTime(e.getTotalTime() + totalDuration); } private void setValueNotifications(NotificationsStatisticsEntryType e, boolean success, int count, long min, long max, long totalDuration) { if (success) { e.setCountSuccess(e.getCountSuccess() + count); } else { e.setCountFailure(e.getCountFailure() + count); } if (e.getMinTime() == null || min < e.getMinTime()) { e.setMinTime(min); } if (e.getMaxTime() == null || max > e.getMaxTime()) { e.setMaxTime(max); } e.setTotalTime(e.getTotalTime() + totalDuration); } private void setValueMapping(MappingsStatisticsEntryType e, int count, long min, long max, long totalDuration) { e.setCount(e.getCount() + count); if (e.getMinTime() == null || min < e.getMinTime()) { e.setMinTime(min); } if (e.getMaxTime() == null || max > e.getMaxTime()) { e.setMaxTime(max); } e.setTotalTime(e.getTotalTime() + totalDuration); } private EnvironmentalPerformanceInformationType aggregate(EnvironmentalPerformanceInformationType startValue, EnvironmentalPerformanceInformationType delta) { if (startValue == null) { return delta; } EnvironmentalPerformanceInformationType rv = new EnvironmentalPerformanceInformationType(); addTo(rv, startValue); addTo(rv, delta); return rv; } public static void addTo(EnvironmentalPerformanceInformationType rv, EnvironmentalPerformanceInformationType delta) { addProvisioningTo(rv, delta.getProvisioningStatistics()); addMappingsTo(rv, delta.getMappingsStatistics()); addNotificationsTo(rv, delta.getNotificationsStatistics()); if (delta.getLastMessageTimestamp() != null) { if (rv.getLastMessageTimestamp() == null || rv.getLastMessageTimestamp().compare(delta.getLastMessageTimestamp()) == DatatypeConstants.LESSER) { rv.setLastMessageTimestamp(delta.getLastMessageTimestamp()); rv.setLastMessage(delta.getLastMessage()); } } } private static void addNotificationsTo(EnvironmentalPerformanceInformationType rv, NotificationsStatisticsType delta) { if (delta == null) { return; } if (rv.getNotificationsStatistics() == null) { rv.setNotificationsStatistics(delta.clone()); return; } NotificationsStatisticsType rvNST = rv.getNotificationsStatistics(); for (NotificationsStatisticsEntryType de : delta.getEntry()) { String transport = de.getTransport(); NotificationsStatisticsEntryType e = findNotificationsEntryType(rvNST.getEntry(), transport); if (e == null) { e = new NotificationsStatisticsEntryType(); e.setTransport(transport); rvNST.getEntry().add(e); } e.setCountSuccess(e.getCountSuccess() + de.getCountSuccess()); e.setCountFailure(e.getCountFailure() + de.getCountFailure()); int count = e.getCountSuccess() + e.getCountFailure(); e.setMinTime(min(e.getMinTime(), de.getMinTime())); e.setMaxTime(max(e.getMaxTime(), de.getMaxTime())); e.setTotalTime(e.getTotalTime() + de.getTotalTime()); if (count > 0) { e.setAverageTime(e.getTotalTime() / count); } else { e.setAverageTime(null); } } } private static NotificationsStatisticsEntryType findNotificationsEntryType(List<NotificationsStatisticsEntryType> list, String transport) { for (NotificationsStatisticsEntryType entry : list) { if (StringUtils.equals(entry.getTransport(), transport)) { return entry; } } return null; } private static void addMappingsTo(EnvironmentalPerformanceInformationType rv, MappingsStatisticsType delta) { if (delta == null) { return; } if (rv.getMappingsStatistics() == null) { rv.setMappingsStatistics(delta.clone()); return; } MappingsStatisticsType rvMST = rv.getMappingsStatistics(); for (MappingsStatisticsEntryType de : delta.getEntry()) { String object = de.getObject(); MappingsStatisticsEntryType e = findMappingsEntryType(rvMST.getEntry(), object); if (e == null) { e = new MappingsStatisticsEntryType(); e.setObject(object); rvMST.getEntry().add(e); } e.setCount(e.getCount() + de.getCount()); e.setMinTime(min(e.getMinTime(), de.getMinTime())); e.setMaxTime(max(e.getMaxTime(), de.getMaxTime())); e.setTotalTime(e.getTotalTime() + de.getTotalTime()); if (e.getCount() > 0) { e.setAverageTime(e.getTotalTime() / e.getCount()); } else { e.setAverageTime(null); } } } private static MappingsStatisticsEntryType findMappingsEntryType(List<MappingsStatisticsEntryType> list, String object) { for (MappingsStatisticsEntryType lineDto : list) { if (StringUtils.equals(lineDto.getObject(), object)) { return lineDto; } } return null; } private static void addProvisioningTo(EnvironmentalPerformanceInformationType rv, ProvisioningStatisticsType delta) { if (delta == null) { return; } if (rv.getProvisioningStatistics() == null) { rv.setProvisioningStatistics(delta.clone()); return; } ProvisioningStatisticsType rvPST = rv.getProvisioningStatistics(); for (ProvisioningStatisticsEntryType de : delta.getEntry()) { String resource = de.getResource(); QName oc = de.getObjectClass(); ProvisioningStatisticsEntryType e = findProvisioningEntryType(rvPST.getEntry(), resource, oc); if (e == null) { e = new ProvisioningStatisticsEntryType(); e.setResource(resource); e.setObjectClass(oc); rvPST.getEntry().add(e); } e.setGetSuccess(e.getGetSuccess() + de.getGetSuccess()); e.setSearchSuccess(e.getSearchSuccess() + de.getSearchSuccess()); e.setCreateSuccess(e.getCreateSuccess() + de.getCreateSuccess()); e.setUpdateSuccess(e.getUpdateSuccess() + de.getUpdateSuccess()); e.setDeleteSuccess(e.getDeleteSuccess() + de.getDeleteSuccess()); e.setSyncSuccess(e.getSyncSuccess() + de.getSyncSuccess()); e.setScriptSuccess(e.getScriptSuccess() + de.getScriptSuccess()); e.setOtherSuccess(e.getOtherSuccess() + de.getOtherSuccess()); e.setGetFailure(e.getGetFailure() + de.getGetFailure()); e.setSearchFailure(e.getSearchFailure() + de.getSearchFailure()); e.setCreateFailure(e.getCreateFailure() + de.getCreateFailure()); e.setUpdateFailure(e.getUpdateFailure() + de.getUpdateFailure()); e.setDeleteFailure(e.getDeleteFailure() + de.getDeleteFailure()); e.setSyncFailure(e.getSyncFailure() + de.getSyncFailure()); e.setScriptFailure(e.getScriptFailure() + de.getScriptFailure()); e.setOtherFailure(e.getOtherFailure() + de.getOtherFailure()); int totalCount = e.getGetSuccess() + e.getGetFailure() + e.getSearchSuccess() + e.getSearchFailure() + e.getCreateSuccess() + e.getCreateFailure() + e.getUpdateSuccess() + e.getUpdateFailure() + e.getDeleteSuccess() + e.getDeleteFailure() + e.getSyncSuccess() + e.getSyncFailure() + e.getScriptSuccess() + e.getScriptFailure() + e.getOtherSuccess() + e.getOtherFailure(); e.setMinTime(min(e.getMinTime(), de.getMinTime())); e.setMaxTime(max(e.getMaxTime(), de.getMaxTime())); e.setTotalTime(e.getTotalTime() + de.getTotalTime()); if (totalCount > 0) { e.setAverageTime(e.getTotalTime() / totalCount); } else { e.setAverageTime(null); } } } private static Long min(Long a, Long b) { if (a == null) { return b; } if (b == null) { return a; } return Math.min(a, b); } private static Long max(Long a, Long b) { if (a == null) { return b; } if (b == null) { return a; } return Math.max(a, b); } public synchronized void recordProvisioningOperation(String resourceOid, String resourceName, QName objectClassName, ProvisioningOperation operation, boolean success, int count, long duration) { ProvisioningStatisticsKey key = new ProvisioningStatisticsKey(resourceOid, resourceName, objectClassName, operation, success); ProvisioningStatisticsData data = provisioningData.get(key); if (data == null) { data = new ProvisioningStatisticsData(); provisioningData.put(key, data); } data.recordOperation(duration, count); } public synchronized void recordNotificationOperation(String transportName, boolean success, long duration) { NotificationsStatisticsKey key = new NotificationsStatisticsKey(transportName, success); GenericStatisticsData data = notificationsData.get(key); if (data == null) { data = new GenericStatisticsData(); notificationsData.put(key, data); } data.recordOperation(duration, 1); } public synchronized void recordMappingOperation(String objectOid, String objectName, String objectTypeName, String mappingName, long duration) { // ignoring mapping name for now MappingsStatisticsKey key = new MappingsStatisticsKey(objectOid, objectName, objectTypeName); GenericStatisticsData data = mappingsData.get(key); if (data == null) { data = new GenericStatisticsData(); mappingsData.put(key, data); } data.recordOperation(duration, 1); } public synchronized StatusMessage getLastMessage() { return lastMessage; } public synchronized void recordState(String message) { lastMessage = new StatusMessage(message); } }