All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.backendless.FootprintsManager Maven / Gradle / Ivy

There is a newer version: 3.1.0
Show newest version
/*
 * ********************************************************************************************************************
 *  

* BACKENDLESS.COM CONFIDENTIAL *

* ******************************************************************************************************************** *

* Copyright 2012 BACKENDLESS.COM. All Rights Reserved. *

* NOTICE: All information contained herein is, and remains the property of Backendless.com and its suppliers, * if any. The intellectual and technical concepts contained herein are proprietary to Backendless.com and its * suppliers and may be covered by U.S. and Foreign Patents, patents in process, and are protected by trade secret * or copyright law. Dissemination of this information or reproduction of this material is strictly forbidden * unless prior written permission is obtained from Backendless.com. *

* ******************************************************************************************************************** */ package com.backendless; import com.backendless.exceptions.BackendlessException; import com.backendless.geo.GeoPoint; import com.backendless.utils.ReflectionUtil; import weborb.reader.AnonymousObject; import weborb.reader.ArrayType; import weborb.reader.NamedObject; import java.util.*; /** * If server sends object object with fields a, b, c * and the client's class declares only fields a, b, then this class saves * field c in memory in order to send it with the next user's request. */ public class FootprintsManager { private static final FootprintsManager instance = new FootprintsManager(); private static Set marked = new HashSet(); //for cyclic entities public final Inner Inner = new Inner(); private final Map persistenceCache = new WeakHashMap(); private FootprintsManager() { } public static FootprintsManager getInstance() { return instance; } public Footprint getEntityFootprint( Object entity ) { return persistenceCache.get( entity ); } public String getObjectId( Object entity ) { Footprint footprint = getEntityFootprint( entity ); if( footprint != null ) return footprint.getObjectId(); else if( entity instanceof BackendlessUser ) return ((BackendlessUser) entity).getObjectId(); return null; } public String getMeta( Object entity ) { if( persistenceCache.containsKey( entity ) ) { return getEntityFootprint( entity ).get__meta(); } return null; } public Date getCreated( Object entity ) { if( persistenceCache.containsKey( entity ) ) { return getEntityFootprint( entity ).getCreated(); } return null; } public Date getUpdated( Object entity ) { if( persistenceCache.containsKey( entity ) ) { return getEntityFootprint( entity ).getUpdated(); } return null; } public class Inner { /** * Puts missing properties like objectId, ___meta etc. into entity map. * * @param entity entity object * @param entityMap entity map */ public void putMissingPropsToEntityMap( Object entity, Map entityMap ) { //put objectId if exists in cache if( !entityMap.containsKey( Persistence.DEFAULT_OBJECT_ID_FIELD ) ) { String objectId = getObjectId( entity ); if( objectId != null ) { entityMap.put( Persistence.DEFAULT_OBJECT_ID_FIELD, objectId ); } } //put __meta if exists in cache if( !entityMap.containsKey( Persistence.DEFAULT_META_FIELD ) ) { String meta = getMeta( entity ); if( meta != null ) { entityMap.put( Persistence.DEFAULT_META_FIELD, meta ); } } } /** * When the object is created on server, client gets new instance of it. In order to remember the system fields * (objectId, __meta etc.) it is required to duplicate the old instance in cache. * * @param serializedEntity entity's map used to iterate through fields and duplicate footprints recursively * @param persistedEntity entity from server * @param initialEntity entity on which a method was called (.save(), .create() etc.) */ void duplicateFootprintForObject( Map serializedEntity, Object persistedEntity, Object initialEntity ) { //to avoid endless recursion if( marked.contains( persistedEntity ) ) { return; } else { marked.add( persistedEntity ); } try { //iterate through fields in the object and duplicate entities recursively Set> entries = serializedEntity.entrySet(); for( Map.Entry entry : entries ) { if( entry.getValue() instanceof Map ) { // retrieve persisted entity's field value Object persistedEntityFieldValue = ReflectionUtil.getFieldValue( persistedEntity, entry.getKey() ); // retrieve initial entity's field value Object initialEntityFieldValue = ReflectionUtil.getFieldValue( initialEntity, entry.getKey() ); // duplicate footprint recursively duplicateFootprintForObject( (Map) entry.getValue(), persistedEntityFieldValue, initialEntityFieldValue ); } else if( entry.getValue() instanceof Collection ) { // TODO: discuss and decide what to do with GeoPoints here if( ((Collection) entry.getValue()).iterator().next() instanceof GeoPoint ) { continue; } // retrieve persisted entity's field value (which is collection) Collection persistedEntityFieldValue = (Collection) ReflectionUtil.getFieldValue( persistedEntity, entry.getKey() ); // retrieve initial entity's field value (which is collection) Collection initialEntityFieldValue = (Collection) ReflectionUtil.getFieldValue( initialEntity, entry.getKey() ); Collection mapCollection = (Collection) entry.getValue(); // recursively duplicate footprint for each object in collection Iterator persistedEntityFieldValueIterator = persistedEntityFieldValue.iterator(); Iterator initialEntityFieldValueIterator = initialEntityFieldValue.iterator(); Iterator mapCollectionIterator = mapCollection.iterator(); while( initialEntityFieldValueIterator.hasNext() ) { duplicateFootprintForObject( (Map) mapCollectionIterator.next(), persistedEntityFieldValueIterator.next(), initialEntityFieldValueIterator.next() ); } } } Footprint footprint = persistenceCache.get( initialEntity ); if( footprint != null ) { persistenceCache.put( persistedEntity, footprint ); } } catch( NoSuchFieldException e ) { throw new BackendlessException( e ); } finally { marked.remove( persistedEntity ); } } /** * Updates footprint for entity. Specifically: * 1. Sets Footprint object from OLD to NEW * 2. Removed Footprint object for OLD * * @param serialized entity's map used to iterate through fields and update footprints recursively * @param newEntity entity from server * @param oldEntity entity on which a method was called (.save(), .create() etc.) */ void updateFootprintForObject( Map serialized, Object newEntity, Object oldEntity ) { //to avoid endless recursion if( marked.contains( newEntity ) ) { return; } else { marked.add( newEntity ); } try { //update footprints recursively Set> entries = serialized.entrySet(); for( Map.Entry entry : entries ) { String key = entry.getKey(); String upperKey = entry.getKey().substring( 0, 1 ).toUpperCase().concat( entry.getKey().substring( 1 ) ); if( entry.getValue() instanceof Map ) { Object newEntityField = getFieldValue( newEntity, key, upperKey ); Object oldEntityField = getFieldValue( oldEntity, key, upperKey ); updateFootprintForObject( (Map) entry.getValue(), newEntityField, oldEntityField ); } else if( entry.getValue() instanceof Collection ) { Collection valueCollection = (Collection) entry.getValue(); Iterator valueIterator = valueCollection.iterator(); if( valueIterator.hasNext() && valueIterator.next() instanceof GeoPoint ) continue; Collection newObjectCollection= getFieldCollection( newEntity, key, upperKey ); Collection oldObjectCollection = getFieldCollection( oldEntity, key, upperKey ); Collection mapCollection = (Collection) entry.getValue(); Iterator mapCollectionIterator = mapCollection.iterator(); Iterator oldObjectCollectionIterator = oldObjectCollection.iterator(); Iterator newObjectCollectionIterator = newObjectCollection.iterator(); while( oldObjectCollectionIterator.hasNext() ) updateFootprintForObject( (Map) mapCollectionIterator.next(), newObjectCollectionIterator.next(), oldObjectCollectionIterator.next() ); } } //Footprint footprint = persistenceCache.get( newEntity ); //persistenceCache.put( oldEntity, footprint ); //removeFootprintForObject( serialized, newEntity ); Footprint footprint = persistenceCache.get( oldEntity ); persistenceCache.put( newEntity, footprint ); removeFootprintForObject( serialized, oldEntity ); } finally { marked.remove( newEntity ); } } /** * Removes entity's footprint from cache. * * @param serializedEntity entity's map used to iterate through fields and remove footprints recursively * @param entity entity to be removed */ void removeFootprintForObject( Map serializedEntity, Object entity ) { //to avoid endless recursion if( marked.contains( entity ) ) { return; } else { marked.add( entity ); } try { //iterate through object's properties and remove footprints recursively Set> entries = serializedEntity.entrySet(); for( Map.Entry entry : entries ) { String key = entry.getKey(); String upperKey = entry.getKey().substring( 0, 1 ).toUpperCase().concat( entry.getKey().substring( 1 ) ); if( entry.getValue() instanceof Map ) { Object entityFieldValue = getFieldValue( entity, key, upperKey ); // remove footprints recursively removeFootprintForObject( (Map) entry.getValue(), entityFieldValue ); } else if( entry.getValue() instanceof Collection ) { Collection objectCollection = getFieldCollection( entity, key, upperKey ); Collection mapCollection = (Collection) entry.getValue(); // remove footprints recursively for each object in collection Iterator objectCollectionIterator = objectCollection.iterator(); Iterator mapCollectionIterator = mapCollection.iterator(); while( objectCollectionIterator.hasNext() ) removeFootprintForObject( (Map) mapCollectionIterator.next(), objectCollectionIterator.next() ); } } persistenceCache.remove( entity ); } finally { marked.remove( entity ); } } /** * Puts saved entity's footprint to cache. * Key - instance, value - footprint, initialized from instance. * Footprint is just an object, containing only fields * objectId, created, updated, meta. * * @param instance saved object received from server * @param entity IAdaptingType */ public void putEntityFootprintToCache( Object instance, Object entity ) { //to avoid endless recursion if( marked.contains( entity ) ) { return; } else { marked.add( entity ); } try { if( instance instanceof BackendlessCollection ) { AnonymousObject typedObject = (AnonymousObject) ((NamedObject) entity).getTypedObject(); ArrayType dataArray = (ArrayType) typedObject.getProperties().get( "data" ); Object[] instances = ((BackendlessCollection) instance).getCurrentPage().toArray(); putEntityFootprintToCache( instances, dataArray ); } else if( entity instanceof NamedObject ) { putEntityFootprintToCache( instance, ((NamedObject) entity).getTypedObject() ); } else if( entity instanceof AnonymousObject ) { putEntityFootprintToCache( instance, ((AnonymousObject) entity).getProperties() ); } else if( entity instanceof ArrayType ) { Object[] entities = (Object[]) ((ArrayType) entity).getArray(); Object[] arrayInstance = instance instanceof List ? ((List) instance).toArray() : (Object[]) instance; for( int i = 0; i < arrayInstance.length; i++ ) { putEntityFootprintToCache( arrayInstance[ i ], entities[ i ] ); } } else { Map e = (Map) entity; for( Map.Entry entityEntry : e.entrySet() ) { Object entityEntryValue = entityEntry.getValue(); if( entityEntryValue instanceof NamedObject || entityEntryValue instanceof ArrayType ) { Object innerInstance = ReflectionUtil.getFieldValue( instance, entityEntry.getKey() ); putEntityFootprintToCache( innerInstance, entityEntry.getValue() ); } } Map cachedEntity = (Map) entity; persistenceCache.put( instance, Footprint.initFromEntity( cachedEntity ) ); } } catch( Exception e ) {/*Error in caching process should not fail application*/ } marked.remove( entity ); } private Collection getFieldCollection( Object entity, String lowerKey, String upperKey ) { Collection collection; if( entity instanceof BackendlessUser ) { Object newObjectArray = ((BackendlessUser) entity).getProperty( lowerKey ); if( newObjectArray == null ) newObjectArray = ((BackendlessUser) entity).getProperty( upperKey ); if( newObjectArray == null ) collection = new ArrayList(); else if( newObjectArray instanceof Collection ) collection = (Collection) newObjectArray; else if( newObjectArray.getClass().isArray() ) collection = Arrays.asList( (Object[]) newObjectArray ); else throw new RuntimeException( "unknown data type - " + newObjectArray.getClass() ); } else { try { collection = (Collection) ReflectionUtil.getFieldValue( entity, lowerKey ); } catch( NoSuchFieldException nfe ) { try { //try to find field with first letter in uppercase collection = (Collection) ReflectionUtil.getFieldValue( entity, upperKey ); } catch( NoSuchFieldException e ) { throw new RuntimeException( "unable to find field in object. Fields (" + lowerKey + "," + upperKey + "). Class - " + entity.getClass() ); } } } return collection; } private Object getFieldValue( Object entity, String lowerKey, String upperKey ) { Object entityFieldValue; if( entity instanceof BackendlessUser ) { entityFieldValue = ((BackendlessUser) entity).getProperty( lowerKey ); if( entityFieldValue == null ) entityFieldValue = ((BackendlessUser) entity).getProperty( upperKey ); } else { try { // retrieve entity field value entityFieldValue = ReflectionUtil.getFieldValue( entity, lowerKey ); } catch( NoSuchFieldException nfe ) { try { //try to find field with first letter in uppercase entityFieldValue = ReflectionUtil.getFieldValue( entity, upperKey ); } catch( NoSuchFieldException e ) { throw new RuntimeException( "unable to find field in object. Fields (" + lowerKey + "," + upperKey + "). Class - " + entity.getClass() ); } } } return entityFieldValue; } } }