github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/schema/dirreader.go (about) 1 /* 2 Copyright 2011 Google Inc. 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 schema 18 19 import ( 20 "encoding/json" 21 "errors" 22 "fmt" 23 "io" 24 25 "camlistore.org/pkg/blob" 26 ) 27 28 // A DirReader reads the entries of a "directory" schema blob's 29 // referenced "static-set" blob. 30 type DirReader struct { 31 fetcher blob.SeekFetcher 32 ss *superset 33 34 staticSet []blob.Ref 35 current int 36 } 37 38 // NewDirReader creates a new directory reader and prepares to 39 // fetch the static-set entries 40 func NewDirReader(fetcher blob.SeekFetcher, dirBlobRef blob.Ref) (*DirReader, error) { 41 ss := new(superset) 42 err := ss.setFromBlobRef(fetcher, dirBlobRef) 43 if err != nil { 44 return nil, err 45 } 46 if ss.Type != "directory" { 47 return nil, fmt.Errorf("schema/filereader: expected \"directory\" schema blob for %s, got %q", dirBlobRef, ss.Type) 48 } 49 dr, err := ss.NewDirReader(fetcher) 50 if err != nil { 51 return nil, fmt.Errorf("schema/filereader: creating DirReader for %s: %v", dirBlobRef, err) 52 } 53 dr.current = 0 54 return dr, nil 55 } 56 57 func (b *Blob) NewDirReader(fetcher blob.SeekFetcher) (*DirReader, error) { 58 return b.ss.NewDirReader(fetcher) 59 } 60 61 func (ss *superset) NewDirReader(fetcher blob.SeekFetcher) (*DirReader, error) { 62 if ss.Type != "directory" { 63 return nil, fmt.Errorf("Superset not of type \"directory\"") 64 } 65 return &DirReader{fetcher: fetcher, ss: ss}, nil 66 } 67 68 func (ss *superset) setFromBlobRef(fetcher blob.SeekFetcher, blobRef blob.Ref) error { 69 if !blobRef.Valid() { 70 return errors.New("schema/filereader: blobref invalid") 71 } 72 ss.BlobRef = blobRef 73 rsc, _, err := fetcher.Fetch(blobRef) 74 if err != nil { 75 return fmt.Errorf("schema/filereader: fetching schema blob %s: %v", blobRef, err) 76 } 77 defer rsc.Close() 78 if err = json.NewDecoder(rsc).Decode(ss); err != nil { 79 return fmt.Errorf("schema/filereader: decoding schema blob %s: %v", blobRef, err) 80 } 81 return nil 82 } 83 84 // StaticSet returns the whole of the static set members of that directory 85 func (dr *DirReader) StaticSet() ([]blob.Ref, error) { 86 if dr.staticSet != nil { 87 return dr.staticSet, nil 88 } 89 staticSetBlobref := dr.ss.Entries 90 if !staticSetBlobref.Valid() { 91 return nil, fmt.Errorf("schema/filereader: Invalid blobref\n") 92 } 93 rsc, _, err := dr.fetcher.Fetch(staticSetBlobref) 94 if err != nil { 95 return nil, fmt.Errorf("schema/filereader: fetching schema blob %s: %v", staticSetBlobref, err) 96 } 97 defer rsc.Close() 98 ss, err := parseSuperset(rsc) 99 if err != nil { 100 return nil, fmt.Errorf("schema/filereader: decoding schema blob %s: %v", staticSetBlobref, err) 101 } 102 if ss.Type != "static-set" { 103 return nil, fmt.Errorf("schema/filereader: expected \"static-set\" schema blob for %s, got %q", staticSetBlobref, ss.Type) 104 } 105 for _, member := range ss.Members { 106 if !member.Valid() { 107 return nil, fmt.Errorf("schema/filereader: invalid (static-set member) blobref\n") 108 } 109 dr.staticSet = append(dr.staticSet, member) 110 } 111 return dr.staticSet, nil 112 } 113 114 // Readdir implements the Directory interface. 115 func (dr *DirReader) Readdir(n int) (entries []DirectoryEntry, err error) { 116 sts, err := dr.StaticSet() 117 if err != nil { 118 return nil, fmt.Errorf("schema/filereader: can't get StaticSet: %v\n", err) 119 } 120 up := dr.current + n 121 if n <= 0 { 122 dr.current = 0 123 up = len(sts) 124 } else { 125 if n > (len(sts) - dr.current) { 126 err = io.EOF 127 up = len(sts) 128 } 129 } 130 131 // TODO(bradfitz): push down information to the fetcher 132 // (e.g. cachingfetcher -> remote client http) that we're 133 // going to load a bunch, so the HTTP client (if not using 134 // SPDY) can do discovery and see if the server supports a 135 // batch handler, then get them all in one round-trip, rather 136 // than attacking the server with hundreds of parallel TLS 137 // setups. 138 139 type res struct { 140 ent DirectoryEntry 141 err error 142 } 143 var cs []chan res 144 145 // Kick off all directory entry loads. 146 // TODO: bound this? 147 for _, entRef := range sts[dr.current:up] { 148 c := make(chan res, 1) 149 cs = append(cs, c) 150 go func(entRef blob.Ref) { 151 entry, err := NewDirectoryEntryFromBlobRef(dr.fetcher, entRef) 152 c <- res{entry, err} 153 }(entRef) 154 } 155 156 for _, c := range cs { 157 res := <-c 158 if res.err != nil { 159 return nil, fmt.Errorf("schema/filereader: can't create dirEntry: %v\n", err) 160 } 161 entries = append(entries, res.ent) 162 } 163 return entries, nil 164 }