RemoteCapablePersistenceFacade.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2015 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
12 
26 
27 /**
28  * RemoteCapablePersistenceFacade delegates local persistence operations to the
29  * default PersistenceFacadeImpl and remote operations to a remote server.
30  *
31  * @author ingo herwig <ingo@wemove.com>
32  */
34 
35  // constants
36  const PROXY_OBJECTS_SESSION_VARNAME = 'RemoteCapablePersistenceFacadeImpl.proxyObjects';
37  const REMOTE_OBJECTS_SESSION_VARNAME = 'RemoteCapablePersistenceFacadeImpl.remoteObjects';
38 
39  private $_isResolvingProxies = true;
40  private $_isTranslatingValues = true;
41 
42  private $_remotingServer = null;
43 
44  private static $_logger = null;
45 
46  /**
47  * Constructor
48  * @param $eventManager
49  * @param $logStrategy
50  * @param $session
51  */
52  public function __construct(EventManager $eventManager,
53  OutputStrategy $logStrategy,
54  Session $session) {
55  parent::__construct($eventManager, $logStrategy);
56  $this->_session = $session;
57  if (self::$_logger == null) {
58  self::$_logger = LogManager::getLogger(__CLASS__);
59  }
60  // initialize session variables
61  if (!$this->_session->exist(self::PROXY_OBJECTS_SESSION_VARNAME)) {
62  $proxies = array();
63  $this->_session->set(self::PROXY_OBJECTS_SESSION_VARNAME, $proxies);
64  }
65  if (!$this->_session->exist(self::REMOTE_OBJECTS_SESSION_VARNAME)) {
66  $objs = array();
67  $this->_session->set(self::REMOTE_OBJECTS_SESSION_VARNAME, $objs);
68  }
69  $this->_remotingServer = new RemotingServer();
70  parent::__construct();
71  }
72 
73  /**
74  * Tell the PersistenceFacade implementation to resolve proxies or not.
75  * @param $isResolvingProxies Boolean whether proxies should be resolved or not
76  */
77  public function setResolveProxies($isResolvingProxies) {
78  $this->_isResolvingProxies = $isResolvingProxies;
79  }
80 
81  /**
82  * Check if the PersistenceFacade implementation is resolving proxies or not.
83  * @return Boolean whether proxies are resolved or not
84  */
85  public function isResolvingProxies() {
86  return $this->_isResolvingProxies;
87  }
88 
89  /**
90  * Tell the PersistenceFacade implementation to translate remote values or not.
91  * @param $isTranslatingValues Boolean whether values should be translated or not
92  */
93  public function setTranslatingValues($isTranslatingValues) {
94  $this->_isTranslatingValues = $isTranslatingValues;
95  }
96 
97  /**
98  * Check if the PersistenceFacade implementation is translating remote values or not.
99  * @return Boolean whether values are tanslated or not
100  */
101  public function isTranslatingValues() {
102  return $this->_isTranslatingValues;
103  }
104 
105  /**
106  * @see PersistenceFacade::load()
107  */
108  public function load(ObjectId $oid, $buildDepth=BuildDepth::SINGLE) {
109  $obj = null;
110  if ($this->isResolvingProxies() && strlen($oid->getPrefix()) > 0) {
111  // load real subject
112  $obj = $this->loadRemoteObject($oid, $buildDepth);
113  }
114  else {
115  $obj = parent::load($oid, $buildDepth);
116  if ($obj && $this->isResolvingProxies() && strlen($umi = $obj->getValue('umi')) > 0) {
117  // store proxy for later reference
118  $this->registerProxyObject($umi, $obj, $buildDepth);
119  // load real subject
120  $obj = $this->loadRemoteObject($umi, $buildDepth);
121  }
122  }
123  return $obj;
124  }
125 
126  /**
127  * @see PersistenceFacade::create()
128  */
129  public function create($type, $buildDepth=BuildDepth::SINGLE) {
130  $obj = parent::create($type, $buildDepth);
131  return $obj;
132  }
133 
134  /**
135  * @see PersistenceFacade::getOIDs()
136  */
137  public function getOIDs($type, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
138  $result = parent::getOIDs($type, $criteria, $orderby, $pagingInfo);
139  return $result;
140  }
141 
142  /**
143  * @see PersistenceFacade::loadObjects()
144  */
145  public function loadObjects($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null) {
146 
147  $tmpResult = parent::loadObjects($type, $buildDepth, $criteria, $orderby, $pagingInfo);
148  $result = array();
149  foreach($tmpResult as $obj) {
150  if ($obj && $this->isResolvingProxies() && strlen($umi = $obj->getValue('umi')) > 0) {
151  // store proxy for later reference
152  $this->registerProxyObject($umi, $obj, $buildDepth);
153  // load real subject
154  $result[] = $this->loadRemoteObject($umi, $buildDepth);
155  }
156  else {
157  $result[] = $obj;
158  }
159  }
160  return $result;
161  }
162 
163  /**
164  * Get the proxy object for a remote object.
165  * This method makes sure that a proxy for the given remote object exists.
166  * If it does not exist, it will be created.
167  * @param $umi The universal model id (oid with server prefix)
168  * @param $buildDepth buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build (except BuildDepth::REQUIRED)
169  * @return The proxy object.
170  */
171  protected function getProxyObject(ObjectId $umi, $buildDepth) {
172  self::$_logger->debug("Get proxy object for: ".$umi);
173 
174  // local objects don't have a proxy
175  if (strlen($umi->getPrefix()) == 0) {
176  return null;
177  }
178 
179  // check if the proxy object was loaded already
180  $proxy = $this->getRegisteredProxyObject($umi, $buildDepth);
181 
182  // search the proxy object if requested for the first time
183  if (!$proxy) {
184  $persistenceFacade = ObjectFactory::getInstance('persistenceFacade');
185  $isRemoteCapableFacade = ($persistenceFacade instanceof RemoteCapablePersistenceFacadeImpl);
186  $oldState = true;
187  if ($isRemoteCapableFacade) {
188  $oldState = $persistenceFacade->isResolvingProxies();
189  $persistenceFacade->setResolveProxies(false);
190  }
191  $proxy = $persistenceFacade->loadFirstObject($umi->getType(), $buildDepth, array($umi->getType().'.umi' => $umi->toString()));
192  if ($isRemoteCapableFacade) {
193  $persistenceFacade->setResolveProxies($oldState);
194  }
195  if (!$proxy) {
196  // the proxy has to be created
197  self::$_logger->debug("Creating...");
198  $proxy = $persistenceFacade->create($umi->getType(), BuildDepth::SINGLE);
199  $proxy->setValue('umi', $umi);
200  $proxy->save();
201  }
202  $this->registerProxyObject($umi, $proxy, $buildDepth);
203  }
204  self::$_logger->debug("Proxy oid: ".$proxy->getOID());
205  return $proxy;
206  }
207 
208  /**
209  * Load the real subject of a proxy from the remote instance.
210  * @param $umi The universal model id (oid with server prefix)
211  * @param $buildDepth buildDepth One of the BUILDDEPTH constants or a number describing the number of generations to build (except BuildDepth::REQUIRED)
212  */
213  protected function loadRemoteObject(ObjectId $umi, $buildDepth) {
214  self::$_logger->debug("Resolve proxy object for: ".$umi);
215 
216  // check if the remote object was loaded already
217  $obj = $this->getRegisteredRemoteObject($umi, $buildDepth);
218 
219  // resolve the object if requested for the first time
220  if (!$obj) {
221  self::$_logger->debug("Retrieving...");
222 
223  // determine remote oid
224  $oid = new ObjectId($umi->getType(), $umi->getId());
225  $serverKey = array_pop(preg_split('/:/', $umi->getPrefix()));
226 
227  // create the request
228  $request = ObjectFactory::getInstance('request');
229  $request->setAction('display');
230  $request->setValues(
231  array(
232  'oid' => $oid->toString(),
233  'depth' => "".$buildDepth,
234  'omitMetaData' => true,
235  'translateValues' => $this->_isTranslatingValues
236  )
237  );
238  self::$_logger->debug("Request:\n".$request->toString());
239 
240  // do the remote call
241  $response = $this->_remotingServer->doCall($serverKey, $request);
242  $obj = $response->getValue('node');
243  if ($obj) {
244  // set umis instead of oids
245  $umiPrefix = $umi->getPrefix();
246  $iter = new NodeIterator($obj);
247  foreach($iter as $oid => $curNode) {
248  $oids = $this->makeUmis(array($curNode->getOID()), $umiPrefix);
249  $curNode->setOID($oids[0]);
250  // TODO implement this for new Node class
251  /*
252  $parentOIDs = $this->makeUmis($curNode->getProperty('parentoids'), $umiPrefix);
253  $curNode->setProperty('parentoids', $parentOIDs);
254  $childOIDs = $this->makeUmis($curNode->getProperty('childoids'), $umiPrefix);
255  $curNode->setProperty('childoids', $childOIDs);
256  */
257  }
258  // set the proxy oid as attribute
259  $proxy = $this->getProxyObject($umi, $buildDepth);
260  if ($proxy) {
261  $proxyOID = $proxy->getOID();
262  if (strlen($proxyOID->getPrefix()) > 0) {
263  self::$_logger->debug("NOT A PROXY");
264  }
265  $obj->setValue('_proxyOid', $proxyOID);
266 
267  // add proxy relations to the remote object
268  $children = $proxy->getChildren();
269  for($i=0, $count=sizeof($children); $i<$count; $i++) {
270  $obj->addNode($children[$i]);
271  }
272  $parents = $proxy->getParents();
273  for($i=0, $count=sizeof($parents); $i<$count; $i++) {
274  $obj->addParent($parents[$i]);
275  }
276  }
277  $this->registerRemoteObject($umi, $obj, $buildDepth);
278  }
279  }
280 
281  if (self::$_logger->isDebugEnabled()) {
282  if ($obj) {
283  self::$_logger->debug("Resolved to: ".$obj->toString());
284  }
285  else {
286  self::$_logger->debug("Could not resolve: ".$umi);
287  }
288  }
289  return $obj;
290  }
291 
292  /**
293  * Save a proxy object in the session.
294  * @param $umi The universal model id (oid with server prefix)
295  * @param $obj The proxy object.
296  * @param $buildDepth The depth the object was loaded.
297  */
298  protected function registerProxyObject(ObjectID $umi, PersistentObject $obj, $buildDepth) {
299  $oid = $obj->getOID();
300  if (strlen($oid->getPrefix()) > 0) {
301  self::$_logger->debug("NOT A PROXY");
302  return;
303  }
304  $this->registerObject($umi, $obj, $buildDepth, self::PROXY_OBJECTS_SESSION_VARNAME);
305  }
306 
307  /**
308  * Save a remote object in the session.
309  * @param $umi The universal model id (oid with server prefix)
310  * @param $obj The remote object.
311  * @param $buildDepth The depth the object was loaded.
312  */
313  protected function registerRemoteObject(ObjectId $umi, PersistentObject $obj, $buildDepth) {
314  // TODO: fix caching remote objects (invalidate cache entry, if an association to the object changes)
315  return;
316 
317  $this->registerObject($umi, $obj, $buildDepth, self::REMOTE_OBJECTS_SESSION_VARNAME);
318  }
319 
320  /**
321  * Save a object in the given session variable.
322  * @param $umi The universal model id (oid with server prefix)
323  * @param $obj The object to register.
324  * @param $buildDepth The depth the object was loaded.
325  * @param $varName The session variable name.
326  */
327  protected function registerObject(ObjectId $umi, PersistentObject $obj, $buildDepth, $varName) {
328  if ($buildDepth == 0) {
329  $buildDepth=BuildDepth::SINGLE;
330  }
331  // save the object in the session
332  $umiStr = $umi->toString();
333  $objects = $this->_session->get($varName);
334  if (!isset($objects[$umiStr])) {
335  $objects[$umiStr] = array();
336  }
337  $objects[$umiStr][$buildDepth] = $obj;
338  $this->_session->set($varName, $objects);
339  }
340 
341  /**
342  * Get a proxy object from the session.
343  * @param $umi The universal model id (oid with server prefix)
344  * @param $buildDepth The requested build depth.
345  * @return The proxy object or null if not found.
346  */
347  protected function getRegisteredProxyObject(ObjectId $umi, $buildDepth) {
348  $proxy = $this->getRegisteredObject($umi, $buildDepth, self::PROXY_OBJECTS_SESSION_VARNAME);
349  return $proxy;
350  }
351 
352  /**
353  * Get a remote object from the session.
354  * @param $umi The universal model id (oid with server prefix)
355  * @param $buildDepth The requested build depth.
356  * @return The remote object or null if not found.
357  */
358  protected function getRegisteredRemoteObject(ObjectId $umi, $buildDepth) {
359  $object = $this->getRegisteredObject($umi, $buildDepth, self::REMOTE_OBJECTS_SESSION_VARNAME);
360  return $object;
361  }
362 
363  /**
364  * Get a object from the given session variable.
365  * @param $umi The universal model id (oid with server prefix)
366  * @param $buildDepth The requested build depth.
367  * @param $varName The session variable name
368  * @return The object or null if not found.
369  */
370  protected function getRegisteredObject(ObjectId $umi, $buildDepth, $varName) {
371  if ($buildDepth == 0) {
372  $buildDepth=BuildDepth::SINGLE;
373  }
374  $umiStr = $umi->toString();
375  $objects = $this->_session->get($varName);
376  if (isset($objects[$umiStr]) && isset($objects[$umiStr][$buildDepth])) {
377  return $objects[$umiStr][$buildDepth];
378  }
379  // check if an object with larger build depth was stored already
380  if ($buildDepth == BuildDepth::SINGLE) {
381  $existingDepths = array_keys($objects[$umiStr]);
382  foreach($existingDepths as $depth) {
383  if ($depth > 0 || $depth == BuildDepth::INFINITE) {
384  return $objects[$umiStr][$depth];
385  }
386  }
387  }
388  return null;
389  }
390 
391  /**
392  * Replace all object ids in an array with the umis according to
393  * the given umiPrefix.
394  * @param $oids The array of oids
395  * @param $umiPrefix The umi prefix
396  * @return The array of umis
397  */
398  protected function makeUmis($oids, $umiPrefix) {
399  $result = array();
400  foreach ($oids as $oid) {
401  if (strlen($oid->getPrefix()) == 0) {
402  $umi = new ObjectId($oid->getType(), $oid->getId(), $umiPrefix);
403  $result[] = $umi;
404  }
405  }
406  return $result;
407  }
408 }
409 ?>
getOID()
Get the object id of the PersistentObject.
getRegisteredProxyObject(ObjectId $umi, $buildDepth)
Get a proxy object from the session.
EventManager is responsible for dispatching events to registered listeners.
getRegisteredRemoteObject(ObjectId $umi, $buildDepth)
Get a remote object from the session.
registerRemoteObject(ObjectId $umi, PersistentObject $obj, $buildDepth)
Save a remote object in the session.
OutputStrategy defines the interface for classes that write an object's content to a destination (cal...
getPrefix()
Get the prefix.
Definition: ObjectId.php:98
loadObjects($type, $buildDepth=BuildDepth::SINGLE, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
NodeIterator is used to iterate over a tree/list built of Nodes using a Depth-First-Algorithm.
registerProxyObject(ObjectID $umi, PersistentObject $obj, $buildDepth)
Save a proxy object in the session.
ObjectId is the unique identifier of an object.
Definition: ObjectId.php:27
static getLogger($name)
Get the logger with the given name.
Definition: LogManager.php:35
registerObject(ObjectId $umi, PersistentObject $obj, $buildDepth, $varName)
Save a object in the given session variable.
getProxyObject(ObjectId $umi, $buildDepth)
Get the proxy object for a remote object.
static getInstance($name, $dynamicConfiguration=array())
PagingInfo contains information about a paged list.
Definition: PagingInfo.php:18
RemotingServer is used to communicate with other wCMF instances.
Session is the interface for session implementations and defines access to session variables...
Definition: Session.php:21
setTranslatingValues($isTranslatingValues)
Tell the PersistenceFacade implementation to translate remote values or not.
RemoteCapablePersistenceFacade delegates local persistence operations to the default PersistenceFacad...
isTranslatingValues()
Check if the PersistenceFacade implementation is translating remote values or not.
makeUmis($oids, $umiPrefix)
Replace all object ids in an array with the umis according to the given umiPrefix.
Default PersistenceFacade implementation.
getRegisteredObject(ObjectId $umi, $buildDepth, $varName)
Get a object from the given session variable.
getOIDs($type, $criteria=null, $orderby=null, PagingInfo $pagingInfo=null)
isResolvingProxies()
Check if the PersistenceFacade implementation is resolving proxies or not.
__construct(EventManager $eventManager, OutputStrategy $logStrategy, Session $session)
Constructor.
getType()
Get the type (including namespace)
Definition: ObjectId.php:106
setResolveProxies($isResolvingProxies)
Tell the PersistenceFacade implementation to resolve proxies or not.
loadRemoteObject(ObjectId $umi, $buildDepth)
Load the real subject of a proxy from the remote instance.
PersistentObject defines the interface of all persistent objects.