001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: EntityModel.java,v 1.14.2.5 2008/01/07 15:14:20 cwl Exp $
007: */
008:
009: package com.sleepycat.persist.model;
010:
011: import java.util.ArrayList;
012: import java.util.Collections;
013: import java.util.List;
014: import java.util.Set;
015:
016: import com.sleepycat.persist.EntityStore;
017: import com.sleepycat.persist.PrimaryIndex;
018: import com.sleepycat.persist.SecondaryIndex;
019: import com.sleepycat.persist.impl.Format;
020: import com.sleepycat.persist.impl.PersistCatalog;
021: import com.sleepycat.persist.raw.RawObject;
022: import com.sleepycat.persist.raw.RawType;
023:
024: /**
025: * The base class for classes that provide entity model metadata. An {@link
026: * EntityModel} defines entity classes, primary keys, secondary keys, and
027: * relationships between entities. For each entity class that is part of the
028: * model, a single {@link PrimaryIndex} object and zero or more {@link
029: * SecondaryIndex} objects may be accessed via an {@link EntityStore}.
030: *
031: * <p>The built-in entity model, the {@link AnnotationModel}, is based on
032: * annotations that are added to entity classes and their key fields.
033: * Annotations are used in the examples in this package, and it is expected
034: * that annotations will normally be used; most readers should therefore skip
035: * to the {@link AnnotationModel} class. However, a custom entity model class
036: * may define its own metadata. This can be used to define entity classes and
037: * keys using mechanisms other than annotations.</p>
038: *
039: * <p>A concrete entity model class should extend this class and implement the
040: * {@link #getClassMetadata}, {@link #getEntityMetadata} and {@link
041: * #getKnownClasses} methods.</p>
042: *
043: * <p>This is an abstract class rather than an interface to allow adding
044: * capabilities to the model at a future date without causing
045: * incompatibilities. For example, a method may be added in the future for
046: * returning new information about the model and subclasses may override this
047: * method to return the new information. Any new methods will have default
048: * implementations that return default values, and the use of the new
049: * information will be optional.</p>
050: *
051: * @author Mark Hayes
052: */
053: public abstract class EntityModel {
054:
055: private PersistCatalog catalog;
056:
057: /**
058: * The default constructor for use by subclasses.
059: */
060: protected EntityModel() {
061: }
062:
063: /**
064: * Returns whether the model is associated with an open store.
065: *
066: * <p>The {@link #registerClass} method may only be called when the model
067: * is not yet open. Certain other methods may only be called when the
068: * model is open:</p>
069: * <ul>
070: * <li>{@link #convertRawObject}</li>
071: * <li>{@link #getAllRawTypeVersions}</li>
072: * <li>{@link #getRawType}</li>
073: * <li>{@link #getRawTypeVersion}</li>
074: * </ul>
075: */
076: public final boolean isOpen() {
077: return catalog != null;
078: }
079:
080: /**
081: * Registers a persistent class, most importantly, a {@link
082: * PersistentProxy} class. Any persistent class may be registered in
083: * advance of using it, to avoid the overhead of updating the catalog
084: * database when an instance of the class is first stored. This method
085: * <em>must</em> be called to register {@link PersistentProxy} classes.
086: * This method must be called before opening a store based on this model.
087: *
088: * @throws IllegalStateException if this method is called for a model that
089: * is associated with an open store.
090: *
091: * @throws IllegalArgumentException if the given class is not persistent
092: * or has a different class loader than previously registered classes.
093: */
094: public final void registerClass(Class persistentClass) {
095: if (catalog != null) {
096: throw new IllegalStateException("Store is already open");
097: } else {
098: String className = persistentClass.getName();
099: 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: * @throws IllegalStateException if this method is called for a model that
173: * is not associated with an open store.
174: */
175: public final RawType getRawTypeVersion(String className, int version) {
176: if (catalog != null) {
177: Format format = catalog.getLatestVersion(className);
178: while (format != null) {
179: if (version == format.getVersion()) {
180: return format;
181: }
182: }
183: return null;
184: } else {
185: throw new IllegalStateException("Store is not open");
186: }
187: }
188:
189: /**
190: * Returns all known versions of type information for a given class name,
191: * or null if no persistent version of the class is known.
192: *
193: * @param className the name of the latest version of the class.
194: *
195: * @return an unmodifiable list of types for the given class name in order
196: * from most recent to least recent.
197: *
198: * @throws IllegalStateException if this method is called for a model that
199: * is not associated with an open store.
200: */
201: public final List<RawType> getAllRawTypeVersions(String className) {
202: if (catalog != null) {
203: Format format = catalog.getLatestVersion(className);
204: if (format != null) {
205: List<RawType> list = new ArrayList<RawType>();
206: while (format != null) {
207: list.add(format);
208: format = format.getPreviousVersion();
209: }
210: return Collections.unmodifiableList(list);
211: } else {
212: return null;
213: }
214: } else {
215: throw new IllegalStateException("Store is not open");
216: }
217: }
218:
219: /**
220: * Converts a given raw object to a live object according to the current
221: * class definitions.
222: *
223: * <p>The given raw object must conform to the current class definitions.
224: * However, the raw type ({@link RawObject#getType}) is allowed to be from
225: * a different store, as long as the class names and the value types match.
226: * This allows converting raw objects that are read from one store to live
227: * objects in another store, for example, in a conversion program.</p>
228: */
229: public final Object convertRawObject(RawObject raw) {
230: return catalog.convertRawObject(raw, null);
231: }
232:
233: /**
234: * Calls Class.forName with the current thread context class loader. This
235: * method should be called by entity model implementations instead of
236: * calling Class.forName whenever loading an application class.
237: */
238: public static Class classForName(String className)
239: throws ClassNotFoundException {
240:
241: try {
242: return Class.forName(className, true /*initialize*/,
243: Thread.currentThread().getContextClassLoader());
244: } catch (ClassNotFoundException e) {
245: return Class.forName(className);
246: }
247: }
248: }
|