Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / spread / publish.py
1 # -*- test-case-name: twisted.test.test_pb -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 Persistently cached objects for PB.
7
8 Maintainer: Glyph Lefkowitz
9
10 Future Plans: None known.
11 """
12
13 import time
14
15 from twisted.internet import defer
16 from twisted.spread import banana, jelly, flavors
17
18
19 class Publishable(flavors.Cacheable):
20     """An object whose cached state persists across sessions.
21     """
22     def __init__(self, publishedID):
23         self.republish()
24         self.publishedID = publishedID
25
26     def republish(self):
27         """Set the timestamp to current and (TODO) update all observers.
28         """
29         self.timestamp = time.time()
30
31     def view_getStateToPublish(self, perspective):
32         '(internal)'
33         return self.getStateToPublishFor(perspective)
34     
35     def getStateToPublishFor(self, perspective):
36         """Implement me to special-case your state for a perspective.
37         """
38         return self.getStateToPublish()
39
40     def getStateToPublish(self):
41         """Implement me to return state to copy as part of the publish phase.
42         """
43         raise NotImplementedError("%s.getStateToPublishFor" % self.__class__)
44
45     def getStateToCacheAndObserveFor(self, perspective, observer):
46         """Get all necessary metadata to keep a clientside cache.
47         """
48         if perspective:
49             pname = perspective.perspectiveName
50             sname = perspective.getService().serviceName
51         else:
52             pname = "None"
53             sname = "None"
54
55         return {"remote": flavors.ViewPoint(perspective, self),
56                 "publishedID": self.publishedID,
57                 "perspective": pname,
58                 "service": sname,
59                 "timestamp": self.timestamp}
60
61 class RemotePublished(flavors.RemoteCache):
62     """The local representation of remote Publishable object.
63     """
64     isActivated = 0
65     _wasCleanWhenLoaded = 0
66     def getFileName(self, ext='pub'):
67         return ("%s-%s-%s.%s" %
68                 (self.service, self.perspective, str(self.publishedID), ext))
69     
70     def setCopyableState(self, state):
71         self.__dict__.update(state)
72         self._activationListeners = []
73         try:
74             dataFile = file(self.getFileName(), "rb")
75             data = dataFile.read()
76             dataFile.close()
77         except IOError:
78             recent = 0
79         else:
80             newself = jelly.unjelly(banana.decode(data))
81             recent = (newself.timestamp == self.timestamp)
82         if recent:
83             self._cbGotUpdate(newself.__dict__)
84             self._wasCleanWhenLoaded = 1
85         else:
86             self.remote.callRemote('getStateToPublish').addCallbacks(self._cbGotUpdate)
87
88     def __getstate__(self):
89         other = self.__dict__.copy()
90         # Remove PB-specific attributes
91         del other['broker']
92         del other['remote']
93         del other['luid']
94         # remove my own runtime-tracking stuff
95         del other['_activationListeners']
96         del other['isActivated']
97         return other
98
99     def _cbGotUpdate(self, newState):
100         self.__dict__.update(newState)
101         self.isActivated = 1
102         # send out notifications
103         for listener in self._activationListeners:
104             listener(self)
105         self._activationListeners = []
106         self.activated()
107         dataFile = file(self.getFileName(), "wb")
108         dataFile.write(banana.encode(jelly.jelly(self)))
109         dataFile.close()
110
111
112     def activated(self):
113         """Implement this method if you want to be notified when your
114         publishable subclass is activated.
115         """
116         
117     def callWhenActivated(self, callback):
118         """Externally register for notification when this publishable has received all relevant data.
119         """
120         if self.isActivated:
121             callback(self)
122         else:
123             self._activationListeners.append(callback)
124
125 def whenReady(d):
126     """
127     Wrap a deferred returned from a pb method in another deferred that
128     expects a RemotePublished as a result.  This will allow you to wait until
129     the result is really available.
130
131     Idiomatic usage would look like::
132
133         publish.whenReady(serverObject.getMeAPublishable()).addCallback(lookAtThePublishable)
134     """
135     d2 = defer.Deferred()
136     d.addCallbacks(_pubReady, d2.errback,
137                    callbackArgs=(d2,))
138     return d2
139
140 def _pubReady(result, d2):
141     '(internal)'
142     result.callWhenActivated(d2.callback)