|
@@ -73,6 +73,14 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
*/
|
|
|
private static $tmpFiles = array();
|
|
|
|
|
|
+ /**
|
|
|
+ * Key value cache mapping path to data object. Maps path to
|
|
|
+ * \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject for existing
|
|
|
+ * paths and path to false for not existing paths.
|
|
|
+ * @var \OCP\ICache
|
|
|
+ */
|
|
|
+ private $objectCache;
|
|
|
+
|
|
|
/**
|
|
|
* @param string $path
|
|
|
*/
|
|
@@ -96,18 +104,31 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
* @param string $path
|
|
|
* @return string
|
|
|
*/
|
|
|
- private function getContainerName($path) {
|
|
|
- $path = trim(trim($this->root, '/') . "/" . $path, '/.');
|
|
|
- return str_replace('/', '\\', $path);
|
|
|
- }
|
|
|
|
|
|
/**
|
|
|
+ * Fetches an object from the API.
|
|
|
+ * If the object is cached already or a
|
|
|
+ * failed "doesn't exist" response was cached,
|
|
|
+ * that one will be returned.
|
|
|
+ *
|
|
|
* @param string $path
|
|
|
+ * @return \OpenCloud\OpenStack\ObjectStorage\Resource\DataObject|bool object
|
|
|
+ * or false if the object did not exist
|
|
|
*/
|
|
|
- private function doesObjectExist($path) {
|
|
|
+ private function fetchObject($path) {
|
|
|
+ if ($this->objectCache->hasKey($path)) {
|
|
|
+ // might be "false" if object did not exist from last check
|
|
|
+ return $this->objectCache->get($path);
|
|
|
+ }
|
|
|
try {
|
|
|
- $this->getContainer()->getPartialObject($path);
|
|
|
- return true;
|
|
|
+ $object = $this->getContainer()->getPartialObject($path);
|
|
|
+ $this->objectCache->set($path, $object);
|
|
|
+ return $object;
|
|
|
+ } catch (ClientErrorResponseException $e) {
|
|
|
+ // this exception happens when the object does not exist, which
|
|
|
+ // is expected in most cases
|
|
|
+ $this->objectCache->set($path, false);
|
|
|
+ return false;
|
|
|
} catch (ClientErrorResponseException $e) {
|
|
|
// Expected response is "404 Not Found", so only log if it isn't
|
|
|
if ($e->getResponse()->getStatusCode() !== 404) {
|
|
@@ -117,6 +138,17 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Returns whether the given path exists.
|
|
|
+ *
|
|
|
+ * @param string $path
|
|
|
+ *
|
|
|
+ * @return bool true if the object exist, false otherwise
|
|
|
+ */
|
|
|
+ private function doesObjectExist($path) {
|
|
|
+ return $this->fetchObject($path) !== false;
|
|
|
+ }
|
|
|
+
|
|
|
public function __construct($params) {
|
|
|
if ((empty($params['key']) and empty($params['password']))
|
|
|
or empty($params['user']) or empty($params['bucket'])
|
|
@@ -144,6 +176,8 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
}
|
|
|
|
|
|
$this->params = $params;
|
|
|
+ // FIXME: private class...
|
|
|
+ $this->objectCache = new \OC\Cache\CappedMemoryCache();
|
|
|
}
|
|
|
|
|
|
public function mkdir($path) {
|
|
@@ -162,6 +196,9 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$metadataHeaders = DataObject::stockHeaders(array());
|
|
|
$allHeaders = $customHeaders + $metadataHeaders;
|
|
|
$this->getContainer()->uploadObject($path, '', $allHeaders);
|
|
|
+ // invalidate so that the next access gets the real object
|
|
|
+ // with all properties
|
|
|
+ $this->objectCache->remove($path);
|
|
|
} catch (Exceptions\CreateUpdateError $e) {
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
return false;
|
|
@@ -202,6 +239,7 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
|
|
|
try {
|
|
|
$this->getContainer()->dataObject()->setName($path . '/')->delete();
|
|
|
+ $this->objectCache->remove($path . '/');
|
|
|
} catch (Exceptions\DeleteError $e) {
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
return false;
|
|
@@ -256,7 +294,10 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
|
|
|
try {
|
|
|
/** @var DataObject $object */
|
|
|
- $object = $this->getContainer()->getPartialObject($path);
|
|
|
+ $object = $this->fetchObject($path);
|
|
|
+ if (!$object) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
} catch (ClientErrorResponseException $e) {
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
return false;
|
|
@@ -310,8 +351,12 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
|
|
|
try {
|
|
|
$this->getContainer()->dataObject()->setName($path)->delete();
|
|
|
+ $this->objectCache->remove($path);
|
|
|
+ $this->objectCache->remove($path . '/');
|
|
|
} catch (ClientErrorResponseException $e) {
|
|
|
- \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
+ if ($e->getResponse()->getStatusCode() !== 404) {
|
|
|
+ \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
+ }
|
|
|
return false;
|
|
|
}
|
|
|
|
|
@@ -391,8 +436,11 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$path .= '/';
|
|
|
}
|
|
|
|
|
|
- $object = $this->getContainer()->getPartialObject($path);
|
|
|
- $object->saveMetadata($metadata);
|
|
|
+ $object = $this->fetchObject($path);
|
|
|
+ if ($object->saveMetadata($metadata)) {
|
|
|
+ // invalidate target object to force repopulation on fetch
|
|
|
+ $this->objectCache->remove($path);
|
|
|
+ }
|
|
|
return true;
|
|
|
} else {
|
|
|
$mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
|
|
@@ -400,6 +448,8 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$metadataHeaders = DataObject::stockHeaders($metadata);
|
|
|
$allHeaders = $customHeaders + $metadataHeaders;
|
|
|
$this->getContainer()->uploadObject($path, '', $allHeaders);
|
|
|
+ // invalidate target object to force repopulation on fetch
|
|
|
+ $this->objectCache->remove($path);
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
@@ -415,8 +465,11 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$this->unlink($path2);
|
|
|
|
|
|
try {
|
|
|
- $source = $this->getContainer()->getPartialObject($path1);
|
|
|
+ $source = $this->fetchObject($path1);
|
|
|
$source->copy($this->bucket . '/' . $path2);
|
|
|
+ // invalidate target object to force repopulation on fetch
|
|
|
+ $this->objectCache->remove($path2);
|
|
|
+ $this->objectCache->remove($path2 . '/');
|
|
|
} catch (ClientErrorResponseException $e) {
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
return false;
|
|
@@ -428,8 +481,11 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$this->unlink($path2);
|
|
|
|
|
|
try {
|
|
|
- $source = $this->getContainer()->getPartialObject($path1 . '/');
|
|
|
+ $source = $this->fetchObject($path1 . '/');
|
|
|
$source->copy($this->bucket . '/' . $path2 . '/');
|
|
|
+ // invalidate target object to force repopulation on fetch
|
|
|
+ $this->objectCache->remove($path2);
|
|
|
+ $this->objectCache->remove($path2 . '/');
|
|
|
} catch (ClientErrorResponseException $e) {
|
|
|
\OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR);
|
|
|
return false;
|
|
@@ -461,10 +517,6 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
$fileType = $this->filetype($path1);
|
|
|
|
|
|
if ($fileType === 'dir' || $fileType === 'file') {
|
|
|
-
|
|
|
- // make way
|
|
|
- $this->unlink($path2);
|
|
|
-
|
|
|
// copy
|
|
|
if ($this->copy($path1, $path2) === false) {
|
|
|
return false;
|
|
@@ -564,6 +616,8 @@ class Swift extends \OC\Files\Storage\Common {
|
|
|
}
|
|
|
$fileData = fopen($tmpFile, 'r');
|
|
|
$this->getContainer()->uploadObject(self::$tmpFiles[$tmpFile], $fileData);
|
|
|
+ // invalidate target object to force repopulation on fetch
|
|
|
+ $this->objectCache->remove(self::$tmpFiles[$tmpFile]);
|
|
|
unlink($tmpFile);
|
|
|
}
|
|
|
|