diff --git a/lib/compile/jar/commons-beanutils-1.8.0.jar b/lib/compile/jar/commons-beanutils-1.8.0.jar new file mode 100644 index 0000000..caf7ae3 Binary files /dev/null and b/lib/compile/jar/commons-beanutils-1.8.0.jar differ diff --git a/lib/compile/jar/commons-codec-1.4.jar b/lib/compile/jar/commons-codec-1.4.jar new file mode 100644 index 0000000..458d432 Binary files /dev/null and b/lib/compile/jar/commons-codec-1.4.jar differ diff --git a/lib/compile/jar/http-builder-0.5.1.jar b/lib/compile/jar/http-builder-0.5.1.jar new file mode 100644 index 0000000..47aa035 Binary files /dev/null and b/lib/compile/jar/http-builder-0.5.1.jar differ diff --git a/lib/compile/jar/httpclient-4.1.1.jar b/lib/compile/jar/httpclient-4.1.1.jar new file mode 100644 index 0000000..c845ef9 Binary files /dev/null and b/lib/compile/jar/httpclient-4.1.1.jar differ diff --git a/lib/compile/jar/httpcore-4.1.jar b/lib/compile/jar/httpcore-4.1.jar new file mode 100644 index 0000000..a357c07 Binary files /dev/null and b/lib/compile/jar/httpcore-4.1.jar differ diff --git a/lib/compile/jar/jcl-over-slf4j-1.6.1.jar b/lib/compile/jar/jcl-over-slf4j-1.6.1.jar new file mode 100644 index 0000000..79e1ec2 Binary files /dev/null and b/lib/compile/jar/jcl-over-slf4j-1.6.1.jar differ diff --git a/lib/compile/jar/slf4j-api-1.6.1.jar b/lib/compile/jar/slf4j-api-1.6.1.jar new file mode 100644 index 0000000..42e0ad0 Binary files /dev/null and b/lib/compile/jar/slf4j-api-1.6.1.jar differ diff --git a/lib/runtime/jar/commons-beanutils-1.8.0.jar b/lib/runtime/jar/commons-beanutils-1.8.0.jar new file mode 100644 index 0000000..caf7ae3 Binary files /dev/null and b/lib/runtime/jar/commons-beanutils-1.8.0.jar differ diff --git a/lib/runtime/jar/commons-codec-1.4.jar b/lib/runtime/jar/commons-codec-1.4.jar new file mode 100644 index 0000000..458d432 Binary files /dev/null and b/lib/runtime/jar/commons-codec-1.4.jar differ diff --git a/lib/runtime/jar/commons-collections-3.2.1.jar b/lib/runtime/jar/commons-collections-3.2.1.jar new file mode 100644 index 0000000..c35fa1f Binary files /dev/null and b/lib/runtime/jar/commons-collections-3.2.1.jar differ diff --git a/lib/runtime/jar/commons-lang-2.4.jar b/lib/runtime/jar/commons-lang-2.4.jar new file mode 100644 index 0000000..532939e Binary files /dev/null and b/lib/runtime/jar/commons-lang-2.4.jar differ diff --git a/lib/runtime/jar/ezmorph-1.0.6.jar b/lib/runtime/jar/ezmorph-1.0.6.jar new file mode 100644 index 0000000..30fad12 Binary files /dev/null and b/lib/runtime/jar/ezmorph-1.0.6.jar differ diff --git a/lib/runtime/jar/http-builder-0.5.1.jar b/lib/runtime/jar/http-builder-0.5.1.jar new file mode 100644 index 0000000..47aa035 Binary files /dev/null and b/lib/runtime/jar/http-builder-0.5.1.jar differ diff --git a/lib/runtime/jar/httpclient-4.1.1.jar b/lib/runtime/jar/httpclient-4.1.1.jar new file mode 100644 index 0000000..c845ef9 Binary files /dev/null and b/lib/runtime/jar/httpclient-4.1.1.jar differ diff --git a/lib/runtime/jar/httpclient-cache-4.1.1.jar b/lib/runtime/jar/httpclient-cache-4.1.1.jar new file mode 100644 index 0000000..3e1da08 Binary files /dev/null and b/lib/runtime/jar/httpclient-cache-4.1.1.jar differ diff --git a/lib/runtime/jar/httpcore-4.1.jar b/lib/runtime/jar/httpcore-4.1.jar new file mode 100644 index 0000000..a357c07 Binary files /dev/null and b/lib/runtime/jar/httpcore-4.1.jar differ diff --git a/lib/runtime/jar/httpmime-4.1.1.jar b/lib/runtime/jar/httpmime-4.1.1.jar new file mode 100644 index 0000000..01af40b Binary files /dev/null and b/lib/runtime/jar/httpmime-4.1.1.jar differ diff --git a/lib/runtime/jar/jcl-over-slf4j-1.6.1.jar b/lib/runtime/jar/jcl-over-slf4j-1.6.1.jar new file mode 100644 index 0000000..79e1ec2 Binary files /dev/null and b/lib/runtime/jar/jcl-over-slf4j-1.6.1.jar differ diff --git a/lib/runtime/jar/jdb-util-1.1.jar b/lib/runtime/jar/jdb-util-1.1.jar new file mode 100644 index 0000000..94962af Binary files /dev/null and b/lib/runtime/jar/jdb-util-1.1.jar differ diff --git a/lib/runtime/jar/json-lib-2.3-jdk15.jar b/lib/runtime/jar/json-lib-2.3-jdk15.jar new file mode 100644 index 0000000..29d59b9 Binary files /dev/null and b/lib/runtime/jar/json-lib-2.3-jdk15.jar differ diff --git a/lib/runtime/jar/logback-classic-0.9.26.jar b/lib/runtime/jar/logback-classic-0.9.26.jar new file mode 100644 index 0000000..b900b64 Binary files /dev/null and b/lib/runtime/jar/logback-classic-0.9.26.jar differ diff --git a/lib/runtime/jar/logback-core-0.9.26.jar b/lib/runtime/jar/logback-core-0.9.26.jar new file mode 100644 index 0000000..d50f3cd Binary files /dev/null and b/lib/runtime/jar/logback-core-0.9.26.jar differ diff --git a/lib/runtime/jar/nekohtml-1.9.9.jar b/lib/runtime/jar/nekohtml-1.9.9.jar new file mode 100644 index 0000000..2e06271 Binary files /dev/null and b/lib/runtime/jar/nekohtml-1.9.9.jar differ diff --git a/lib/runtime/jar/signpost-commonshttp4-1.2.1.1.jar b/lib/runtime/jar/signpost-commonshttp4-1.2.1.1.jar new file mode 100644 index 0000000..e8dae4d Binary files /dev/null and b/lib/runtime/jar/signpost-commonshttp4-1.2.1.1.jar differ diff --git a/lib/runtime/jar/signpost-core-1.2.1.1.jar b/lib/runtime/jar/signpost-core-1.2.1.1.jar new file mode 100644 index 0000000..86e9dac Binary files /dev/null and b/lib/runtime/jar/signpost-core-1.2.1.1.jar differ diff --git a/lib/runtime/jar/slf4j-api-1.6.1.jar b/lib/runtime/jar/slf4j-api-1.6.1.jar new file mode 100644 index 0000000..42e0ad0 Binary files /dev/null and b/lib/runtime/jar/slf4j-api-1.6.1.jar differ diff --git a/lib/runtime/jar/xercesImpl-2.8.1.jar b/lib/runtime/jar/xercesImpl-2.8.1.jar new file mode 100644 index 0000000..3b351f6 Binary files /dev/null and b/lib/runtime/jar/xercesImpl-2.8.1.jar differ diff --git a/lib/runtime/jar/xml-apis-1.3.04.jar b/lib/runtime/jar/xml-apis-1.3.04.jar new file mode 100644 index 0000000..d42c0ea Binary files /dev/null and b/lib/runtime/jar/xml-apis-1.3.04.jar differ diff --git a/lib/runtime/jar/xml-resolver-1.2.jar b/lib/runtime/jar/xml-resolver-1.2.jar new file mode 100644 index 0000000..e535bdc Binary files /dev/null and b/lib/runtime/jar/xml-resolver-1.2.jar differ diff --git a/project.properties b/project.properties index 166aab9..5eaef17 100644 --- a/project.properties +++ b/project.properties @@ -1,6 +1,6 @@ -#Thu, 16 Jun 2011 12:41:01 -0500 +#Mon, 27 Jun 2011 17:12:00 -0500 name=timestamper-lib -version=1.0 +version=1.1 lib.local=true -build.number=2 +build.number=1 diff --git a/release/timestamper-lib-1.0.jar b/release/timestamper-lib-1.0.jar deleted file mode 100644 index f760bac..0000000 Binary files a/release/timestamper-lib-1.0.jar and /dev/null differ diff --git a/release/timestamper-lib-1.1.jar b/release/timestamper-lib-1.1.jar new file mode 100644 index 0000000..7aca496 Binary files /dev/null and b/release/timestamper-lib-1.1.jar differ diff --git a/resources/main/test.groovy b/resources/main/test.groovy new file mode 100644 index 0000000..2e23cc1 --- /dev/null +++ b/resources/main/test.groovy @@ -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) diff --git a/src/main/com/jdblabs/timestamper/core/FileTimelineSource.java b/src/main/com/jdblabs/timestamper/core/FileTimelineSource.java index 770c93e..db99a78 100644 --- a/src/main/com/jdblabs/timestamper/core/FileTimelineSource.java +++ b/src/main/com/jdblabs/timestamper/core/FileTimelineSource.java @@ -1,5 +1,6 @@ package com.jdblabs.timestamper.core; +import com.jdbernard.util.SmartConfig; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -15,7 +16,7 @@ public class FileTimelineSource extends TimelineSource { private File file; - public FileTimelineSource(URI uri) { + public FileTimelineSource(URI uri, SmartConfig config) { super(uri); this.file = new File(uri); } diff --git a/src/main/com/jdblabs/timestamper/core/JDBLabsWebTimelineSource.groovy b/src/main/com/jdblabs/timestamper/core/JDBLabsWebTimelineSource.groovy new file mode 100644 index 0000000..7175455 --- /dev/null +++ b/src/main/com/jdblabs/timestamper/core/JDBLabsWebTimelineSource.groovy @@ -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); + } +} diff --git a/src/main/com/jdblabs/timestamper/core/Timeline.java b/src/main/com/jdblabs/timestamper/core/Timeline.java index 7c590b4..8391a65 100644 --- a/src/main/com/jdblabs/timestamper/core/Timeline.java +++ b/src/main/com/jdblabs/timestamper/core/Timeline.java @@ -18,7 +18,7 @@ public class Timeline implements Iterable { public static SimpleDateFormat shortFormat = new SimpleDateFormat("HH:mm:ss"); public static SimpleDateFormat longFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); - private TreeSet timelineList; + protected TreeSet timelineList; /** * Create a new, empty Timeline. @@ -42,7 +42,7 @@ public class Timeline implements Iterable { * @return true if this Timeline was modified. */ 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 { public boolean addAll(Timeline t) { boolean modified = false; for (TimelineMarker tm : t) { - if (!timelineList.contains(tm)) { - timelineList.add(tm); - modified = true; - } - } + modified = addMarker(tm) || modified; } return modified; } diff --git a/src/main/com/jdblabs/timestamper/core/TimelineProperties.java b/src/main/com/jdblabs/timestamper/core/TimelineProperties.java index 8f9967b..047fec7 100644 --- a/src/main/com/jdblabs/timestamper/core/TimelineProperties.java +++ b/src/main/com/jdblabs/timestamper/core/TimelineProperties.java @@ -72,7 +72,7 @@ public class TimelineProperties { URI timelineURI = timelineFile.toURI(); timeline = new Timeline(); - timelineSource = TimelineSourceFactory.newInstance(timelineURI); + timelineSource = TimelineSourceFactory.newInstance(timelineURI, config); persistOnUpdate = true; try { timelineSource.persist(timeline); } catch (IOException ioe) { @@ -84,8 +84,8 @@ public class TimelineProperties { } /** - * Load TimelineProperties from an InputStream. - * @param is + * Load TimelineProperties from a config file. + * @param propFile */ public TimelineProperties(File propFile) throws IOException { String strURI; @@ -97,8 +97,10 @@ public class TimelineProperties { persistOnUpdate = (Boolean) config.getProperty( LOCAL_TIMELINE_PERSIST_ON_UPDATE, true); - // load local timeline + // get the URI for the primary timeline strURI = (String) config.getProperty(LOCAL_TIMELINE_URI, ""); + + // no primary timeline, default to file-based timeline if ("".equals(strURI)) { File defaultTimelineFile = new File("timeline.default.txt"); try { @@ -108,7 +110,7 @@ public class TimelineProperties { // TODO } timelineURI = defaultTimelineFile.toURI(); - } else { + } else { // we do have a URI try { timelineURI = new URI(strURI); } catch (URISyntaxException urise) { 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(); // search keys for remote timeline entries @@ -143,7 +146,7 @@ public class TimelineProperties { // add a new SyncTarget to the list st = new SyncTarget(stName, TimelineSourceFactory - .newInstance(timelineURI), timeline); + .newInstance(timelineURI, config), timeline); syncTargets.add(st); // check for synch options diff --git a/src/main/com/jdblabs/timestamper/core/TimelineSourceFactory.java b/src/main/com/jdblabs/timestamper/core/TimelineSourceFactory.java index d4c4c16..a785af4 100644 --- a/src/main/com/jdblabs/timestamper/core/TimelineSourceFactory.java +++ b/src/main/com/jdblabs/timestamper/core/TimelineSourceFactory.java @@ -1,5 +1,6 @@ package com.jdblabs.timestamper.core; +import com.jdbernard.util.SmartConfig; import java.io.File; import java.net.URI; @@ -9,10 +10,10 @@ import java.net.URI; */ public class TimelineSourceFactory { - public static TimelineSource newInstance(URI uri) { + public static TimelineSource newInstance(URI uri, SmartConfig config) { // File based if ("file".equalsIgnoreCase(uri.getScheme())) { - return new FileTimelineSource(uri); + return new FileTimelineSource(uri, config); } // Twitter @@ -23,6 +24,11 @@ public class TimelineSourceFactory { + "sources are not yet supported."); } + // Other HTTP, assume JDB Labs web app + else if ("http".equalsIgnoreCase(uri.getScheme())) { + return new JDBLabsWebTimelineSource(uri, config); + } + // SSH else if ("ssh".equalsIgnoreCase(uri.getScheme())) { throw new UnsupportedOperationException("SSH based timeline sources" diff --git a/src/main/com/jdblabs/timestamper/core/TwitterTimelineSource.java b/src/main/com/jdblabs/timestamper/core/TwitterTimelineSource.java index 9ec9a1b..f7af0fb 100644 --- a/src/main/com/jdblabs/timestamper/core/TwitterTimelineSource.java +++ b/src/main/com/jdblabs/timestamper/core/TwitterTimelineSource.java @@ -1,5 +1,6 @@ package com.jdblabs.timestamper.core; +import com.jdbernard.util.SmartConfig; import java.io.IOException; import java.net.URI;