/**
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012 ForgeRock AS. All Rights Reserved
*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://forgerock.org/license/CDDLv1.0.html
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at http://forgerock.org/license/CDDLv1.0.html
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
*/
package org.forgerock.openidm.sync.impl;
//import java.text.SimpleDateFormat;
import org.forgerock.openidm.sync.ReconAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicLong;
/**
* Holds the (source/target) Phase specific statistics
*
*/
public class PhaseStatistic {
public enum Phase { SOURCE, TARGET }
private ReconciliationStatistic parentStat;
Phase phase;
private String name;
private Map<Situation, List<String>> ids = Collections.synchronizedMap(new EnumMap<Situation, List<String>>(Situation.class));
private AtomicLong processedEntries = new AtomicLong();
private List<String> notValid;
long queryStartTime;
long queryEndTime;
long phaseStartTime;
long phaseEndTime;
public PhaseStatistic(ReconciliationStatistic parentStat, Phase phase, String name) {
this.parentStat = parentStat;
this.phase = phase;
this.name = name;
ids.put(Situation.CONFIRMED, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.FOUND, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.ABSENT, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.AMBIGUOUS, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.MISSING, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.UNQUALIFIED, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.UNASSIGNED, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.SOURCE_MISSING, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.SOURCE_IGNORED, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.TARGET_IGNORED, Collections.synchronizedList(new ArrayList<String>()));
ids.put(Situation.FOUND_ALREADY_LINKED, Collections.synchronizedList(new ArrayList<String>()));
notValid = Collections.synchronizedList(new ArrayList<String>());
}
/**
* Handle the processed notification to update the statistics appropriately
* @param sourceId The source id processed, or null if none
* @param targetId The target id processed, or null if none
* @param linkExisted indication if the link existed before the operation
* @param linkId the link identifier, if available. For created links this may not currently be available.
* @param linkCreated indication if a new link was created during the operation
* @param situation the assessed situation
* @param action the action that was processed
*/
public void processed(String sourceId, String targetId, boolean linkExisted, String linkId, boolean linkCreated,
Situation situation, ReconAction action) {
String id;
if (phase == Phase.SOURCE) {
id = sourceId;
} else {
id = targetId;
}
parentStat.processed(sourceId, targetId, linkExisted, linkId, linkCreated, situation, action);
if (id != null) {
processedEntries.incrementAndGet();
if (situation != null) {
List<String> situationIds = ids.get(situation);
if (situationIds != null) {
situationIds.add(id); // TODO: option to not keep all results in memory
}
}
}
}
// TODO: phase out notValid and replace with source ignored, target ignored, unqualified
// situation processing
public void addNotValid(String id) {
notValid.add(id);
}
public long getProcessed() {
return processedEntries.get();
}
public Map<String, Object> asMap() {
Map<String, Object> results = new HashMap<String, Object>();
results.put("name", name);
results.put("startTime", parentStat.getFormattedTime(phaseStartTime));
results.put("endTime", parentStat.getFormattedTime(phaseEndTime));
results.put("duration", parentStat.getDuration(phaseStartTime, phaseEndTime));
results.put("entryListDuration", parentStat.getDuration(queryStartTime, queryEndTime));
results.put("processed", getProcessed());
Map<String, Object> nv = new HashMap<String, Object>();
nv.put("count", notValid.size());
nv.put("ids", notValid);
results.put("NOTVALID", nv);
int entries = 0;
for (Entry<Situation, List<String>> e : ids.entrySet()) {
Map<String, Object> res = new HashMap<String, Object>();
entries += e.getValue().size();
res.put("count", e.getValue().size());
res.put("ids", e.getValue());
results.put(e.getKey().name(), res);
}
results.put("entries", entries);
return results;
}
public void updateSummary(Map<String, Integer> simpleSummary) {
for (Entry<Situation, List<String>> e : ids.entrySet()) {
String key = e.getKey().name();
Integer existing = simpleSummary.get(key);
if (existing == null) {
existing = 0;
}
Integer updated = existing + e.getValue().size();
simpleSummary.put(key, updated);
}
}
}