1 8 9 package com.sleepycat.persist.impl; 10 11 import java.io.ByteArrayInputStream ; 12 import java.io.ByteArrayOutputStream ; 13 import java.io.IOException ; 14 import java.io.ObjectInputStream ; 15 import java.io.ObjectOutputStream ; 16 import java.io.Serializable ; 17 import java.util.ArrayList ; 18 import java.util.Collection ; 19 import java.util.HashMap ; 20 import java.util.HashSet ; 21 import java.util.IdentityHashMap ; 22 import java.util.List ; 23 import java.util.Map ; 24 import java.util.NoSuchElementException ; 25 import java.util.Set ; 26 27 import com.sleepycat.bind.tuple.IntegerBinding; 28 import com.sleepycat.je.Database; 29 import com.sleepycat.je.DatabaseConfig; 30 import com.sleepycat.je.DatabaseEntry; 31 import com.sleepycat.je.DatabaseException; 32 import com.sleepycat.je.Environment; 33 import com.sleepycat.je.OperationStatus; 34 import com.sleepycat.je.Transaction; 35 import com.sleepycat.persist.evolve.DeletedClassException; 36 import com.sleepycat.persist.evolve.IncompatibleClassException; 37 import com.sleepycat.persist.evolve.Mutations; 38 import com.sleepycat.persist.evolve.Renamer; 39 import com.sleepycat.persist.model.AnnotationModel; 40 import com.sleepycat.persist.model.ClassMetadata; 41 import com.sleepycat.persist.model.EntityMetadata; 42 import com.sleepycat.persist.model.EntityModel; 43 import com.sleepycat.persist.raw.RawObject; 44 import com.sleepycat.util.RuntimeExceptionWrapper; 45 46 52 public class PersistCatalog implements Catalog { 53 54 59 private static final byte[] DATA_KEY = getIntBytes(-1); 60 61 66 private static final byte[] BETA_MUTATIONS_KEY = getIntBytes(-2); 67 68 private static byte[] getIntBytes(int val) { 69 DatabaseEntry entry = new DatabaseEntry(); 70 IntegerBinding.intToEntry(val, entry); 71 assert entry.getSize() == 4 && entry.getData().length == 4; 72 return entry.getData(); 73 } 74 75 78 public static boolean expectNoClassChanges; 79 80 83 private static class Data implements Serializable { 84 List <Format> formatList; 85 Mutations mutations; 86 transient boolean betaVersion; 87 } 88 89 98 private volatile List <Format> formatList; 99 100 106 private volatile Map <String ,Format> formatMap; 107 108 115 private volatile Map <String ,Format> latestFormatMap; 116 117 122 private Map <String ,String > proxyClassMap; 123 124 private boolean rawAccess; 125 private EntityModel model; 126 private Mutations mutations; 127 private Database db; 128 private int openCount; 129 130 134 private Store store; 135 136 145 public PersistCatalog(Transaction txn, 146 Environment env, 147 String storePrefix, 148 String dbName, 149 DatabaseConfig dbConfig, 150 EntityModel modelParam, 151 Mutations mutationsParam, 152 boolean rawAccess, 153 Store store) 154 throws DatabaseException { 155 156 this.rawAccess = rawAccess; 157 this.store = store; 158 db = env.openDatabase(txn, dbName, dbConfig); 159 openCount = 1; 160 boolean success = false; 161 try { 162 Data catalogData = readData(txn); 163 mutations = catalogData.mutations; 164 if (mutations == null) { 165 mutations = new Mutations(); 166 } 167 168 173 boolean forceWriteData = catalogData.betaVersion; 174 boolean disallowClassChanges = catalogData.betaVersion; 175 176 180 boolean forceEvolution = false; 181 if (mutationsParam != null && 182 !mutations.equals(mutationsParam)) { 183 mutations = mutationsParam; 184 forceWriteData = true; 185 forceEvolution = true; 186 } 187 188 189 formatList = catalogData.formatList; 190 if (formatList == null) { 191 formatList = SimpleCatalog.copyFormatList(); 192 193 197 Format format = new NonPersistentFormat(Object .class); 198 format.setId(Format.ID_OBJECT); 199 formatList.set(Format.ID_OBJECT, format); 200 format = new NonPersistentFormat(Number .class); 201 format.setId(Format.ID_NUMBER); 202 formatList.set(Format.ID_NUMBER, format); 203 } else { 204 if (SimpleCatalog.copyMissingFormats(formatList)) { 205 forceWriteData = true; 206 } 207 } 208 209 210 if (catalogData.betaVersion) { 211 Map <String ,Format> formatMap = new HashMap <String ,Format>(); 212 for (Format format : formatList) { 213 if (format != null) { 214 formatMap.put(format.getClassName(), format); 215 } 216 } 217 for (Format format : formatList) { 218 if (format != null) { 219 format.migrateFromBeta(formatMap); 220 } 221 } 222 } 223 224 228 formatMap = new HashMap <String ,Format>(formatList.size()); 229 latestFormatMap = new HashMap <String ,Format>(formatList.size()); 230 if (rawAccess) { 231 for (Format format : formatList) { 232 if (format != null) { 233 String name = format.getClassName(); 234 if (format.isCurrentVersion()) { 235 formatMap.put(name, format); 236 } 237 if (format == format.getLatestVersion()) { 238 latestFormatMap.put(name, format); 239 } 240 } 241 } 242 for (Format format : formatList) { 243 if (format != null) { 244 format.initializeIfNeeded(this); 245 } 246 } 247 model = new StoredModel(this); 248 success = true; 249 return; 250 } 251 252 256 if (modelParam != null) { 257 model = modelParam; 258 } else { 259 model = new AnnotationModel(); 260 } 261 262 266 for (int i = 0; i <= Format.ID_PREDEFINED; i += 1) { 267 Format simpleFormat = formatList.get(i); 268 if (simpleFormat != null) { 269 formatMap.put(simpleFormat.getClassName(), simpleFormat); 270 } 271 } 272 273 277 List <String > knownClasses = 278 new ArrayList <String >(model.getKnownClasses()); 279 addPredefinedProxies(knownClasses); 280 281 287 proxyClassMap = new HashMap <String ,String >(); 288 for (Format oldFormat : formatList) { 289 if (oldFormat == null || Format.isPredefined(oldFormat)) { 290 continue; 291 } 292 String oldName = oldFormat.getClassName(); 293 Renamer renamer = mutations.getRenamer 294 (oldName, oldFormat.getVersion(), null); 295 String newName = 296 (renamer != null) ? renamer.getNewName() : oldName; 297 addProxiedClass(newName); 298 } 299 for (String className : knownClasses) { 300 addProxiedClass(className); 301 } 302 303 312 Map <String ,Format> newFormats = new HashMap <String ,Format>(); 313 for (String className : knownClasses) { 314 createFormat(className, newFormats); 315 } 316 317 322 Evolver evolver = new Evolver 323 (this, storePrefix, mutations, newFormats, forceEvolution, 324 disallowClassChanges); 325 for (Format oldFormat : formatList) { 326 if (oldFormat == null || Format.isPredefined(oldFormat)) { 327 continue; 328 } 329 if (oldFormat.isEntity()) { 330 evolver.evolveFormat(oldFormat); 331 } else { 332 evolver.addNonEntityFormat(oldFormat); 333 } 334 } 335 evolver.finishEvolution(); 336 String errors = evolver.getErrors(); 337 if (errors != null) { 338 throw new IncompatibleClassException(errors); 339 } 340 341 345 for (Format newFormat : newFormats.values()) { 346 addFormat(newFormat); 347 } 348 349 350 for (Format format : formatList) { 351 if (format != null) { 352 format.initializeIfNeeded(this); 353 if (format == format.getLatestVersion()) { 354 latestFormatMap.put(format.getClassName(), format); 355 } 356 } 357 } 358 359 boolean needWrite = 360 newFormats.size() > 0 || 361 evolver.areFormatsChanged(); 362 363 364 if (expectNoClassChanges && needWrite) { 365 throw new IllegalStateException 366 ("Unexpected changes " + 367 " newFormats.size=" + newFormats.size() + 368 " areFormatsChanged=" + evolver.areFormatsChanged()); 369 } 370 371 372 if ((needWrite || forceWriteData) && 373 !db.getConfig().getReadOnly()) { 374 375 379 evolver.renameAndRemoveDatabases(env, txn); 380 381 386 catalogData.formatList = formatList; 387 catalogData.mutations = mutations; 388 writeData(txn, catalogData); 389 } else if (forceWriteData) { 390 throw new IllegalArgumentException 391 ("When an upgrade is required the store may not be " + 392 "opened read-only"); 393 } 394 395 396 proxyClassMap = null; 397 398 success = true; 399 } finally { 400 if (!success) { 401 close(); 402 } 403 } 404 } 405 406 public void getEntityFormats(Collection <Format> entityFormats) { 407 for (Format format : formatMap.values()) { 408 if (format.isEntity()) { 409 entityFormats.add(format); 410 } 411 } 412 } 413 414 private void addProxiedClass(String className) { 415 ClassMetadata metadata = model.getClassMetadata(className); 416 if (metadata != null) { 417 String proxiedClassName = metadata.getProxiedClassName(); 418 if (proxiedClassName != null) { 419 proxyClassMap.put(proxiedClassName, className); 420 } 421 } 422 } 423 424 private void addPredefinedProxies(List <String > knownClasses) { 425 knownClasses.add(CollectionProxy.ArrayListProxy.class.getName()); 426 knownClasses.add(CollectionProxy.LinkedListProxy.class.getName()); 427 knownClasses.add(CollectionProxy.HashSetProxy.class.getName()); 428 knownClasses.add(CollectionProxy.TreeSetProxy.class.getName()); 429 knownClasses.add(MapProxy.HashMapProxy.class.getName()); 430 knownClasses.add(MapProxy.TreeMapProxy.class.getName()); 431 } 432 433 439 Map <Format,Set <Format>> getSubclassMap() { 440 Map <Format,Set <Format>> subclassMap = 441 new HashMap <Format,Set <Format>>(); 442 for (Format format : formatList) { 443 if (format == null || Format.isPredefined(format)) { 444 continue; 445 } 446 Format superFormat = format.getSuperFormat(); 447 if (superFormat != null) { 448 Set <Format> subclass = subclassMap.get(superFormat); 449 if (subclass == null) { 450 subclass = new HashSet <Format>(); 451 subclassMap.put(superFormat, subclass); 452 } 453 subclass.add(format); 454 } 455 } 456 return subclassMap; 457 } 458 459 462 public EntityModel getResolvedModel() { 463 return model; 464 } 465 466 469 public void openExisting() { 470 openCount += 1; 471 } 472 473 478 public boolean close() 479 throws DatabaseException { 480 481 if (openCount == 0) { 482 throw new IllegalStateException ("Catalog is not open"); 483 } else { 484 openCount -= 1; 485 if (openCount == 0) { 486 Database dbToClose = db; 487 db = null; 488 dbToClose.close(); 489 return true; 490 } else { 491 return false; 492 } 493 } 494 } 495 496 499 public Mutations getMutations() { 500 return mutations; 501 } 502 503 507 public Format createFormat(String clsName, Map <String ,Format> newFormats) { 508 Class type; 509 try { 510 type = SimpleCatalog.classForName(clsName); 511 } catch (ClassNotFoundException e) { 512 throw new IllegalStateException 513 ("Class does not exist: " + clsName); 514 } 515 return createFormat(type, newFormats); 516 } 517 518 523 public Format createFormat(Class type, Map <String ,Format> newFormats) { 524 525 String className = type.getName(); 526 Format format = newFormats.get(className); 527 if (format != null) { 528 return format; 529 } 530 format = formatMap.get(className); 531 if (format != null) { 532 return format; 533 } 534 535 assert !SimpleCatalog.isSimpleType(type) : className; 536 537 String proxyClassName = null; 538 if (proxyClassMap != null) { 539 proxyClassName = proxyClassMap.get(className); 540 } 541 if (proxyClassName != null) { 542 format = new ProxiedFormat(type, proxyClassName); 543 } else if (type.isArray()) { 544 format = type.getComponentType().isPrimitive() ? 545 (new PrimitiveArrayFormat(type)) : 546 (new ObjectArrayFormat(type)); 547 } else if (type.isEnum()) { 548 format = new EnumFormat(type); 549 } else if (type == Object .class || type.isInterface()) { 550 format = new NonPersistentFormat(type); 551 } else { 552 ClassMetadata metadata = model.getClassMetadata(className); 553 if (metadata == null) { 554 throw new IllegalArgumentException 555 ("Class is not persistent: " + className); 556 } 557 if (metadata.getCompositeKeyFields() != null && 558 (metadata.getPrimaryKey() != null || 559 metadata.getSecondaryKeys() != null)) { 560 throw new IllegalArgumentException 561 ("A composite key class may not have primary or" + 562 " secondary key fields: " + type.getName()); 563 } 564 try { 565 type.getDeclaredConstructor(); 566 } catch (NoSuchMethodException e) { 567 throw new IllegalArgumentException 568 ("No default constructor: " + type.getName(), e); 569 } 570 if (metadata.getCompositeKeyFields() != null) { 571 format = new CompositeKeyFormat 572 (type, metadata, metadata.getCompositeKeyFields()); 573 } else { 574 EntityMetadata entityMetadata = 575 model.getEntityMetadata(className); 576 format = new ComplexFormat(type, metadata, entityMetadata); 577 } 578 } 579 580 newFormats.put(className, format); 581 format.collectRelatedFormats(this, newFormats); 582 583 return format; 584 } 585 586 589 private void addFormat(Format format) { 590 addFormat(format, formatList, formatMap); 591 } 592 593 597 private void addFormat(Format format, 598 List <Format> list, 599 Map <String ,Format> map) { 600 format.setId(list.size()); 601 list.add(format); 602 map.put(format.getClassName(), format); 603 } 604 605 609 void useExistingFormat(Format oldFormat) { 610 assert oldFormat.isCurrentVersion(); 611 formatMap.put(oldFormat.getClassName(), oldFormat); 612 } 613 614 617 Set <String > getModelClasses() { 618 Set <String > classes = new HashSet <String >(); 619 for (Format format : formatMap.values()) { 620 if (format.isModelClass()) { 621 classes.add(format.getClassName()); 622 } 623 } 624 return classes; 625 } 626 627 public Format getFormat(int formatId) { 628 try { 629 Format format = formatList.get(formatId); 630 if (format == null) { 631 throw new DeletedClassException 632 ("Format does not exist: " + formatId); 633 } 634 return format; 635 } catch (NoSuchElementException e) { 636 throw new DeletedClassException 637 ("Format does not exist: " + formatId); 638 } 639 } 640 641 642 652 public Format getFormat(Class cls) { 653 Format format = formatMap.get(cls.getName()); 654 if (format == null) { 655 if (model != null) { 656 format = addNewFormat(cls); 657 658 if (store != null) { 659 Format entityFormat = format.getEntityFormat(); 660 if (entityFormat != null && entityFormat != format) { 661 try { 662 store.openSecondaryIndexes 663 (entityFormat.getEntityMetadata()); 664 } catch (DatabaseException e) { 665 throw new RuntimeExceptionWrapper(e); 666 } 667 } 668 } 669 } 670 if (format == null) { 671 throw new IllegalArgumentException 672 ("Class is not persistent: " + cls.getName()); 673 } 674 } 675 return format; 676 } 677 678 public Format getFormat(String className) { 679 return formatMap.get(className); 680 } 681 682 public Format getLatestVersion(String className) { 683 return latestFormatMap.get(className); 684 } 685 686 693 private synchronized Format addNewFormat(Class cls) { 694 695 702 Format format = formatMap.get(cls.getName()); 703 if (format != null) { 704 return format; 705 } 706 707 708 List <Format> newFormatList = new ArrayList <Format>(formatList); 709 Map <String ,Format> newFormatMap = 710 new HashMap <String ,Format>(formatMap); 711 Map <String ,Format> newLatestFormatMap = 712 new HashMap <String ,Format>(latestFormatMap); 713 714 715 Map <String ,Format> newFormats = new HashMap <String ,Format>(); 716 format = createFormat(cls, newFormats); 717 for (Format newFormat : newFormats.values()) { 718 addFormat(newFormat, newFormatList, newFormatMap); 719 } 720 721 725 Catalog newFormatCatalog = 726 new ReadOnlyCatalog(newFormatList, newFormatMap); 727 for (Format newFormat : newFormats.values()) { 728 newFormat.initializeIfNeeded(newFormatCatalog); 729 newLatestFormatMap.put(newFormat.getClassName(), newFormat); 730 } 731 732 738 try { 739 Data catalogData = new Data(); 740 catalogData.formatList = newFormatList; 741 catalogData.mutations = mutations; 742 writeData(null, catalogData); 743 } catch (DatabaseException e) { 744 throw new RuntimeExceptionWrapper(e); 745 } 746 formatList = newFormatList; 747 formatMap = newFormatMap; 748 latestFormatMap = newLatestFormatMap; 749 750 return format; 751 } 752 753 758 public synchronized void flush() 759 throws DatabaseException { 760 761 Data catalogData = new Data(); 762 catalogData.formatList = formatList; 763 catalogData.mutations = mutations; 764 writeData(null, catalogData); 765 } 766 767 772 private Data readData(Transaction txn) 773 throws DatabaseException { 774 775 Data catalogData; 776 DatabaseEntry key = new DatabaseEntry(DATA_KEY); 777 DatabaseEntry data = new DatabaseEntry(); 778 OperationStatus status = db.get(txn, key, data, null); 779 if (status == OperationStatus.SUCCESS) { 780 ByteArrayInputStream bais = new ByteArrayInputStream 781 (data.getData(), data.getOffset(), data.getSize()); 782 try { 783 ObjectInputStream ois = new ObjectInputStream (bais); 784 Object object = ois.readObject(); 785 assert ois.available() == 0; 786 if (object instanceof Data) { 787 catalogData = (Data) object; 788 } else { 789 if (!(object instanceof List )) { 790 throw new IllegalStateException 791 (object.getClass().getName()); 792 } 793 catalogData = new Data(); 794 catalogData.formatList = (List ) object; 795 catalogData.betaVersion = true; 796 } 797 return catalogData; 798 } catch (ClassNotFoundException e) { 799 throw new DatabaseException(e); 800 } catch (IOException e) { 801 throw new DatabaseException(e); 802 } 803 } else { 804 catalogData = new Data(); 805 } 806 return catalogData; 807 } 808 809 812 private void writeData(Transaction txn, Data catalogData) 813 throws DatabaseException { 814 815 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 816 try { 817 ObjectOutputStream oos = new ObjectOutputStream (baos); 818 oos.writeObject(catalogData); 819 } catch (IOException e) { 820 throw new DatabaseException(e); 821 } 822 DatabaseEntry key = new DatabaseEntry(DATA_KEY); 823 DatabaseEntry data = new DatabaseEntry(baos.toByteArray()); 824 db.put(txn, key, data); 825 826 830 if (catalogData.betaVersion) { 831 key.setData(BETA_MUTATIONS_KEY); 832 db.delete(txn, key); 833 catalogData.betaVersion = false; 834 } 835 } 836 837 public boolean isRawAccess() { 838 return rawAccess; 839 } 840 841 public Object convertRawObject(RawObject o, IdentityHashMap converted) { 842 Format format = (Format) o.getType(); 843 if (this != format.getCatalog()) { 844 String className = format.getClassName(); 845 format = getFormat(className); 846 if (format == null) { 847 throw new IllegalArgumentException 848 ("External raw type not found: " + className); 849 } 850 } 851 Format proxiedFormat = format.getProxiedFormat(); 852 if (proxiedFormat != null) { 853 format = proxiedFormat; 854 } 855 if (converted == null) { 856 converted = new IdentityHashMap (); 857 } 858 return format.convertRawObject(this, false, o, converted); 859 } 860 861 public void dump() { 862 System.out.println("--- Begin formats ---"); 863 for (Format format : formatList) { 864 if (format != null) { 865 System.out.println 866 ("ID: " + format.getId() + 867 " class: " + format.getClassName() + 868 " version: " + format.getVersion() + 869 " current: " + 870 (format == formatMap.get(format.getClassName()))); 871 } 872 } 873 System.out.println("--- End formats ---"); 874 } 875 } 876 | Popular Tags |