wCMF  3.6
All Classes Namespaces Files Functions Variables Groups Pages
class.RemoteCapablePersistenceFacadeImpl.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2010 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id$
18  */
19 require_once(BASE."wcmf/lib/persistence/class.PersistenceFacadeImpl.php");
20 require_once(BASE."wcmf/lib/remoting/class.RemotingFacade.php");
21 
22 /**
23  * @class RemoteCapablePersistenceFacade
24  * @ingroup Persistence
25  * @brief RemoteCapablePersistenceFacade delegates local persistence operations to the
26  * default PersistenceFacadeImpl and remote operations to a remote server.
27  *
28  * @author ingo herwig <ingo@wemove.com>
29  */
31 {
32  // constants
33  const PROXY_OBJECTS_SESSION_VARNAME = 'RemoteCapablePersistenceFacadeImpl.proxyObjects';
34  const REMOTE_OBJECTS_SESSION_VARNAME = 'RemoteCapablePersistenceFacadeImpl.remoteObjects';
35 
38 
39  /**
40  * Constructor
41  */
43  {
44  // initialize session variables
45  $session = &SessionData::getInstance();
46  if (!$session->exist(self::PROXY_OBJECTS_SESSION_VARNAME)) {
47  $proxies = array();
48  $session->set(self::PROXY_OBJECTS_SESSION_VARNAME, $proxies);
49  }
50  if (!$session->exist(self::REMOTE_OBJECTS_SESSION_VARNAME)) {
51  $objs = array();
52  $session->set(self::REMOTE_OBJECTS_SESSION_VARNAME, $objs);
53  }
54  }
55  /**
56  * Tell the PersistenceFacade implementation to resolve proxies or not.
57  * @param isResolvingProxies True/False whether proxies should be resolved or not
58  */
59  function setResolveProxies($isResolvingProxies)
60  {
61  $this->_isResolvingProxies = $isResolvingProxies;
62  }
63  /**
64  * Check if the PersistenceFacade implementation is resolving proxies or not.
65  * @return True/False whether proxies are resolved or not
66  */
67  function isResolvingProxies()
68  {
70  }
71  /**
72  * Tell the PersistenceFacade implementation to translate remote values or not.
73  * @param isTranslatingValues True/False whether values should be translated or not
74  */
75  function setTranslatingValues($isTranslatingValues)
76  {
77  $this->_isTranslatingValues = $isTranslatingValues;
78  }
79  /**
80  * Check if the PersistenceFacade implementation is translating remote values or not.
81  * @return True/False whether values are tanslated or not
82  */
84  {
86  }
87  /**
88  * @see PersistenceFacade::load()
89  */
90  function &load($oid, $buildDepth, $buildAttribs=null, $buildTypes=null)
91  {
92  if ($this->isResolvingProxies() && strlen(PersistenceFacade::getOIDParameter($oid, 'prefix')) > 0) {
93  // load real subject
94  $obj = $this->loadRemoteObject($oid, $buildDepth);
95  }
96  else
97  {
98  $obj = &parent::load($oid, $buildDepth, $buildAttribs, $buildTypes);
99  if ($obj && $this->isResolvingProxies() && strlen($umi = $obj->getValue('umi')) > 0) {
100  // store proxy for later reference
101  $this->registerProxyObject($umi, $obj, $buildDepth);
102  // load real subject
103  $obj = $this->loadRemoteObject($umi, $buildDepth);
104  }
105  }
106  return $obj;
107  }
108  /**
109  * @see PersistenceFacade::create()
110  */
111  function &create($type, $buildDepth, $buildAttribs=null)
112  {
113  $obj = &parent::create($type, $buildDepth, $buildAttribs);
114  return $obj;
115  }
116  /**
117  * @see PersistenceFacade::save()
118  */
119  function save(&$object)
120  {
121  if (strlen(PersistenceFacade::getOIDParameter($object->getOID(), 'prefix')) > 0) {
122  WCMFException::throwEx("The remote object '".$object->getOID()."' is immutable.", __FILE__, __LINE__);
123  return false;
124  }
125  $result = parent::save($object);
126  return $result;
127  }
128  /**
129  * @see PersistenceFacade::delete()
130  */
131  function delete($oid, $recursive=true)
132  {
133  if (strlen(PersistenceFacade::getOIDParameter($oid, 'prefix')) > 0) {
134  WCMFException::throwEx("The remote object '".$oid."' is immutable.", __FILE__, __LINE__);
135  return false;
136  }
137  $result = parent::delete($oid, $recursive);
138  return $result;
139  }
140  /**
141  * @see PersistenceFacade::getOIDs()
142  */
143  function getOIDs($type, $criteria=null, $orderby=null, &$pagingInfo)
144  {
145  $result = parent::getOIDs($type, $criteria, $orderby, $pagingInfo);
146  return $result;
147  }
148  /**
149  * @see PersistenceFacade::loadObjects()
150  */
151  function loadObjects($type, $buildDepth, $criteria=null, $orderby=null, &$pagingInfo, $buildAttribs=null, $buildTypes=null)
152  {
153  $tmpResult = parent::loadObjects($type, $buildDepth, $criteria, $orderby, $pagingInfo, $buildAttribs, $buildTypes);
154  $result = array();
155  foreach($tmpResult as $obj)
156  {
157  if ($obj && $this->isResolvingProxies() && strlen($umi = $obj->getValue('umi')) > 0) {
158  // store proxy for later reference
159  $this->registerProxyObject($umi, $obj, $buildDepth);
160  // load real subject
161  $result[] = $this->loadRemoteObject($umi, $buildDepth);
162  }
163  else {
164  $result[] = $obj;
165  }
166  }
167  return $result;
168  }
169 
170  /**
171  * Get the proxy object for a remote object.
172  * This method makes sure that a proxy for the given remote object exists.
173  * If it does not exist, it will be created.
174  * @param umi The universal model id (oid with server prefix)
175  * @return The proxy object.
176  */
177  function getProxyObject($umi, $buildDepth)
178  {
179  Log::debug("Get proxy object for: ".$umi, __CLASS__);
180 
181  // analyze umi
182  $umiParts = PersistenceFacade::decomposeOID($umi);
183  $umiPrefix = $umiParts['prefix'];
184 
185  // local objects don't have a proxy
186  if (strlen($umiPrefix) == 0) {
187  return null;
188  }
189 
190  // check if the proxy object was loaded already
191  $proxy = $this->getRegisteredProxyObject($umi, $buildDepth);
192 
193  // search the proxy object if requested for the first time
194  if (!$proxy)
195  {
196  $persistenceFacade = PersistenceFacade::getInstance();
197  if ($persistenceFacade instanceof RemoteCapablePersistenceFacadeImpl) {
198  $oldState = $persistenceFacade->isResolvingProxies();
199  $persistenceFacade->setResolveProxies(false);
200  }
201  $proxy = $persistenceFacade->loadFirstObject($umiParts['type'], $buildDepth, array($umiParts['type'].'.umi' => $umi));
202  if ($persistenceFacade instanceof RemoteCapablePersistenceFacadeImpl) {
203  $persistenceFacade->setResolveProxies($oldState);
204  }
205  if (!$proxy) {
206  // the proxy has to be created
207  Log::debug("Creating...", __CLASS__);
208  $proxy = $persistenceFacade->create($umiParts['type'], BUILDDEPTH_SINGLE);
209  $proxy->setValue('umi', $umi, DATATYPE_ATTRIBUTE);
210  $proxy->save();
211  }
212  $this->registerProxyObject($umi, $proxy, $buildDepth);
213  }
214  Log::debug("Proxy oid: ".$proxy->getOID(), __CLASS__);
215  return $proxy;
216  }
217 
218  /**
219  * Load the real subject of a proxy from the remote instance.
220  * @param umi The universal model id (oid with server prefix)
221  * @param buildDepth buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build (except BUILDDEPTH_REQUIRED)
222  */
223  function loadRemoteObject($umi, $buildDepth)
224  {
225  Log::debug("Resolve proxy object for: ".$umi, __CLASS__);
226 
227  // analyze umi
228  $umiParts = PersistenceFacade::decomposeOID($umi);
229  $umiPrefix = $umiParts['prefix'];
230 
231  // check if the remote object was loaded already
232  $obj = $this->getRegisteredRemoteObject($umi, $buildDepth);
233 
234  // resolve the object if requested for the first time
235  if (!$obj)
236  {
237  Log::debug("Retrieving...", __CLASS__);
238 
239  // determine remote oid
241  array('type' => $umiParts['type'], 'id' => $umiParts['id'])
242  );
243  $serverKey = array_pop(split(':', $umiPrefix));
244 
245  // create the request
246  $request = new Request(
247  '',
248  '',
249  'display',
250  array(
251  'oid' => $oid,
252  'depth' => "".$buildDepth,
253  'omitMetaData' => true,
254  'translateValues' => $this->_isTranslatingValues
255  )
256  );
257  Log::debug("Request:\n".$request->toString(), __CLASS__);
258 
259  // do the remote call
260  $facade = RemotingFacade::getInstance();
261  $response = $facade->doCall($serverKey, $request);
262  $obj = $response->getValue('node');
263  if ($obj)
264  {
265  // set umis instead of oids
266  $iter = new NodeIterator($obj);
267  while (!$iter->isEnd())
268  {
269  $curNode = &$iter->getCurrentObject();
270  $oids = $this->makeUmis(array($curNode->getOID()), $umiPrefix);
271  $curNode->setOID($oids[0]);
272  $parentOIDs = $this->makeUmis($curNode->getProperty('parentoids'), $umiPrefix);
273  $curNode->setProperty('parentoids', $parentOIDs);
274  $childOIDs = $this->makeUmis($curNode->getProperty('childoids'), $umiPrefix);
275  $curNode->setProperty('childoids', $childOIDs);
276  $iter->proceed();
277  }
278  // set the proxy oid as attribute
279  $proxy = $this->getProxyObject($umi, $buildDepth);
280  if ($proxy) {
281  if (strlen(PersistenceFacade::getOIDParameter($proxy->getOID(), 'prefix')) > 0) {
282  Log::debug("NOT A PROXY: ".WCMFException::getStackTrace(), __CLASS__);
283  }
284  $obj->setValue('_proxyOid', $proxy->getOID(), DATATYPE_IGNORE);
285 
286  // add proxy relations to the remote object
287  $children = $proxy->getChildren();
288  for($i=0, $count=sizeof($children); $i<$count; $i++) {
289  $obj->addChild($children[$i]);
290  }
291  $parents = $proxy->getParents();
292  for($i=0, $count=sizeof($parents); $i<$count; $i++) {
293  $obj->addParent($parents[$i]);
294  }
295  }
296  $this->registerRemoteObject($umi, $obj, $buildDepth);
297  }
298  }
299 
300  if (Log::isDebugEnabled(__CLASS__)) {
301  if ($obj) {
302  Log::debug("Resolved to: ".$obj->toString(), __CLASS__);
303  }
304  else {
305  Log::debug("Could not resolve: ".$umi, __CLASS__);
306  }
307  }
308  return $obj;
309  }
310 
311  /**
312  * Save a proxy object in the session.
313  * @param umi The universal model id (oid with server prefix)
314  * @param obj The proxy object.
315  * @param buildDepth The depth the object was loaded.
316  */
317  function registerProxyObject($umi, &$obj, $buildDepth)
318  {
319  if (strlen(PersistenceFacade::getOIDParameter($obj->getOID(), 'prefix')) > 0) {
320  Log::debug("NOT A PROXY: ".WCMFException::getStackTrace(), __CLASS__);
321  return;
322  }
323  $this->registerObject($umi, $obj, $buildDepth, self::PROXY_OBJECTS_SESSION_VARNAME);
324  }
325 
326  /**
327  * Save a remote object in the session.
328  * @param umi The universal model id (oid with server prefix)
329  * @param obj The remote object.
330  * @param buildDepth The depth the object was loaded.
331  */
332  function registerRemoteObject($umi, &$obj, $buildDepth)
333  {
334  // TODO: fix caching remote objects (invalidate cache entry, if an association to the object changes)
335  return;
336 
337  $this->registerObject($umi, $obj, $buildDepth, self::REMOTE_OBJECTS_SESSION_VARNAME);
338  }
339 
340  /**
341  * Save a object in the given session variable.
342  * @param umi The universal model id (oid with server prefix)
343  * @param obj The object to register.
344  * @param buildDepth The depth the object was loaded [default: BUILDDEPTH_SINGLE].
345  * @param varName The session variable name.
346  */
347  function registerObject($umi, &$obj, $buildDepth=BUILDDEPTH_SINGLE, $varName)
348  {
349  if ($buildDepth == 0) {
350  $buildDepth=BUILDDEPTH_SINGLE;
351  }
352  // save the object in the session
353  $session = &SessionData::getInstance();
354  $objects = &$session->get($varName);
355  if (!isset($objects[$umi])) {
356  $objects[$umi] = array();
357  }
358  $objects[$umi][$buildDepth] = &$obj;
359  $session->set($varName, $objects);
360 
361  // register class definitions in session
362  $objectFactory = &ObjectFactory::getInstance();
363  $classFile = BASE.$objectFactory->getClassfileFromConfig(get_class($obj));
364  $mapperClassFile = BASE.$objectFactory->getClassfileFromConfig(get_class($obj->getMapper()));
365  $session->addClassDefinitions(array($classFile, $mapperClassFile));
366  }
367 
368  /**
369  * Get a proxy object from the session.
370  * @param umi The universal model id (oid with server prefix)
371  * @param buildDepth The requested build depth.
372  * @return The proxy object or null if not found.
373  */
374  function getRegisteredProxyObject($umi, $buildDepth)
375  {
376  $proxy = $this->getRegisteredObject($umi, $buildDepth, self::PROXY_OBJECTS_SESSION_VARNAME);
377  return $proxy;
378  }
379 
380  /**
381  * Get a remote object from the session.
382  * @param umi The universal model id (oid with server prefix)
383  * @param buildDepth The requested build depth.
384  * @return The remote object or null if not found.
385  */
386  function getRegisteredRemoteObject($umi, $buildDepth)
387  {
388  $object = $this->getRegisteredObject($umi, $buildDepth, self::REMOTE_OBJECTS_SESSION_VARNAME);;
389  return $object;
390  }
391 
392  /**
393  * Get a object from the given session variable.
394  * @param umi The universal model id (oid with server prefix)
395  * @param buildDepth The requested build depth [default: BUILDDEPTH_SINGLE].
396  * @return The object or null if not found.
397  */
398  function getRegisteredObject($umi, $buildDepth=BUILDDEPTH_SINGLE, $varName)
399  {
400  if ($buildDepth == 0) {
401  $buildDepth=BUILDDEPTH_SINGLE;
402  }
403  $session = &SessionData::getInstance();
404  $objects = &$session->get($varName);
405  if (isset($objects[$umi]) && isset($objects[$umi][$buildDepth])) {
406  return $objects[$umi][$buildDepth];
407  }
408  // check if an object with larger build depth was stored already
409  if ($buildDepth == BUILDDEPTH_SINGLE) {
410  $existingDepths = array_keys($objects[$umi]);
411  foreach($existingDepths as $depth) {
412  if ($depth > 0 || $depth == BUILDDEPTH_INFINITE) {
413  return $objects[$umi][$depth];
414  }
415  }
416  }
417  return null;
418  }
419  /**
420  * Replace all object ids in an array with the umis according to
421  * the given umiPrefix.
422  * @param oids The array of oids
423  * @param umiPrefix The umi prefix
424  * @return The array of umis
425  */
426  function makeUmis($oids, $umiPrefix)
427  {
428  $result = array();
429  foreach ($oids as $oid)
430  {
431  $oidParts = PersistenceFacade::decomposeOID($oid);
432  if (strlen($oidParts['prefix']) == 0)
433  {
435  array('prefix' => $umiPrefix, 'type' => $oidParts['type'], 'id' => $oidParts['id'])
436  );
437  $result[] = $umi;
438  }
439  }
440  return $result;
441  }
442 }
443 ?>
debug($message, $category)
Definition: class.Log.php:39
const DATATYPE_ATTRIBUTE
NodeIterator is used to iterate over a tree/list build of objects using a Depth-First-Algorithm. Classes used with the NodeIterator must implement the getChildren() and getOID() methods. NodeIterator implements the 'Iterator Pattern'. The base class NodeIterator defines the interface for all specialized Iterator classes.
loadObjects($type, $buildDepth, $criteria=null, $orderby=null, &$pagingInfo, $buildAttribs=null, $buildTypes=null)
Request holds the request values that are used as input to Controller instances. It is typically inst...
throwEx($message, $file='', $line='')
PersistenceFacadeImpl delegates persistence operations to the type-specific PersistenceMappers.
const BUILDDEPTH_INFINITE
const DATATYPE_IGNORE
decomposeOID($oid, $validate=true)
registerObject($umi, &$obj, $buildDepth=BUILDDEPTH_SINGLE, $varName)
& create($type, $buildDepth, $buildAttribs=null)
isDebugEnabled($category)
Definition: class.Log.php:89
getRegisteredObject($umi, $buildDepth=BUILDDEPTH_SINGLE, $varName)
getOIDParameter($oid, $param, $validate=true)
& load($oid, $buildDepth, $buildAttribs=null, $buildTypes=null)
getOIDs($type, $criteria=null, $orderby=null, &$pagingInfo)
const BUILDDEPTH_SINGLE