github.com/hoffie/larasync@v0.0.0-20151025221940-0384d2bddcef/repository/transactionContainerManager.go (about) 1 package repository 2 3 import ( 4 "bytes" 5 "errors" 6 "io/ioutil" 7 "os" 8 "sync" 9 10 "github.com/golang/protobuf/proto" 11 12 "github.com/hoffie/larasync/helpers/lock" 13 "github.com/hoffie/larasync/repository/content" 14 "github.com/hoffie/larasync/repository/odf" 15 ) 16 17 // TransactionContainerManager is used to manage the transaction containers 18 // and to keep track of the most current transaction manager, 19 type TransactionContainerManager struct { 20 storage *content.UUIDStorage 21 lock sync.Locker 22 } 23 24 // newTransactionContainerManager initializes a container manager 25 // the passed content storage which is used to access the stored 26 // data entries. 27 func newTransactionContainerManager(storage content.Storage, lockingPath string) *TransactionContainerManager { 28 uuidStorage := content.NewUUIDStorage(storage) 29 return &TransactionContainerManager{ 30 storage: uuidStorage, 31 lock: lock.CurrentManager().Get( 32 lockingPath, 33 "transaction_container_manager", 34 ), 35 } 36 } 37 38 // Get returns the TransactionContainer with the given UUID. 39 func (tcm TransactionContainerManager) Get(transactionContainerUUID string) (*TransactionContainer, error) { 40 reader, err := tcm.storage.Get(transactionContainerUUID) 41 if err != nil { 42 return nil, err 43 } 44 defer reader.Close() 45 46 data, err := ioutil.ReadAll(reader) 47 if err != nil { 48 return nil, err 49 } 50 51 protoTransactionContainer := &odf.TransactionContainer{} 52 err = proto.Unmarshal( 53 data, 54 protoTransactionContainer) 55 56 if err != nil { 57 return nil, err 58 } 59 60 transactionContainer := newTransactionContainerFromPb(protoTransactionContainer) 61 return transactionContainer, nil 62 } 63 64 // Set sets the transactionContainer in the storage backend. 65 func (tcm TransactionContainerManager) Set(transactionContainer *TransactionContainer) error { 66 if transactionContainer.UUID == "" { 67 return errors.New("UUID must not be empty") 68 } 69 lock := tcm.lock 70 71 lock.Lock() 72 err := func() error { 73 protoTransactionContainer, err := transactionContainer.toPb() 74 if err != nil { 75 return err 76 } 77 78 data, err := proto.Marshal(protoTransactionContainer) 79 if err != nil { 80 return err 81 } 82 83 err = tcm.storage.Set( 84 transactionContainer.UUID, 85 bytes.NewBuffer(data)) 86 if err != nil { 87 return err 88 } 89 90 return tcm.storage.Set( 91 "current", 92 bytes.NewBufferString(transactionContainer.UUID)) 93 }() 94 lock.Unlock() 95 return err 96 } 97 98 // Exists returns if a TransactionContainer with the given UUID exists in the system. 99 func (tcm TransactionContainerManager) Exists(transactionContainerUUID string) bool { 100 return tcm.storage.Exists(transactionContainerUUID) 101 } 102 103 // currentTransactionContainerUUID reads the stored currently 104 // configured UUID for the transaction container. 105 func (tcm TransactionContainerManager) currentTransactionContainerUUID() (string, error) { 106 reader, err := tcm.storage.Get("current") 107 108 if err != nil { 109 if os.IsNotExist(err) { 110 return "", nil 111 } 112 return "", err 113 } 114 defer reader.Close() 115 116 data, err := ioutil.ReadAll(reader) 117 if err != nil { 118 return "", err 119 } 120 121 return string(data), nil 122 } 123 124 // CurrentTransactionContainer returns the TransactionContainer which is the most recent 125 // for the given repository. 126 func (tcm TransactionContainerManager) CurrentTransactionContainer() (*TransactionContainer, error) { 127 currentUUID, err := tcm.currentTransactionContainerUUID() 128 if err != nil { 129 return nil, err 130 } 131 132 if currentUUID != "" { 133 return tcm.Get(currentUUID) 134 } 135 // Have to create a new TransactionContainer due to no current existing yet. 136 return tcm.NewContainer() 137 } 138 139 // NewContainer returns a newly container with a new UUID which has been added to the 140 // storage backend. 141 func (tcm TransactionContainerManager) NewContainer() (*TransactionContainer, error) { 142 data, err := tcm.storage.FindFreeUUID() 143 if err != nil { 144 return nil, err 145 } 146 147 uuid := formatUUID(data) 148 previousUUID, err := tcm.currentTransactionContainerUUID() 149 if err != nil { 150 return nil, err 151 } 152 153 transactionContainer := &TransactionContainer{ 154 UUID: uuid, 155 Transactions: []*Transaction{}, 156 PreviousUUID: previousUUID} 157 158 err = tcm.Set(transactionContainer) 159 if err != nil { 160 return nil, err 161 } 162 163 return transactionContainer, nil 164 }