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  }