diff --git a/html/compressed_form.js b/html/compressed_form.js index 1966925..5dad559 100644 --- a/html/compressed_form.js +++ b/html/compressed_form.js @@ -48,8 +48,8 @@ mobwrite.sniffUserAgent=function(){if(window.opera)mobwrite.UA_opera=true;else{v mobwrite.minSyncInterval=1000;mobwrite.maxSyncInterval=10000;mobwrite.syncInterval=2000;mobwrite.idPrefix="";mobwrite.nullifyAll=false;mobwrite.clientChange_=false;mobwrite.serverChange_=false;mobwrite.syncAjaxObj_=null;mobwrite.uniqueId=function(){var b="abcdefghijklmnopqrstuvwxyz",c=b.charAt(Math.random()*b.length);b+="0123456789-_:.";for(var d=1;d<8;d++)c+=b.charAt(Math.random()*b.length);if(c.indexOf("--")!=-1)c=mobwrite.uniqueId();return c};mobwrite.syncUsername=mobwrite.uniqueId(); mobwrite.shared={};mobwrite.shareHandlers=[];mobwrite.shareObj=function(b){if(b){this.file=b;this.dmp=new diff_match_patch;this.dmp.Diff_Timeout=0.5;this.editStack=[];mobwrite.debug&&window.console.info('Creating shareObj: "'+b+'"')}};a=mobwrite.shareObj.prototype;a.shadowText="";a.clientVersion=0;a.serverVersion=0;a.deltaOk=false;a.mergeChanges=true;a.getClientText=function(){window.alert("Defined by subclass");return""};a.setClientText=function(){window.alert("Defined by subclass")}; a.patchClientText=function(b){var c=this.getClientText();b=this.dmp.patch_apply(b,c);c!=b[0]&&this.setClientText(b[0])};a.onSentDiff=function(){};a.fireChange=function(b){if("createEvent"in document){var c=document.createEvent("HTMLEvents");c.initEvent("change",false,false);b.dispatchEvent(c)}else"fireEvent"in b&&b.fireEvent("onchange")};a.nullify=function(){mobwrite.unshare(this.file);return"N:"+mobwrite.idPrefix+this.file+"\n"}; -a.syncText=function(){var b=this.getClientText();if(this.deltaOk){var c=this.dmp.diff_main(this.shadowText,b,true);if(c.length>2){this.dmp.diff_cleanupSemantic(c);this.dmp.diff_cleanupEfficiency(c)}var d=c.length!=1||c[0][0]!=0;if(d){mobwrite.clientChange_=true;this.shadowText=b}if(d||!this.editStack.length){b=(this.mergeChanges?"d:":"D:")+this.clientVersion+":"+this.dmp.diff_toDelta(c);this.editStack.push([this.clientVersion,b]);this.clientVersion++;this.onSentDiff(c)}}else{if(this.shadowText!=b)this.shadowText= -b;this.clientVersion++;b="r:"+this.clientVersion+":"+encodeURI(b).replace(/%20/g," ");this.editStack.push([this.clientVersion,b]);this.deltaOk=true}c="F:"+this.serverVersion+":"+mobwrite.idPrefix+this.file+"\n";for(b=0;b2){this.dmp.diff_cleanupSemantic(c);this.dmp.diff_cleanupEfficiency(c)}var d=c.length!=1||c[0][0]!=0;if(d){mobwrite.clientChange_=true;this.shadowText=b}if(d||!this.editStack.length){b=(this.mergeChanges?"d:":"D:")+this.clientVersion+":"+this.dmp.diff_toDelta(c);this.editStack.push([this.clientVersion,b]);this.clientVersion++;this.onSentDiff(c)}}else{this.shadowText=b;this.clientVersion++; +b="r:"+this.clientVersion+":"+encodeURI(b).replace(/%20/g," ");this.editStack.push([this.clientVersion,b]);this.deltaOk=true}c="F:"+this.serverVersion+":"+mobwrite.idPrefix+this.file+"\n";for(b=0;b dict = new HashMap(); - try { - BufferedReader input = new BufferedReader(new FileReader(tmpfile)); - - try { - String line; - Pattern lineRegex = Pattern.compile("^(\\w+)\\s*=\\s*(.+)$"); - while ((line = input.readLine()) != null) { - line = line.trim(); - // Comment lines start with a ; - if (!line.startsWith(";")) { - Matcher m = lineRegex.matcher(line); - if (m.matches()) { - String name = m.group(1); - String value = m.group(2); - name = URLDecoder.decode(name, "UTF-8"); - value = URLDecoder.decode(value, "UTF-8"); - dict.put(name, value); - } - } - } - } - finally { - input.close(); - } - } catch (FileNotFoundException ex){ - // No config to load. - } catch (IOException ex){ - ex.printStackTrace(); - } - - // Set each of the configuration parameters. - String value; - value = dict.get(Fields.SyncGateway.toString()); - if (value != null) { - this.mobwrite.syncGateway = value; - } - value = dict.get(Fields.SyncUsername.toString()); - if (value != null) { - this.mobwrite.syncUsername = value; - } - - String id = dict.get(Fields.ID.toString()); - String filename = dict.get(Fields.Filename.toString()); - if (id != null && filename != null) { - this.shareFile = new ShareFile(filename, id); - value = dict.get(Fields.EditStack.toString()); - if (value != null) { - // Decode the edit stack. - this.shareFile.editStack.clear(); - String[] lines = value.split("\n"); - Integer version = null; - for (String line : lines) { - // Even lines are version numbers, odd lines are action strings. - if (version == null) { - version = Integer.getInteger(line); - } else { - this.shareFile.editStack.push(new Object[]{version, line}); - version = null; - } - } - } - value = dict.get(Fields.ShadowText.toString()); - if (value != null) { - this.shareFile.shadowText = value; - } - value = dict.get(Fields.ClientVersion.toString()); - if (value != null) { - this.shareFile.clientVersion = Integer.valueOf(value); - } - value = dict.get(Fields.ServerVersion.toString()); - if (value != null) { - this.shareFile.serverVersion = Integer.valueOf(value); - } - value = dict.get(Fields.DeltaOk.toString()); - if (value != null) { - this.shareFile.deltaOk = Boolean.parseBoolean(value); - } - value = dict.get(Fields.MergeChanges.toString()); - if (value != null) { - this.shareFile.mergeChanges = Boolean.parseBoolean(value); - } - } - } - - private enum Fields { - SyncGateway, SyncUsername, Filename, ID, EditStack, ShadowText, - ClientVersion, ServerVersion, DeltaOk, MergeChanges - } - - /** - * Usage: FileSharer - * @param args Command line arguments. - */ - public static void main(String[] args) { - if (args.length != 3) { - System.err.println("Usage: FileSharer "); - return; - } - String syncgateway = args[0]; - String docname = args[1]; - String filename = args[2]; - String configname = filename + "." + docname + ".mobwrite"; - - FileSharer sharer = new FileSharer(); - sharer.mobwrite.syncGateway = syncgateway; - - sharer.loadConfig(configname); - if (sharer.shareFile == null || !args[1].equals(sharer.shareFile.file)) { - // Provided ID is different from the one we loaded (or none was loaded). - sharer.shareFile = sharer.new ShareFile(filename, docname); - } - - // Add the ShareFile to MobWrite and sync once. - sharer.mobwrite.share(sharer.shareFile); - - sharer.saveConfig(configname); - } - -} +/** + * FileSharer - Sharing a document via MobWrite + * + * Copyright 2009 Google Inc. + * http://code.google.com/p/google-mobwrite/ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.mobwrite; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.logging.Level; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class FileSharer { + + /** + * @author fraser + * + */ + private class MobWriteSingleExec extends MobWriteClient { + /** + * Constructor. Initializes a MobWrite client. + * @param syncGateway URL of the server. + */ + public MobWriteSingleExec(String syncGateway) { + super(syncGateway); + } + + /** + * Share the specified object(s) for one cycle. + */ + public void share(ShareObj ... shareObjs) { + for (int i = 0; i < shareObjs.length; i++) { + ShareObj shareObj = shareObjs[i]; + shareObj.mobwrite = this; + this.shared.put(shareObj.file, shareObj); + this.logger.log(Level.INFO, "Sharing shareObj: \"" + shareObj.file + "\""); + } + + syncRun1_(); + + for (int i = 0; i < shareObjs.length; i++) { + ShareObj shareObj = this.shared.remove(shareObjs[i].file); + if (shareObj != null) { + shareObj.mobwrite = null; + this.logger.log(Level.INFO, "Unshared: \"" + shareObj.file + "\""); + } + } + } + } + + private class ShareFile extends ShareObj { + + /** + * The name of the file on disk to be shared. + */ + private String filename; + + /** + * Constructor of shared object representing a file. + * @param filename Filename of file on disk. + * @param docname Document name to share as. + */ + public ShareFile(String filename, String docname) { + super(docname); + this.filename = filename; + } + + /** + * Retrieve the file contents. + * @return Plaintext content. + */ + public String getClientText() { + StringBuilder sb = new StringBuilder(); + BufferedReader input; + try { + input = new BufferedReader(new FileReader(filename)); + + try { + String line; + while ((line = input.readLine()) != null) { + sb.append(line); + } + } + finally { + input.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + return null; + } + return sb.toString(); + } + + /** + * Set the file contents. + * @param text New content. + */ + @Override + public void setClientText(String text) { + try { + BufferedWriter output = new BufferedWriter(new FileWriter(filename)); + + try { + output.write(text); + } finally { + output.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } + + + /** + * Instance of MobWrite. + */ + private MobWriteSingleExec mobwrite; + + /** + * Instance of ShareFile, a ShareObj. + */ + private ShareFile shareFile; + + /** + * Constructor for FileSharer. + * Creates a MobWrite client. + * @param syncGateway URL of the server. + */ + public FileSharer(String syncGateway) { + mobwrite = new MobWriteSingleExec(syncGateway); + } + + /** + * Save MobWrite's current state to disk. + */ + private void saveConfig(String tmpfile) { + try { + BufferedWriter output = new BufferedWriter(new FileWriter(tmpfile)); + + try { + writeOneConfigLine(output, Fields.SyncGateway, this.mobwrite.getSyncGateway()); + writeOneConfigLine(output, Fields.SyncUsername, this.mobwrite.syncUsername); + writeOneConfigLine(output, Fields.Filename, this.shareFile.filename); + writeOneConfigLine(output, Fields.ID, this.shareFile.file); + StringBuilder sb = new StringBuilder(); + for (Object[] pair : this.shareFile.editStack) { + sb.append(String.valueOf(pair[0])).append('\n'); + sb.append((String) pair[1]).append('\n'); + } + writeOneConfigLine(output, Fields.EditStack, sb.toString()); + writeOneConfigLine(output, Fields.ShadowText, this.shareFile.shadowText); + writeOneConfigLine(output, Fields.ClientVersion, Integer.toString(this.shareFile.clientVersion)); + writeOneConfigLine(output, Fields.ServerVersion, Integer.toString(this.shareFile.serverVersion)); + writeOneConfigLine(output, Fields.DeltaOk, Boolean.toString(this.shareFile.deltaOk)); + writeOneConfigLine(output, Fields.MergeChanges, Boolean.toString(this.shareFile.mergeChanges)); + } finally { + output.close(); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + } + + /** + * Write one "name = value" line, properly encoded. + * @param output The output writer. + * @param field Name of tuple. + * @param value Value of tuple. + * @throws IOException + */ + private void writeOneConfigLine(BufferedWriter output, Fields field, + String value) throws IOException { + String name = URLEncoder.encode(field.toString(), "UTF-8"); + value = URLEncoder.encode(value, "UTF-8"); + output.write(name + " = " + value + "\n"); + } + + /** + * Load MobWrite's current state from disk. + */ + private void loadConfig(String tmpfile) { + HashMap dict = new HashMap(); + try { + BufferedReader input = new BufferedReader(new FileReader(tmpfile)); + + try { + String line; + Pattern lineRegex = Pattern.compile("^(\\w+)\\s*=\\s*(.+)$"); + while ((line = input.readLine()) != null) { + line = line.trim(); + // Comment lines start with a ; + if (!line.startsWith(";")) { + Matcher m = lineRegex.matcher(line); + if (m.matches()) { + String name = m.group(1); + String value = m.group(2); + name = URLDecoder.decode(name, "UTF-8"); + value = URLDecoder.decode(value, "UTF-8"); + dict.put(name, value); + } + } + } + } + finally { + input.close(); + } + } catch (FileNotFoundException ex){ + // No config to load. + } catch (IOException ex){ + ex.printStackTrace(); + } + + // Set each of the configuration parameters. + // Ignore the syncGateway parameter, it was already specified on the command line. + String value; + value = dict.get(Fields.SyncUsername.toString()); + if (value != null) { + this.mobwrite.syncUsername = value; + } + + String id = dict.get(Fields.ID.toString()); + String filename = dict.get(Fields.Filename.toString()); + if (id != null && filename != null) { + this.shareFile = new ShareFile(filename, id); + value = dict.get(Fields.EditStack.toString()); + if (value != null) { + // Decode the edit stack. + this.shareFile.editStack.clear(); + String[] lines = value.split("\n"); + Integer version = null; + for (String line : lines) { + // Even lines are version numbers, odd lines are action strings. + if (version == null) { + version = Integer.getInteger(line); + } else { + this.shareFile.editStack.push(new Object[]{version, line}); + version = null; + } + } + } + value = dict.get(Fields.ShadowText.toString()); + if (value != null) { + this.shareFile.shadowText = value; + } + value = dict.get(Fields.ClientVersion.toString()); + if (value != null) { + this.shareFile.clientVersion = Integer.valueOf(value); + } + value = dict.get(Fields.ServerVersion.toString()); + if (value != null) { + this.shareFile.serverVersion = Integer.valueOf(value); + } + value = dict.get(Fields.DeltaOk.toString()); + if (value != null) { + this.shareFile.deltaOk = Boolean.parseBoolean(value); + } + value = dict.get(Fields.MergeChanges.toString()); + if (value != null) { + this.shareFile.mergeChanges = Boolean.parseBoolean(value); + } + } + } + + private enum Fields { + SyncGateway, SyncUsername, Filename, ID, EditStack, ShadowText, + ClientVersion, ServerVersion, DeltaOk, MergeChanges + } + + /** + * Usage: FileSharer + * @param args Command line arguments. + */ + public static void main(String[] args) { + if (args.length != 3) { + System.err.println("Usage: FileSharer "); + return; + } + String syncgateway = args[0]; + String docname = args[1]; + String filename = args[2]; + String configname = filename + "." + docname + ".mobwrite"; + + FileSharer sharer = new FileSharer(syncgateway); + + sharer.loadConfig(configname); + if (sharer.shareFile == null || !args[1].equals(sharer.shareFile.file)) { + // Provided ID is different from the one we loaded (or none was loaded). + sharer.shareFile = sharer.new ShareFile(filename, docname); + } + + // Add the ShareFile to MobWrite and sync once. + sharer.mobwrite.share(sharer.shareFile); + + sharer.saveConfig(configname); + } + +} diff --git a/java/com/google/mobwrite/MobWriteClient.java b/java/com/google/mobwrite/MobWriteClient.java index e52dce7..c9df44d 100644 --- a/java/com/google/mobwrite/MobWriteClient.java +++ b/java/com/google/mobwrite/MobWriteClient.java @@ -27,6 +27,7 @@ import java.net.URLConnection; import java.net.URLDecoder; import java.net.URLEncoder; +import java.security.SecureRandom; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; @@ -41,11 +42,6 @@ * Class representing a MobWrite client. */ public class MobWriteClient { - /** - * URL of web gateway. - */ - public String syncGateway = "http://mobwrite3.appspot.com/scripts/q.py"; - /** * Time to wait for a connection before giving up and retrying. */ @@ -98,51 +94,78 @@ public class MobWriteClient { protected String syncUsername; /** - * Hash of all shared objects. + * URL of web gateway. */ - protected Map shared; + private String syncGateway; /** - * Logging object. + * Hash of all shared objects. */ - protected Logger logger; + protected Map shared; /** * Currently running synchronization thread. */ private Thread syncThread = null; + /** + * Number of digits in the username. + */ + private static final int IDSIZE = 8; + + /** + * Logging object. + */ + protected static final Logger logger = Logger.getLogger("MobWrite"); + + /** + * Cryptographically strong pseudo-random number generator. + */ + private static final SecureRandom RANDOM = new SecureRandom(); + /** * Constructor. Initializes a MobWrite client. + * @param syncGateway URL of the server. */ - public MobWriteClient() { - this.logger = Logger.getLogger("MobWrite"); - this.syncUsername = MobWriteClient.uniqueId(); + public MobWriteClient(String syncGateway) { + this.syncUsername = MobWriteClient.uniqueId(IDSIZE); + this.syncGateway = syncGateway; logger.log(Level.INFO, "Username: " + this.syncUsername); + logger.log(Level.INFO, "Gateway: " + this.syncGateway); this.shared = new HashMap(); } /** - * Return a random id that's 8 letters long. - * 26*(26+10+4)^7 = 4,259,840,000,000 + * Return the URL of the server. + * @return URL of the server. + */ + public String getSyncGateway() { + return this.syncGateway; + } + + + /** + * Return a random id. + * For size = 8: 26*(26+10+4)^7 = 4,259,840,000,000 + * @param size The number of characters to use. * @return Random id. */ - public static String uniqueId() { + public static String uniqueId(int size) { // First character must be a letter. // IE is case insensitive (in violation of the W3 spec). String soup = "abcdefghijklmnopqrstuvwxyz"; StringBuffer sb = new StringBuffer(); - sb.append(soup.charAt((int) (Math.random() * soup.length()))); + sb.append(soup.charAt(RANDOM.nextInt(soup.length()))); // Subsequent characters may include these. soup += "0123456789-_:."; - for (int x = 1; x < 8; x++) { - sb.append(soup.charAt((int) (Math.random() * soup.length()))); + for (int x = 1; x < size; x++) { + sb.append(soup.charAt(RANDOM.nextInt(soup.length()))); } String id = sb.toString(); // Don't allow IDs with '--' in them since it might close a comment. if (id.indexOf("--") != -1) { - id = uniqueId(); + id = uniqueId(size); } return id; // Getting the maximum possible density in the ID is worth the extra code, @@ -219,6 +242,7 @@ protected void syncRun1_() { /** * Parse all server-side changes and distribute them to the shared objects. + * @param text The commands from the server to parse and execute. */ private void syncRun2_(String text) { // Initialize serverChange_, to be checked at the end of syncRun2_. @@ -232,8 +256,7 @@ private void syncRun2_(String text) { String[] lines = text.split("\n"); ShareObj file = null; int clientVersion = -1; - for (int i = 0; i < lines.length; i++) { - String line = lines[i]; + for (String line : lines) { if (line.isEmpty()) { // Terminate on blank line. break; @@ -281,7 +304,7 @@ private void syncRun2_(String text) { // Remove any elements from the edit stack with low version numbers // which have been acked by the server. Iterator pairPointer = file.editStack.iterator(); - while (pairPointer.hasNext() ){ + while (pairPointer.hasNext()) { Object[] pair = pairPointer.next(); if ((Integer) pair[0] <= clientVersion) { pairPointer.remove(); @@ -315,8 +338,8 @@ private void syncRun2_(String text) { file.setClientText(file.shadowText); } catch (Exception e) { // Potential call to untrusted 3rd party code. - this.logger.log(Level.SEVERE, "Error calling setClientText on '" + file.file + "': " + e); - e.printStackTrace(); + this.logger.log(Level.SEVERE, "Error calling setClientText on '" + + file.file + "'", e); } } // Server-side activity. @@ -362,7 +385,8 @@ private void syncRun2_(String text) { throw new Error("This system does not support UTF-8.", e); } } - if (diffs != null && (diffs.size() != 1 || diffs.getFirst().operation != Operation.EQUAL)) { + if (diffs != null && (diffs.size() != 1 + || diffs.getFirst().operation != Operation.EQUAL)) { // Compute and apply the patches. if (name == 'D') { // Overwrite text. @@ -371,8 +395,8 @@ private void syncRun2_(String text) { file.setClientText(file.shadowText); } catch (Exception e) { // Potential call to untrusted 3rd party code. - this.logger.log(Level.SEVERE, "Error calling setClientText on '" + file.file + "': " + e); - e.printStackTrace(); + this.logger.log(Level.SEVERE, "Error calling setClientText on '" + + file.file + "'", e); } } else { // Merge text. @@ -385,7 +409,8 @@ private void syncRun2_(String text) { file.patchClientText(patches); } catch (Exception e) { // Potential call to untrusted 3rd party code. - this.logger.log(Level.SEVERE, "Error calling patchClientText on '" + file.file + "': " + e); + this.logger.log(Level.SEVERE, "Error calling patchClientText on '" + + file.file + "'", e); e.printStackTrace(); } } @@ -429,10 +454,10 @@ protected void computeSyncInterval_() { /** * Start sharing the specified object(s). + * @param shareObjs Object(s) to start sharing. */ public void share(ShareObj ... shareObjs) { - for (int i = 0; i < shareObjs.length; i++) { - ShareObj shareObj = shareObjs[i]; + for (ShareObj shareObj : shareObjs) { shareObj.mobwrite = this; this.shared.put(shareObj.file, shareObj); this.logger.log(Level.INFO, "Sharing shareObj: \"" + shareObj.file + "\""); @@ -451,12 +476,12 @@ public void share(ShareObj ... shareObjs) { /** * Stop sharing the specified object(s). + * @param shareObjs Object(s) to stop sharing. */ public void unshare(ShareObj ... shareObjs) { - for (int i = 0; i < shareObjs.length; i++) { - ShareObj shareObj = this.shared.remove(shareObjs[i].file); - if (shareObj == null) { - this.logger.log(Level.INFO, "Ignoring \"" + shareObjs[i].file + "\". Not currently shared."); + for (ShareObj shareObj : shareObjs) { + if (this.shared.remove(shareObj.file) == null) { + this.logger.log(Level.INFO, "Ignoring \"" + shareObj.file + "\". Not currently shared."); } else { shareObj.mobwrite = null; this.logger.log(Level.INFO, "Unshared: \"" + shareObj.file + "\""); @@ -467,12 +492,13 @@ public void unshare(ShareObj ... shareObjs) { /** * Stop sharing the specified file ID(s). + * @param shareFiles ID(s) to stop sharing. */ public void unshare(String ... shareFiles) { - for (int i = 0; i < shareFiles.length; i++) { + for (String shareFile : shareFiles) { ShareObj shareObj = this.shared.remove(shareFiles); if (shareObj == null) { - this.logger.log(Level.INFO, "Ignoring \"" + shareFiles[i] + "\". Not currently shared."); + this.logger.log(Level.INFO, "Ignoring \"" + shareFile + "\". Not currently shared."); } else { shareObj.mobwrite = null; this.logger.log(Level.INFO, "Unshared: \"" + shareObj.file + "\""); @@ -529,8 +555,5 @@ public void run() { } this.client.logger.log(Level.INFO, "MobWrite task stopped."); } - - } - } diff --git a/java/com/google/mobwrite/MobWriteClientTest.java b/java/com/google/mobwrite/MobWriteClientTest.java index e1119f5..b505961 100755 --- a/java/com/google/mobwrite/MobWriteClientTest.java +++ b/java/com/google/mobwrite/MobWriteClientTest.java @@ -24,15 +24,16 @@ public class MobWriteClientTest extends TestCase { public void testUniqueId() { // Test length. - assertEquals("uniqueId: Length", 8, MobWriteClient.uniqueId().length()); + assertEquals("uniqueId: Length", 8, MobWriteClient.uniqueId(8).length()); // Two IDs should not be the same. // There's a 1 in 4 trillion chance that this test could fail normally. - assertFalse("uniqueId: Identical", MobWriteClient.uniqueId().equals(MobWriteClient.uniqueId())); + assertFalse("uniqueId: Identical", + MobWriteClient.uniqueId(8).equals(MobWriteClient.uniqueId(8))); } public void testComputeSyncInterval() { - MobWriteClient mobwrite = new MobWriteClient(); + MobWriteClient mobwrite = new MobWriteClient("http://www.example.com/"); // Check 10% growth when no change. mobwrite.serverChange_ = false; mobwrite.clientChange_ = false; diff --git a/java/com/google/mobwrite/ShareObj.java b/java/com/google/mobwrite/ShareObj.java index 0a63a26..b833e8c 100644 --- a/java/com/google/mobwrite/ShareObj.java +++ b/java/com/google/mobwrite/ShareObj.java @@ -179,8 +179,8 @@ protected String syncText() { } } catch (Exception e) { // Potential call to untrusted 3rd party code. - this.mobwrite.logger.log(Level.SEVERE, "Error calling getClientText on '" + this.file + "': " + e); - e.printStackTrace(); + this.mobwrite.logger.log(Level.SEVERE, "Error calling getClientText on '" + + this.file + "'", e); return ""; } if (this.deltaOk) { @@ -208,8 +208,8 @@ protected String syncText() { this.onSentDiff(diffs); } catch (Exception e) { // Potential call to untrusted 3rd party code. - this.mobwrite.logger.log(Level.SEVERE, "Error calling onSentDiff on '" + this.file + "': " + e); - e.printStackTrace(); + this.mobwrite.logger.log(Level.SEVERE, "Error calling onSentDiff on '" + + this.file + "'", e); } } } else { @@ -224,9 +224,7 @@ protected String syncText() { throw new Error("This system does not support UTF-8.", e); } data = MobWriteClient.unescapeForEncodeUriCompatability(data); - if (this.shadowText != clientText) { - this.shadowText = clientText; - } + this.shadowText = clientText; this.clientVersion++; String action = "r:" + this.clientVersion + ':' + data; // Append the action to the edit stack. diff --git a/tools/sync.jar b/tools/sync.jar index 4989f99..2d5afa9 100644 Binary files a/tools/sync.jar and b/tools/sync.jar differ