001package arez.persist.runtime; 002 003import arez.Arez; 004import arez.SafeProcedure; 005import java.util.Map; 006import java.util.Objects; 007import javax.annotation.Nonnull; 008import javax.annotation.Nullable; 009 010/** 011 * Interface implemented to provide the state storage and retrieval services. 012 */ 013public interface StorageService 014{ 015 /** 016 * Dispose the service. 017 * The service may release any resources no longer required after dispose has been 018 * called. It is expected that no other methods will be invoked after this method 019 * has been invoked. 020 */ 021 void dispose(); 022 023 /** 024 * Method invoked when state has changed. 025 * The storage service is expected to invoke the {@code commitTriggerAction.call()} 026 * method when service wants to commit changes to the service. This is likely to be 027 * invoked many times in quick succession and it is up to the storage service to decide 028 * how to batch up the changes and thus when to schedule the commit. 029 * 030 * @param commitTriggerAction the action to invoke when a commit should be triggered. 031 */ 032 void scheduleCommit( @Nonnull SafeProcedure commitTriggerAction ); 033 034 /** 035 * Invoked by the store to save the state to the storage service. 036 * The map contains scopes, mapped to component types, mapped to component ids, map 037 * to instance data for that component. 038 * 039 * @param state the state to store. 040 */ 041 void commit( @Nonnull final Map<Scope, Map<String, Map<String, Entry>>> state ); 042 043 /** 044 * Restore state from the service into the specified state parameter. 045 * 046 * @param state the map in which to store data retrieved from the service. 047 */ 048 void restore( @Nonnull final Map<Scope, Map<String, Map<String, Entry>>> state ); 049 050 /** 051 * Encode state in a way that makes storage to the backend easier. 052 * This is invoked when the state change occurs. 053 * 054 * @param state the component state. 055 * @param converter the converter for the type. 056 * @return the encoded form for this service. 057 */ 058 @Nonnull 059 Object encodeState( @Nonnull Map<String, Object> state, @Nonnull final TypeConverter converter ); 060 061 /** 062 * Decode state from backend storage form. 063 * This occurs on access to state. 064 * 065 * @param encoded the encoded component state. 066 * @param converter the converter for the type. 067 * @return the decoded form of component state. 068 */ 069 @Nonnull 070 Map<String, Object> decodeState( @Nonnull Object encoded, @Nonnull final TypeConverter converter ); 071 072 /** 073 * An entry containing the state for a particular component. 074 */ 075 final class Entry 076 { 077 @Nullable 078 private Map<String, Object> _data; 079 @Nonnull 080 private final Object _encoded; 081 082 public Entry( @Nullable final Map<String, Object> data, @Nonnull final Object encoded ) 083 { 084 _data = data; 085 _encoded = Objects.requireNonNull( encoded ); 086 } 087 088 /** 089 * Return the decoded data. 090 * 091 * @return the decoded data. 092 */ 093 @Nullable 094 public Map<String, Object> getData() 095 { 096 return _data; 097 } 098 099 void setData( @Nullable final Map<String, Object> data ) 100 { 101 _data = data; 102 } 103 104 /** 105 * Return the encoded data. 106 * 107 * @return the encoded data. 108 */ 109 @Nonnull 110 public Object getEncoded() 111 { 112 return _encoded; 113 } 114 115 @Override 116 public String toString() 117 { 118 if ( Arez.areNamesEnabled() ) 119 { 120 return String.valueOf( _data ); 121 } 122 else 123 { 124 return super.toString(); 125 } 126 } 127 } 128}