Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
d720c6c645 |
@ -1,5 +1,5 @@
|
|||||||
#Sun, 22 Sep 2013 14:58:43 -0500
|
#Fri, 11 Oct 2013 18:08:21 +0000
|
||||||
name=timestamper-lib
|
name=timestamper-lib
|
||||||
version=1.5
|
version=2.0
|
||||||
lib.local=true
|
lib.local=true
|
||||||
build.number=1
|
build.number=1
|
||||||
|
@ -3,6 +3,7 @@ package com.jdblabs.timestamper.core
|
|||||||
import com.jdbernard.util.SmartConfig
|
import com.jdbernard.util.SmartConfig
|
||||||
import groovyx.net.http.HTTPBuilder
|
import groovyx.net.http.HTTPBuilder
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import static groovyx.net.http.ContentType.*
|
import static groovyx.net.http.ContentType.*
|
||||||
import static groovyx.net.http.Method.*
|
import static groovyx.net.http.Method.*
|
||||||
@ -27,7 +28,7 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
private URI baseUri
|
private URI baseUri
|
||||||
private HTTPBuilder http
|
private HTTPBuilder http
|
||||||
|
|
||||||
private Map entryHashes
|
private Map serverEntryIds
|
||||||
|
|
||||||
public JDBLabsWebTimelineSource(URI uri, SmartConfig config) {
|
public JDBLabsWebTimelineSource(URI uri, SmartConfig config) {
|
||||||
super(uri)
|
super(uri)
|
||||||
@ -50,8 +51,8 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
"communicating to the web timeline.\n" +
|
"communicating to the web timeline.\n" +
|
||||||
"${resp.statusLine}: ${json}") }
|
"${resp.statusLine}: ${json}") }
|
||||||
|
|
||||||
// init our hash of known entries
|
// init our map of known entries
|
||||||
entryHashes = [:]
|
serverEntryIds = [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
public Timeline read() {
|
public Timeline read() {
|
||||||
@ -76,11 +77,12 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
|
|
||||||
timeline = new Timeline()
|
timeline = new Timeline()
|
||||||
|
|
||||||
// create start and end times
|
// create start and end times to use as parameters to the web service
|
||||||
startDate = Calendar.getInstance()
|
startDate = Calendar.getInstance()
|
||||||
startDate.timeInMillis = 0
|
startDate.timeInMillis = 0 // Beginning of the epoch
|
||||||
startDate = isoDateFormat.format(startDate.time)
|
startDate = isoDateFormat.format(startDate.time)
|
||||||
|
|
||||||
|
// Until now
|
||||||
endDate = isoDateFormat.format(Calendar.getInstance().time)
|
endDate = isoDateFormat.format(Calendar.getInstance().time)
|
||||||
|
|
||||||
// load the timeline entries
|
// load the timeline entries
|
||||||
@ -96,11 +98,12 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
|
|
||||||
// parse and create the timeline marker
|
// parse and create the timeline marker
|
||||||
def timestamp = isoDateFormat.parse(entry.timestamp)
|
def timestamp = isoDateFormat.parse(entry.timestamp)
|
||||||
def marker = new TimelineMarker(timestamp, entry.mark, entry.notes)
|
def marker = new TimelineMarker(timestamp, entry.mark, entry.notes,
|
||||||
|
UUID.fromString(entry.uuid));
|
||||||
|
|
||||||
// add it to the timeline and our map of hashes
|
// add it to the timeline and our map of hashes
|
||||||
timeline.addMarker(marker)
|
timeline.addMarker(marker)
|
||||||
entryHashes[fullHash(marker)] = entry.id
|
serverEntryIds[marker.uuid] = entry.id
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the created timeline
|
// return the created timeline
|
||||||
@ -114,28 +117,38 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
// make sure we have a fresh session
|
// make sure we have a fresh session
|
||||||
authenticate()
|
authenticate()
|
||||||
|
|
||||||
// find differences since we last persisted
|
// Find differences since we last persisted.
|
||||||
deletedEntries = entryHashes.clone() // shallow copy
|
|
||||||
|
// We want to identify the set of entries that were previously present
|
||||||
|
// on the server but are no longer present locally and delete them from
|
||||||
|
// the server. We start with the set of all entries last seen on the
|
||||||
|
// server. We look through our local entries and if we find the entry
|
||||||
|
// locally we remove it from our set of deletion candidates.
|
||||||
|
|
||||||
|
// We will also be constructing a set of entries that we have locally
|
||||||
|
// which were not present on the server. This set of entries will be
|
||||||
|
// added to the server.
|
||||||
|
|
||||||
|
deletedEntries = serverEntryIds.clone() // shallow copy
|
||||||
newEntries = []
|
newEntries = []
|
||||||
|
|
||||||
t.each { marker ->
|
t.each { marker ->
|
||||||
def hash = fullHash(marker)
|
// This marker is present on the server and is still present
|
||||||
|
// locally, remove from deletedEntries
|
||||||
|
if (deletedEntries.containsKey(marker.uuid)) {
|
||||||
|
deletedEntries.remove(marker.uuid) }
|
||||||
|
|
||||||
// this marker is present, remove from deletedEntries
|
// This marker is not present on the server, add to newEntries.
|
||||||
if (deletedEntries.containsKey(hash)) {
|
|
||||||
deletedEntries.remove(hash) }
|
|
||||||
|
|
||||||
// this marker is not present, add to newEntries
|
|
||||||
else { newEntries << marker }
|
else { newEntries << marker }
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete all entries that used to be present but are not any longer
|
// Delete all entries that used to be present but are not any longer
|
||||||
deletedEntries.each { hash, entryId ->
|
deletedEntries.each { hash, entryId ->
|
||||||
http.request(DELETE) {
|
http.request(DELETE) {
|
||||||
uri.path = "entries/${username}/${timelineId}/${entryId}"
|
uri.path = "entries/${username}/${timelineId}/${entryId}"
|
||||||
|
|
||||||
response.'404' = { resp -> entryHashes.remove(hash) }
|
response.'404' = { resp -> serverEntryIds.remove(hash) }
|
||||||
response.success = { resp -> entryHashes.remove(hash) }
|
response.success = { resp -> serverEntryIds.remove(hash) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,6 +158,7 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
def entryBody = [
|
def entryBody = [
|
||||||
mark: entry.mark,
|
mark: entry.mark,
|
||||||
notes: entry.notes,
|
notes: entry.notes,
|
||||||
|
uuid: entry.uuid.toString(),
|
||||||
timestamp: isoDateFormat.format(entry.timestamp)
|
timestamp: isoDateFormat.format(entry.timestamp)
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -154,7 +168,7 @@ public class JDBLabsWebTimelineSource extends TimelineSource {
|
|||||||
body = entryBody
|
body = entryBody
|
||||||
|
|
||||||
response.success = { resp, json ->
|
response.success = { resp, json ->
|
||||||
entryHashes.put(fullHash(entry), json?.id ?: 0) }
|
serverEntryIds.put(entry.uuid, json?.id ?: 0) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import java.io.Writer;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@ -71,8 +72,9 @@ public class StreamBasedTimelineSource extends TimelineSource {
|
|||||||
Writer out = new OutputStreamWriter(stream);
|
Writer out = new OutputStreamWriter(stream);
|
||||||
for (TimelineMarker tm : timeline) {
|
for (TimelineMarker tm : timeline) {
|
||||||
|
|
||||||
// write timestamp
|
// write timestamp and UUID
|
||||||
out.write(Timeline.longFormat.format(tm.getTimestamp()) + "\n");
|
out.write(Timeline.longFormat.format(tm.getTimestamp()) + "," +
|
||||||
|
tm.getUuid().toString() + "\n");
|
||||||
|
|
||||||
// write mark
|
// write mark
|
||||||
String mark = tm.getMark().replace('\n', '\u0000');
|
String mark = tm.getMark().replace('\n', '\u0000');
|
||||||
@ -127,7 +129,8 @@ public class StreamBasedTimelineSource extends TimelineSource {
|
|||||||
null : new PrintWriter(commentStream);
|
null : new PrintWriter(commentStream);
|
||||||
|
|
||||||
ReadingState readingState = ReadingState.NewMarker;
|
ReadingState readingState = ReadingState.NewMarker;
|
||||||
Date d = null;
|
Date date = null;
|
||||||
|
UUID uuid = null;
|
||||||
StringBuilder mark = null;
|
StringBuilder mark = null;
|
||||||
StringBuilder notes = null;
|
StringBuilder notes = null;
|
||||||
String line;
|
String line;
|
||||||
@ -146,11 +149,24 @@ public class StreamBasedTimelineSource extends TimelineSource {
|
|||||||
switch (readingState) {
|
switch (readingState) {
|
||||||
|
|
||||||
case NewMarker:
|
case NewMarker:
|
||||||
try { d = Timeline.longFormat.parse(line); }
|
try {
|
||||||
|
String[] parts = line.split(",");
|
||||||
|
date = Timeline.longFormat.parse(parts[0]);
|
||||||
|
|
||||||
|
// If there is no UUID, we will ignore it. This allows us
|
||||||
|
// to support timeline files generated by 1.x versions of
|
||||||
|
// this library.
|
||||||
|
// TODO: Remove this check in version 3.x
|
||||||
|
if (parts.length > 1) uuid = UUID.fromString(parts[1]);
|
||||||
|
else uuid = null; }
|
||||||
catch (ParseException pe) {
|
catch (ParseException pe) {
|
||||||
throw new IOException("Error parsing timeline file at line "
|
throw new IOException("Error parsing timeline file at line "
|
||||||
+ lineNumber + ": expected a new marker date but could not parse"
|
+ lineNumber + ": expected a new marker date but could not parse"
|
||||||
+ " the date. Error: " + pe.getLocalizedMessage()); }
|
+ " the date. Error: " + pe.getLocalizedMessage()); }
|
||||||
|
catch (IllegalArgumentException iae) {
|
||||||
|
throw new IOException("Error parsing timeline file at line "
|
||||||
|
+ lineNumber + ": Expected a UUID but could not parse "
|
||||||
|
+ "the value. Details: " + iae.getLocalizedMessage()); }
|
||||||
readingState = ReadingState.StartMark;
|
readingState = ReadingState.StartMark;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -179,7 +195,13 @@ public class StreamBasedTimelineSource extends TimelineSource {
|
|||||||
case EndMarker:
|
case EndMarker:
|
||||||
String sMark = mark.toString().replace('\u0000', '\n');
|
String sMark = mark.toString().replace('\u0000', '\n');
|
||||||
String sNotes = notes.toString().replace('\u0000', '\n');
|
String sNotes = notes.toString().replace('\u0000', '\n');
|
||||||
timeline.addMarker(d, sMark, sNotes);
|
TimelineMarker marker;
|
||||||
|
|
||||||
|
// Support a missing UUID until version 3.x
|
||||||
|
if (uuid == null)
|
||||||
|
marker = new TimelineMarker(date, sMark, sNotes);
|
||||||
|
else marker = new TimelineMarker(date, sMark, sNotes, uuid);
|
||||||
|
timeline.addMarker(marker);
|
||||||
readingState = ReadingState.NewMarker; } }
|
readingState = ReadingState.NewMarker; } }
|
||||||
|
|
||||||
return timeline; }
|
return timeline; }
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.jdblabs.timestamper.core;
|
package com.jdblabs.timestamper.core;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
|
* @author Jonathan Bernard {@literal <jdbernard@gmail.com>}
|
||||||
@ -11,16 +12,23 @@ public class TimelineMarker implements Comparable<TimelineMarker> {
|
|||||||
|
|
||||||
private final Date timestamp;
|
private final Date timestamp;
|
||||||
private final String mark;
|
private final String mark;
|
||||||
|
private final UUID uuid;
|
||||||
private String notes;
|
private String notes;
|
||||||
|
|
||||||
public TimelineMarker(Date timestamp, String mark, String notes) {
|
public TimelineMarker(Date timestamp, String mark, String notes) {
|
||||||
|
this(timestamp, mark, notes, UUID.randomUUID()); }
|
||||||
|
|
||||||
|
public TimelineMarker(Date timestamp, String mark, String notes, UUID uuid) {
|
||||||
if (timestamp == null || mark == null)
|
if (timestamp == null || mark == null)
|
||||||
throw new IllegalArgumentException("Null timestamp or mark"
|
throw new IllegalArgumentException("Null timestamp or mark"
|
||||||
+ " is not permitted.");
|
+ " is not permitted.");
|
||||||
|
|
||||||
this.timestamp = timestamp;
|
// We truncate milliseconds.
|
||||||
|
this.timestamp = 1000 * (timestamp / 1000);
|
||||||
|
|
||||||
this.mark = mark;
|
this.mark = mark;
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
|
this.uuid = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getTimestamp() { return timestamp; }
|
public Date getTimestamp() { return timestamp; }
|
||||||
@ -29,12 +37,19 @@ public class TimelineMarker implements Comparable<TimelineMarker> {
|
|||||||
|
|
||||||
public String getNotes() { return notes; }
|
public String getNotes() { return notes; }
|
||||||
|
|
||||||
|
public UUID getUuid() { return uuid; }
|
||||||
|
|
||||||
public void setNotes(String notes) { this.notes = notes; }
|
public void setNotes(String notes) { this.notes = notes; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(TimelineMarker that) {
|
public int compareTo(TimelineMarker that) {
|
||||||
|
// Always greater than null
|
||||||
if (that == null) return Integer.MAX_VALUE;
|
if (that == null) return Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
// Always equal to other instances of itself (same UUID).
|
||||||
|
if (this.uuid.equals(that.uuid)) return 0;
|
||||||
|
|
||||||
|
// Check the timestamp, then the mark if the timestamps are equal.
|
||||||
int val = this.timestamp.compareTo(that.timestamp);
|
int val = this.timestamp.compareTo(that.timestamp);
|
||||||
if (val == 0) val = this.mark.compareTo(that.mark);
|
if (val == 0) val = this.mark.compareTo(that.mark);
|
||||||
|
|
||||||
@ -47,8 +62,7 @@ public class TimelineMarker implements Comparable<TimelineMarker> {
|
|||||||
if (!(o instanceof TimelineMarker)) return false;
|
if (!(o instanceof TimelineMarker)) return false;
|
||||||
|
|
||||||
TimelineMarker that = (TimelineMarker) o;
|
TimelineMarker that = (TimelineMarker) o;
|
||||||
return this.timestamp.equals(that.timestamp) &&
|
return this.uuid.equals(that.uuid);
|
||||||
this.mark.equals(that.mark);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Reference in New Issue
Block a user