Package flumotion :: Package component :: Package misc :: Package httpserver :: Package httpcached :: Module resource_manager
[hide private]

Source Code for Module flumotion.component.misc.httpserver.httpcached.resource_manager

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_component_providers -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3   
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  import errno 
 19   
 20  from twisted.internet import defer 
 21   
 22  from flumotion.common import log 
 23   
 24  from flumotion.component.misc.httpserver import fileprovider 
 25  from flumotion.component.misc.httpserver import cachestats 
 26   
 27  LOG_CATEGORY = "resource-manager" 
 28   
 29   
 30  errnoLookup = {errno.ENOENT: fileprovider.NotFoundError, 
 31                 errno.EISDIR: fileprovider.CannotOpenError, 
 32                 errno.EACCES: fileprovider.AccessError} 
 33   
 34   
35 -class DataSource(object):
36 """ 37 Base class for all resources data source. 38 """ 39 40 url = None 41 identifier = None 42 mimeType = None 43 mtime = None 44 size = None 45
46 - def read(self, offset, size):
47 raise NotImplementedError()
48
49 - def close(self):
50 raise NotImplementedError()
51 52
53 -class ResourceManager(log.Loggable):
54 """ 55 Provide file-like resources for URLs. 56 """ 57 58 logCategory = LOG_CATEGORY 59
60 - def __init__(self, strategy, stats):
61 self.strategy = strategy 62 self.stats = stats
63
64 - def getResourceFor(self, url):
65 self.debug("Resource requested with %s", url) 66 67 stats = cachestats.RequestStatistics(self.stats) 68 69 d = defer.Deferred() 70 d.addCallback(self.strategy.getSourceFor, stats) 71 d.addCallback(Resource, stats) 72 73 d.callback(url) 74 75 return d
76 77
78 -class Resource(object):
79 """ 80 Offers a file-like interface of a data source. 81 Handle errors and asynchronous readings and file offset. 82 """ 83
84 - def __init__(self, source, stats):
85 self._open(source) 86 self._offset = 0 87 self._reading = False 88 self.stats = stats
89
90 - def getMimeType(self):
91 return self.mimeType
92
93 - def getmtime(self):
94 return self._source.mtime
95
96 - def getsize(self):
97 return self._source.size
98
99 - def tell(self):
100 return self._offset
101
102 - def seek(self, offset):
103 self._check() 104 self._offset = offset
105
106 - def read(self, size):
107 self._check() 108 assert not self._reading, "Simultaneous read not supported" 109 try: 110 d = self._source.read(self._offset, size) 111 if isinstance(d, defer.Deferred): 112 self._reading = True 113 return d.addCallback(self._cbUpdateOffset) 114 self._offset += len(d) 115 return defer.succeed(d) 116 except IOError, e: 117 cls = errnoLookup.get(e.errno, fileprovider.FileError) 118 return defer.fail(cls("Failed to read data: %s", str(e))) 119 except: 120 return defer.fail()
121
122 - def produce(self, consumer, fromOffset=None):
123 """ 124 Returns a producer that produce data from the specified position 125 or from the current position if None is specified. 126 Can return None if a producer cannot be provided or is not convenient. 127 """ 128 self._check() 129 return self._source.produce(consumer, fromOffset or self._offset)
130
131 - def close(self):
132 self._check() 133 self._source.close() 134 self._source = None
135
136 - def getLogFields(self):
137 return self.stats.getLogFields()
138 139 ### Protected Methods ### 140
141 - def _check(self):
142 if self._source is None: 143 raise fileprovider.FileClosedError("File Closed")
144
145 - def _open(self, source):
146 self._source = source 147 self.mimeType = source.mimeType
148 149 ### Private Methods ### 150
151 - def _cbUpdateOffset(self, data):
152 self._reading = False 153 self._offset += len(data) 154 return data
155