github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/orderer/ledger/json/factory.go (about)

     1  /*
     2  Copyright IBM Corp. 2017 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package jsonledger
    18  
    19  import (
    20  	"fmt"
    21  	"io/ioutil"
    22  	"os"
    23  	"path/filepath"
    24  	"sync"
    25  
    26  	"github.com/golang/protobuf/jsonpb"
    27  	"github.com/hyperledger/fabric/orderer/ledger"
    28  )
    29  
    30  type jsonLedgerFactory struct {
    31  	directory string
    32  	ledgers   map[string]ledger.ReadWriter
    33  	mutex     sync.Mutex
    34  }
    35  
    36  // GetOrCreate gets an existing ledger (if it exists) or creates it if it does not
    37  func (jlf *jsonLedgerFactory) GetOrCreate(chainID string) (ledger.ReadWriter, error) {
    38  	jlf.mutex.Lock()
    39  	defer jlf.mutex.Unlock()
    40  
    41  	key := chainID
    42  
    43  	l, ok := jlf.ledgers[key]
    44  	if ok {
    45  		return l, nil
    46  	}
    47  
    48  	directory := filepath.Join(jlf.directory, fmt.Sprintf(chainDirectoryFormatString, chainID))
    49  
    50  	logger.Debugf("Initializing chain at: %s", directory)
    51  
    52  	if err := os.MkdirAll(directory, 0700); err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	ch := newChain(directory)
    57  	jlf.ledgers[key] = ch
    58  	return ch, nil
    59  }
    60  
    61  // newChain creates a new chain backed by a JSON ledger
    62  func newChain(directory string) ledger.ReadWriter {
    63  	jl := &jsonLedger{
    64  		directory: directory,
    65  		signal:    make(chan struct{}),
    66  		marshaler: &jsonpb.Marshaler{Indent: "  "},
    67  	}
    68  	jl.initializeBlockHeight()
    69  	logger.Debugf("Initialized to block height %d with hash %x", jl.height-1, jl.lastHash)
    70  	return jl
    71  }
    72  
    73  // initializeBlockHeight verifies that all blocks exist between 0 and the block
    74  // height, and populates the lastHash
    75  func (jl *jsonLedger) initializeBlockHeight() {
    76  	infos, err := ioutil.ReadDir(jl.directory)
    77  	if err != nil {
    78  		logger.Panic(err)
    79  	}
    80  	nextNumber := uint64(0)
    81  	for _, info := range infos {
    82  		if info.IsDir() {
    83  			continue
    84  		}
    85  		var number uint64
    86  		_, err := fmt.Sscanf(info.Name(), blockFileFormatString, &number)
    87  		if err != nil {
    88  			continue
    89  		}
    90  		if number != nextNumber {
    91  			logger.Panicf("Missing block %d in the chain", nextNumber)
    92  		}
    93  		nextNumber++
    94  	}
    95  	jl.height = nextNumber
    96  	if jl.height == 0 {
    97  		return
    98  	}
    99  	block, found := jl.readBlock(jl.height - 1)
   100  	if !found {
   101  		logger.Panicf("Block %d was in directory listing but error reading", jl.height-1)
   102  	}
   103  	if block == nil {
   104  		logger.Panicf("Error reading block %d", jl.height-1)
   105  	}
   106  	jl.lastHash = block.Header.Hash()
   107  }
   108  
   109  // ChainIDs returns the chain IDs the factory is aware of
   110  func (jlf *jsonLedgerFactory) ChainIDs() []string {
   111  	jlf.mutex.Lock()
   112  	defer jlf.mutex.Unlock()
   113  	ids := make([]string, len(jlf.ledgers))
   114  
   115  	i := 0
   116  	for key := range jlf.ledgers {
   117  		ids[i] = key
   118  		i++
   119  	}
   120  
   121  	return ids
   122  }
   123  
   124  // Close is a no-op for the JSON ledger
   125  func (jlf *jsonLedgerFactory) Close() {
   126  	return // nothing to do
   127  }
   128  
   129  // New creates a new ledger factory
   130  func New(directory string) ledger.Factory {
   131  	logger.Debugf("Initializing ledger at: %s", directory)
   132  	if err := os.MkdirAll(directory, 0700); err != nil {
   133  		logger.Fatalf("Could not create directory %s: %s", directory, err)
   134  	}
   135  
   136  	jlf := &jsonLedgerFactory{
   137  		directory: directory,
   138  		ledgers:   make(map[string]ledger.ReadWriter),
   139  	}
   140  
   141  	infos, err := ioutil.ReadDir(jlf.directory)
   142  	if err != nil {
   143  		logger.Panicf("Error reading from directory %s while initializing ledger: %s", jlf.directory, err)
   144  	}
   145  
   146  	for _, info := range infos {
   147  		if !info.IsDir() {
   148  			continue
   149  		}
   150  		var chainID string
   151  		_, err := fmt.Sscanf(info.Name(), chainDirectoryFormatString, &chainID)
   152  		if err != nil {
   153  			continue
   154  		}
   155  		jl, err := jlf.GetOrCreate(chainID)
   156  		if err != nil {
   157  			logger.Warningf("Failed to initialize chain from %s: %s", chainID, err)
   158  			continue
   159  		}
   160  		jlf.ledgers[chainID] = jl
   161  	}
   162  
   163  	return jlf
   164  }