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