1 8 9 package com.sleepycat.persist.impl; 10 11 import java.util.ArrayList ; 12 import java.util.List ; 13 import java.util.HashMap ; 14 import java.util.HashSet ; 15 import java.util.IdentityHashMap ; 16 import java.util.Map ; 17 import java.util.Set ; 18 19 import com.sleepycat.je.DatabaseException; 20 import com.sleepycat.je.DatabaseNotFoundException; 21 import com.sleepycat.je.Environment; 22 import com.sleepycat.je.Transaction; 23 import com.sleepycat.persist.evolve.Converter; 24 import com.sleepycat.persist.evolve.Deleter; 25 import com.sleepycat.persist.evolve.Mutation; 26 import com.sleepycat.persist.evolve.Mutations; 27 import com.sleepycat.persist.evolve.Renamer; 28 import com.sleepycat.persist.model.SecondaryKeyMetadata; 29 30 36 class Evolver { 37 38 static final int EVOLVE_NONE = 0; 39 static final int EVOLVE_NEEDED = 1; 40 static final int EVOLVE_FAILURE = 2; 41 42 private PersistCatalog catalog; 43 private String storePrefix; 44 private Mutations mutations; 45 private Map <String ,Format> newFormats; 46 private boolean forceEvolution; 47 private boolean disallowClassChanges; 48 private boolean formatsChanged; 49 private boolean nestedFormatsChanged; 50 private StringBuilder errors; 51 private Set <String > deleteDbs; 52 private Map <String ,String > renameDbs; 53 private Map <Format,Format> renameFormats; 54 private Map <Integer ,Boolean > evolvedFormats; 55 private List <Format> unprocessedFormats; 56 private Map <Format,Set <Format>> subclassMap; 57 58 Evolver(PersistCatalog catalog, 59 String storePrefix, 60 Mutations mutations, 61 Map <String ,Format> newFormats, 62 boolean forceEvolution, 63 boolean disallowClassChanges) { 64 this.catalog = catalog; 65 this.storePrefix = storePrefix; 66 this.mutations = mutations; 67 this.newFormats = newFormats; 68 this.forceEvolution = forceEvolution; 69 this.disallowClassChanges = disallowClassChanges; 70 errors = new StringBuilder (); 71 deleteDbs = new HashSet <String >(); 72 renameDbs = new HashMap <String ,String >(); 73 renameFormats = new IdentityHashMap <Format,Format>(); 74 evolvedFormats = new HashMap <Integer ,Boolean >(); 75 unprocessedFormats = new ArrayList <Format>(); 76 subclassMap = catalog.getSubclassMap(); 77 } 78 79 final Mutations getMutations() { 80 return mutations; 81 } 82 83 87 boolean areFormatsChanged() { 88 return formatsChanged; 89 } 90 91 private void setFormatsChanged(Format oldFormat) { 92 checkClassChangesAllowed(oldFormat); 93 formatsChanged = true; 94 nestedFormatsChanged = true; 95 96 if (PersistCatalog.expectNoClassChanges) { 97 throw new IllegalStateException ("expectNoClassChanges"); 98 } 99 } 100 101 private void checkClassChangesAllowed(Format oldFormat) { 102 if (disallowClassChanges) { 103 throw new IllegalStateException 104 ("When performing an upgrade changes are not allowed " + 105 "but were made to: " + oldFormat.getClassName()); 106 } 107 } 108 109 113 Set <Format> getSubclassFormats(Format superFormat) { 114 return subclassMap.get(superFormat); 115 } 116 117 122 String getErrors() { 123 if (errors.length() > 0) { 124 return errors.toString(); 125 } else { 126 return null; 127 } 128 } 129 130 133 private void addError(String error) { 134 if (errors.length() > 0) { 135 errors.append("\n---\n"); 136 } 137 errors.append(error); 138 } 139 140 private String getClassVersionLabel(Format format, String prefix) { 141 if (format != null) { 142 return prefix + 143 " class: " + format.getClassName() + 144 " version: " + format.getVersion(); 145 } else { 146 return ""; 147 } 148 } 149 150 153 void addEvolveError(Format oldFormat, 154 Format newFormat, 155 String scenario, 156 String error) { 157 checkClassChangesAllowed(oldFormat); 158 if (scenario == null) { 159 scenario = "Error"; 160 } 161 addError(scenario + " when evolving" + 162 getClassVersionLabel(oldFormat, "") + 163 getClassVersionLabel(newFormat, " to") + 164 " Error: " + error); 165 } 166 167 170 void addInvalidMutation(Format oldFormat, 171 Format newFormat, 172 Mutation mutation, 173 String error) { 174 checkClassChangesAllowed(oldFormat); 175 addError("Invalid mutation: " + mutation + 176 getClassVersionLabel(oldFormat, " For") + 177 getClassVersionLabel(newFormat, " New") + 178 " Error: " + error); 179 } 180 181 184 void addMissingMutation(Format oldFormat, 185 Format newFormat, 186 String error) { 187 checkClassChangesAllowed(oldFormat); 188 addError("Mutation is missing to evolve" + 189 getClassVersionLabel(oldFormat, "") + 190 getClassVersionLabel(newFormat, " to") + 191 " Error: " + error); 192 } 193 194 197 void addNonEntityFormat(Format oldFormat) { 198 unprocessedFormats.add(oldFormat); 199 } 200 201 209 void finishEvolution() { 210 211 for (Format oldFormat : unprocessedFormats) { 212 oldFormat.setUnused(true); 213 Integer oldFormatId = oldFormat.getId(); 214 if (!evolvedFormats.containsKey(oldFormatId)) { 215 evolvedFormats.put(oldFormatId, true); 216 boolean result = evolveFormatInternal(oldFormat); 217 evolvedFormats.put(oldFormatId, result); 218 } 219 } 220 } 221 222 226 boolean evolveFormat(Format oldFormat) { 227 boolean result; 228 boolean trackEntityChanges = 229 oldFormat.getLatestVersion().getEntityFormat() != null; 230 boolean saveNestedFormatsChanged = nestedFormatsChanged; 231 if (trackEntityChanges) { 232 nestedFormatsChanged = false; 233 } 234 Integer oldFormatId = oldFormat.getId(); 235 if (evolvedFormats.containsKey(oldFormatId)) { 236 result = evolvedFormats.get(oldFormatId); 237 } else { 238 evolvedFormats.put(oldFormatId, true); 239 result = evolveFormatInternal(oldFormat); 240 evolvedFormats.put(oldFormatId, result); 241 } 242 if (oldFormat.getLatestVersion().isNew()) { 243 nestedFormatsChanged = true; 244 } 245 if (trackEntityChanges) { 246 if (nestedFormatsChanged) { 247 Format latest = oldFormat.getLatestVersion().getEntityFormat(); 248 if (latest != null) { 249 latest.setEvolveNeeded(true); 250 } 251 } 252 nestedFormatsChanged = saveNestedFormatsChanged; 253 } 254 return result; 255 } 256 257 262 private boolean evolveFormatInternal(Format oldFormat) { 263 264 265 if (Format.isPredefined(oldFormat) || oldFormat.isDeleted()) { 266 return true; 267 } 268 269 270 String oldName = oldFormat.getClassName(); 271 int oldVersion = oldFormat.getVersion(); 272 Renamer renamer = mutations.getRenamer(oldName, oldVersion, null); 273 Deleter deleter = mutations.getDeleter(oldName, oldVersion, null); 274 Converter converter = 275 mutations.getConverter(oldName, oldVersion, null); 276 if (deleter != null && (converter != null || renamer != null)) { 277 addInvalidMutation 278 (oldFormat, null, deleter, 279 "Class Deleter not allowed along with a Renamer or " + 280 "Converter for the same class"); 281 return false; 282 } 283 284 289 String newName; 290 if (oldFormat.isArray()) { 291 if (deleter != null || converter != null || renamer != null) { 292 Mutation mutation = (deleter != null) ? deleter : 293 ((converter != null) ? converter : renamer); 294 addInvalidMutation 295 (oldFormat, null, mutation, 296 "Mutations not allowed for an array"); 297 return false; 298 } 299 Format compFormat = oldFormat.getComponentType(); 300 if (!evolveFormat(compFormat)) { 301 return false; 302 } 303 Format latest = compFormat.getLatestVersion(); 304 if (latest != compFormat) { 305 newName = (latest.isArray() ? "[" : "[L") + 306 latest.getClassName() + ';'; 307 } else { 308 newName = oldName; 309 } 310 } else { 311 newName = (renamer != null) ? renamer.getNewName() : oldName; 312 } 313 314 315 Format newFormat; 316 String newFormatException; 317 try { 318 Class newClass = SimpleCatalog.classForName(newName); 319 try { 320 newFormat = catalog.createFormat(newClass, newFormats); 321 assert newFormat != oldFormat : newFormat.getClassName(); 322 newFormatException = null; 323 } catch (Exception e) { 324 newFormat = null; 325 newFormatException = e.toString(); 326 } 327 } catch (ClassNotFoundException e) { 328 newFormat = null; 329 newFormatException = e.toString(); 330 } 331 332 if (newFormat != null) { 333 334 342 if (oldFormat != oldFormat.getLatestVersion() && 343 newFormat.getPreviousVersion() == null) { 344 assert newFormats.containsValue(newFormat); 345 Format oldLatestFormat = oldFormat.getLatestVersion(); 346 evolveFormat(oldLatestFormat); 347 if (oldLatestFormat == oldLatestFormat.getLatestVersion()) { 348 assert !newFormats.containsValue(newFormat); 349 350 newFormat = oldLatestFormat; 351 } 352 } 353 354 364 if (!forceEvolution && 365 newFormat == oldFormat.getLatestVersion()) { 366 return true; 367 } 368 } 369 370 371 if (renamer != null) { 372 if (!applyRenamer(renamer, oldFormat, newFormat)) { 373 return false; 374 } 375 } 376 377 378 if (converter != null) { 379 if (oldFormat.isEntity()) { 380 if (newFormat == null || !newFormat.isEntity()) { 381 addInvalidMutation 382 (oldFormat, newFormat, deleter, 383 "Class converter not allowed for an entity class " + 384 "that is no longer present or not having an " + 385 "@Entity annotation"); 386 return false; 387 } 388 if (!oldFormat.evolveMetadata(newFormat, converter, this)) { 389 return false; 390 } 391 } 392 return applyConverter(converter, oldFormat, newFormat); 393 } 394 395 396 boolean needDeleter = 397 (newFormat == null) || 398 (newFormat.isEntity() != oldFormat.isEntity()); 399 if (deleter != null) { 400 if (!needDeleter) { 401 addInvalidMutation 402 (oldFormat, newFormat, deleter, 403 "Class deleter not allowed when the class and its " + 404 "@Entity or @Persistent annotation is still present"); 405 return false; 406 } 407 return applyDeleter(deleter, oldFormat, newFormat); 408 } else { 409 if (needDeleter) { 410 if (newFormat == null) { 411 assert newFormatException != null; 412 413 addMissingMutation 414 (oldFormat, newFormat, newFormatException); 415 } else { 416 addMissingMutation 417 (oldFormat, newFormat, 418 "@Entity switched to/from @Persistent"); 419 } 420 return false; 421 } 422 } 423 424 429 return oldFormat.evolve(newFormat, this); 430 } 431 432 436 void useOldFormat(Format oldFormat, Format newFormat) { 437 Format renamedFormat = renameFormats.get(oldFormat); 438 if (renamedFormat != null) { 439 440 445 assert renamedFormat == newFormat; 446 useEvolvedFormat(oldFormat, renamedFormat, renamedFormat); 447 } else if (newFormat != null && 448 oldFormat.getVersion() != newFormat.getVersion()) { 449 450 useEvolvedFormat(oldFormat, newFormat, newFormat); 451 } else { 452 453 catalog.useExistingFormat(oldFormat); 454 if (newFormat != null) { 455 newFormats.remove(newFormat.getClassName()); 456 } 457 } 458 } 459 460 464 void useEvolvedFormat(Format oldFormat, 465 Reader evolveReader, 466 Format newFormat) { 467 oldFormat.setReader(evolveReader); 468 if (newFormat != null) { 469 oldFormat.setLatestVersion(newFormat); 470 } 471 setFormatsChanged(oldFormat); 472 } 473 474 private boolean applyRenamer(Renamer renamer, 475 Format oldFormat, 476 Format newFormat) { 477 if (!checkUpdatedVersion(renamer, oldFormat, newFormat)) { 478 return false; 479 } 480 if (oldFormat.isEntity() && oldFormat.isCurrentVersion()) { 481 String newClassName = newFormat.getClassName(); 482 String oldClassName = oldFormat.getClassName(); 483 484 renameDbs.put 485 (Store.makePriDbName(storePrefix, oldClassName), 486 Store.makePriDbName(storePrefix, newClassName)); 487 for (SecondaryKeyMetadata keyMeta : 488 oldFormat.getEntityMetadata().getSecondaryKeys().values()) { 489 String keyName = keyMeta.getKeyName(); 490 renameDbs.put 491 (Store.makeSecDbName(storePrefix, oldClassName, keyName), 492 Store.makeSecDbName(storePrefix, newClassName, keyName)); 493 } 494 } 495 496 500 renameFormats.put(oldFormat, newFormat); 501 502 setFormatsChanged(oldFormat); 503 return true; 504 } 505 506 509 void renameSecondaryDatabase(Format oldFormat, 510 Format newFormat, 511 String oldKeyName, 512 String newKeyName) { 513 renameDbs.put 514 (Store.makeSecDbName 515 (storePrefix, oldFormat.getClassName(), oldKeyName), 516 Store.makeSecDbName 517 (storePrefix, newFormat.getClassName(), newKeyName)); 518 } 519 520 private boolean applyDeleter(Deleter deleter, 521 Format oldFormat, 522 Format newFormat) { 523 if (!checkUpdatedVersion(deleter, oldFormat, newFormat)) { 524 return false; 525 } 526 if (oldFormat.isEntity() && oldFormat.isCurrentVersion()) { 527 528 String className = oldFormat.getClassName(); 529 deleteDbs.add(Store.makePriDbName(storePrefix, className)); 530 for (SecondaryKeyMetadata keyMeta : 531 oldFormat.getEntityMetadata().getSecondaryKeys().values()) { 532 deleteDbs.add(Store.makeSecDbName 533 (storePrefix, className, keyMeta.getKeyName())); 534 } 535 } 536 537 541 oldFormat.setDeleted(true); 542 if (newFormat != null) { 543 oldFormat.setLatestVersion(newFormat); 544 } 545 546 setFormatsChanged(oldFormat); 547 return true; 548 } 549 550 553 void deleteSecondaryDatabase(Format oldFormat, String keyName) { 554 deleteDbs.add(Store.makeSecDbName 555 (storePrefix, oldFormat.getClassName(), keyName)); 556 } 557 558 private boolean applyConverter(Converter converter, 559 Format oldFormat, 560 Format newFormat) { 561 if (!checkUpdatedVersion(converter, oldFormat, newFormat)) { 562 return false; 563 } 564 Reader reader = new ConverterReader(converter); 565 useEvolvedFormat(oldFormat, reader, newFormat); 566 return true; 567 } 568 569 boolean isClassConverted(Format format) { 570 return format.getReader() instanceof ConverterReader; 571 } 572 573 private boolean checkUpdatedVersion(Mutation mutation, 574 Format oldFormat, 575 Format newFormat) { 576 if (newFormat != null && 577 !oldFormat.isEnum() && 578 newFormat.getVersion() <= oldFormat.getVersion()) { 579 addInvalidMutation 580 (oldFormat, newFormat, mutation, 581 "A new higher version number must be assigned"); 582 return false; 583 } else { 584 return true; 585 } 586 } 587 588 boolean checkUpdatedVersion(String scenario, 589 Format oldFormat, 590 Format newFormat) { 591 if (newFormat != null && 592 !oldFormat.isEnum() && 593 newFormat.getVersion() <= oldFormat.getVersion()) { 594 addEvolveError 595 (oldFormat, newFormat, scenario, 596 "A new higher version number must be assigned"); 597 return false; 598 } else { 599 return true; 600 } 601 } 602 603 void renameAndRemoveDatabases(Environment env, Transaction txn) 604 throws DatabaseException { 605 606 for (String dbName : deleteDbs) { 607 try { 608 env.removeDatabase(txn, dbName); 609 } catch (DatabaseNotFoundException ignored) { 610 } 611 } 612 for (Map.Entry <String ,String > entry : renameDbs.entrySet()) { 613 try { 614 env.renameDatabase(txn, entry.getKey(), entry.getValue()); 615 } catch (DatabaseNotFoundException ignored) { 616 } 617 } 618 } 619 620 623 int evolveRequiredKeyField(Format oldParent, 624 Format newParent, 625 FieldInfo oldField, 626 FieldInfo newField) { 627 int result = EVOLVE_NONE; 628 String oldName = oldField.getName(); 629 final String FIELD_KIND = 630 "primary key field or composite key class field"; 631 final String FIELD_LABEL = 632 FIELD_KIND + ": " + oldName; 633 634 if (newField == null) { 635 addMissingMutation 636 (oldParent, newParent, 637 "Field is missing and deletion is not allowed for a " + 638 FIELD_LABEL); 639 return EVOLVE_FAILURE; 640 } 641 642 643 Deleter deleter = mutations.getDeleter 644 (oldParent.getClassName(), oldParent.getVersion(), oldName); 645 if (deleter != null) { 646 addInvalidMutation 647 (oldParent, newParent, deleter, 648 "Deleter is not allowed for a " + FIELD_LABEL); 649 return EVOLVE_FAILURE; 650 } 651 Converter converter = mutations.getConverter 652 (oldParent.getClassName(), oldParent.getVersion(), oldName); 653 if (converter != null) { 654 addInvalidMutation 655 (oldParent, newParent, converter, 656 "Converter is not allowed for a " + FIELD_LABEL); 657 return EVOLVE_FAILURE; 658 } 659 Renamer renamer = mutations.getRenamer 660 (oldParent.getClassName(), oldParent.getVersion(), oldName); 661 String newName = newField.getName(); 662 if (renamer != null) { 663 if (!renamer.getNewName().equals(newName)) { 664 addInvalidMutation 665 (oldParent, newParent, converter, 666 "Converter is not allowed for a " + FIELD_LABEL); 667 return EVOLVE_FAILURE; 668 } 669 result = EVOLVE_NEEDED; 670 } else { 671 if (!oldName.equals(newName)) { 672 addMissingMutation 673 (oldParent, newParent, 674 "Renamer is required when field name is changed from: " + 675 oldName + " to: " + newName); 676 return EVOLVE_FAILURE; 677 } 678 } 679 680 683 Format oldFieldFormat = oldField.getType(); 684 if (!evolveFormat(oldFieldFormat)) { 685 return EVOLVE_FAILURE; 686 } 687 Format oldLatestFormat = oldFieldFormat.getLatestVersion(); 688 Format newFieldFormat = newField.getType(); 689 690 if (oldLatestFormat.getClassName().equals 691 (newFieldFormat.getClassName())) { 692 693 return result; 694 } else if ((oldLatestFormat.getWrapperFormat() != null && 695 oldLatestFormat.getWrapperFormat().getId() == 696 newFieldFormat.getId()) || 697 (newFieldFormat.getWrapperFormat() != null && 698 newFieldFormat.getWrapperFormat().getId() == 699 oldLatestFormat.getId())) { 700 701 return EVOLVE_NEEDED; 702 } else { 703 704 addEvolveError 705 (oldParent, newParent, 706 "Type may not be changed for a " + FIELD_KIND, 707 "Old field type: " + oldLatestFormat.getClassName() + 708 " is not compatible with the new type: " + 709 newFieldFormat.getClassName() + 710 " for field: " + oldName); 711 return EVOLVE_FAILURE; 712 } 713 } 714 } 715 | Popular Tags |