001: /* Copyright (C) 2004 - 2007 db4objects Inc. http://www.db4o.com
002:
003: This file is part of the db4o open source object database.
004:
005: db4o is free software; you can redistribute it and/or modify it under
006: the terms of version 2 of the GNU General Public License as published
007: by the Free Software Foundation and as clarified by db4objects' GPL
008: interpretation policy, available at
009: http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
010: Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
011: Suite 350, San Mateo, CA 94403, USA.
012:
013: db4o is distributed in the hope that it will be useful, but WITHOUT ANY
014: WARRANTY; without even the implied warranty of MERCHANTABILITY or
015: FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
016: for more details.
017:
018: You should have received a copy of the GNU General Public License along
019: with this program; if not, write to the Free Software Foundation, Inc.,
020: 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
021: package com.db4o.db4ounit.common.reflect.custom;
022:
023: import com.db4o.*;
024: import com.db4o.config.*;
025: import com.db4o.foundation.*;
026: import com.db4o.foundation.io.*;
027: import com.db4o.internal.*;
028: import com.db4o.query.*;
029:
030: /**
031: * Custom class information is stored to db4o itself as
032: * a CustomClassRepository singleton.
033: */
034: public class Db4oPersistenceProvider implements PersistenceProvider {
035:
036: static class MyContext {
037:
038: public final CustomClassRepository repository;
039: public final ObjectContainer metadata;
040: public final ObjectContainer data;
041:
042: public MyContext(CustomClassRepository repository,
043: ObjectContainer metadata, ObjectContainer data) {
044: this .repository = repository;
045: this .metadata = metadata;
046: this .data = data;
047: }
048: }
049:
050: public void createEntryClass(PersistenceContext context,
051: String className, String[] fieldNames, String[] fieldTypes) {
052: logMethodCall("createEntryClass", context, className);
053:
054: CustomClassRepository repository = repository(context);
055: repository.defineClass(className, fieldNames, fieldTypes);
056: updateMetadata(context, repository);
057: }
058:
059: public void createIndex(PersistenceContext context,
060: String className, String fieldName) {
061: markIndexedField(context, className, fieldName, true);
062: }
063:
064: public void dropIndex(PersistenceContext context, String className,
065: String fieldName) {
066: markIndexedField(context, className, fieldName, false);
067: }
068:
069: private void markIndexedField(PersistenceContext context,
070: String className, String fieldName, boolean indexed) {
071: CustomField field = customClass(context, className)
072: .customField(fieldName);
073: field.indexed(indexed);
074: updateMetadata(context, field);
075: restart(context);
076: }
077:
078: private void restart(PersistenceContext context) {
079: closeContext(context);
080: initContext(context);
081: }
082:
083: public int delete(PersistenceContext context, String className,
084: Object uid) {
085: // TODO Auto-generated method stub
086: return 0;
087: }
088:
089: public void dropEntryClass(PersistenceContext context,
090: String className) {
091: // TODO Auto-generated method stub
092:
093: }
094:
095: public void initContext(PersistenceContext context) {
096: logMethodCall("initContext", context);
097:
098: ObjectContainer metadata = openMetadata(context.url());
099: try {
100: CustomClassRepository repository = initializeClassRepository(metadata);
101: CustomReflector reflector = new CustomReflector(repository);
102: ObjectContainer data = openData(reflector, context.url());
103: context.setProviderContext(new MyContext(repository,
104: metadata, data));
105: } catch (Exception e) {
106:
107: e.printStackTrace();
108:
109: // make sure metadata container is not left open
110: // in case something goes wrong with the setup
111: closeIgnoringExceptions(metadata);
112:
113: // cant use exception chaining here because the
114: // the must run in jdk 1.1
115: throw new RuntimeException(e.getMessage());
116: }
117: }
118:
119: private void closeIgnoringExceptions(ObjectContainer container) {
120: try {
121: container.close();
122: } catch (Exception e) {
123: e.printStackTrace();
124: }
125: }
126:
127: public void insert(PersistenceContext context, PersistentEntry entry) {
128: logMethodCall("insert", context, entry);
129:
130: // clone the entry because clients are allowed to reuse
131: // entry objects
132: dataContainer(context).set(clone(entry));
133: }
134:
135: public Iterator4 select(PersistenceContext context,
136: PersistentEntryTemplate template) {
137: logMethodCall("select", context, template);
138:
139: Query query = queryFromTemplate(context, template);
140: return new ObjectSetIterator(query.execute());
141: }
142:
143: public void update(PersistenceContext context, PersistentEntry entry) {
144: PersistentEntry existing = selectByUid(context,
145: entry.className, entry.uid);
146: existing.fieldValues = entry.fieldValues;
147:
148: dataContainer(context).set(existing);
149: }
150:
151: private PersistentEntry selectByUid(PersistenceContext context,
152: String className, Object uid) {
153: Query query = newQuery(context, className);
154: query.descend("uid").constrain(uid);
155: return (PersistentEntry) query.execute().next();
156: }
157:
158: private void addClassConstraint(PersistenceContext context,
159: Query query, String className) {
160: query.constrain(customClass(context, className));
161: }
162:
163: private CustomClass customClass(PersistenceContext context,
164: String className) {
165: return repository(context).forName(className);
166: }
167:
168: private Constraint addFieldConstraint(Query query,
169: PersistentEntryTemplate template, int index) {
170: return query.descend(template.fieldNames[index]).constrain(
171: template.fieldValues[index]);
172: }
173:
174: private void addFieldConstraints(Query query,
175: PersistentEntryTemplate template) {
176: if (template.fieldNames.length == 0) {
177: return;
178: }
179: Constraint c = addFieldConstraint(query, template, 0);
180: for (int i = 1; i < template.fieldNames.length; ++i) {
181: c = c.and(addFieldConstraint(query, template, i));
182: }
183: }
184:
185: private PersistentEntry clone(PersistentEntry entry) {
186: return new PersistentEntry(entry.className, entry.uid,
187: entry.fieldValues);
188: }
189:
190: public void closeContext(PersistenceContext context) {
191: logMethodCall("closeContext", context);
192:
193: MyContext customContext = my(context);
194: if (null != customContext) {
195: closeIgnoringExceptions(customContext.metadata);
196: closeIgnoringExceptions(customContext.data);
197: context.setProviderContext(null);
198: }
199: }
200:
201: private MyContext my(PersistenceContext context) {
202: return ((MyContext) context.getProviderContext());
203: }
204:
205: private Configuration dataConfiguration(CustomReflector reflector) {
206: Configuration config = Db4o.newConfiguration();
207: config.reflectWith(reflector);
208: configureCustomClasses(config, reflector);
209: return config;
210: }
211:
212: private void configureCustomClasses(Configuration config,
213: CustomReflector reflector) {
214: Iterator4 classes = reflector.customClasses();
215: while (classes.moveNext()) {
216: CustomClass cc = (CustomClass) classes.current();
217: configureFields(config, cc);
218: }
219: }
220:
221: private void configureFields(Configuration config, CustomClass cc) {
222: Iterator4 fields = cc.customFields();
223: while (fields.moveNext()) {
224: CustomField field = (CustomField) fields.current();
225: config.objectClass(cc).objectField(field.getName())
226: .indexed(field.indexed());
227: }
228: }
229:
230: public ObjectContainer dataContainer(PersistenceContext context) {
231: return my(context).data;
232: }
233:
234: private CustomClassRepository initializeClassRepository(
235: ObjectContainer container) {
236: CustomClassRepository repository = queryClassRepository(container);
237: if (repository == null) {
238: log("Initializing new class repository.");
239: repository = new CustomClassRepository();
240: store(container, repository);
241: } else {
242: log("Found existing class repository: " + repository);
243: }
244: return repository;
245: }
246:
247: private Configuration metaConfiguration() {
248: Configuration config = Db4o.newConfiguration();
249: config.exceptionsOnNotStorable(true);
250:
251: // the following line is only necessary for the tests to run
252: // in OSGi environment
253: config.reflectWith(Platform4
254: .reflectorForType(CustomClassRepository.class));
255:
256: cascade(config, CustomClassRepository.class);
257: cascade(config, Hashtable4.class);
258: cascade(config, CustomClass.class);
259: return config;
260: }
261:
262: private void cascade(Configuration config, Class klass) {
263: config.objectClass(klass).cascadeOnUpdate(true);
264: config.objectClass(klass).cascadeOnActivate(true);
265: }
266:
267: private ObjectContainer metadataContainer(PersistenceContext context) {
268: return my(context).metadata;
269: }
270:
271: private String metadataFile(String fname) {
272: return fname + ".metadata";
273: }
274:
275: private ObjectContainer openData(CustomReflector reflector,
276: String fname) {
277: return Db4o.openFile(dataConfiguration(reflector), fname);
278: }
279:
280: private ObjectContainer openMetadata(String fname) {
281: return Db4o.openFile(metaConfiguration(), metadataFile(fname));
282: }
283:
284: public void purge(String url) {
285: File4.delete(url);
286: File4.delete(metadataFile(url));
287: }
288:
289: private CustomClassRepository queryClassRepository(
290: ObjectContainer container) {
291: ObjectSet found = container.query(CustomClassRepository.class);
292: if (!found.hasNext()) {
293: return null;
294: }
295: return (CustomClassRepository) found.next();
296: }
297:
298: private Query queryFromTemplate(PersistenceContext context,
299: PersistentEntryTemplate template) {
300: Query query = newQuery(context, template.className);
301: addFieldConstraints(query, template);
302: return query;
303: }
304:
305: private Query newQuery(PersistenceContext context, String className) {
306: Query query = dataContainer(context).query();
307: addClassConstraint(context, query, className);
308: return query;
309: }
310:
311: private CustomClassRepository repository(PersistenceContext context) {
312: return my(context).repository;
313: }
314:
315: private void store(ObjectContainer container, Object obj) {
316: container.set(obj);
317: container.commit();
318: }
319:
320: private void updateMetadata(PersistenceContext context,
321: Object metadata) {
322: store(metadataContainer(context), metadata);
323: }
324:
325: private void log(String message) {
326: Logger.log("Db4oPersistenceProvider: " + message);
327: }
328:
329: private void logMethodCall(String methodName, Object arg) {
330: Logger
331: .logMethodCall("Db4oPersistenceProvider", methodName,
332: arg);
333: }
334:
335: private void logMethodCall(String methodName, Object arg1,
336: Object arg2) {
337: Logger.logMethodCall("Db4oPersistenceProvider", methodName,
338: arg1, arg2);
339: }
340:
341: }
|