X-Git-Url: https://git.cyclocoop.org/%242?a=blobdiff_plain;f=includes%2Ffilerepo%2Fbackend%2FSwiftFileBackend.php;h=08a311933777ab9b47d9855eea9be1bbd56d314a;hb=1abcd5e30eea04bb03f01eb84f9c77bca7980729;hp=f40c3236e42bc9662f6cc6230f05b28130eea150;hpb=f34f7bfd56ac92a53b9d5687cf51293908272ed5;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/filerepo/backend/SwiftFileBackend.php b/includes/filerepo/backend/SwiftFileBackend.php index f40c3236e4..08a3119337 100644 --- a/includes/filerepo/backend/SwiftFileBackend.php +++ b/includes/filerepo/backend/SwiftFileBackend.php @@ -47,6 +47,7 @@ class SwiftFileBackend extends FileBackendStore { protected $conn; // Swift connection handle protected $connStarted = 0; // integer UNIX timestamp protected $connContainers = array(); // container object cache + protected $connException; // CloudFiles exception /** * @see FileBackendStore::__construct() @@ -64,6 +65,9 @@ class SwiftFileBackend extends FileBackendStore { */ public function __construct( array $config ) { parent::__construct( $config ); + if ( !MWInit::classExists( 'CF_Constants' ) ) { + throw new MWException( 'SwiftCloudFiles extension not installed.' ); + } // Required settings $this->auth = new CF_Authentication( $config['swiftUser'], @@ -110,9 +114,8 @@ class SwiftFileBackend extends FileBackendStore { $this->getContainer( $container ); return true; // container exists } catch ( NoSuchContainerException $e ) { - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, array( 'path' => $storagePath ) ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, array( 'path' => $storagePath ) ); } return false; @@ -143,12 +146,8 @@ class SwiftFileBackend extends FileBackendStore { } catch ( NoSuchContainerException $e ) { $status->fatal( 'backend-fail-create', $params['dst'] ); return $status; - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -167,20 +166,32 @@ class SwiftFileBackend extends FileBackendStore { $obj->set_etag( md5( $params['content'] ) ); // Use the same content type as StreamFile for security $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] ); - // Actually write the object in Swift - $obj->write( $params['content'] ); + if ( !empty( $params['async'] ) ) { // deferred + $handle = $obj->write_async( $params['content'] ); + $status->value = new SwiftFileOpHandle( $this, $params, 'Create', $handle ); + } else { // actually write the object in Swift + $obj->write( $params['content'] ); + } } catch ( BadContentTypeException $e ) { $status->fatal( 'backend-fail-contenttype', $params['dst'] ); - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } return $status; } + /** + * @see SwiftFileBackend::doExecuteOpHandlesInternal() + */ + protected function _getResponseCreate( CF_Async_Op $cfOp, Status $status, array $params ) { + try { + $cfOp->getLastResponse(); + } catch ( BadContentTypeException $e ) { + $status->fatal( 'backend-fail-contenttype', $params['dst'] ); + } + } + /** * @see FileBackendStore::doStoreInternal() * @return Status @@ -206,12 +217,8 @@ class SwiftFileBackend extends FileBackendStore { } catch ( NoSuchContainerException $e ) { $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); return $status; - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -234,22 +241,44 @@ class SwiftFileBackend extends FileBackendStore { $obj->set_etag( md5_file( $params['src'] ) ); // Use the same content type as StreamFile for security $obj->content_type = StreamFile::contentTypeFromPath( $params['dst'] ); - // Actually write the object in Swift - $obj->load_from_filename( $params['src'], True ); // calls $obj->write() + if ( !empty( $params['async'] ) ) { // deferred + wfSuppressWarnings(); + $fp = fopen( $params['src'], 'rb' ); + wfRestoreWarnings(); + if ( !$fp ) { + $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); + } else { + $handle = $obj->write_async( $fp, filesize( $params['src'] ), true ); + $status->value = new SwiftFileOpHandle( $this, $params, 'Store', $handle ); + $status->value->resourcesToClose[] = $fp; + } + } else { // actually write the object in Swift + $obj->load_from_filename( $params['src'], true ); // calls $obj->write() + } } catch ( BadContentTypeException $e ) { $status->fatal( 'backend-fail-contenttype', $params['dst'] ); } catch ( IOException $e ) { $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } return $status; } + /** + * @see SwiftFileBackend::doExecuteOpHandlesInternal() + */ + protected function _getResponseStore( CF_Async_Op $cfOp, Status $status, array $params ) { + try { + $cfOp->getLastResponse(); + } catch ( BadContentTypeException $e ) { + $status->fatal( 'backend-fail-contenttype', $params['dst'] ); + } catch ( IOException $e ) { + $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); + } + } + /** * @see FileBackendStore::doCopyInternal() * @return Status @@ -282,30 +311,104 @@ class SwiftFileBackend extends FileBackendStore { } catch ( NoSuchContainerException $e ) { $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); return $status; - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } // (b) Actually copy the file to the destination try { - $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel ); + if ( !empty( $params['async'] ) ) { // deferred + $handle = $sContObj->copy_object_to_async( $srcRel, $dContObj, $dstRel ); + $status->value = new SwiftFileOpHandle( $this, $params, 'Copy', $handle ); + } else { // actually write the object in Swift + $sContObj->copy_object_to( $srcRel, $dContObj, $dstRel ); + } + } catch ( NoSuchObjectException $e ) { // source object does not exist + $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); + } + + return $status; + } + + /** + * @see SwiftFileBackend::doExecuteOpHandlesInternal() + */ + protected function _getResponseCopy( CF_Async_Op $cfOp, Status $status, array $params ) { + try { + $cfOp->getLastResponse(); } catch ( NoSuchObjectException $e ) { // source object does not exist $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] ); - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } + } + + /** + * @see FileBackendStore::doMoveInternal() + * @return Status + */ + protected function doMoveInternal( array $params ) { + $status = Status::newGood(); + + list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] ); + if ( $srcRel === null ) { + $status->fatal( 'backend-fail-invalidpath', $params['src'] ); + return $status; + } + + list( $dstCont, $dstRel ) = $this->resolveStoragePathReal( $params['dst'] ); + if ( $dstRel === null ) { + $status->fatal( 'backend-fail-invalidpath', $params['dst'] ); + return $status; + } + + // (a) Check the source/destination containers and destination object + try { + $sContObj = $this->getContainer( $srcCont ); + $dContObj = $this->getContainer( $dstCont ); + if ( empty( $params['overwrite'] ) && + $this->fileExists( array( 'src' => $params['dst'], 'latest' => 1 ) ) ) + { + $status->fatal( 'backend-fail-alreadyexists', $params['dst'] ); + return $status; + } + } catch ( NoSuchContainerException $e ) { + $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] ); + return $status; + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); + return $status; + } + + // (b) Actually move the file to the destination + try { + if ( !empty( $params['async'] ) ) { // deferred + $handle = $sContObj->move_object_to_async( $srcRel, $dContObj, $dstRel ); + $status->value = new SwiftFileOpHandle( $this, $params, 'Move', $handle ); + } else { // actually write the object in Swift + $sContObj->move_object_to( $srcRel, $dContObj, $dstRel ); + } + } catch ( NoSuchObjectException $e ) { // source object does not exist + $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } return $status; } + /** + * @see SwiftFileBackend::doExecuteOpHandlesInternal() + */ + protected function _getResponseMove( CF_Async_Op $cfOp, Status $status, array $params ) { + try { + $cfOp->getLastResponse(); + } catch ( NoSuchObjectException $e ) { // source object does not exist + $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] ); + } + } + /** * @see FileBackendStore::doDeleteInternal() * @return Status @@ -321,23 +424,40 @@ class SwiftFileBackend extends FileBackendStore { try { $sContObj = $this->getContainer( $srcCont ); - $sContObj->delete_object( $srcRel ); + if ( !empty( $params['async'] ) ) { // deferred + $handle = $sContObj->delete_object_async( $srcRel ); + $status->value = new SwiftFileOpHandle( $this, $params, 'Delete', $handle ); + } else { // actually write the object in Swift + $sContObj->delete_object( $srcRel ); + } } catch ( NoSuchContainerException $e ) { $status->fatal( 'backend-fail-delete', $params['src'] ); } catch ( NoSuchObjectException $e ) { if ( empty( $params['ignoreMissingSource'] ) ) { $status->fatal( 'backend-fail-delete', $params['src'] ); } - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } return $status; } + /** + * @see SwiftFileBackend::doExecuteOpHandlesInternal() + */ + protected function _getResponseDelete( CF_Async_Op $cfOp, Status $status, array $params ) { + try { + $cfOp->getLastResponse(); + } catch ( NoSuchContainerException $e ) { + $status->fatal( 'backend-fail-delete', $params['src'] ); + } catch ( NoSuchObjectException $e ) { + if ( empty( $params['ignoreMissingSource'] ) ) { + $status->fatal( 'backend-fail-delete', $params['src'] ); + } + } + } + /** * @see FileBackendStore::doPrepareInternal() * @return Status @@ -352,12 +472,8 @@ class SwiftFileBackend extends FileBackendStore { return $status; // already exists } catch ( NoSuchContainerException $e ) { // NoSuchContainerException thrown: container does not exist - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -372,12 +488,8 @@ class SwiftFileBackend extends FileBackendStore { array( $this->auth->username ) // write ) ); } - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -408,11 +520,8 @@ class SwiftFileBackend extends FileBackendStore { // metadata, we can make use of that to avoid RTTs $contObj->mw_wasSecured = true; // avoid useless RTTs } - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } } @@ -436,12 +545,8 @@ class SwiftFileBackend extends FileBackendStore { $contObj = $this->getContainer( $fullCont, true ); } catch ( NoSuchContainerException $e ) { return $status; // ok, nothing to do - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -451,12 +556,10 @@ class SwiftFileBackend extends FileBackendStore { $this->deleteContainer( $fullCont ); } catch ( NoSuchContainerException $e ) { return $status; // race? - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-internal', $this->name ); - $this->logException( $e, __METHOD__, $params ); + } catch ( NonEmptyContainerException $e ) { + return $status; // race? consistency delay? + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } } @@ -487,11 +590,9 @@ class SwiftFileBackend extends FileBackendStore { ); } catch ( NoSuchContainerException $e ) { } catch ( NoSuchObjectException $e ) { - } catch ( InvalidResponseException $e ) { + } catch ( CloudFilesException $e ) { // some other exception? $stat = null; - } catch ( Exception $e ) { // some other exception? - $stat = null; - $this->logException( $e, __METHOD__, $params ); + $this->handleException( $e, null, __METHOD__, $params ); } return $stat; @@ -512,7 +613,8 @@ class SwiftFileBackend extends FileBackendStore { $status = Status::newGood(); $scopeLockS = $this->getScopedFileLocks( array( $path ), LockManager::LOCK_UW, $status ); if ( $status->isOK() ) { - $tmpFile = $this->getLocalCopy( array( 'src' => $path, 'latest' => 1 ) ); + # Do not stat the file in getLocalCopy() to avoid infinite loops + $tmpFile = $this->getLocalCopy( array( 'src' => $path, 'latest' => 1, 'nostat' => 1 ) ); if ( $tmpFile ) { $hash = $tmpFile->getSha1Base36(); if ( $hash !== false ) { @@ -546,9 +648,8 @@ class SwiftFileBackend extends FileBackendStore { $obj = new CF_Object( $sContObj, $srcRel, false, false ); // skip HEAD request $data = $obj->read( $this->headersFromParams( $params ) ); } catch ( NoSuchContainerException $e ) { - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, $params ); } return $data; @@ -565,9 +666,9 @@ class SwiftFileBackend extends FileBackendStore { return ( count( $container->list_objects( 1, null, $prefix ) ) > 0 ); } catch ( NoSuchContainerException $e ) { return false; - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, array( 'cont' => $fullCont, 'dir' => $dir ) ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, + array( 'cont' => $fullCont, 'dir' => $dir ) ); } return null; // error @@ -642,9 +743,9 @@ class SwiftFileBackend extends FileBackendStore { } } } catch ( NoSuchContainerException $e ) { - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, array( 'cont' => $fullCont, 'dir' => $dir ) ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, + array( 'cont' => $fullCont, 'dir' => $dir ) ); } return $dirs; @@ -685,9 +786,9 @@ class SwiftFileBackend extends FileBackendStore { $after = end( $files ); // update last item reset( $files ); // reset pointer } catch ( NoSuchContainerException $e ) { - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, array( 'cont' => $fullCont, 'dir' => $dir ) ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, + array( 'cont' => $fullCont, 'dir' => $dir ) ); } return $files; @@ -723,12 +824,8 @@ class SwiftFileBackend extends FileBackendStore { } catch ( NoSuchContainerException $e ) { $status->fatal( 'backend-fail-stream', $params['src'] ); return $status; - } catch ( InvalidResponseException $e ) { - $status->fatal( 'backend-fail-connect', $this->name ); - return $status; - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-stream', $params['src'] ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); return $status; } @@ -736,11 +833,8 @@ class SwiftFileBackend extends FileBackendStore { $output = fopen( 'php://output', 'wb' ); $obj = new CF_Object( $cont, $srcRel, false, false ); // skip HEAD request $obj->stream( $output, $this->headersFromParams( $params ) ); - } catch ( InvalidResponseException $e ) { // 404? connection problem? - $status->fatal( 'backend-fail-stream', $params['src'] ); - } catch ( Exception $e ) { // some other exception? - $status->fatal( 'backend-fail-stream', $params['src'] ); - $this->logException( $e, __METHOD__, $params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, __METHOD__, $params ); } return $status; @@ -756,7 +850,8 @@ class SwiftFileBackend extends FileBackendStore { return null; } - if ( !$this->fileExists( $params ) ) { + # Check the recursion guard to avoid loops when filling metadata + if ( empty( $params['nostat'] ) && !$this->fileExists( $params ) ) { return null; } @@ -779,11 +874,9 @@ class SwiftFileBackend extends FileBackendStore { } } catch ( NoSuchContainerException $e ) { $tmpFile = null; - } catch ( InvalidResponseException $e ) { - $tmpFile = null; - } catch ( Exception $e ) { // some other exception? + } catch ( CloudFilesException $e ) { // some other exception? $tmpFile = null; - $this->logException( $e, __METHOD__, $params ); + $this->handleException( $e, null, __METHOD__, $params ); } return $tmpFile; @@ -813,6 +906,39 @@ class SwiftFileBackend extends FileBackendStore { return $hdrs; } + /** + * @see FileBackendStore::doExecuteOpHandlesInternal() + * @return Array List of corresponding Status objects + */ + protected function doExecuteOpHandlesInternal( array $fileOpHandles ) { + $statuses = array(); + + $cfOps = array(); // list of CF_Async_Op objects + foreach ( $fileOpHandles as $index => $fileOpHandle ) { + $cfOps[$index] = $fileOpHandle->cfOp; + } + $batch = new CF_Async_Op_Batch( $cfOps ); + + $cfOps = $batch->execute(); + foreach ( $cfOps as $index => $cfOp ) { + $status = Status::newGood(); + try { // catch exceptions; update status + $function = '_getResponse' . $fileOpHandles[$index]->call; + $this->$function( $cfOp, $status, $fileOpHandles[$index]->params ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, $status, + __CLASS__ . ":$function", $fileOpHandles[$index]->params ); + } + $statuses[$index] = $status; + } + + foreach ( $fileOpHandles as $fileOpHandle ) { + $fileOpHandle->closeResources(); + } + + return $statuses; + } + /** * Set read/write permissions for a Swift container * @@ -841,11 +967,11 @@ class SwiftFileBackend extends FileBackendStore { * Get a connection to the Swift proxy * * @return CF_Connection|bool False on failure - * @throws InvalidResponseException + * @throws CloudFilesException */ protected function getConnection() { - if ( $this->conn === false ) { - throw new InvalidResponseException; // failed last attempt + if ( $this->connException instanceof Exception ) { + throw $this->connException; // failed last attempt } // Session keys expire after a while, so we renew them periodically if ( $this->conn && ( time() - $this->connStarted ) > $this->authTTL ) { @@ -853,21 +979,18 @@ class SwiftFileBackend extends FileBackendStore { $this->conn = null; } // Authenticate with proxy and get a session key... - if ( $this->conn === null ) { + if ( !$this->conn ) { + $this->connStarted = 0; $this->connContainers = array(); try { $this->auth->authenticate(); $this->conn = new CF_Connection( $this->auth ); $this->connStarted = time(); - } catch ( AuthenticationException $e ) { - $this->conn = false; // don't keep re-trying - } catch ( InvalidResponseException $e ) { - $this->conn = false; // don't keep re-trying + } catch ( CloudFilesException $e ) { + $this->connException = $e; // don't keep re-trying + throw $e; // throw it back } } - if ( !$this->conn ) { - throw new InvalidResponseException; // auth/connection problem - } return $this->conn; } @@ -885,7 +1008,7 @@ class SwiftFileBackend extends FileBackendStore { * @param $container string Container name * @param $bypassCache bool Bypass all caches and load from Swift * @return CF_Container - * @throws InvalidResponseException + * @throws CloudFilesException */ protected function getContainer( $container, $bypassCache = false ) { $conn = $this->getConnection(); // Swift proxy connection @@ -954,31 +1077,54 @@ class SwiftFileBackend extends FileBackendStore { $info['bytes'] ); } - } catch ( InvalidResponseException $e ) { - } catch ( Exception $e ) { // some other exception? - $this->logException( $e, __METHOD__, array() ); + } catch ( CloudFilesException $e ) { // some other exception? + $this->handleException( $e, null, __METHOD__, array() ); } } /** - * Log an unexpected exception for this backend + * Log an unexpected exception for this backend. + * This also sets the Status object to have a fatal error. * * @param $e Exception + * @param $status Status|null * @param $func string * @param $params Array * @return void */ - protected function logException( Exception $e, $func, array $params ) { + protected function handleException( Exception $e, $status, $func, array $params ) { + if ( $status instanceof Status ) { + if ( $e instanceof AuthenticationException ) { + $status->fatal( 'backend-fail-connect', $this->name ); + } else { + $status->fatal( 'backend-fail-internal', $this->name ); + } + } + if ( $e->getMessage() ) { + trigger_error( "$func: " . $e->getMessage(), E_USER_WARNING ); + } wfDebugLog( 'SwiftBackend', get_class( $e ) . " in '{$func}' (given '" . FormatJson::encode( $params ) . "')" . - ( $e instanceof InvalidResponseException - ? ": {$e->getMessage()}" - : "" - ) + ( $e->getMessage() ? ": {$e->getMessage()}" : "" ) ); } } +/** + * @see FileBackendStoreOpHandle + */ +class SwiftFileOpHandle extends FileBackendStoreOpHandle { + /** @var CF_Async_Op */ + public $cfOp; + + public function __construct( $backend, array $params, $call, CF_Async_Op $cfOp ) { + $this->backend = $backend; + $this->params = $params; + $this->call = $call; + $this->cfOp = $cfOp; + } +} + /** * SwiftFileBackend helper class to page through listings. * Swift also has a listing limit of 10,000 objects for sanity.