X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Fjvcard%2Fremote%2FSync.java;h=a1e5e2429cc75f5a7b35a854e440773df34cf6fb;hb=59597d59aa262e31c2e1b7f66b4cb299f88ebd1b;hp=771798c353b651f3dd3d1a9243d118cfa7418a24;hpb=02b341aa6dcd14dc311f6ea218e199f20e242f36;p=jvcard.git diff --git a/src/be/nikiroo/jvcard/remote/Sync.java b/src/be/nikiroo/jvcard/remote/Sync.java index 771798c..a1e5e24 100644 --- a/src/be/nikiroo/jvcard/remote/Sync.java +++ b/src/be/nikiroo/jvcard/remote/Sync.java @@ -16,16 +16,18 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.MissingResourceException; -import java.util.ResourceBundle; import be.nikiroo.jvcard.Card; import be.nikiroo.jvcard.Contact; import be.nikiroo.jvcard.Data; +import be.nikiroo.jvcard.launcher.CardResult; +import be.nikiroo.jvcard.launcher.CardResult.MergeCallback; import be.nikiroo.jvcard.parsers.Format; import be.nikiroo.jvcard.parsers.Vcard21Parser; -import be.nikiroo.jvcard.resources.Bundles; +import be.nikiroo.jvcard.remote.SimpleSocket.BlockAppendable; import be.nikiroo.jvcard.resources.StringUtils; +import be.nikiroo.jvcard.resources.bundles.RemoteBundle; +import be.nikiroo.jvcard.resources.enums.RemotingOption; /** * This class will synchronise {@link Card}s between a local instance an a @@ -157,6 +159,8 @@ public class Sync { * * @param force * force the synchronisation to occur + * @param callback + * the {@link MergeCallback} to call in case of conflict * * @return the synchronised (or not) {@link Card} * @@ -165,23 +169,23 @@ public class Sync { * @throws IOException * in case of IO error */ - public Card sync(boolean force) throws UnknownHostException, IOException { - + public CardResult sync(boolean force, MergeCallback callback) + throws UnknownHostException, IOException { long tsOriginal = getLastModified(); Card local = new Card(getCache(cacheDir), Format.VCard21); - local.setRemote(true); // do NOT update unless we are in autoSync or forced mode or we don't // have the file on cache if (!autoSync && !force && tsOriginal != -1) { - return local; + return new CardResult(local, true, false, false); } SimpleSocket s = new SimpleSocket(new Socket(host, port), "sync client"); // get the server time stamp long tsServer = -1; + boolean serverChanges = false; try { s.open(true); s.sendCommand(Command.LIST_CARD); @@ -207,7 +211,7 @@ public class Sync { } // Check changes - boolean serverChanges = (tsServer - tsOriginal) > GRACE_TIME; + serverChanges = (tsServer - tsOriginal) > GRACE_TIME; boolean localChanges = false; Card original = null; if (tsOriginal != -1) { @@ -256,7 +260,7 @@ public class Sync { System.err.println("DEBUG: it changed. retry."); s.sendCommand(Command.SELECT); s.close(); - return sync(force); + return sync(force, callback); } switch (action) { @@ -264,8 +268,7 @@ public class Sync { s.sendCommand(Command.GET_CARD); List data = s.receiveBlock(); setLastModified(data.remove(0)); - Card server = new Card(Vcard21Parser.parseContact(data)); - local.replaceListContent(server); + local.replaceListContent(Vcard21Parser.parseContact(data)); if (local.isDirty()) local.save(); @@ -274,7 +277,9 @@ public class Sync { } case POST_CARD: { s.sendCommand(Command.POST_CARD); - s.sendBlock(Vcard21Parser.toStrings(local)); + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, local); + app.close(); local.saveAs(getCache(cacheDirOrig), Format.VCard21); setLastModified(s.receiveLine()); break; @@ -298,12 +303,10 @@ public class Sync { break; } case HELP: { - if (true) - throw new IOException("two-way sync not supported yet"); - // note: we are holding the server here, so it could throw // us away if we take too long + // TODO: check if those files are deleted File mergeF = File.createTempFile("contact-merge", ".vcf"); File serverF = File .createTempFile("contact-server", ".vcf"); @@ -312,15 +315,42 @@ public class Sync { Card server = new Card(serverF, Format.VCard21); updateFromServer(s, server); - // TODO: auto merge into mergeF (from original, local, - // server) - local.saveAs(mergeF, Format.VCard21); + // Do an auto sync + server.saveAs(mergeF, Format.VCard21); Card merge = new Card(mergeF, Format.VCard21); - - // TODO: ask client if ok or to change it herself - - String serverLastModifTime = updateToServer(s, original, - merge); + List added = new LinkedList(); + List removed = new LinkedList(); + original.compare(local, added, removed, removed, added); + for (Contact c : removed) + merge.getById(c.getId()).delete(); + for (Contact c : added) + merge.add(Vcard21Parser.clone(c)); + + merge.save(); + + // defer to client: + if (callback == null) { + throw new IOException( + "Conflicting changes detected and merge operation not allowed"); + } + + merge = callback.merge(original, local, server, merge); + if (merge == null) { + throw new IOException( + "Conflicting changes detected and merge operation cancelled"); + } + + // TODO: something like: + // String serverLastModifTime = updateToServer(s, original, + // merge); + // ...but without starting with original since it is not + // true here + s.sendCommand(Command.POST_CARD); + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, merge); + app.close(); + String serverLastModifTime = s.receiveLine(); + // merge.saveAs(getCache(cacheDir), Format.VCard21); merge.saveAs(getCache(cacheDirOrig), Format.VCard21); @@ -331,20 +361,22 @@ public class Sync { break; } + default: + // will not happen + break; } s.sendCommand(Command.SELECT); } } catch (IOException e) { - throw e; + return new CardResult(e); } catch (Exception e) { - e.printStackTrace(); - return local; + return new CardResult(new IOException(e)); } finally { s.close(); } - return local; + return new CardResult(local, true, true, serverChanges); } /** @@ -382,7 +414,9 @@ public class Sync { } for (Contact c : added) { s.sendCommand(Command.POST_CONTACT, c.getId()); - s.sendBlock(Vcard21Parser.toStrings(c, -1)); + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, c, -1); + s.close(); } if (from.size() > 0) { for (int index = 0; index < from.size(); index++) { @@ -392,14 +426,17 @@ public class Sync { List subadded = new LinkedList(); List subremoved = new LinkedList(); f.compare(t, subadded, subremoved, subremoved, subadded); - s.sendCommand(Command.PUT_CONTACT, name); + s.sendCommand(Command.PUT_CONTACT, f.getId()); for (Data d : subremoved) { - s.sendCommand(Command.DELETE_DATA, d.getContentState()); + s.sendCommand(Command.DELETE_DATA, d.getContentState(true)); } for (Data d : subadded) { - s.sendCommand(Command.POST_DATA, d.getContentState()); - s.sendBlock(Vcard21Parser.toStrings(d)); + s.sendCommand(Command.POST_DATA, d.getContentState(true)); + BlockAppendable app = s.createBlockAppendable(); + Vcard21Parser.write(app, d); + app.close(); } + s.sendCommand(Command.PUT_CONTACT); } } @@ -445,7 +482,7 @@ public class Sync { String hash = remote.get(c.getId()); if (hash == null) { deleted.add(c); - } else if (!hash.equals(c.getContentState())) { + } else if (!hash.equals(c.getContentState(true))) { changed.add(c); } } @@ -559,10 +596,10 @@ public class Sync { */ static private void config() { String dir = null; - ResourceBundle bundle = Bundles.getBundle("remote"); + RemoteBundle bundle = new RemoteBundle(); try { - dir = bundle.getString("CLIENT_CACHE_DIR").trim(); + dir = bundle.getString(RemotingOption.CLIENT_CACHE_DIR); cacheDir = new File(dir + File.separator + "current"); cacheDir.mkdir(); @@ -576,14 +613,8 @@ public class Sync { + dir); } - String autoStr = bundle.getString("CLIENT_AUTO_SYNC"); - if (autoStr != null && autoStr.trim().equalsIgnoreCase("true")) { - autoSync = true; - } - - } catch (MissingResourceException e) { - throw new InvalidParameterException( - "Cannot access remote.properties configuration file"); + autoSync = bundle + .getBoolean(RemotingOption.CLIENT_AUTO_SYNC, false); } catch (Exception e) { throw new InvalidParameterException( "Cannot open or create cache store at: " + dir);