proxy._pike
Go to the documentation of this file.
1 /* Copyright (C) 2000-2010 Thomas Bopp, Thorsten Hampel, Ludger Merkens
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  *
17  * $Id: proxy.pike,v 1.6 2010/08/21 19:38:00 astra Exp $
18  */
19 #include <macros.h>
20 #include <exception.h>
21 #include <database.h>
22 #include <events.h>
23 class proxy {
24 public:
25 
26 
27 
28 
29 private object oSteamObj;
30 public object oNext, oPrev;
31 private int iOID;
32 private int iClassID;
33 private int iTime;
34 private mapping mDecorationObjects;
35 private array aDecorationObjects; // cache
36 
37 private int iStatus;
38 private Thread.Mutex loadMutex = Thread.Mutex(); // save loading
39 
40 /*
41  * @function create
42  * create a proxy for a sTeam-Object
43  * @returns void (is a constructor)
44  * @args int _id - the object ID of the associated object
45  * int init - create proxy only, or create new object
46  * string prog - class for the associated object
47  */
48 final void create(int _id, object|void oTrue, void|int _class)
49 {
50  iStatus = PSTAT_DISK;
51  iOID = _id;
52  iClassID = _class;
53  iTime = time();
54  mDecorationObjects = ([ ]);
55  aDecorationObjects = ({ });
56 
57  if (objectp(oTrue))
58  {
59  oSteamObj = oTrue;
60  iStatus = PSTAT_SAVE_OK;
61  }
62  // master()->append(this_object());
63 }
64 
65 /**
66  * @function get_object_id
67  * @returns int (the object id to the associated object)
68  */
69 final int get_object_id()
70 {
71  return iOID;
72 }
73 
74 final int get_object_class()
75 {
76  if ( iClassID )
77  return iClassID;
78 
79  function f = find_function("get_object_class");
80  catch(iClassID = f());
81  return iClassID;
82 }
83 
84 final void set_steam_obj(object o)
85 {
86  if ((CALLER == _Database || CALLER == _Persistence) && !objectp(oSteamObj))
87  oSteamObj = o;
88 }
89 
90 
91 /**
92  * set the status of the proxy. Changes can only be done by either server
93  * or database object.
94  *
95  * @param int _status - new status to set
96  * @see status
97  */
98 final void set_status(int _status)
99 {
100  object c = CALLER;
101  if (c == _Database || c == _Server || c == _Persistence || c == oSteamObj)
102  iStatus = _status;
103 }
104 
105 private:
106 private int i_am_in_backtrace()
107 {
108  foreach(backtrace(), mixed preceed)
109  {
110  if (function_object(preceed[2]) == oSteamObj) {
111  return 1;
112  }
113  }
114  return 0;
115 }
116 
117 public:
118 
119 /**
120  * Drop the corresponding steam object.
121  *
122  * @return 0 or 1 depending if the drop is successfull.
123  */
124 final int drop()
125 {
126  if ( !objectp(oSteamObj) )
127  return 1;
128  else if ( iStatus == PSTAT_SAVE_PENDING)
129  {
130  call(drop, 2.0); // drop later
131  return 0;
132  }
133  else if ( i_am_in_backtrace()) // don't drop an object wich is a caller
134  {
135  call(drop, 0.0);
136  return 0;
137  }
138  object lock = loadMutex->trylock();
139  if (!objectp(lock)) {
140  call(drop, 2.0); // drop later
141  return 0;
142  }
143 
144  // the object should also be removed from memory ?!
145  destruct(oSteamObj);
146  oSteamObj = 0;
147  master()->got_dropped(this_object());
148  iClassID = 0;
149  iStatus = PSTAT_DISK;
150  destruct(lock);
151  return 1;
152 }
153 
154 /**
155  * Find a function inside the proxy object.
156  *
157  * @param string fun - the function to find
158  * @return a functionp of the function or 0
159  */
160 final function find_function (string fun)
161 {
162  if ( !objectp(oSteamObj) )
163  f_load_object();
164  if ( !objectp(oSteamObj) )
165  return 0;
166  function f;
167 
168  if ( f = oSteamObj[fun] )
169  return f;
170  foreach ( aDecorationObjects, object decoration ) {
171  if ( !objectp(decoration) ) continue;
172  f = decoration->find_function( fun );
173  if ( f ) return f;
174  }
175  return 0;
176 }
177 
178 protected:
179  void f_load_object()
180 {
181  object loadLock = loadMutex->lock();
182  if ( !objectp(oSteamObj) ) {
183  iTime = time();
184  mixed err = catch {
185  switch(_Persistence->load_object(this_object(), iOID)) {
186  case 1:
187  iStatus = PSTAT_FAIL_COMPILE;
188  break;
189  case 0:
190  iStatus = PSTAT_FAIL_DELETED;
191  break;
192  case 2:
193  iStatus = PSTAT_FAIL_DELETED;
194  break;
195  case 3:
196  iStatus = PSTAT_FAIL_UNSERIALIZE;
197  break;
198  default:
199  if ( iStatus == PSTAT_DISK )
200  iStatus = PSTAT_SAVE_OK;
201  }
202  if (!objectp(oSteamObj)) {
203  destruct(loadLock);
204  return;
205  }
206 
207  master()->got_loaded(this_object());
208  };
209  if ( err ) {
210  destruct(loadLock);
211  throw(err);
212  }
213  }
214  destruct(loadLock);
215 }
216 
217 public:
218 
219 mapping (string:mixed) fLocal = ([
220  "get_object_class": get_object_class,
221  "get_object_id" : get_object_id,
222  "get_object_class": get_object_class,
223  "set_status": set_status,
224  "set_decoration_object": set_decoration_object,
225  "drop" : drop,
226  "find_function" : find_function,
227  "swap": swap,
228  "set_steam_obj": set_steam_obj,
229  "destroy": destroy,
230  "status" : status,
231  "get_object" : get_object,
232 ]);
233 
234 /**
235  * `->() The indexing operator is replaced in proxy.pike to redirect function
236  * calls to the associated object.
237  * @param string func - the function to redirect
238  * @return mixed - usually the function pointer to the function in question
239  * __null() in case of error
240  * @see __null
241  * @see find_function
242  */
243 final mixed `->(string func)
244 {
245  function f;
246 
247  if (!oSteamObj) {
248  if ((f=fLocal[func]) && (func!="get_object") && (func!="find_function"))
249  return f;
250  // double check for loadlock due to performance reasons
251  // usually loading is only called once and this way performance
252  // is better when already loaded
253  f_load_object();
254  if (!objectp(oSteamObj))
255  return __null;
256  }
257 
258  if (iStatus<0)
259  return __null;
260 
261  if (f = fLocal[func])
262  return f;
263 
264  if ( !(f = oSteamObj[func]) ) {
265  foreach ( aDecorationObjects, object decoration ) {
266  if ( !objectp(decoration) ) continue;
267  mixed deco_f = decoration->find_function( func );
268  if ( deco_f ) return deco_f;
269  }
270  return __null;
271  }
272 
273  if (func == "get_identifier")
274  master()->front(this_object());
275  iTime = time();
276  return f;
277 }
278 
279 /**
280  * dummy function, replacing a broken function, in case of error
281  * @param none
282  * @return 0
283  * @see `->()
284  */
285 final mixed __null()
286 {
287  return 0;
288 }
289 
290 
291 /**
292  * forces to load the content of an object from the database
293  * is this function used ? - astra
294  *
295  */
296 final int|object force_load()
297 {
298  int x;
299  object loadLock = loadMutex->lock();
300  mixed err = catch {
301  if (oSteamObj)
302  {
303  x= _Persistence->load_object(this_object(), oSteamObj);
304  switch (x)
305  {
306  case 1: iStatus = PSTAT_FAIL_COMPILE;
307  break;
308  case 0: iStatus = PSTAT_FAIL_DELETED;
309  break;
310  case 2: iStatus = PSTAT_FAIL_COMPILE;
311  break;
312  }
313  }
314  else
315  {
316  x= _Persistence->load_object(this_object(), iOID);
317  switch (x)
318  {
319  case 1: iStatus = PSTAT_FAIL_COMPILE;
320  break;
321  case 0: iStatus = PSTAT_FAIL_DELETED;
322  break;
323  case 2: iStatus = PSTAT_FAIL_COMPILE;
324  }
325  }
326  };
327  if ( err ) {
328  throw(err);
329  destruct(loadLock);
330  }
331  return x;
332 }
333 
334 /**
335  * get the associated Object from this proxy
336  * @param none
337  * @return object | 0
338  * @see set_steam_obj
339  * @see _Database.load_object
340  */
341 final object get_object()
342 {
343  return oSteamObj;
344 }
345 
346 /**
347  * Called when the object including the proxy are destructed.
348  *
349  */
350 final void destroy()
351 {
352  master()->remove(this_object());
353 }
354 
355 /**
356  *
357  * The function returns the status of the proxy, which is actually
358  * the status of the corresponding object.
359  *
360  * @param none
361  * @return PSTAT_DISK ( 0) - on disk
362  * PSTAT_SAVE_OK ( 1) - in memory
363  * PSTAT_SAVE_PENDING ( 2) - in memory, but dirty (not implemented)
364  * PSTAT_DELETED ( 3) - deleted from database
365  * PSTAT_FAIL_COMPILE (-1) - failed to load (compilation failure)
366  * PSTAT_FAIL_UNSERIALIZE (-2) - failed to load (serialization failure)
367  * PSTAT_FAIL_DELETED (-3) - failed to load (deleted from database)
368  * @see database.h for PSTAT constants.
369  */
370 final int status()
371 {
372  if (iStatus <0)
373  return iStatus;
374  if (!objectp(oSteamObj))
375  return 0;
376 
377  return iStatus;
378 }
379 
380 string _sprintf()
381 {
382  return "/kernel/proxy.pike("+iOID+"/"+
383  ({ "PSTAT_FAIL_DELETED", "PSTAT_FAIL_UNSERIALIZE" ,
384  "PSTAT_FAIL_COMPILE", "PSTAT_DISK", "PSTAT_SAVE_OK",
385  "PSTAT_SAVE_PENDING", "PSTAT_DELETED" })[iStatus+3]+")";
386 }
387 
388 void set_decoration_object ( string path, object|void decoration )
389 {
390  if ( CALLER != oSteamObj )
391  steam_error( "No access to set decoration instance in proxy !" );
392  if ( objectp(decoration) )
393  mDecorationObjects[ path ] = decoration;
394  else
395  m_delete( mDecorationObjects, path );
396  aDecorationObjects = values( mDecorationObjects ); // refresh cache
397 }
398 
399 public int swap(int minSwapTime)
400 {
401  if (time() - iTime > minSwapTime) {
402  drop();
403  return 1;
404  }
405  return 0;
406 }
407 
408 };