| 1 18 package sync4j.server.engine; 19 20 import java.security.Principal ; 21 import java.sql.Timestamp ; 22 import java.util.*; 23 import java.util.logging.Level ; 24 import java.util.logging.Logger ; 25 26 import org.apache.commons.collections.ListUtils; 27 28 import sync4j.framework.core.Add; 29 import sync4j.framework.core.ModificationCommand; 30 import sync4j.framework.core.StatusCode; 31 import sync4j.framework.engine.*; 32 import sync4j.framework.engine.Util; 33 import sync4j.framework.engine.source.MemorySyncSource; 34 import sync4j.framework.engine.source.ObjectSizeMismatchException; 35 import sync4j.framework.engine.source.SyncSource; 36 import sync4j.framework.engine.source.SyncSourceException; 37 38 86 public class Sync4jStrategy implements SyncStrategy, java.io.Serializable { 87 88 90 public static String LOG_NAME = "sync4j.framework.engine"; 91 92 private transient static Logger log = Logger.getLogger(LOG_NAME); 93 94 96 99 private transient SyncSource[] sources = null; 100 public SyncSource[] getSources(){ return sources; } 101 102 public void setSources(SyncSource[] sources){ this.sources = sources; } 103 104 107 public String getName(){ return name; } 108 109 private String name; 110 public void setName(String name){ this.name = name; } 111 112 114 private HashMap newBAll; 132 private HashMap updatedBAll; 133 private HashMap deletedBAll; 134 135 137 public Sync4jStrategy() { 138 newBAll = new HashMap(); 139 updatedBAll = new HashMap(); 140 deletedBAll = new HashMap(); 141 } 142 143 public Sync4jStrategy(SyncSource[] syncSources) { 144 this(); 145 sources = syncSources; 146 } 147 148 150 171 public SyncOperation[] prepareSlowSync(SyncSource[] sources , 172 Principal principal, 173 Timestamp nextSync , 174 boolean last ) 175 throws SyncException { 176 177 if (sources != null) { 178 this.sources = sources; 179 } 180 181 SyncItem[] Am, Bm; 182 List syncOperations = new ArrayList(); 183 184 Logger log = Logger.getLogger(LOG_NAME); 185 186 if (log.isLoggable(Level.INFO)) { 187 log.info( "Preparing slow synchronization of source '" 188 + sources[1].getSourceURI() 189 + "' for " 190 + principal 191 + " ..." 192 ); 193 if (last) { 194 log.info("Last call"); 195 } else { 196 log.info("Not the last call"); 197 } 198 } 199 200 Am = sources[1].getUpdatedSyncItems(principal, null); 204 Bm = sources[0].getAllSyncItems(principal); 205 206 if (Am == null) { 210 Am = new SyncItem[0]; 211 } 212 213 if (Bm == null) { 214 Bm = new SyncItem[0]; 215 } 216 217 ArrayList newlyMappedItems = new ArrayList(); 224 fixMappedItems(newlyMappedItems, Am, sources[0], principal); 225 226 EngineHelper.resetState(Am); 230 EngineHelper.resetState(Bm); 231 232 for (int i=0; i<Am.length; ++i) { 233 if (!Am[i].isMapped()) { 234 syncOperations.add(checkSyncOperation(principal, nextSync, Am[i], null)); 235 } 236 } 237 238 Iterator j = newlyMappedItems.iterator(); 239 while (j.hasNext()) { 240 SyncItemMapping m = (SyncItemMapping)j.next(); 241 m.getSyncItemA().setState(SyncItemState.SYNCHRONIZED); 242 m.getSyncItemB().setState(SyncItemState.SYNCHRONIZED); 243 syncOperations.add(checkSyncOperation(principal, nextSync, m.getSyncItemA(), m.getSyncItemB())); 244 } 245 246 if (last) { 251 EngineHelper.resetState(Am); 258 259 List all = new ArrayList(Arrays.asList(sources[1].getAllSyncItems(principal))); 260 all.addAll(Arrays.asList(Am)); 261 262 for (int i=0; i<Bm.length; ++i) { 263 if (!(all.contains(Bm[i]))) { 264 syncOperations.add(checkSyncOperation(principal, nextSync, null, Bm[i])); 265 } 266 } 267 } 268 269 if (log.isLoggable(Level.FINEST)) { 270 log.finest("operations: " + syncOperations); 271 } 272 273 if (log.isLoggable(Level.INFO)) { 274 log.info("Preparation completed."); 275 } 276 277 return (SyncOperation[])syncOperations.toArray(new SyncOperationImpl[] {}); 278 } 279 280 299 public SyncOperation[] prepareFastSync(SyncSource[] sources , 300 Principal principal, 301 Timestamp lastSync , 302 Timestamp nextSync , 303 boolean last ) 304 throws SyncException { 305 if (sources != null) { 306 this.sources = sources; 307 } 308 309 311 List Am , Bm , AmBm , AAmBm , AmBBm ; 317 ArrayList syncOperations = null; 318 319 SyncItem[] newA = null, newB = null, 320 updatedA = null, updatedB = null, 321 deletedA = null, deletedB = null; 322 323 if (log.isLoggable(Level.INFO)) { 324 log.info( "Preparing fast synchronization of source '" 325 + sources[1].getSourceURI() 326 + "' for " 327 + principal 328 + " since " 329 + lastSync 330 + "..." 331 ); 332 333 if (last) { 334 log.info("Last call"); 335 } else { 336 log.info("Not the last call"); 337 } 338 } 339 340 342 newA = sources[1].getNewSyncItems (principal, lastSync); 347 updatedA = sources[1].getUpdatedSyncItems (principal, lastSync); 348 deletedA = sources[1].getDeletedSyncItems (principal, lastSync); 349 350 if (log.isLoggable(Level.FINEST)) { 351 log.finest("newA: " + Util.arrayToString(newA)); 352 log.finest("updatedA: " + Util.arrayToString(updatedA)); 353 log.finest("deletedA: " + Util.arrayToString(deletedA)); 354 } 355 356 String uri = sources[0].getSourceURI(); 357 if (newBAll.get(uri) == null) { 358 if (log.isLoggable(Level.FINEST)) { 359 log.finest("Detecting server changes..."); 360 } 361 newB = sources[0].getNewSyncItems (principal, lastSync); 362 updatedB = sources[0].getUpdatedSyncItems (principal, lastSync); 363 deletedB = sources[0].getDeletedSyncItems (principal, lastSync); 364 365 if (newB == null) { 369 newB = new SyncItemImpl[0]; 370 } 371 if (updatedB == null) { 372 updatedB = new SyncItemImpl[0]; 373 } 374 if (deletedB == null) { 375 deletedB = new SyncItemImpl[0]; 376 } 377 378 if (log.isLoggable(Level.FINEST)) { 379 log.finest("newB: " + Util.arrayToString(newB)); 380 log.finest("updatedB: " + Util.arrayToString(updatedB)); 381 log.finest("deletedB: " + Util.arrayToString(deletedB)); 382 } 383 384 newBAll .put(uri, newB); 385 updatedBAll.put(uri, updatedB); 386 deletedBAll.put(uri, deletedB); 387 } else { 388 newB = (SyncItem[])newBAll .get(uri); 389 updatedB = (SyncItem[])updatedBAll.get(uri); 390 deletedB = (SyncItem[])deletedBAll.get(uri); 391 } 392 393 ArrayList newlyMappedItems = new ArrayList(); 400 401 fixMappedItems(newlyMappedItems, newA, sources[0], principal); 402 fixMappedItems(newlyMappedItems, updatedA, sources[0], principal); 403 fixMappedItems(newlyMappedItems, deletedA, sources[0], principal); 404 405 if (log.isLoggable(Level.FINEST)) { 406 log.finest("Newly mapped items: " + newlyMappedItems); 407 } 408 409 Am = new ArrayList(); Bm = new ArrayList(); 410 Am.addAll(Arrays.asList(newA )); 411 Am.addAll(Arrays.asList(updatedA )); 412 Am.addAll(Arrays.asList(deletedA )); 413 Bm.addAll(Arrays.asList(newB )); 414 Bm.addAll(Arrays.asList(updatedB )); 415 Bm.addAll(Arrays.asList(deletedB )); 416 417 if (log.isLoggable(Level.FINEST)) { 418 log.finest("Am: " + Am); 419 log.finest("Bm: " + Bm); 420 log.finest("Am-Bm: " + ListUtils.subtract(Am, Bm)); 421 log.finest("Bm-Am: " + ListUtils.subtract(Bm, Am)); 422 } 423 424 AmBm = EngineHelper.intersect(Am, Bm); 430 AmBBm = EngineHelper.buildAmBBm(ListUtils.subtract(Am, Bm), sources[0], principal); 431 432 AAmBm = new ArrayList(); 433 if (last) { 434 newA = ((MemorySyncSource)sources[1]).getAllNewSyncItems (principal, lastSync); 439 updatedA = ((MemorySyncSource)sources[1]).getAllUpdatedSyncItems (principal, lastSync); 440 deletedA = ((MemorySyncSource)sources[1]).getAllDeletedSyncItems (principal, lastSync); 441 442 AAmBm = EngineHelper.buildAAmBm(ListUtils.subtract(Bm, Am), sources[1], principal); 443 } else { 444 Bm.clear(); 445 } 446 447 if (log.isLoggable(Level.FINEST)) { 448 log.finest("AmBm: " + AmBm ); 449 log.finest("AmBBm: " + AmBBm); 450 log.finest("AAmBm: " + AAmBm); 451 } 452 453 syncOperations = 457 checkSyncOperations(principal, nextSync, Am, Bm, AmBm, AmBBm, AAmBm); 458 459 if (log.isLoggable(Level.FINEST)) { 460 log.finest("operations: " + syncOperations); 461 } 462 463 if (log.isLoggable(Level.INFO)) { 464 log.info("Preparation completed."); 465 } 466 467 return (SyncOperation[])syncOperations.toArray(new SyncOperationImpl[] {}); 468 } 469 470 475 public SyncOperationStatus[] sync(SyncOperation[] syncOperations) { 476 Logger log = Logger.getLogger(LOG_NAME); 477 478 if (log.isLoggable(Level.INFO)) { 479 log.info("Synchronizing..."); 480 } 481 482 if ((syncOperations == null) || (syncOperations.length == 0)) { 483 return new SyncOperationStatus[0]; 484 } 485 486 ArrayList status = new ArrayList(); 487 488 SyncOperationStatus[] operationStatus = null; 489 for(int i=0; i<syncOperations.length; ++i) { 490 if (log.isLoggable(Level.FINEST)) { 491 log.finest("Executing " + syncOperations[i]); 492 } 493 494 operationStatus = execSyncOperation((SyncOperationImpl)syncOperations[i]); 499 500 for (int j=0; j<operationStatus.length; ++j) { 501 status.add(operationStatus[j]); 502 } } 505 if (log.isLoggable(Level.FINEST)) { 506 log.finest("status: " + status); 507 } 508 509 return (SyncOperationStatus[])status.toArray(new SyncOperationStatus[0]); 510 } 511 512 517 public void endSync() throws SyncException { 518 Logger log = Logger.getLogger(LOG_NAME); 519 520 if (log.isLoggable(Level.INFO)) { 521 log.info("Synchronization completed."); 522 } 523 } 524 525 527 542 protected ArrayList checkSyncOperations(Principal principal, 543 Timestamp nextSync , 544 List Am , 545 List Bm , 546 List AmBm , 547 List AmBBm , 548 List AAmBm ) { 549 SyncItemMapping mapping = null; 550 SyncItem syncItemA = null, syncItemB = null; 551 552 ArrayList all = new ArrayList(); 553 ArrayList operations = new ArrayList(); 554 555 557 all.addAll(AmBm ); 558 all.addAll(AmBBm); 559 all.addAll(AAmBm); 560 561 Iterator i = all.iterator(); 565 while (i.hasNext()) { 566 mapping = (SyncItemMapping)i.next(); 567 568 syncItemA = mapping.getSyncItemA(); 569 syncItemB = mapping.getSyncItemB(); 570 571 operations.add( 572 checkSyncOperation( 573 principal, 574 nextSync , 575 syncItemA, 576 syncItemB 577 ) 578 ); 579 Am.remove(syncItemA); 580 Bm.remove(syncItemB); 581 } 582 583 i = Am.iterator(); 587 while (i.hasNext()) { 588 syncItemA = (SyncItem)i.next(); 589 590 operations.add(checkSyncOperation(principal, nextSync, syncItemA, null)); 591 } 593 i = Bm.iterator(); 597 while (i.hasNext()) { 598 syncItemB = (SyncItem)i.next(); 599 600 operations.add(checkSyncOperation(principal, nextSync, null, syncItemB)); 601 } 603 return operations; 604 } 605 606 616 protected SyncOperation checkSyncOperation(Principal principal, 617 Timestamp nextSync , 618 SyncItem syncItemA, 619 SyncItem syncItemB) { 620 if (log.isLoggable(Level.FINEST)) { 621 log.finest( "check: syncItemA: " 622 + syncItemA 623 + " syncItemB: " 624 + syncItemB 625 ); 626 } 627 628 if (syncItemA == null) { 629 syncItemA = SyncItemImpl.getNotExistingSyncItem(null); 630 } 631 if (syncItemB == null) { 632 syncItemB = SyncItemImpl.getNotExistingSyncItem(null); 633 } 634 635 switch (syncItemA.getState()) { 636 case SyncItemState.NEW: 640 switch (syncItemB.getState()) { 641 case SyncItemState.NEW: 642 return new SyncConflict(syncItemA, syncItemB, 643 String.valueOf(SyncItemState.NEW) + 644 String.valueOf(SyncItemState.NEW) ); 645 case SyncItemState.UPDATED: 646 return new SyncConflict(syncItemA, syncItemB, 647 String.valueOf(SyncItemState.NEW) + 648 String.valueOf(SyncItemState.UPDATED) ); 649 case SyncItemState.DELETED: 650 return new SyncConflict(syncItemA, syncItemB, 651 String.valueOf(SyncItemState.NEW) + 652 String.valueOf(SyncItemState.DELETED) ); 653 case SyncItemState.SYNCHRONIZED: 654 return new SyncConflict(syncItemA, syncItemB, 655 String.valueOf(SyncItemState.NEW) + 656 String.valueOf(SyncItemState.SYNCHRONIZED) ); 657 case SyncItemState.NOT_EXISTING: 658 syncItemA.setPropertyValue(syncItemB.PROPERTY_TIMESTAMP, nextSync); 659 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NEW, false, true); 660 } 662 case SyncItemState.DELETED: 666 switch (syncItemB.getState()) { 667 case SyncItemState.NEW: 668 return new SyncConflict(syncItemA, syncItemB, 669 String.valueOf(SyncItemState.DELETED) + 670 String.valueOf(SyncItemState.NEW) ); 671 case SyncItemState.UPDATED: 672 syncItemB.setState(SyncItemState.NEW); 673 return new SyncConflict(syncItemA, syncItemB, 674 String.valueOf(SyncItemState.DELETED) + 675 String.valueOf(SyncItemState.NEW) ); 676 case SyncItemState.SYNCHRONIZED: 677 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.DELETE, false, true); 678 679 case SyncItemState.DELETED: 680 case SyncItemState.NOT_EXISTING: 681 return new SyncOperationImpl(principal, syncItemB, syncItemA, SyncOperation.NOP, false, false); 682 } 684 case SyncItemState.UPDATED: 688 switch (syncItemB.getState()) { 689 case SyncItemState.NEW: 690 return new SyncConflict(syncItemA, syncItemB, 691 String.valueOf(SyncItemState.UPDATED) + 692 String.valueOf(SyncItemState.NEW) ); 693 case SyncItemState.UPDATED: 694 return new SyncConflict(syncItemA, syncItemB, 695 String.valueOf(SyncItemState.UPDATED) + 696 String.valueOf(SyncItemState.UPDATED) ); 697 case SyncItemState.DELETED: 698 return new SyncConflict(syncItemA, syncItemB, 699 String.valueOf(SyncItemState.UPDATED) + 700 String.valueOf(SyncItemState.DELETED) ); 701 case SyncItemState.SYNCHRONIZED: 702 syncItemA.setPropertyValue(syncItemB.PROPERTY_TIMESTAMP, nextSync); 703 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.UPDATE, false, true); 704 case SyncItemState.NOT_EXISTING: 705 syncItemA.setPropertyValue(syncItemB.PROPERTY_TIMESTAMP, nextSync); 706 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NEW, false, true); 707 } 709 case SyncItemState.SYNCHRONIZED: 713 switch (syncItemB.getState()) { 714 case SyncItemState.NEW: 715 return new SyncConflict(syncItemA, syncItemB, 716 String.valueOf(SyncItemState.SYNCHRONIZED) + 717 String.valueOf(SyncItemState.NEW) ); 718 case SyncItemState.UPDATED: 719 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.UPDATE, true, false); 720 case SyncItemState.DELETED: 721 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.DELETE, true, false); 722 case SyncItemState.SYNCHRONIZED: 723 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NOP, false, false); 724 case SyncItemState.NOT_EXISTING: 725 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NEW, false, true); 726 } 728 case SyncItemState.NOT_EXISTING: 732 switch (syncItemB.getState()) { 733 case SyncItemState.NEW: 734 case SyncItemState.UPDATED: 735 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NEW, true, false); 736 case SyncItemState.SYNCHRONIZED: 737 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NEW, true, false); 738 case SyncItemState.NOT_EXISTING: 739 case SyncItemState.DELETED: 740 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NOP, false, false); 741 } 743 case SyncItemState.PARTIAL: 747 switch (syncItemB.getState()) { 748 case SyncItemState.NEW: 749 return new SyncConflict(syncItemA, syncItemB, 750 String.valueOf(SyncItemState.UPDATED) + 751 String.valueOf(SyncItemState.NEW) ); 752 case SyncItemState.UPDATED: 753 return new SyncConflict(syncItemA, syncItemB, 754 String.valueOf(SyncItemState.UPDATED) + 755 String.valueOf(SyncItemState.UPDATED) ); 756 case SyncItemState.DELETED: 757 return new SyncConflict(syncItemA, syncItemB, 758 String.valueOf(SyncItemState.UPDATED) + 759 String.valueOf(SyncItemState.DELETED) ); 760 case SyncItemState.SYNCHRONIZED: 761 return new SyncOperationImpl(principal, syncItemA, null, SyncOperation.ACCEPT_CHUNK, false, true); 762 case SyncItemState.NOT_EXISTING: 763 return new SyncOperationImpl(principal, syncItemA, null, SyncOperation.ACCEPT_CHUNK, false, true); 764 } 766 case SyncItemState.CONFLICT: 771 return new SyncConflict(syncItemA, syncItemB, "CX"); 772 } 774 return new SyncOperationImpl(principal, syncItemA, syncItemB, SyncOperation.NOP, false, false); 775 } 776 777 790 protected SyncOperationStatus[] execSyncOperation(SyncOperationImpl operation) { 791 SyncItem syncItemA = operation.getSyncItemA(), 792 syncItemB = operation.getSyncItemB(); 793 794 Principal owner = operation.getOwner(); 795 SyncOperationStatus[] status = null; 796 ModificationCommand cmd = null; 797 798 int size = 0, s = 0; 799 800 switch (operation.getOperation()) { 801 case SyncOperation.NEW: 802 status = new SyncOperationStatus[1]; 803 if (operation.isAOperation()) { 804 cmd = (ModificationCommand)syncItemA.getPropertyValue(SyncItemHelper.PROPERTY_COMMAND); 805 try { 806 syncItemA = sources[1].setSyncItem(owner, syncItemB); 807 operation.setSyncItemA(syncItemA);
|