1/*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002,2008 Oracle. All rights reserved. 5 * 6 * $Id: EntityModel.java,v 1.1 2008/02/07 17:12:28 mark Exp $ 7 */ 8 9package com.sleepycat.persist.model; 10 11import java.util.ArrayList; 12import java.util.Collections; 13import java.util.List; 14import java.util.Set; 15 16import com.sleepycat.persist.EntityStore; 17import com.sleepycat.persist.PrimaryIndex; 18import com.sleepycat.persist.SecondaryIndex; 19import com.sleepycat.persist.impl.Format; 20import com.sleepycat.persist.impl.PersistCatalog; 21import com.sleepycat.persist.raw.RawObject; 22import com.sleepycat.persist.raw.RawType; 23 24/** 25 * The base class for classes that provide entity model metadata. An {@link 26 * EntityModel} defines entity classes, primary keys, secondary keys, and 27 * relationships between entities. For each entity class that is part of the 28 * model, a single {@link PrimaryIndex} object and zero or more {@link 29 * SecondaryIndex} objects may be accessed via an {@link EntityStore}. 30 * 31 * <p>The built-in entity model, the {@link AnnotationModel}, is based on 32 * annotations that are added to entity classes and their key fields. 33 * Annotations are used in the examples in this package, and it is expected 34 * that annotations will normally be used; most readers should therefore skip 35 * to the {@link AnnotationModel} class. However, a custom entity model class 36 * may define its own metadata. This can be used to define entity classes and 37 * keys using mechanisms other than annotations.</p> 38 * 39 * <p>A concrete entity model class should extend this class and implement the 40 * {@link #getClassMetadata}, {@link #getEntityMetadata} and {@link 41 * #getKnownClasses} methods.</p> 42 * 43 * <p>This is an abstract class rather than an interface to allow adding 44 * capabilities to the model at a future date without causing 45 * incompatibilities. For example, a method may be added in the future for 46 * returning new information about the model and subclasses may override this 47 * method to return the new information. Any new methods will have default 48 * implementations that return default values, and the use of the new 49 * information will be optional.</p> 50 * 51 * @author Mark Hayes 52 */ 53public abstract class EntityModel { 54 55 private PersistCatalog catalog; 56 57 /** 58 * The default constructor for use by subclasses. 59 */ 60 protected EntityModel() { 61 } 62 63 /** 64 * Returns whether the model is associated with an open store. 65 * 66 * <p>The {@link #registerClass} method may only be called when the model 67 * is not yet open. Certain other methods may only be called when the 68 * model is open:</p> 69 * <ul> 70 * <li>{@link #convertRawObject}</li> 71 * <li>{@link #getAllRawTypeVersions}</li> 72 * <li>{@link #getRawType}</li> 73 * <li>{@link #getRawTypeVersion}</li> 74 * </ul> 75 */ 76 public final boolean isOpen() { 77 return catalog != null; 78 } 79 80 /** 81 * Registers a persistent class, most importantly, a {@link 82 * PersistentProxy} class. Any persistent class may be registered in 83 * advance of using it, to avoid the overhead of updating the catalog 84 * database when an instance of the class is first stored. This method 85 * <em>must</em> be called to register {@link PersistentProxy} classes. 86 * This method must be called before opening a store based on this model. 87 * 88 * @throws IllegalStateException if this method is called for a model that 89 * is associated with an open store. 90 * 91 * @throws IllegalArgumentException if the given class is not persistent 92 * or has a different class loader than previously registered classes. 93 */ 94 public final void registerClass(Class persistentClass) { 95 if (catalog != null) { 96 throw new IllegalStateException("Store is already open"); 97 } else { 98 String className = persistentClass.getName(); 99 ClassMetadata meta = getClassMetadata(className); 100 if (meta == null) { 101 throw new IllegalArgumentException 102 ("Class is not persistent: " + className); 103 } 104 } 105 } 106 107 /** 108 * Gives this model access to the catalog, which is used for returning 109 * raw type information. 110 */ 111 void setCatalog(PersistCatalog catalog) { 112 this.catalog = catalog; 113 } 114 115 /** 116 * Returns the metadata for a given persistent class name, including proxy 117 * classes and entity classes. 118 * 119 * @return the metadata or null if the class is not persistent or does not 120 * exist. 121 */ 122 public abstract ClassMetadata getClassMetadata(String className); 123 124 /** 125 * Returns the metadata for a given entity class name. 126 * 127 * @return the metadata or null if the class is not an entity class or does 128 * not exist. 129 */ 130 public abstract EntityMetadata getEntityMetadata(String className); 131 132 /** 133 * Returns the names of all known persistent classes. A type becomes known 134 * when an instance of the type is stored for the first time or metadata or 135 * type information is queried for a specific class name. 136 * 137 * @return an unmodifiable set of class names. 138 * 139 * @throws IllegalStateException if this method is called for a model that 140 * is not associated with an open store. 141 */ 142 public abstract Set<String> getKnownClasses(); 143 144 /** 145 * Returns the type information for the current version of a given class, 146 * or null if the class is not currently persistent. 147 * 148 * @param className the name of the current version of the class. 149 * 150 * @throws IllegalStateException if this method is called for a model that 151 * is not associated with an open store. 152 */ 153 public final RawType getRawType(String className) { 154 if (catalog != null) { 155 return catalog.getFormat(className); 156 } else { 157 throw new IllegalStateException("Store is not open"); 158 } 159 } 160 161 /** 162 * Returns the type information for a given version of a given class, 163 * or null if the given version of the class is unknown. 164 * 165 * @param className the name of the latest version of the class. 166 * 167 * @param version the desired version of the class. 168 * 169 * @throws IllegalStateException if this method is called for a model that 170 * is not associated with an open store. 171 */ 172 public final RawType getRawTypeVersion(String className, int version) { 173 if (catalog != null) { 174 Format format = catalog.getLatestVersion(className); 175 while (format != null) { 176 if (version == format.getVersion()) { 177 return format; 178 } 179 } 180 return null; 181 } else { 182 throw new IllegalStateException("Store is not open"); 183 } 184 } 185 186 /** 187 * Returns all known versions of type information for a given class name, 188 * or null if no persistent version of the class is known. 189 * 190 * @param className the name of the latest version of the class. 191 * 192 * @return an unmodifiable list of types for the given class name in order 193 * from most recent to least recent. 194 * 195 * @throws IllegalStateException if this method is called for a model that 196 * is not associated with an open store. 197 */ 198 public final List<RawType> getAllRawTypeVersions(String className) { 199 if (catalog != null) { 200 Format format = catalog.getLatestVersion(className); 201 if (format != null) { 202 List<RawType> list = new ArrayList<RawType>(); 203 while (format != null) { 204 list.add(format); 205 format = format.getPreviousVersion(); 206 } 207 return Collections.unmodifiableList(list); 208 } else { 209 return null; 210 } 211 } else { 212 throw new IllegalStateException("Store is not open"); 213 } 214 } 215 216 /** 217 * Converts a given raw object to a live object according to the current 218 * class definitions. 219 * 220 * <p>The given raw object must conform to the current class definitions. 221 * However, the raw type ({@link RawObject#getType}) is allowed to be from 222 * a different store, as long as the class names and the value types match. 223 * This allows converting raw objects that are read from one store to live 224 * objects in another store, for example, in a conversion program.</p> 225 */ 226 public final Object convertRawObject(RawObject raw) { 227 return catalog.convertRawObject(raw, null); 228 } 229 230 /** 231 * Calls Class.forName with the current thread context class loader. This 232 * method should be called by entity model implementations instead of 233 * calling Class.forName whenever loading an application class. 234 */ 235 public static Class classForName(String className) 236 throws ClassNotFoundException { 237 238 try { 239 return Class.forName(className, true /*initialize*/, 240 Thread.currentThread().getContextClassLoader()); 241 } catch (ClassNotFoundException e) { 242 return Class.forName(className); 243 } 244 } 245} 246