KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > indexing > ObjectStore


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.indexing;
12
13 import java.util.*;
14
15 public class ObjectStore implements Observer {
16
17     public static final int MAXIMUM_OBJECT_SIZE = ObjectPage.ObjectSpaceSize - ObjectHeader.SIZE;
18     protected static final int CurrentObjectStoreVersion = 1;
19     protected static final int ObjectStoreMetadataAreaID = 1;
20
21     protected PageStore pageStore; /* initialized in open */
22     protected String JavaDoc name; /* initialized in open */
23     protected Map acquiredObjects; // objects which are currently active
24
protected Map modifiedObjects; // objects that have been modified since the last sync point.
25
protected LinkedList cachedObjects; // objects that are just hanging around waiting to be acquired. Kept for performance.
26
protected Set phantoms; // the set of objects that needs to be deleted since the last sync point.
27
protected ReservationTable reservations;
28
29     protected ObjectStorePagePolicy pagePolicy;
30     protected AbstractObjectPolicy objectPolicy;
31
32     /**
33      * Creates a repository for the pathname.
34      */

35     public static void create(String JavaDoc path) throws ObjectStoreException {
36         try {
37             PageStore.create(path);
38         } catch (PageStoreException e) {
39             throw new ObjectStoreException(ObjectStoreException.StoreCreateFailure, e);
40         }
41     }
42
43     /**
44      * Deletes the underlying page store.
45      */

46     public static void delete(String JavaDoc path) {
47         PageStore.delete(path);
48     }
49
50     /**
51      * Checks for existence of an object store file.
52      */

53     public static boolean exists(String JavaDoc path) {
54         return PageStore.exists(path);
55     }
56
57     /**
58      * Creates an object store. This store is unusable until opened.
59      */

60     public ObjectStore(AbstractObjectPolicy objectPolicy) {
61         this.pagePolicy = new ObjectStorePagePolicy();
62         this.objectPolicy = objectPolicy;
63     }
64
65     /**
66      * Opens an object store.
67      */

68     public void open(String JavaDoc name) throws ObjectStoreException {
69         try {
70             pageStore = new PageStore(pagePolicy);
71             pageStore.open(name);
72         } catch (PageStoreException e) {
73             throw new ObjectStoreException(ObjectStoreException.StoreOpenFailure, e);
74         }
75         checkMetadata();
76         acquiredObjects = new HashMap();
77         modifiedObjects = new HashMap();
78         cachedObjects = new LinkedList();
79         phantoms = new HashSet();
80         reservations = new ReservationTable();
81     }
82
83     /**
84      * Closes the object store.
85      */

86     public void close() throws ObjectStoreException {
87         try {
88             commit();
89         } catch (ObjectStoreException e) {
90             //make sure the page store file gets closed no matter what
91
pageStore.close(false);
92             throw e;
93         }
94         try {
95             pageStore.close();
96         } catch (Exception JavaDoc e) {
97             //ignore failure to close
98
}
99         acquiredObjects = null;
100         modifiedObjects = null;
101         cachedObjects = null;
102         phantoms = null;
103         reservations = null;
104     }
105
106     public Buffer getMetadataArea(int i) throws ObjectStoreException {
107         try {
108             return new Buffer(pageStore.readMetadataArea(i));
109         } catch (PageStoreException e) {
110             throw new ObjectStoreException(ObjectStoreException.MetadataRequestFailure, e);
111         }
112     }
113
114     public void putMetadataArea(int i, Buffer buffer) throws ObjectStoreException {
115         try {
116             pageStore.writeMetadataArea(i, buffer.getByteArray());
117         } catch (PageStoreException e) {
118             throw new ObjectStoreException(ObjectStoreException.MetadataRequestFailure, e);
119         }
120     }
121
122     /**
123      * Checks to see if the metadata stored in the object store matches that expected by this
124      * code. If not, a conversion is necessary.
125      */

126     protected void checkMetadata() throws ObjectStoreException {
127         Buffer metadata = getMetadataArea(ObjectStoreMetadataAreaID);
128         Field versionField = metadata.getField(0, 4);
129         int objectStoreVersion = versionField.getInt();
130         if (objectStoreVersion == 0) {
131             // 0 indicates that the store is new and opened for read/write
132
versionField.put(CurrentObjectStoreVersion);
133             putMetadataArea(ObjectStoreMetadataAreaID, metadata);
134             return;
135         }
136         if (objectStoreVersion == CurrentObjectStoreVersion)
137             return;
138         convert(objectStoreVersion);
139     }
140
141     /**
142      * Converts the object store from a previous to the current version.
143      * No conversions are yet defined.
144      */

145     protected void convert(int fromVersion) throws ObjectStoreException {
146         throw new ObjectStoreException(ObjectStoreException.StoreConversionFailure);
147     }
148
149     /**
150      * Commits the modified object collection to the underlying page store.
151      */

152     public void commit() throws ObjectStoreException {
153         for (Iterator z = acquiredObjects.values().iterator(); z.hasNext();) {
154             StoredObject object = (StoredObject) z.next();
155             object.notifyObservers();
156         }
157         for (Iterator z = phantoms.iterator(); z.hasNext();) {
158             ObjectAddress address = (ObjectAddress) z.next();
159             int pageNumber = address.getPageNumber();
160             ObjectPage page = acquireObjectPage(pageNumber);
161             page.removeObject(address.getObjectNumber());
162             updateSpaceMapPage(page.getPageNumber(), page.getFreeSpace());
163             page.release();
164         }
165         phantoms.clear();
166         for (Iterator z = modifiedObjects.values().iterator(); z.hasNext();) {
167             StoredObject object = (StoredObject) z.next();
168             z.remove();
169             addToCache(object);
170             ObjectAddress address = object.getAddress();
171             int pageNumber = address.getPageNumber();
172             ObjectPage page = acquireObjectPage(pageNumber);
173             if (reservations.contains(address)) {
174                 page.insertObject(object);
175                 updateSpaceMapPage(pageNumber, page.getFreeSpace());
176             } else {
177                 page.updateObject(object);
178             }
179             page.release();
180         }
181         reservations.clear();
182         try {
183             pageStore.commit();
184         } catch (PageStoreException e) {
185             throw new ObjectStoreException(ObjectStoreException.PageWriteFailure, e);
186         }
187     }
188
189     /**
190      * Returns the StoredObject at a given address. This registers the store as an
191      * observer of changes to this object.
192      */

193     public StoredObject acquireObject(ObjectAddress address) throws ObjectStoreException {
194         if (phantoms.contains(address)) {
195             throw new ObjectStoreException(ObjectStoreException.ObjectExistenceFailure);
196         }
197         StoredObject object = (StoredObject) acquiredObjects.get(address);
198         if (object == null) {
199             object = (StoredObject) modifiedObjects.get(address);
200             if (object == null) {
201                 object = removeFromCache(address);
202                 if (object == null) {
203                     int pageNumber = address.getPageNumber();
204                     ObjectPage page = acquireObjectPage(pageNumber);
205                     try {
206                         Field f = page.getObjectField(address.getObjectNumber());
207                         if (f == null)
208                             throw new ObjectStoreException(ObjectStoreException.ObjectExistenceFailure);
209                         object = objectPolicy.createObject(f, this, address);
210                     } catch (ObjectStoreException e) {
211                         page.release();
212                         throw e;
213                     }
214                     page.release();
215                 }
216             }
217             acquiredObjects.put(address, object);
218             object.addObserver(this);
219         }
220         object.addReference();
221         return object;
222     }
223
224     /**
225      * Releases an object. If there are no more references and the object is not
226      * in the modified cache, return the object to
227      * the standard cache. Objects in the standard cache always maintain a
228      * reference count of 0.
229      */

230     public void releaseObject(StoredObject object) throws ObjectStoreException {
231         object.removeReference();
232         if (object.hasReferences())
233             return;
234         object.notifyObservers(); // one last chance to collect changed objects
235
object.deleteObserver(this);
236         acquiredObjects.remove(object.getAddress());
237         addToCache(object);
238     }
239
240     /**
241      * Updates the store when receiving an object change notification. Required by
242      * Observer. This places the object in the modified objects cache. This will
243      * be cleared during commit/rollback processing. An object may reside simultaneously
244      * in the modified and acquired object caches.
245      */

246     public void update(Observable object, Object JavaDoc arg) {
247         StoredObject storedObject = (StoredObject) object;
248         modifiedObjects.put(storedObject.getAddress(), storedObject);
249     }
250
251     /**
252      * Adds an object to the backing cache. Objects in this cache are neither in
253      * the modified set or the acquired set.
254      */

255     protected void addToCache(StoredObject object) {
256         synchronized (cachedObjects) {
257             if (acquiredObjects.containsKey(object.getAddress()))
258                 return;
259             if (modifiedObjects.containsKey(object.getAddress()))
260                 return;
261             cachedObjects.addFirst(object);
262             if (cachedObjects.size() <= 50)
263                 return;
264             cachedObjects.removeLast();
265         }
266     }
267
268     /**
269      * Removes an object from the backing cache given its address.
270      */

271     protected StoredObject removeFromCache(ObjectAddress address) {
272         synchronized (cachedObjects) {
273             StoredObject object = null;
274             for (Iterator z = cachedObjects.iterator(); z.hasNext();) {
275                 StoredObject o = (StoredObject) z.next();
276                 if (o.getAddress().equals(address)) {
277                     z.remove();
278                     object = o;
279                     break;
280                 }
281             }
282             return object;
283         }
284     }
285
286     /**
287      * Places an object into the store. This assigns it an address. The address
288      * is returned. The object is not observed until it is acquired.
289      */

290     // public ObjectAddress insertObject(StoredObject object) throws ObjectStoreException {
291
// int bytesNeeded = object.length() + ObjectHeader.SIZE;
292
// ObjectPage page = acquireObjectPageForSize(bytesNeeded);
293
// int objectNumber = page.insertObject(object);
294
// int pageNumber = page.getPageNumber();
295
// updateSpaceMapPage(page.getPageNumber(), page.getFreeSpace());
296
// page.release();
297
// object.setAddress(new ObjectAddress(pageNumber, objectNumber));
298
// object.setStore(this);
299
// return object.getAddress();
300
// }
301
/**
302      * "Inserts" an object into the store by reserving a place for the
303      * object. This assigns it an address and
304      * places it in the modified objects map. A reservation is created that
305      * records the address and the amount of space used. The object is not
306      * actually added to the underlying store until a commit operation is executed.
307      */

308     public ObjectAddress insertObject(StoredObject object) throws ObjectStoreException {
309         int bytesNeeded = object.length() + ObjectHeader.SIZE;
310         ObjectPage page = acquireObjectPageForSize(bytesNeeded);
311         int pageNumber = page.getPageNumber();
312         int objectNumber = page.reserveObject(object, reservations);
313         page.release();
314         ObjectAddress address = new ObjectAddress(pageNumber, objectNumber);
315         object.setAddress(address);
316         object.setStore(this);
317         modifiedObjects.put(address, object);
318         return address;
319     }
320
321     /**
322      * Removes an object from the object store. In doing so, it must remove it from the cache as well.
323      */

324     public void removeObject(ObjectAddress address) throws ObjectStoreException {
325         if (phantoms.contains(address)) {
326             throw new ObjectStoreException(ObjectStoreException.ObjectExistenceFailure);
327         }
328         if (acquiredObjects.containsKey(address)) {
329             throw new ObjectStoreException(ObjectStoreException.ObjectIsLocked);
330         }
331         StoredObject object = (StoredObject) modifiedObjects.get(address);
332         boolean inStore = !reservations.contains(address);
333         if (object != null) {
334             reservations.remove(address);
335             modifiedObjects.remove(address);
336         }
337         removeFromCache(address);
338         if (inStore)
339             phantoms.add(address);
340     }
341
342     /**
343      * Places the object in the modified objects cache and marks it as in use. The modified objects
344      * cache is flushed at commit or rollback time. At that point, the reference to the object
345      * is dropped.
346      */

347     // public void updateObject(StoredObject object) {
348
// ObjectAddress address = object.getAddress();
349
// if (modifiedObjects.get(address) == null) {
350
// modifiedObjects.put(address, object);
351
// try {
352
// acquireObject(address);
353
// } catch (ObjectStoreException e) {
354
// }
355
// }
356
// }
357

358     protected void updateSpaceMapPage(int objectPageNumber, int freeSpace) throws ObjectStoreException {
359         SpaceMapPage p = acquireSpaceMapPage(objectPageNumber);
360         p.setFreeSpace(objectPageNumber, freeSpace);
361         p.release();
362     }
363
364     /**
365      * Acquires an object page. This is a convenience method to translate exceptions.
366      */

367     protected ObjectPage acquireObjectPage(int pageNumber) throws ObjectStoreException {
368         ObjectPage page;
369         try {
370             page = (ObjectPage) pageStore.acquire(pageNumber);
371         } catch (PageStoreException e) {
372             throw new ObjectStoreException(ObjectStoreException.PageReadFailure, e);
373         }
374         return page;
375     }
376
377     /**
378      * Looks for the first page that guarantees enough space to meet the criteria.
379      * This is the "first fit" algorithm and will get slow as the page file grows since
380      * it is O(n**2). (Each addition of a new page is preceded by a search of all pages.)
381      * We reduce the overhead by maintaining SpaceMapPages that tell us how full each page
382      * is. A space map page is the first page of a span of 8K pages (64M total).
383      * Each byte in a space map page indicates the fullness of each page in the span.
384      * Since databases are expected to be quite small (<200Mb) we might be able to live with
385      * this simple algorithm.
386      */

387     protected ObjectPage acquireObjectPageForSize(int bytesNeeded) throws ObjectStoreException {
388         int oPageNumber = 0;
389         int numberOfSpans = ((pageStore.numberOfPages() - 1) / ObjectStorePage.SIZE) + 1;
390         for (int i = 0; i <= numberOfSpans; i++) {
391             try {
392                 int sPageNumber = i * ObjectStorePage.SIZE;
393                 SpaceMapPage sPage = (SpaceMapPage) pageStore.acquire(sPageNumber);
394                 for (int j = 1; j < ObjectStorePage.SIZE; j++) {
395                     int n = sPageNumber + j;
396                     Reservation r = reservations.get(n);
397                     int bytesAvailable = (r == null) ? sPage.getFreeSpace(n) : r.getFreeSpace();
398                     if (bytesNeeded <= bytesAvailable) {
399                         oPageNumber = n;
400                         break;
401                     }
402                 }
403                 sPage.release();
404             } catch (PageStoreException e) {
405                 throw new ObjectStoreException(ObjectStoreException.PageReadFailure, e);
406             }
407             if (oPageNumber != 0)
408                 break;
409         }
410         if (oPageNumber == 0) {
411             throw new ObjectStoreException(ObjectStoreException.PageReadFailure);
412         }
413         try {
414             ObjectPage oPage = (ObjectPage) pageStore.acquire(oPageNumber);
415             return oPage;
416         } catch (PageStoreException e) {
417             throw new ObjectStoreException(ObjectStoreException.PageReadFailure, e);
418         }
419     }
420
421     /**
422      * Acquires a space map page. This is a convenience method to translate exceptions.
423      */

424     protected SpaceMapPage acquireSpaceMapPage(int objectPageNumber) throws ObjectStoreException {
425         int pageNumber = objectPageNumber & 0xFFFFE000;
426         SpaceMapPage p = null;
427         try {
428             p = (SpaceMapPage) pageStore.acquire(pageNumber);
429         } catch (PageStoreException e) {
430             throw new ObjectStoreException(ObjectStoreException.PageReadFailure, e);
431         }
432         return p;
433     }
434
435 }
436
Popular Tags