/** * Copyright (c) 2006-2011 Floggy Open Source Group. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sourceforge.floggy.persistence.impl; import java.util.Enumeration; import javax.microedition.rms.RecordComparator; import javax.microedition.rms.RecordEnumeration; import javax.microedition.rms.RecordFilter; import javax.microedition.rms.RecordStore; import javax.microedition.rms.RecordStoreException; import net.sourceforge.floggy.persistence.Comparator; import net.sourceforge.floggy.persistence.Filter; import net.sourceforge.floggy.persistence.FloggyException; import net.sourceforge.floggy.persistence.IndexFilter; import net.sourceforge.floggy.persistence.ObjectSet; import net.sourceforge.floggy.persistence.Persistable; import net.sourceforge.floggy.persistence.PersistableManager; import net.sourceforge.floggy.persistence.PolymorphicObjectSet; import net.sourceforge.floggy.persistence.SingleObjectSet; import net.sourceforge.floggy.persistence.impl.strategy.JoinedStrategyObjectFilter; import net.sourceforge.floggy.persistence.impl.strategy.PerClassStrategyObjectFilter; import net.sourceforge.floggy.persistence.impl.strategy.SingleStrategyObjectFilter; /** * This is the main class of the framework. All persistence operations * methods (such as loading, saving, deleting and searching for objects) are * declared in this class. * * @since 1.0 */ public class PersistableManagerImpl extends PersistableManager { /** * DOCUMENT ME! */ protected Class deletableClass; /** * DOCUMENT ME! */ protected Class perClassStrategyClass; /** * DOCUMENT ME! */ protected Class singleStrategyClass; /** * Creates a new instance of PersistableManager. */ public PersistableManagerImpl() throws Exception { deletableClass = Class.forName( "net.sourceforge.floggy.persistence.Deletable"); perClassStrategyClass = Class.forName( "net.sourceforge.floggy.persistence.strategy.PerClassStrategy"); singleStrategyClass = Class.forName( "net.sourceforge.floggy.persistence.strategy.SingleStrategy"); SerializationManager.setPersistableManager(this); PersistableMetadataManager.init(); IndexManager.init(); } /** * DOCUMENT ME! * * @param persistable DOCUMENT ME! * * @return DOCUMENT ME! * * @throws FloggyException DOCUMENT ME! */ public int batchSave(Persistable persistable) throws FloggyException { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); int id = __persistable.__getId(); if (id > 0) { throw new FloggyException( "You should not use this method to update the persistable object."); } else { id = save(persistable); __persistable.__setId(0); } return id; } /** * Removes an object from the repository. If the object is not stored in * the repository then a <code>FloggyException</code> will be thrown. * * @param persistable Object to be removed. * * @throws FloggyException Exception thrown if an error occurs while removing * the object. */ public void delete(Persistable persistable) throws FloggyException { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); int id = __persistable.__getId(); if (id > 0) { RecordStore rs = RecordStoreManager.getRecordStore(__persistable); try { __persistable.__delete(); rs.deleteRecord(id); IndexManager.afterDelete(__persistable); __persistable.__setId(0); } catch (Exception ex) { throw Utils.handleException(ex); } finally { RecordStoreManager.closeRecordStore(rs); } } } /** * Removes all objects from the repository. * * @throws FloggyException Exception thrown if an error occurs while removing * the object. */ public void deleteAll() throws FloggyException { try { Enumeration metadatas = PersistableMetadataManager.getClassBasedMetadatas(); while (metadatas.hasMoreElements()) { PersistableMetadata metadata = (PersistableMetadata) metadatas.nextElement(); IndexManager.deleteIndex(metadata.getClassName()); RecordStoreManager.deleteRecordStore(metadata.getRecordStoreName()); } } catch (Exception ex) { throw Utils.handleException(ex); } } /** * Removes all objects from the repository. * * @param persistableClass The persistable class to search the objects. * * @throws FloggyException Exception thrown if an error occurs while removing * the object. */ public void deleteAll(Class persistableClass) throws FloggyException { __Persistable persistable = Utils.createInstance(persistableClass); PersistableMetadata metadata = PersistableMetadataManager.getClassBasedMetadata(persistableClass.getName()); if (deletableClass.isAssignableFrom(persistableClass) || (metadata.getSuperClassName() != null)) { ObjectSet os = find(persistableClass, null, null); for (int i = 0; i < os.size(); i++) { persistable = (__Persistable) os.get(i); delete(persistable); } } else { try { RecordStoreManager.deleteRecordStore(persistable.getRecordStoreName()); IndexManager.deleteIndex(persistableClass.getName()); } catch (Exception ex) { throw Utils.handleException(ex); } } } /** * Searches objects of an specific persistable class from the * repository. <br> * <br> * An optional application-defined search criteria can be defined using a <code>Filter</code>.<br> * <br> * An optional application-defined sort order can be defined using a * <code>Comparator</code>. * * @param persistableClass The persistable class to search the objects. * @param filter An optional application-defined criteria for searching * objects. * @param comparator An optional application-defined criteria for sorting * objects. * * @return List of objects that matches the defined criteria. * * @throws FloggyException DOCUMENT ME! */ public SingleObjectSet find(Class persistableClass, Filter filter, Comparator comparator) throws FloggyException { return find(persistableClass, filter, comparator, false); } /** * Searches objects of an specific persistable class from the * repository. <br> * <br> * An optional application-defined search criteria can be defined using a <code>Filter</code>.<br> * <br> * An optional application-defined sort order can be defined using a * <code>Comparator</code>. * * @param persistableClass The persistable class to search the objects. * @param filter An optional application-defined criteria for searching * objects. * @param comparator An optional application-defined criteria for sorting * objects. * @param lazy DOCUMENT ME! * * @return List of objects that matches the defined criteria. * * @throws FloggyException DOCUMENT ME! */ public SingleObjectSet find(Class persistableClass, Filter filter, Comparator comparator, boolean lazy) throws FloggyException { RecordFilter objectFilter = null; RecordComparator objectComparator = null; /* * this is a auxiliary object used to return the name of the * RecordStore. If the argument filter is not null this object is passed * to the ObjectFilter constructor */ __Persistable persistable = Utils.createInstance(persistableClass); objectFilter = getFilter(persistable, filter, lazy); if (comparator != null) { objectComparator = new ObjectComparator(comparator, Utils.createInstance(persistableClass), Utils.createInstance(persistableClass), lazy); } int[] ids = null; RecordStore rs = RecordStoreManager.getRecordStore(persistable); try { RecordEnumeration en = rs.enumerateRecords(objectFilter, objectComparator, false); int numRecords = en.numRecords(); if (numRecords > 0) { ids = new int[numRecords]; for (int i = 0; i < numRecords; i++) { ids[i] = en.nextRecordId(); } } en.destroy(); } catch (RecordStoreException ex) { throw Utils.handleException(ex); } finally { RecordStoreManager.closeRecordStore(rs); } return new ObjectSetImpl(ids, persistableClass, this, lazy); } /** * Searches objects of an especific persistable class from the * repository. <br> * <br> * An optional application-defined search criteria can be defined using a <code>IndexFilter</code>.<br> * <br> * An optional application-defined sort order can be defined using a * <code>Comparator</code>. * * @param persistableClass The persistable class to search the objects. * @param indexFilter An optional application-defined criteria for searching * objects. * @param lazy An optional application-defined criteria for sorting objects. * * @return List of objects that matches the defined criteria. * * @throws FloggyException DOCUMENT ME! * @throws IllegalArgumentException DOCUMENT ME! */ public SingleObjectSet find(Class persistableClass, IndexFilter indexFilter, boolean lazy) throws FloggyException { if (indexFilter == null) throw new IllegalArgumentException("The indexFilter cannot be null"); Utils.validatePersistableClassArgument(persistableClass); try { int[] ids = IndexManager.getId(persistableClass, indexFilter.getIndexName(), indexFilter.getIndexValue()); return new ObjectSetImpl(ids, persistableClass, this, lazy); } catch (Exception ex) { throw Utils.handleException(ex); } } /** * DOCUMENT ME! * * @param persistable DOCUMENT ME! * * @return DOCUMENT ME! */ public int getId(Persistable persistable) { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); return __persistable.__getId(); } /** * DOCUMENT ME! * * @param name DOCUMENT ME! * * @return DOCUMENT ME! */ public Object getProperty(String name) { if ((name == null) || (name.trim().length() == 0)) { throw new IllegalArgumentException( "The property name cannot be null or empty"); } else if (name.equals(PersistableManager.BATCH_MODE)) { return (RecordStoreManager.getBatchMode()) ? Utils.TRUE : Utils.FALSE; } else if (name.equals(PersistableManager.STORE_INDEX_AFTER_SAVE_OPERATION)) { return (IndexManager.getStoreIndexAfterSave()) ? Utils.TRUE : Utils.FALSE; } else { throw new IllegalArgumentException("Unreconized property: " + name); } } /** * DOCUMENT ME! * * @param persistable DOCUMENT ME! * * @return DOCUMENT ME! */ public boolean isPersisted(Persistable persistable) { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); return __persistable.__getId() > 0; } /** * Load an previously stored object from the repository using the object ID.<br> * The object ID is the result of a save operation or you can obtain it * executing a search. * * @param persistable An instance where the object data will be loaded into. * Cannot be <code>null</code>. * @param id The ID of the object to be loaded from the repository. * * @throws FloggyException Exception thrown if an error occurs while loading * the object. * * @see #save(Persistable) */ public void load(Persistable persistable, int id) throws FloggyException { load(persistable, id, false); } /** * Load an previously stored object from the repository using the object ID.<br> * The object ID is the result of a save operation or you can obtain it * executing a search. * * @param persistable An instance where the object data will be loaded into. * Cannot be <code>null</code>. * @param id The ID of the object to be loaded from the repository. * @param lazy DOCUMENT ME! * * @throws FloggyException Exception thrown if an error occurs while loading * the object. * * @see #save(Persistable) */ public void load(Persistable persistable, int id, boolean lazy) throws FloggyException { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); RecordStore rs = RecordStoreManager.getRecordStore(__persistable); try { byte[] buffer = rs.getRecord(id); if (buffer != null) { __persistable.__deserialize(buffer, lazy); } __persistable.__setId(id); } catch (Exception ex) { throw Utils.handleException(ex); } finally { RecordStoreManager.closeRecordStore(rs); } } /** * DOCUMENT ME! * * @param persistableClass DOCUMENT ME! * @param filter DOCUMENT ME! * @param lazy DOCUMENT ME! * * @return DOCUMENT ME! * * @throws FloggyException DOCUMENT ME! */ public PolymorphicObjectSet polymorphicFind(Class persistableClass, Filter filter, boolean lazy) throws FloggyException { RecordFilter objectFilter = null; __Persistable persistable = null; /* * this is a auxiliary object used to return the name of the * RecordStore. If the argument filter is not null this object is passed * to the ObjectFilter constructor */ int[] ids = null; PersistableMetadata metadata = PersistableMetadataManager.getClassBasedMetadata(persistableClass.getName()); PolymorphicObjectSetImpl objectSet = new PolymorphicObjectSetImpl(this, lazy); String[] persistables = metadata.getDescendents(); if ((persistables == null) || (persistables.length == 0)) { persistables = new String[] { persistableClass.getName() }; } for (int i = 0; i < persistables.length; i++) { try { persistableClass = Class.forName(persistables[i]); persistable = Utils.createInstance(persistableClass); objectFilter = getFilter(persistable, filter, lazy); } catch (ClassNotFoundException ex) { throw Utils.handleException(ex); } RecordStore rs = RecordStoreManager.getRecordStore(persistable); try { RecordEnumeration en = rs.enumerateRecords(objectFilter, null, false); int numRecords = en.numRecords(); if (numRecords > 0) { ids = new int[numRecords]; for (int j = 0; j < numRecords; j++) { ids[j] = en.nextRecordId(); } objectSet.addList(ids, persistableClass); } en.destroy(); } catch (RecordStoreException ex) { throw Utils.handleException(ex); } finally { RecordStoreManager.closeRecordStore(rs); } } return objectSet; } /** * Store an object in the repository. If the object is already in the * repository, the object data will be overwritten.<br> * The object ID obtained from this operation can be used in the load * operations. * * @param persistable Object to be stored. * * @return The ID of the object. * * @throws FloggyException Exception thrown if an error occurs while storing * the object. * * @see #load(Persistable, int) */ public int save(Persistable persistable) throws FloggyException { __Persistable __persistable = Utils.checkArgumentAndCast(persistable); RecordStore rs = RecordStoreManager.getRecordStore(__persistable); try { byte[] buffer = __persistable.__serialize(true); int id = __persistable.__getId(); if (id <= 0) { id = rs.addRecord(buffer, 0, buffer.length); __persistable.__setId(id); } else { rs.setRecord(id, buffer, 0, buffer.length); } IndexManager.afterSave(__persistable); return id; } catch (Exception ex) { throw Utils.handleException(ex); } finally { RecordStoreManager.closeRecordStore(rs); } } /** * DOCUMENT ME! * * @param name DOCUMENT ME! * @param value DOCUMENT ME! */ public void setProperty(String name, Object value) { if ((name == null) || (name.trim().length() == 0)) { throw new IllegalArgumentException( "The property name cannot be null or empty"); } else if (value == null) { throw new IllegalArgumentException("The property value cannot be null"); } else if (name.equals(PersistableManager.BATCH_MODE)) { if (value instanceof Boolean) { RecordStoreManager.setBatchMode(((Boolean) value).booleanValue()); } else { throw new IllegalArgumentException( "The property PersistableManager.BATCH_MODE must be an instance of Boolean"); } } else if (name.equals(PersistableManager.STORE_INDEX_AFTER_SAVE_OPERATION)) { if (value instanceof Boolean) { IndexManager.setStoreIndexAfterSave(((Boolean) value).booleanValue()); } else { throw new IllegalArgumentException( "The property PersistableManager.STORE_INDEX_AFTER_SAVE_OPERATION must be an instance of Boolean"); } } else { throw new IllegalArgumentException("Unreconized property: " + name); } } /** * DOCUMENT ME! * * @throws FloggyException DOCUMENT ME! */ public void shutdown() throws FloggyException { try { IndexManager.shutdown(); RecordStoreManager.shutdown(); } catch (Exception ex) { throw Utils.handleException(ex); } } /** * DOCUMENT ME! * * @param persistable DOCUMENT ME! * @param filter DOCUMENT ME! * @param lazy DOCUMENT ME! * * @return DOCUMENT ME! */ protected RecordFilter getFilter(__Persistable persistable, Filter filter, boolean lazy) { Class persistableClass = persistable.getClass(); RecordFilter recordFilter = null; if (perClassStrategyClass.isAssignableFrom(persistableClass)) { if (filter != null) { recordFilter = new PerClassStrategyObjectFilter(persistable, filter, lazy); } } else if (singleStrategyClass.isAssignableFrom(persistableClass)) { if (filter != null) { recordFilter = new SingleStrategyObjectFilter(persistable, filter, lazy); } else { recordFilter = new SingleStrategyObjectFilter(persistable, lazy); } } else { if (filter != null) { recordFilter = new JoinedStrategyObjectFilter(persistable, filter, lazy); } else { recordFilter = new JoinedStrategyObjectFilter(lazy); } } return recordFilter; } }