r108749 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r108748‎ | r108749 | r108750 >
Date:20:01, 12 January 2012
Author:aaron
Status:ok (Comments)
Tags:filebackend 
Comment:
* Merged (added) SwiftFileBackend class from branch.
* Added i18n messages used by the new class.
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/filerepo/backend/SwiftFileBackend.php (added) (history)
  • /trunk/phase3/languages/messages/MessagesEn.php (modified) (history)
  • /trunk/phase3/maintenance/language/messages.inc (modified) (history)

Diff [purge]

Index: trunk/phase3/maintenance/language/messages.inc
@@ -1364,7 +1364,9 @@
13651365 'backend-fail-read',
13661366 'backend-fail-create',
13671367 'backend-fail-readonly',
1368 - 'backend-fail-synced'
 1368+ 'backend-fail-synced',
 1369+ 'backend-fail-connect',
 1370+ 'backend-fail-internal'
13691371 ),
13701372
13711373 'lockmanager-errors' => array(
Index: trunk/phase3/includes/filerepo/backend/SwiftFileBackend.php
@@ -0,0 +1,697 @@
 2+<?php
 3+/**
 4+ * @file
 5+ * @ingroup FileBackend
 6+ * @author Russ Nelson
 7+ * @author Aaron Schulz
 8+ */
 9+
 10+/**
 11+ * Class for a Swift based file backend.
 12+ * Status messages should avoid mentioning the Swift account name
 13+ * Likewise, error suppression should be used to avoid path disclosure.
 14+ *
 15+ * This requires the php-cloudfiles library is present,
 16+ * which is available at https://github.com/rackspace/php-cloudfiles.
 17+ * All of the library classes must be registed in $wgAutoloadClasses.
 18+ *
 19+ * @TODO: update MessagesEn for status errors.
 20+ * @TODO: handle 'latest' param as "X-Newest: true".
 21+ *
 22+ * @ingroup FileBackend
 23+ */
 24+class SwiftFileBackend extends FileBackend {
 25+ /** @var CF_Authentication */
 26+ protected $auth; // swift authentication handler
 27+ /** @var CF_Connection */
 28+ protected $conn; // swift connection handle
 29+ protected $connStarted = 0; // integer UNIX timestamp
 30+
 31+ protected $swiftProxyUser; // string
 32+ protected $connTTL = 60; // integer seconds
 33+
 34+ /**
 35+ * @see FileBackend::__construct()
 36+ * Additional $config params include:
 37+ * swiftAuthUrl : Swift authentication server URL
 38+ * swiftUser : Swift user used by MediaWiki
 39+ * swiftKey : Swift authentication key for the above user
 40+ * swiftProxyUser : Swift user used for end-user hits to proxy server
 41+ * shardViaHashLevels : Map of container names to the number of hash levels
 42+ */
 43+ public function __construct( array $config ) {
 44+ parent::__construct( $config );
 45+ // Required settings
 46+ $this->auth = new CF_Authentication(
 47+ $config['swiftUser'], $config['swiftKey'], null, $config['swiftAuthUrl'] );
 48+ // Optional settings
 49+ $this->connTTL = isset( $config['connTTL'] )
 50+ ? $config['connTTL']
 51+ : 60; // some sane number
 52+ $this->swiftProxyUser = isset( $config['swiftProxyUser'] )
 53+ ? $config['swiftProxyUser']
 54+ : '';
 55+ $this->shardViaHashLevels = isset( $config['shardViaHashLevels'] )
 56+ ? $config['shardViaHashLevels']
 57+ : '';
 58+ }
 59+
 60+ /**
 61+ * @see FileBackend::resolveContainerPath()
 62+ */
 63+ protected function resolveContainerPath( $container, $relStoragePath ) {
 64+ if ( strlen( urlencode( $relStoragePath ) ) > 1024 ) {
 65+ return null; // too long for swift
 66+ }
 67+ return $relStoragePath;
 68+ }
 69+
 70+ /**
 71+ * @see FileBackend::doCopyInternal()
 72+ */
 73+ protected function doCreateInternal( array $params ) {
 74+ $status = Status::newGood();
 75+
 76+ list( $dstCont, $destRel ) = $this->resolveStoragePathReal( $params['dst'] );
 77+ if ( $destRel === null ) {
 78+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 79+ return $status;
 80+ }
 81+
 82+ // (a) Get a swift proxy connection
 83+ $conn = $this->getConnection();
 84+ if ( !$conn ) {
 85+ $status->fatal( 'backend-fail-connect', $this->name );
 86+ return $status;
 87+ }
 88+
 89+ // (b) Check the destination container
 90+ try {
 91+ $dContObj = $conn->get_container( $dstCont );
 92+ } catch ( NoSuchContainerException $e ) {
 93+ $status->fatal( 'backend-fail-create', $params['dst'] );
 94+ return $status;
 95+ } catch ( InvalidResponseException $e ) {
 96+ $status->fatal( 'backend-fail-connect', $this->name );
 97+ return $status;
 98+ } catch ( Exception $e ) { // some other exception?
 99+ $status->fatal( 'backend-fail-internal' );
 100+ $this->logException( $e, __METHOD__, $params );
 101+ return $status;
 102+ }
 103+
 104+ // (c) Check if the destination object already exists
 105+ try {
 106+ $dContObj->get_object( $destRel ); // throws NoSuchObjectException
 107+ // NoSuchObjectException not thrown: file must exist
 108+ if ( empty( $params['overwriteDest'] ) ) {
 109+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 110+ return $status;
 111+ }
 112+ } catch ( NoSuchObjectException $e ) {
 113+ // NoSuchObjectException thrown: file does not exist
 114+ } catch ( InvalidResponseException $e ) {
 115+ $status->fatal( 'backend-fail-connect', $this->name );
 116+ return $status;
 117+ } catch ( Exception $e ) { // some other exception?
 118+ $status->fatal( 'backend-fail-internal' );
 119+ $this->logException( $e, __METHOD__, $params );
 120+ return $status;
 121+ }
 122+
 123+ // (d) Get a SHA-1 hash of the object
 124+ $sha1Hash = wfBaseConvert( sha1( $params['content'] ), 16, 36, 31 );
 125+
 126+ // (e) Actually create the object
 127+ try {
 128+ $obj = $dContObj->create_object( $destRel );
 129+ // Note: metadata keys stored as [Upper case char][[Lower case char]...]
 130+ $obj->metadata = array( 'Sha1base36' => $sha1Hash );
 131+ $obj->write( $params['content'] );
 132+ } catch ( BadContentTypeException $e ) {
 133+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
 134+ } catch ( InvalidResponseException $e ) {
 135+ $status->fatal( 'backend-fail-connect', $this->name );
 136+ } catch ( Exception $e ) { // some other exception?
 137+ $status->fatal( 'backend-fail-internal' );
 138+ $this->logException( $e, __METHOD__, $params );
 139+ }
 140+
 141+ return $status;
 142+ }
 143+
 144+ /**
 145+ * @see FileBackend::doStoreInternal()
 146+ */
 147+ protected function doStoreInternal( array $params ) {
 148+ $status = Status::newGood();
 149+
 150+ list( $dstCont, $destRel ) = $this->resolveStoragePathReal( $params['dst'] );
 151+ if ( $destRel === null ) {
 152+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 153+ return $status;
 154+ }
 155+
 156+ // (a) Get a swift proxy connection
 157+ $conn = $this->getConnection();
 158+ if ( !$conn ) {
 159+ $status->fatal( 'backend-fail-connect', $this->name );
 160+ return $status;
 161+ }
 162+
 163+ // (b) Check the destination container
 164+ try {
 165+ $dContObj = $conn->get_container( $dstCont );
 166+ } catch ( NoSuchContainerException $e ) {
 167+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 168+ return $status;
 169+ } catch ( InvalidResponseException $e ) {
 170+ $status->fatal( 'backend-fail-connect', $this->name );
 171+ return $status;
 172+ } catch ( Exception $e ) { // some other exception?
 173+ $status->fatal( 'backend-fail-internal' );
 174+ $this->logException( $e, __METHOD__, $params );
 175+ return $status;
 176+ }
 177+
 178+ // (c) Check if the destination object already exists
 179+ try {
 180+ $dContObj->get_object( $destRel ); // throws NoSuchObjectException
 181+ // NoSuchObjectException not thrown: file must exist
 182+ if ( empty( $params['overwriteDest'] ) ) {
 183+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 184+ return $status;
 185+ }
 186+ } catch ( NoSuchObjectException $e ) {
 187+ // NoSuchObjectException thrown: file does not exist
 188+ } catch ( InvalidResponseException $e ) {
 189+ $status->fatal( 'backend-fail-connect', $this->name );
 190+ return $status;
 191+ } catch ( Exception $e ) { // some other exception?
 192+ $status->fatal( 'backend-fail-internal' );
 193+ $this->logException( $e, __METHOD__, $params );
 194+ return $status;
 195+ }
 196+
 197+ // (d) Get a SHA-1 hash of the object
 198+ $sha1Hash = sha1_file( $params['src'] );
 199+ if ( $sha1Hash === false ) { // source doesn't exist?
 200+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 201+ return $status;
 202+ }
 203+ $sha1Hash = wfBaseConvert( $sha1Hash, 16, 36, 31 );
 204+
 205+ // (e) Actually store the object
 206+ try {
 207+ $obj = $dContObj->create_object( $destRel );
 208+ // Note: metadata keys stored as [Upper case char][[Lower case char]...]
 209+ $obj->metadata = array( 'Sha1base36' => $sha1Hash );
 210+ $obj->load_from_filename( $params['src'], True ); // calls $obj->write()
 211+ } catch ( BadContentTypeException $e ) {
 212+ $status->fatal( 'backend-fail-contenttype', $params['dst'] );
 213+ } catch ( IOException $e ) {
 214+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 215+ } catch ( InvalidResponseException $e ) {
 216+ $status->fatal( 'backend-fail-connect', $this->name );
 217+ } catch ( Exception $e ) { // some other exception?
 218+ $status->fatal( 'backend-fail-internal' );
 219+ $this->logException( $e, __METHOD__, $params );
 220+ }
 221+
 222+ return $status;
 223+ }
 224+
 225+ /**
 226+ * @see FileBackend::doCopyInternal()
 227+ */
 228+ protected function doCopyInternal( array $params ) {
 229+ $status = Status::newGood();
 230+
 231+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 232+ if ( $srcRel === null ) {
 233+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 234+ return $status;
 235+ }
 236+
 237+ list( $dstCont, $destRel ) = $this->resolveStoragePathReal( $params['dst'] );
 238+ if ( $destRel === null ) {
 239+ $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
 240+ return $status;
 241+ }
 242+
 243+ // (a) Get a swift proxy connection
 244+ $conn = $this->getConnection();
 245+ if ( !$conn ) {
 246+ $status->fatal( 'backend-fail-connect', $this->name );
 247+ return $status;
 248+ }
 249+
 250+ // (b) Check the source and destination containers
 251+ try {
 252+ $sContObj = $conn->get_container( $srcCont );
 253+ $dContObj = $conn->get_container( $dstCont );
 254+ } catch ( NoSuchContainerException $e ) {
 255+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 256+ return $status;
 257+ } catch ( InvalidResponseException $e ) {
 258+ $status->fatal( 'backend-fail-connect', $this->name );
 259+ return $status;
 260+ } catch ( Exception $e ) { // some other exception?
 261+ $status->fatal( 'backend-fail-internal' );
 262+ $this->logException( $e, __METHOD__, $params );
 263+ return $status;
 264+ }
 265+
 266+ // (c) Check if the destination object already exists
 267+ try {
 268+ $dContObj->get_object( $destRel ); // throws NoSuchObjectException
 269+ // NoSuchObjectException not thrown: file must exist
 270+ if ( empty( $params['overwriteDest'] ) ) {
 271+ $status->fatal( 'backend-fail-alreadyexists', $params['dst'] );
 272+ return $status;
 273+ }
 274+ } catch ( NoSuchObjectException $e ) {
 275+ // NoSuchObjectException thrown: file does not exist
 276+ } catch ( InvalidResponseException $e ) {
 277+ $status->fatal( 'backend-fail-connect', $this->name );
 278+ return $status;
 279+ } catch ( Exception $e ) { // some other exception?
 280+ $status->fatal( 'backend-fail-internal' );
 281+ $this->logException( $e, __METHOD__, $params );
 282+ return $status;
 283+ }
 284+
 285+ // (d) Actually copy the file to the destination
 286+ try {
 287+ $sContObj->copy_object_to( $srcRel, $dContObj, $destRel );
 288+ } catch ( NoSuchObjectException $e ) { // source object does not exist
 289+ $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
 290+ } catch ( InvalidResponseException $e ) {
 291+ $status->fatal( 'backend-fail-connect', $this->name );
 292+ } catch ( Exception $e ) { // some other exception?
 293+ $status->fatal( 'backend-fail-internal' );
 294+ $this->logException( $e, __METHOD__, $params );
 295+ }
 296+
 297+ return $status;
 298+ }
 299+
 300+ /**
 301+ * @see FileBackend::doDeleteInternal()
 302+ */
 303+ protected function doDeleteInternal( array $params ) {
 304+ $status = Status::newGood();
 305+
 306+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 307+ if ( $srcRel === null ) {
 308+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 309+ return $status;
 310+ }
 311+
 312+ // (a) Get a swift proxy connection
 313+ $conn = $this->getConnection();
 314+ if ( !$conn ) {
 315+ $status->fatal( 'backend-fail-connect', $this->name );
 316+ return $status;
 317+ }
 318+
 319+ // (b) Check the source container
 320+ try {
 321+ $sContObj = $conn->get_container( $srcCont );
 322+ } catch ( NoSuchContainerException $e ) {
 323+ $status->fatal( 'backend-fail-delete', $params['src'] );
 324+ return $status;
 325+ } catch ( InvalidResponseException $e ) {
 326+ $status->fatal( 'backend-fail-connect', $this->name );
 327+ return $status;
 328+ } catch ( Exception $e ) { // some other exception?
 329+ $status->fatal( 'backend-fail-internal' );
 330+ $this->logException( $e, __METHOD__, $params );
 331+ return $status;
 332+ }
 333+
 334+ // (c) Actually delete the object
 335+ try {
 336+ $sContObj->delete_object( $srcRel );
 337+ } catch ( NoSuchObjectException $e ) {
 338+ if ( empty( $params['ignoreMissingSource'] ) ) {
 339+ $status->fatal( 'backend-fail-delete', $params['src'] );
 340+ }
 341+ } catch ( InvalidResponseException $e ) {
 342+ $status->fatal( 'backend-fail-connect', $this->name );
 343+ } catch ( Exception $e ) { // some other exception?
 344+ $status->fatal( 'backend-fail-internal' );
 345+ $this->logException( $e, __METHOD__, $params );
 346+ }
 347+
 348+ return $status;
 349+ }
 350+
 351+ /**
 352+ * @see FileBackend::doPrepareInternal()
 353+ */
 354+ protected function doPrepareInternal( $fullCont, $dir, array $params ) {
 355+ $status = Status::newGood();
 356+
 357+ // (a) Get a swift proxy connection
 358+ $conn = $this->getConnection();
 359+ if ( !$conn ) {
 360+ $status->fatal( 'backend-fail-connect', $this->name );
 361+ return $status;
 362+ }
 363+
 364+ // (b) Create the destination container
 365+ try {
 366+ $conn->create_container( $fullCont );
 367+ } catch ( InvalidResponseException $e ) {
 368+ $status->fatal( 'backend-fail-connect', $this->name );
 369+ } catch ( Exception $e ) { // some other exception?
 370+ $status->fatal( 'backend-fail-internal' );
 371+ $this->logException( $e, __METHOD__, $params );
 372+ }
 373+
 374+ return $status;
 375+ }
 376+
 377+ /**
 378+ * @see FileBackend::doSecureInternal()
 379+ */
 380+ protected function doSecureInternal( $fullCont, $dir, array $params ) {
 381+ $status = Status::newGood();
 382+ // @TODO: restrict container from $this->swiftProxyUser
 383+ return $status; // badgers? We don't need no steenking badgers!
 384+ }
 385+
 386+ /**
 387+ * @see FileBackend::doFileExists()
 388+ */
 389+ protected function doGetFileStat( array $params ) {
 390+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 391+ if ( $srcRel === null ) {
 392+ return false; // invalid storage path
 393+ }
 394+
 395+ $conn = $this->getConnection();
 396+ if ( !$conn ) {
 397+ return null;
 398+ }
 399+
 400+ $stat = false;
 401+ try {
 402+ $container = $conn->get_container( $srcCont );
 403+ $obj = $container->get_object( $srcRel );
 404+ // Convert "Tue, 03 Jan 2012 22:01:04 GMT" to TS_MW
 405+ $date = DateTime::createFromFormat( 'D, d F Y G:i:s e', $obj->last_modified );
 406+ if ( $date ) {
 407+ $stat = array(
 408+ 'mtime' => $date->format( 'YmdHis' ),
 409+ 'size' => $obj->content_length,
 410+ 'sha1' => $obj->metadata['Sha1base36']
 411+ );
 412+ } else { // exception will be caught below
 413+ throw new Exception( "Could not parse date for object {$srcRel}" );
 414+ }
 415+ } catch ( NoSuchContainerException $e ) {
 416+ } catch ( NoSuchObjectException $e ) {
 417+ } catch ( InvalidResponseException $e ) {
 418+ $stat = null;
 419+ } catch ( Exception $e ) { // some other exception?
 420+ $stat = null;
 421+ $this->logException( $e, __METHOD__, $params );
 422+ }
 423+
 424+ return $stat;
 425+ }
 426+
 427+ /**
 428+ * @see FileBackendBase::getFileContents()
 429+ */
 430+ public function getFileContents( array $params ) {
 431+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 432+ if ( $srcRel === null ) {
 433+ return false; // invalid storage path
 434+ }
 435+
 436+ $conn = $this->getConnection();
 437+ if ( !$conn ) {
 438+ return false;
 439+ }
 440+
 441+ $data = false;
 442+ try {
 443+ $container = $conn->get_container( $srcCont );
 444+ $obj = $container->get_object( $srcRel );
 445+ $data = $obj->read();
 446+ } catch ( NoSuchContainerException $e ) {
 447+ } catch ( NoSuchObjectException $e ) {
 448+ } catch ( InvalidResponseException $e ) {
 449+ } catch ( Exception $e ) { // some other exception?
 450+ $this->logException( $e, __METHOD__, $params );
 451+ }
 452+
 453+ return $data;
 454+ }
 455+
 456+ /**
 457+ * @see FileBackend::getFileListInternal()
 458+ */
 459+ public function getFileListInternal( $fullCont, $dir, array $params ) {
 460+ return new SwiftFileIterator( $this, $fullCont, $dir );
 461+ }
 462+
 463+ /**
 464+ * Do not call this function outside of SwiftFileIterator
 465+ *
 466+ * @param $fullCont string Resolved container name
 467+ * @param $dir string Resolved storage directory
 468+ * @param $after string Storage path of file to list items after
 469+ * @param $limit integer Max number of items to list
 470+ * @return Array
 471+ */
 472+ public function getFileListPageInternal( $fullCont, $dir, $after, $limit ) {
 473+ $conn = $this->getConnection();
 474+ if ( !$conn ) {
 475+ return null;
 476+ }
 477+
 478+ $files = array();
 479+ try {
 480+ $container = $conn->get_container( $fullCont );
 481+ $files = $container->list_objects( $limit, $after, $dir );
 482+ } catch ( NoSuchContainerException $e ) {
 483+ } catch ( NoSuchObjectException $e ) {
 484+ } catch ( InvalidResponseException $e ) {
 485+ } catch ( Exception $e ) { // some other exception?
 486+ $this->logException( $e, __METHOD__, $params );
 487+ }
 488+
 489+ return $files;
 490+ }
 491+
 492+ /**
 493+ * @see FileBackend::doGetFileSha1base36()
 494+ */
 495+ public function doGetFileSha1base36( array $params ) {
 496+ $stat = $this->getFileStat( $params );
 497+ if ( $stat ) {
 498+ return $stat['sha1'];
 499+ } else {
 500+ return false;
 501+ }
 502+ }
 503+
 504+ /**
 505+ * @see FileBackend::doStreamFile()
 506+ */
 507+ protected function doStreamFile( array $params ) {
 508+ $status = Status::newGood();
 509+
 510+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 511+ if ( $srcRel === null ) {
 512+ $status->fatal( 'backend-fail-invalidpath', $params['src'] );
 513+ }
 514+
 515+ $conn = $this->getConnection();
 516+ if ( !$conn ) {
 517+ $status->fatal( 'backend-fail-connect', $this->name );
 518+ }
 519+
 520+ try {
 521+ $cont = $conn->get_container( $srcCont );
 522+ $obj = $cont->get_object( $srcRel );
 523+ } catch ( NoSuchContainerException $e ) {
 524+ $status->fatal( 'backend-fail-stream', $params['src'] );
 525+ return $status;
 526+ } catch ( NoSuchObjectException $e ) {
 527+ $status->fatal( 'backend-fail-stream', $params['src'] );
 528+ return $status;
 529+ } catch ( IOException $e ) {
 530+ $status->fatal( 'backend-fail-stream', $params['src'] );
 531+ return $status;
 532+ } catch ( Exception $e ) { // some other exception?
 533+ $status->fatal( 'backend-fail-stream', $params['src'] );
 534+ $this->logException( $e, __METHOD__, $params );
 535+ return $status;
 536+ }
 537+
 538+ try {
 539+ $output = fopen("php://output", "w");
 540+ $obj->stream( $output );
 541+ } catch ( InvalidResponseException $e ) {
 542+ $status->fatal( 'backend-fail-connect', $this->name );
 543+ } catch ( Exception $e ) { // some other exception?
 544+ $status->fatal( 'backend-fail-stream', $params['src'] );
 545+ $this->logException( $e, __METHOD__, $params );
 546+ }
 547+
 548+ return $status;
 549+ }
 550+
 551+ /**
 552+ * @see FileBackend::getLocalCopy()
 553+ */
 554+ public function getLocalCopy( array $params ) {
 555+ list( $srcCont, $srcRel ) = $this->resolveStoragePathReal( $params['src'] );
 556+ if ( $srcRel === null ) {
 557+ return null;
 558+ }
 559+
 560+ $conn = $this->getConnection();
 561+ if ( !$conn ) {
 562+ return null;
 563+ }
 564+
 565+ // Get source file extension
 566+ $ext = FileBackend::extensionFromPath( $srcRel );
 567+ // Create a new temporary file...
 568+ $tmpFile = TempFSFile::factory( wfBaseName( $srcRel ) . '_', $ext );
 569+ if ( !$tmpFile ) {
 570+ return null;
 571+ }
 572+
 573+ try {
 574+ $cont = $conn->get_container( $srcCont );
 575+ $obj = $cont->get_object( $srcRel );
 576+ $obj->save_to_filename( $tmpFile->getPath() );
 577+ } catch ( NoSuchContainerException $e ) {
 578+ $tmpFile = null;
 579+ } catch ( NoSuchObjectException $e ) {
 580+ $tmpFile = null;
 581+ } catch ( IOException $e ) {
 582+ $tmpFile = null;
 583+ } catch ( InvalidResponseException $e ) {
 584+ $tmpFile = null;
 585+ } catch ( Exception $e ) { // some other exception?
 586+ $tmpFile = null;
 587+ $this->logException( $e, __METHOD__, $params );
 588+ }
 589+
 590+ return $tmpFile;
 591+ }
 592+
 593+ /**
 594+ * Get a connection to the swift proxy
 595+ *
 596+ * @return CF_Connection|false
 597+ */
 598+ protected function getConnection() {
 599+ if ( $this->conn === false ) {
 600+ return false; // failed last attempt
 601+ }
 602+ // Authenticate with proxy and get a session key.
 603+ // Session keys expire after a while, so we renew them periodically.
 604+ if ( $this->conn === null || ( time() - $this->connStarted ) > $this->connTTL ) {
 605+ try {
 606+ $this->auth->authenticate();
 607+ $this->conn = new CF_Connection( $this->auth );
 608+ $this->connStarted = time();
 609+ } catch ( AuthenticationException $e ) {
 610+ $this->conn = false; // don't keep re-trying
 611+ } catch ( InvalidResponseException $e ) {
 612+ $this->conn = false; // don't keep re-trying
 613+ }
 614+ }
 615+ return $this->conn;
 616+ }
 617+
 618+ /**
 619+ * Log an unexpected exception for this backend
 620+ *
 621+ * @param $e Exception
 622+ * @param $func string
 623+ * @param $params Array
 624+ * @return void
 625+ */
 626+ protected function logException( Exception $e, $func, array $params ) {
 627+ wfDebugLog( 'SwiftBackend',
 628+ get_class( $e ) . " in '{$this->name}': '{$func}' with " . serialize( $params )
 629+ );
 630+ }
 631+}
 632+
 633+/**
 634+ * SwiftFileBackend helper class to page through object listings.
 635+ * Swift also has a listing limit of 10,000 objects for sanity.
 636+ *
 637+ * @ingroup FileBackend
 638+ */
 639+class SwiftFileIterator implements Iterator {
 640+ /** @var Array */
 641+ protected $bufferIter = array();
 642+ protected $bufferAfter = null; // string; list items *after* this path
 643+ protected $pos = 0; // integer
 644+
 645+ /** @var SwiftFileBackend */
 646+ protected $backend;
 647+ protected $container; //
 648+ protected $dir; // string storage directory
 649+
 650+ const PAGE_SIZE = 5000; // file listing buffer size
 651+
 652+ /**
 653+ * Get an FSFileIterator from a file system directory
 654+ *
 655+ * @param $backend SwiftFileBackend
 656+ * @param $fullCont string Resolved container name
 657+ * @param $dir string Resolved relateive path
 658+ */
 659+ public function __construct( SwiftFileBackend $backend, $fullCont, $dir ) {
 660+ $this->container = $fullCont;
 661+ $this->dir = $dir;
 662+ $this->backend = $backend;
 663+ }
 664+
 665+ public function current() {
 666+ return current( $this->bufferIter );
 667+ }
 668+
 669+ public function key() {
 670+ return $this->pos;
 671+ }
 672+
 673+ public function next() {
 674+ // Advance to the next file in the page
 675+ next( $this->bufferIter );
 676+ ++$this->pos;
 677+ // Check if there are no files left in this page and
 678+ // advance to the next page if this page was not empty.
 679+ if ( !$this->valid() && count( $this->bufferIter ) ) {
 680+ $this->bufferAfter = end( $this->bufferIter );
 681+ $this->bufferIter = $this->backend->getFileListPageInternal(
 682+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE
 683+ );
 684+ }
 685+ }
 686+
 687+ public function rewind() {
 688+ $this->pos = 0;
 689+ $this->bufferAfter = null;
 690+ $this->bufferIter = $this->backend->getFileListPageInternal(
 691+ $this->container, $this->dir, $this->bufferAfter, self::PAGE_SIZE
 692+ );
 693+ }
 694+
 695+ public function valid() {
 696+ return ( current( $this->bufferIter ) !== false ); // no paths can have this value
 697+ }
 698+}
Index: trunk/phase3/includes/AutoLoader.php
@@ -491,6 +491,7 @@
492492 'FileBackend' => 'includes/filerepo/backend/FileBackend.php',
493493 'FileBackendMultiWrite' => 'includes/filerepo/backend/FileBackendMultiWrite.php',
494494 'FSFileBackend' => 'includes/filerepo/backend/FSFileBackend.php',
 495+ 'SwiftFileBackend' => 'includes/filerepo/backend/SwiftFileBackend.php',
495496 'FSFileIterator' => 'includes/filerepo/backend/FSFileBackend.php',
496497 'LockManagerGroup' => 'includes/filerepo/backend/lockmanager/LockManagerGroup.php',
497498 'LockManager' => 'includes/filerepo/backend/lockmanager/LockManager.php',
Index: trunk/phase3/languages/messages/MessagesEn.php
@@ -2255,6 +2255,8 @@
22562256 'backend-fail-create' => 'Could not create file $1.',
22572257 'backend-fail-readonly' => 'The backend "$1" is currently read-only. The reason given is: "$2"',
22582258 'backend-fail-synced' => 'The file "$1" is in an inconsistent state within the internal backends',
 2259+'backend-fail-connect' => 'Could not connect to file backend "$1".',
 2260+'backend-fail-internal' => 'An unknown internal file backend error occured.',
22592261
22602262 # Lock manager
22612263 'lockmanager-notlocked' => 'Could not unlock "$1"; it is not locked.',

Follow-up revisions

RevisionCommit summaryAuthorDate
r109239r108749: added missing backend-fail-contenttype messageaaron22:56, 17 January 2012

Comments

#Comment by Raymond (talk | contribs)   10:41, 3 February 2012

Status & tagging log