/** * Replication Benchmarker * https://github.com/score-team/replication-benchmarker/ * Copyright (C) 2013 LORIA / Inria / SCORE Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 jbenchmarker.trace; import collect.VectorClock; import crdt.simulator.IncorrectTraceException; import crdt.simulator.Trace; import crdt.simulator.TraceOperation; import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; import jbenchmarker.core.SequenceOperation; import jbenchmarker.trace.json.ElementJSON; import jbenchmarker.trace.json.JSONTrace; import jbenchmarker.trace.json.VectorClockCS; import jbenchmarker.trace.json.VectorClockCSMapper; import jbenchmarker.trace.json.moulinette.DocumentJSON; import jbenchmarker.trace.json.moulinette.attributs.ElementModif; import jbenchmarker.trace.json.moulinette.attributs.TypeModif; import jbenchmarker.trace.json.moulinette.attributs.VectorClockMapper; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import crdt.simulator.TraceOperationImpl; /** * * @author urso */ public class TraceGenerator { /* * Returns a association between replica number to the ordered list of trace * operation. */ public static Map<Integer, List<TraceOperation>> historyPerReplica(List<TraceOperation> trace) { Map<Integer, List<TraceOperation>> histories = new HashMap<Integer, List<TraceOperation>>(); for (TraceOperation opt : trace) { int r = opt.getReplica(); List<TraceOperation> l = histories.get(r); if (l == null) { l = new ArrayList<TraceOperation>(); histories.put(r, l); } l.add(opt); } return histories; } /** * Parses one element into a trace operation * * @param e JDOM Element * @return the parsed Trace operation */ public static TraceOperation oneXML2OP(Element e) { int p = Integer.parseInt(e.getChildText("Position")); int r = Integer.parseInt(e.getChildText("NumReplica")); List vce = e.getChild("VectorClock").getChildren(); VectorClock v = new VectorClock(); for (Object q : vce) { Element entry = (Element) q; v.put(Integer.parseInt(entry.getChildText("Replica")), Integer.parseInt(entry.getChildText("Clock"))); } if (e.getChildText("Type").equals("Ins")) { return new TraceOperationImpl(SequenceOperation.insert(p, e.getChildText("Text")), r, v); } else { return new TraceOperationImpl(SequenceOperation.delete(p, Integer.parseInt(e.getChildText("Offset"))), r, v); } } static class XMLTrace implements Trace { int docnum; Iterator children; int size; XMLTrace(int docnum, Iterator children) { this.docnum = docnum; this.children = children; this.size = -1; } public XMLTrace(int docnum, Iterator children, int size) { this.docnum = docnum; this.children = children; this.size = size; } private static class TraceIterator implements Enumeration<TraceOperation> { Iterator children; int docnum; TraceOperation next; boolean goNext; int size; int line; private TraceIterator(int docnum, Iterator children, int size) { this.children = children; this.docnum = docnum; this.goNext = true; this.size = size; } @Override public boolean hasMoreElements() { if (line == size) { return false; } if (!children.hasNext()) { return false; } if (goNext) { Element e = (Element) children.next(); while (children.hasNext() && Integer.parseInt(e.getChildText("NumDocument")) != docnum) { e = (Element) children.next(); } if (Integer.parseInt(e.getChildText("NumDocument")) != docnum) { return false; } next = oneXML2OP(e); goNext = false; return true; } return children.hasNext(); } @Override public TraceOperation nextElement() { if (goNext && !hasMoreElements()) { throw new NoSuchElementException(); } goNext = true; line++; return next; } } @Override public Enumeration<TraceOperation> enumeration() { return new TraceIterator(docnum, children, size); } } /** * Parses one elmeent into a trace operation * * @param e JSON Element * @return the parsed Trace operation */ public static TraceOperationImpl oneJSON2OP(ElementJSON e, VectorClockCSMapper vectorClockMapper) { int pos = e.getVal().getPosition(); String ui = e.getVal().getUserId(); int repli = vectorClockMapper.userId(ui); VectorClockCS vc = e.getVal().getVector_clock(); VectorClock v = vectorClockMapper.toVectorClock(vc); if (e.getVal().getOperation().equals("insertion")) { return new TraceOperationImpl(SequenceOperation.insert( pos, e.getVal().getChars_inserted()),repli, v); } else if (e.getVal().getOperation().equals("suppression")) { return new TraceOperationImpl(SequenceOperation.delete( pos, e.getVal().getNumber_charDeleted()),repli, v); } else if (e.getVal().getOperation().equals("remplacement")) { return new TraceOperationImpl(SequenceOperation.replace( pos, e.getVal().getNumber_charDeleted(), e.getVal().getChars_inserted()),repli, v); } else {//stylage return new TraceOperationImpl(SequenceOperation.unsupported(),repli, v); } } /** * Parses one JSON elmeent from a CouchBase DataBase into a trace operation * TO DO : variable pos is wrong. It must be the * * @return the parsed Trace operation */ public static TraceOperation oneJSONDB2OP(int rep, DocumentJSON d, ElementModif e, VectorClockMapper vectorClockMapper) { int pos = d.get(e); int repli = rep; //clone VectorClock : TO TEST VectorClock v = new VectorClock(vectorClockMapper.get(rep)); v.inc(rep); //v is now the last vectorClock of replica rep vectorClockMapper.put(rep, v); if (e.getType() == TypeModif.addText) { return new TraceOperationImpl(SequenceOperation.insert( pos, e.getLigne()),repli, v); } else if (e.getType() == TypeModif.delText) { //when a line deleted, offset is the length of the line deleted return new TraceOperationImpl(SequenceOperation.delete( pos, e.getLigne().length()),repli, v); } else { return new TraceOperationImpl(SequenceOperation.unsupported(),repli, v); } } /** * Extract trace form JSON document */ public static Trace traceFromJson(String nomFichier) throws FileNotFoundException, IOException { return new JSONTrace(nomFichier); } public static Trace traceFromJson(String nomFichier, String padid) throws FileNotFoundException, IOException { return new JSONTrace(nomFichier, padid); } /** * Extract trace form XML JDOM document */ public static Trace traceFromXML(Document document, int docnum) throws JDOMException, IOException { List trace = document.getRootElement().getChild("Trace").getChildren(); return new XMLTrace(docnum, trace.iterator()); } /** * Extract trace form XML JDOM document up to size operations */ public static Trace traceFromXML(Document document, int docnum, int size) throws JDOMException, IOException { List trace = document.getRootElement().getChild("Trace").getChildren(); return new XMLTrace(docnum, trace.iterator(), size); } /** * Extract trace form XML uri. */ public static Trace traceFromXML(String uri, int docnum) throws JDOMException, IOException { return traceFromXML((new SAXBuilder()).build(uri), docnum); } /** * Extract trace form XML uri. */ public static Trace traceFromXML(String uri, int docnum, int size) throws JDOMException, IOException { return traceFromXML((new SAXBuilder()).build(uri), docnum, size); } /** * Verifies causality of an operation according to replicas VectorClock. */ public static void causalCheck(TraceOperation opt, Map<Integer, VectorClock> vcs) throws IncorrectTraceException { int r = opt.getReplica(); if (opt.getVectorClock().getSafe(r) == 0) { throw new IncorrectTraceException("Zero/no entry in VC for replica" + opt); } VectorClock clock = vcs.get(r); if (clock.getSafe(r) > opt.getVectorClock().get(r) - 1) { throw new IncorrectTraceException("Already seen clock operation " + opt); } if (clock.getSafe(r) < opt.getVectorClock().getSafe(r) - 1) { throw new IncorrectTraceException("Missing operation before " + opt); } for (int i : opt.getVectorClock().keySet()) { if ((i != r) && (opt.getVectorClock().get(i) > 0)) { if (vcs.get(i) == null) { throw new IncorrectTraceException("Missing replica " + i + " for " + opt); } if (vcs.get(i).getSafe(i) < opt.getVectorClock().get(i)) { throw new IncorrectTraceException("Missing causal operation before " + opt + " on replica " + i); } } } } }