Version 1.1: Added support for web timelines.
Web timelines as defined by the JDB Labs Web TimeStamper tool are now supported. * `TimelineSource` now also takes the timeline config file as constructor input. * Refactored `Timeline` to use reduce implementation redundencies: internal implementations do not duplicate functionality. * Improved comments in `TimelineProperties`. * Updated `TimelineSourceFactory` to handle timelines that need to use `JDBLabsWebTimelineSource`.
This commit is contained in:
parent
c061ea6c1f
commit
3ca6909b95
BIN
lib/compile/jar/commons-beanutils-1.8.0.jar
Normal file
BIN
lib/compile/jar/commons-beanutils-1.8.0.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/commons-codec-1.4.jar
Normal file
BIN
lib/compile/jar/commons-codec-1.4.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/http-builder-0.5.1.jar
Normal file
BIN
lib/compile/jar/http-builder-0.5.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/httpclient-4.1.1.jar
Normal file
BIN
lib/compile/jar/httpclient-4.1.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/httpcore-4.1.jar
Normal file
BIN
lib/compile/jar/httpcore-4.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/jcl-over-slf4j-1.6.1.jar
Normal file
BIN
lib/compile/jar/jcl-over-slf4j-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/compile/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/commons-beanutils-1.8.0.jar
Normal file
BIN
lib/runtime/jar/commons-beanutils-1.8.0.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/commons-codec-1.4.jar
Normal file
BIN
lib/runtime/jar/commons-codec-1.4.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/commons-collections-3.2.1.jar
Normal file
BIN
lib/runtime/jar/commons-collections-3.2.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/commons-lang-2.4.jar
Normal file
BIN
lib/runtime/jar/commons-lang-2.4.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/ezmorph-1.0.6.jar
Normal file
BIN
lib/runtime/jar/ezmorph-1.0.6.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/http-builder-0.5.1.jar
Normal file
BIN
lib/runtime/jar/http-builder-0.5.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/httpclient-4.1.1.jar
Normal file
BIN
lib/runtime/jar/httpclient-4.1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/httpclient-cache-4.1.1.jar
Normal file
BIN
lib/runtime/jar/httpclient-cache-4.1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/httpcore-4.1.jar
Normal file
BIN
lib/runtime/jar/httpcore-4.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/httpmime-4.1.1.jar
Normal file
BIN
lib/runtime/jar/httpmime-4.1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jcl-over-slf4j-1.6.1.jar
Normal file
BIN
lib/runtime/jar/jcl-over-slf4j-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/jdb-util-1.1.jar
Normal file
BIN
lib/runtime/jar/jdb-util-1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/json-lib-2.3-jdk15.jar
Normal file
BIN
lib/runtime/jar/json-lib-2.3-jdk15.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-classic-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
BIN
lib/runtime/jar/logback-core-0.9.26.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/nekohtml-1.9.9.jar
Normal file
BIN
lib/runtime/jar/nekohtml-1.9.9.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/signpost-commonshttp4-1.2.1.1.jar
Normal file
BIN
lib/runtime/jar/signpost-commonshttp4-1.2.1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/signpost-core-1.2.1.1.jar
Normal file
BIN
lib/runtime/jar/signpost-core-1.2.1.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
BIN
lib/runtime/jar/slf4j-api-1.6.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/xercesImpl-2.8.1.jar
Normal file
BIN
lib/runtime/jar/xercesImpl-2.8.1.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/xml-apis-1.3.04.jar
Normal file
BIN
lib/runtime/jar/xml-apis-1.3.04.jar
Normal file
Binary file not shown.
BIN
lib/runtime/jar/xml-resolver-1.2.jar
Normal file
BIN
lib/runtime/jar/xml-resolver-1.2.jar
Normal file
Binary file not shown.
@ -1,6 +1,6 @@
|
|||||||
#Thu, 16 Jun 2011 12:41:01 -0500
|
#Mon, 27 Jun 2011 17:12:00 -0500
|
||||||
name=timestamper-lib
|
name=timestamper-lib
|
||||||
version=1.0
|
version=1.1
|
||||||
lib.local=true
|
lib.local=true
|
||||||
|
|
||||||
build.number=2
|
build.number=1
|
||||||
|
Binary file not shown.
BIN
release/timestamper-lib-1.1.jar
Normal file
BIN
release/timestamper-lib-1.1.jar
Normal file
Binary file not shown.
12
resources/main/test.groovy
Normal file
12
resources/main/test.groovy
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import groovyx.net.http.HTTPBuilder
|
||||||
|
import com.jdbernard.util.SmartConfig
|
||||||
|
import com.jdblabs.timestamper.core.*
|
||||||
|
|
||||||
|
smartConfig = new SmartConfig("temp.config")
|
||||||
|
smartConfig."web.username" = "jdbernard"
|
||||||
|
smartConfig."web.password" = "Y0uthc"
|
||||||
|
smartConfig."web.timelineId" = "work"
|
||||||
|
|
||||||
|
uri = new URI("http://timestamper-local:8000")
|
||||||
|
|
||||||
|
wtls = new JDBLabsWebTimelineSource(uri, smartConfig)
|
@ -1,5 +1,6 @@
|
|||||||
package com.jdblabs.timestamper.core;
|
package com.jdblabs.timestamper.core;
|
||||||
|
|
||||||
|
import com.jdbernard.util.SmartConfig;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@ -15,7 +16,7 @@ public class FileTimelineSource extends TimelineSource {
|
|||||||
|
|
||||||
private File file;
|
private File file;
|
||||||
|
|
||||||
public FileTimelineSource(URI uri) {
|
public FileTimelineSource(URI uri, SmartConfig config) {
|
||||||
super(uri);
|
super(uri);
|
||||||
this.file = new File(uri);
|
this.file = new File(uri);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,165 @@
|
|||||||
|
package com.jdblabs.timestamper.core
|
||||||
|
|
||||||
|
import com.jdbernard.util.SmartConfig
|
||||||
|
import groovyx.net.http.HTTPBuilder
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
|
import static groovyx.net.http.ContentType.*
|
||||||
|
import static groovyx.net.http.Method.*
|
||||||
|
|
||||||
|
public class JDBLabsWebTimelineSource extends TimelineSource {
|
||||||
|
|
||||||
|
private static final String CONFIG_UNAME = "web.username"
|
||||||
|
private static final String CONFIG_PWD = "web.password"
|
||||||
|
private static final String CONFIG_TIMELINE = "web.timelineId"
|
||||||
|
|
||||||
|
private static isoDateFormat
|
||||||
|
|
||||||
|
static {
|
||||||
|
isoDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
||||||
|
isoDateFormat.timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
}
|
||||||
|
|
||||||
|
private String username
|
||||||
|
private String password
|
||||||
|
private String timelineId
|
||||||
|
|
||||||
|
private URI baseUri
|
||||||
|
private HTTPBuilder http
|
||||||
|
|
||||||
|
private Map entryHashes
|
||||||
|
|
||||||
|
public JDBLabsWebTimelineSource(URI uri, SmartConfig config) {
|
||||||
|
super(uri)
|
||||||
|
|
||||||
|
// load web-timeline specific properties
|
||||||
|
username = config.getProperty(CONFIG_UNAME, "")
|
||||||
|
password = config.getProperty(CONFIG_PWD, "")
|
||||||
|
timelineId = config.getProperty(CONFIG_TIMELINE, "")
|
||||||
|
|
||||||
|
baseUri = new URI(uri.scheme + "://" + uri.authority)
|
||||||
|
http = new HTTPBuilder(baseUri)
|
||||||
|
|
||||||
|
entryHashes = [:]
|
||||||
|
}
|
||||||
|
|
||||||
|
public Timeline read() {
|
||||||
|
def timelineJSON
|
||||||
|
def entryListJSON
|
||||||
|
def startDate, endDate
|
||||||
|
Timeline timeline
|
||||||
|
|
||||||
|
// make sure we have a fresh session
|
||||||
|
authenticate()
|
||||||
|
|
||||||
|
// load the timeline information
|
||||||
|
timelineJSON = http.get(
|
||||||
|
path: "/ts_api/timelines/${username}/${timelineId}",
|
||||||
|
contentType: JSON) { resp, json -> json }
|
||||||
|
|
||||||
|
timeline = new Timeline()
|
||||||
|
|
||||||
|
// create start and end times
|
||||||
|
startDate = Calendar.getInstance()
|
||||||
|
startDate.timeInMillis = 0
|
||||||
|
startDate = isoDateFormat.format(startDate.time)
|
||||||
|
|
||||||
|
endDate = isoDateFormat.format(Calendar.getInstance().time)
|
||||||
|
|
||||||
|
// load the timeline entries
|
||||||
|
entryListJSON = http.get(
|
||||||
|
path: "/ts_api/entries/${username}/${timelineId}",
|
||||||
|
contentType: JSON,
|
||||||
|
query: [
|
||||||
|
byDate: true,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate ] ) { resp, json -> json }
|
||||||
|
|
||||||
|
entryListJSON.each { entry ->
|
||||||
|
|
||||||
|
// parse and create the timeline marker
|
||||||
|
def timestamp = isoDateFormat.parse(entry.timestamp)
|
||||||
|
def marker = new TimelineMarker(timestamp, entry.mark, entry.notes)
|
||||||
|
|
||||||
|
// add it to the timeline and our map of hashes
|
||||||
|
timeline.addMarker(marker)
|
||||||
|
entryHashes[fullHash(marker)] = entry.id
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the created timeline
|
||||||
|
return timeline
|
||||||
|
}
|
||||||
|
|
||||||
|
public void persist(Timeline t) {
|
||||||
|
Map deletedEntries
|
||||||
|
List newEntries
|
||||||
|
|
||||||
|
// make sure we have a fresh session
|
||||||
|
authenticate()
|
||||||
|
|
||||||
|
// find differences since we last persisted
|
||||||
|
deletedEntries = entryHashes.clone() // shallow copy
|
||||||
|
newEntries = []
|
||||||
|
|
||||||
|
t.each { marker ->
|
||||||
|
def hash = fullHash(marker)
|
||||||
|
|
||||||
|
// this marker is present, remove from deletedEntries
|
||||||
|
if (deletedEntries.containsKey(hash)) {
|
||||||
|
deletedEntries.remove(hash) }
|
||||||
|
|
||||||
|
// this marker is not present, add to newEntries
|
||||||
|
else { newEntries << marker }
|
||||||
|
}
|
||||||
|
|
||||||
|
// delete all entries that used to be present but are not any longer
|
||||||
|
deletedEntries.each { hash, entryId ->
|
||||||
|
http.request(DELETE) {
|
||||||
|
uri.path = "/ts_api/entries/${username}/${timelineId}/${entryId}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: error handling, make sure this only happens on success
|
||||||
|
entryHashes.remove(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all new entries
|
||||||
|
newEntries.each { entry ->
|
||||||
|
|
||||||
|
def entryBody = [
|
||||||
|
mark: entry.mark,
|
||||||
|
notes: entry.notes,
|
||||||
|
timestamp: isoDateFormat.format(entry.timestamp)
|
||||||
|
]
|
||||||
|
|
||||||
|
// TODO: error handling
|
||||||
|
http.post(
|
||||||
|
path: "/ts_api/entries/${username}/${timelineId}",
|
||||||
|
contentType: JSON,
|
||||||
|
requestContentType: JSON,
|
||||||
|
body: entryBody) { resp, json ->
|
||||||
|
entryHashes.put(fullHash(entry), entry)
|
||||||
|
json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAuthenticated() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticate() {
|
||||||
|
// TODO: error detection
|
||||||
|
http.post(
|
||||||
|
path: '/ts_api/login',
|
||||||
|
contentType: JSON,
|
||||||
|
requestContentType: JSON,
|
||||||
|
body: [ username: this.username,
|
||||||
|
password: this.password ]) { resp, json -> json }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int fullHash(TimelineMarker tm) {
|
||||||
|
return 61 * tm.hashCode() + (tm.mark != null ? tm.mark.hashCode() : 0);
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,7 @@ public class Timeline implements Iterable<TimelineMarker> {
|
|||||||
|
|
||||||
public static SimpleDateFormat shortFormat = new SimpleDateFormat("HH:mm:ss");
|
public static SimpleDateFormat shortFormat = new SimpleDateFormat("HH:mm:ss");
|
||||||
public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
|
public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy");
|
||||||
private TreeSet<TimelineMarker> timelineList;
|
protected TreeSet<TimelineMarker> timelineList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new, empty Timeline.
|
* Create a new, empty Timeline.
|
||||||
@ -42,7 +42,7 @@ public class Timeline implements Iterable<TimelineMarker> {
|
|||||||
* @return <code>true</code> if this Timeline was modified.
|
* @return <code>true</code> if this Timeline was modified.
|
||||||
*/
|
*/
|
||||||
public boolean addMarker(Date timestamp, String name, String notes) {
|
public boolean addMarker(Date timestamp, String name, String notes) {
|
||||||
return timelineList.add(new TimelineMarker(timestamp, name, notes));
|
return addMarker(new TimelineMarker(timestamp, name, notes));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,11 +107,7 @@ public class Timeline implements Iterable<TimelineMarker> {
|
|||||||
public boolean addAll(Timeline t) {
|
public boolean addAll(Timeline t) {
|
||||||
boolean modified = false;
|
boolean modified = false;
|
||||||
for (TimelineMarker tm : t) {
|
for (TimelineMarker tm : t) {
|
||||||
if (!timelineList.contains(tm)) {
|
modified = addMarker(tm) || modified; }
|
||||||
timelineList.add(tm);
|
|
||||||
modified = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return modified;
|
return modified;
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ public class TimelineProperties {
|
|||||||
URI timelineURI = timelineFile.toURI();
|
URI timelineURI = timelineFile.toURI();
|
||||||
|
|
||||||
timeline = new Timeline();
|
timeline = new Timeline();
|
||||||
timelineSource = TimelineSourceFactory.newInstance(timelineURI);
|
timelineSource = TimelineSourceFactory.newInstance(timelineURI, config);
|
||||||
persistOnUpdate = true;
|
persistOnUpdate = true;
|
||||||
try { timelineSource.persist(timeline); }
|
try { timelineSource.persist(timeline); }
|
||||||
catch (IOException ioe) {
|
catch (IOException ioe) {
|
||||||
@ -84,8 +84,8 @@ public class TimelineProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load TimelineProperties from an InputStream.
|
* Load TimelineProperties from a config file.
|
||||||
* @param is
|
* @param propFile
|
||||||
*/
|
*/
|
||||||
public TimelineProperties(File propFile) throws IOException {
|
public TimelineProperties(File propFile) throws IOException {
|
||||||
String strURI;
|
String strURI;
|
||||||
@ -97,8 +97,10 @@ public class TimelineProperties {
|
|||||||
persistOnUpdate = (Boolean) config.getProperty(
|
persistOnUpdate = (Boolean) config.getProperty(
|
||||||
LOCAL_TIMELINE_PERSIST_ON_UPDATE, true);
|
LOCAL_TIMELINE_PERSIST_ON_UPDATE, true);
|
||||||
|
|
||||||
// load local timeline
|
// get the URI for the primary timeline
|
||||||
strURI = (String) config.getProperty(LOCAL_TIMELINE_URI, "");
|
strURI = (String) config.getProperty(LOCAL_TIMELINE_URI, "");
|
||||||
|
|
||||||
|
// no primary timeline, default to file-based timeline
|
||||||
if ("".equals(strURI)) {
|
if ("".equals(strURI)) {
|
||||||
File defaultTimelineFile = new File("timeline.default.txt");
|
File defaultTimelineFile = new File("timeline.default.txt");
|
||||||
try {
|
try {
|
||||||
@ -108,7 +110,7 @@ public class TimelineProperties {
|
|||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
timelineURI = defaultTimelineFile.toURI();
|
timelineURI = defaultTimelineFile.toURI();
|
||||||
} else {
|
} else { // we do have a URI
|
||||||
try { timelineURI = new URI(strURI); }
|
try { timelineURI = new URI(strURI); }
|
||||||
catch (URISyntaxException urise) {
|
catch (URISyntaxException urise) {
|
||||||
throw new IOException("Unable to load the timeline: the timeline "
|
throw new IOException("Unable to load the timeline: the timeline "
|
||||||
@ -116,7 +118,8 @@ public class TimelineProperties {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
timelineSource = TimelineSourceFactory.newInstance(timelineURI);
|
// create our timeline source and read the timeline
|
||||||
|
timelineSource = TimelineSourceFactory.newInstance(timelineURI, config);
|
||||||
timeline = timelineSource.read();
|
timeline = timelineSource.read();
|
||||||
|
|
||||||
// search keys for remote timeline entries
|
// search keys for remote timeline entries
|
||||||
@ -143,7 +146,7 @@ public class TimelineProperties {
|
|||||||
|
|
||||||
// add a new SyncTarget to the list
|
// add a new SyncTarget to the list
|
||||||
st = new SyncTarget(stName, TimelineSourceFactory
|
st = new SyncTarget(stName, TimelineSourceFactory
|
||||||
.newInstance(timelineURI), timeline);
|
.newInstance(timelineURI, config), timeline);
|
||||||
syncTargets.add(st);
|
syncTargets.add(st);
|
||||||
|
|
||||||
// check for synch options
|
// check for synch options
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jdblabs.timestamper.core;
|
package com.jdblabs.timestamper.core;
|
||||||
|
|
||||||
|
import com.jdbernard.util.SmartConfig;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
@ -9,10 +10,10 @@ import java.net.URI;
|
|||||||
*/
|
*/
|
||||||
public class TimelineSourceFactory {
|
public class TimelineSourceFactory {
|
||||||
|
|
||||||
public static TimelineSource newInstance(URI uri) {
|
public static TimelineSource newInstance(URI uri, SmartConfig config) {
|
||||||
// File based
|
// File based
|
||||||
if ("file".equalsIgnoreCase(uri.getScheme())) {
|
if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
return new FileTimelineSource(uri);
|
return new FileTimelineSource(uri, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Twitter
|
// Twitter
|
||||||
@ -23,6 +24,11 @@ public class TimelineSourceFactory {
|
|||||||
+ "sources are not yet supported.");
|
+ "sources are not yet supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Other HTTP, assume JDB Labs web app
|
||||||
|
else if ("http".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
return new JDBLabsWebTimelineSource(uri, config);
|
||||||
|
}
|
||||||
|
|
||||||
// SSH
|
// SSH
|
||||||
else if ("ssh".equalsIgnoreCase(uri.getScheme())) {
|
else if ("ssh".equalsIgnoreCase(uri.getScheme())) {
|
||||||
throw new UnsupportedOperationException("SSH based timeline sources"
|
throw new UnsupportedOperationException("SSH based timeline sources"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.jdblabs.timestamper.core;
|
package com.jdblabs.timestamper.core;
|
||||||
|
|
||||||
|
import com.jdbernard.util.SmartConfig;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user