package de.persosim.simulator.processing; import static org.globaltester.logging.BasicLogger.TRACE; import static org.globaltester.logging.BasicLogger.WARN; import static org.globaltester.logging.BasicLogger.log; import java.util.HashMap; import java.util.LinkedList; import org.globaltester.logging.InfoSource; import de.persosim.simulator.apdu.CommandApdu; import de.persosim.simulator.apdu.ResponseApdu; import de.persosim.simulator.platform.Iso7816; import de.persosim.simulator.platform.PlatformUtil; /** * The primary purpose of this class is to store data about an APDU which * is accumulated while processing it. * * Every entity that is involved during processing this APDU can use this to * exchange UpdatePropagation instances with other involved entities. * * @author amay * @author slutters */ public class ProcessingData implements Iso7816, InfoSource { protected CommandApdu commandApdu; protected ResponseApdu responseApdu; protected HashMap<Class<? extends UpdatePropagation>, LinkedList<UpdatePropagation>> updatePropagations = new HashMap<>(); protected LinkedList<ProcessingStateUpdate> processingHistory = new LinkedList<>(); /*--------------------------------------------------------------------------------*/ /* Variables concerning APDU processing status */ /*--------------------------------------------------------------------------------*/ public boolean isReportingError() { if (responseApdu != null) { return responseApdu.isReportingError(); } return false; } /*--------------------------------------------------------------------------------*/ public CommandApdu getCommandApdu() { return commandApdu; } public ResponseApdu getResponseApdu() { return responseApdu; } /** * Incorporates the given updates into the current state of this object. * UpdatePropagations are added to their respective lists, command and * response-APDUs are updated with the new value. * * @param source * Source that initiated this update * @param message * User readable message shown in log * @param update * deltas that contain the respective update specification */ public void updateProcessingState(InfoSource source, String message, ProcessingStateDelta... update) { //log modifications accordingly log(source, "Update processing state with " + update.length + " deltas.", TRACE); log(source, "Update message\n" + message, TRACE); for (ProcessingStateDelta curStateDelta : update) { if (curStateDelta != null && curStateDelta.getNrOfModifications() > 0) { // add to state history processingHistory.add(new ProcessingStateUpdate(source, message, curStateDelta)); //log modifications accordingly log(source, curStateDelta.toString(), TRACE); // update command APDU if present if (curStateDelta.getCommandApdu() != null) { //check that current commandApdu is part of history of new commandApdu CommandApdu curPredecessor = curStateDelta.getCommandApdu(); while (curPredecessor != null) { if (curPredecessor == commandApdu) { break; } curPredecessor = curPredecessor.getPredecessor(); } if (curPredecessor != commandApdu) { throw new IllegalArgumentException("New CommandApdu must have current CommandApdu in its line of predecessors"); } this.commandApdu = curStateDelta.getCommandApdu(); log(source, "Command APDU updated\n" + commandApdu, TRACE); } // update response APDU if present if (curStateDelta.getResponseApdu() != null) { this.responseApdu = curStateDelta.getResponseApdu(); log(source, "Response APDU updated\n" + responseApdu + "\nreason is: " + message, TRACE); } // update updatePropagations if present if (curStateDelta.getUpdatePropagations() != null) { HashMap<Class<? extends UpdatePropagation>, UpdatePropagation> newPropagations = curStateDelta.getUpdatePropagations(); for (Class<? extends UpdatePropagation> curKey : newPropagations.keySet()) { LinkedList<UpdatePropagation> curPropagations = this.updatePropagations.get(curKey); // create new list and add it to the HashMap if needed if (curPropagations == null) { curPropagations = new LinkedList<>(); this.updatePropagations.put(curKey, curPropagations); } //skip this propagation if type does not math the curKey UpdatePropagation curNewProp = newPropagations.get(curKey); if (curKey.isInstance(curNewProp)) { // add current new propagation to the list curPropagations.add(curNewProp); } else { log(this, "Skipping one UpdatePropagation, as type does not match key", WARN); } } } } } } public boolean isProcessingFinished() { return (responseApdu != null && !PlatformUtil.is4xxxStatusWord(responseApdu.getStatusWord())) ; } public void updateCommandApdu(InfoSource source, String message, CommandApdu commandApdu) { updateProcessingState(source, message, new ProcessingStateDelta(commandApdu)); } public void updateResponseAPDU(InfoSource source, String message, ResponseApdu respApdu) { updateProcessingState(source, message, new ProcessingStateDelta(respApdu)); } @Override public String getIDString() { return "ProcessingData"; } /** * Return the list of UpdatePropagations for the given key. * * The List as well as all its elements should be regarded as immutable and * thus not be modified. Implementation might change to enforce this * behavior for security reasons. If you need to modify this List you can * provide new UpdatePropagations through * {@link #addUpdatePropagation(InfoSource, String, UpdatePropagation) * addProtocolUpdate()} or { * {@link #updateProcessingState(InfoSource, String, ProcessingStateDelta...) * updateProcessingState} method. * * * @param key * Class for which UpdatePrpopagations are requested * @return possibly empty List, but never null */ public LinkedList<UpdatePropagation> getUpdatePropagations( Class<? extends UpdatePropagation> key) { LinkedList<UpdatePropagation> retVal = updatePropagations.get(key); //ensure reVal is not null if (retVal == null) { retVal = new LinkedList<>(); } return retVal; } /** * Convenience method to add a new UpdatePropagation to the ProcessingData. * * The provided UpatePropagation is added under the key returned by its * getKey()-method. If adding under a different key is desired you need to * build a custom PocessingStateDelta and provide it through * {@link #updateProcessingState(InfoSource, String, ProcessingStateDelta...) updateProcessingState} method. * * @param source * Source that initiated this update * @param message * User readable message shown in log * @param updatePropagation * UpdatePropagation that shall be added */ public void addUpdatePropagation(InfoSource source, String message, UpdatePropagation updatePropagation) { updateProcessingState(source, message, new ProcessingStateDelta(updatePropagation)); } }