1: <?php
2: /**
3: * Copyright 2012-2014 Rackspace US, Inc.
4: *
5: * Licensed under the Apache License, Version 2.0 (the "License");
6: * you may not use this file except in compliance with the License.
7: * You may obtain a copy of the License at
8: *
9: * http://www.apache.org/licenses/LICENSE-2.0
10: *
11: * Unless required by applicable law or agreed to in writing, software
12: * distributed under the License is distributed on an "AS IS" BASIS,
13: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14: * See the License for the specific language governing permissions and
15: * limitations under the License.
16: */
17:
18: namespace OpenCloud\ObjectStore\Upload;
19:
20: use Guzzle\Batch\BatchBuilder;
21: use Guzzle\Common\Collection;
22: use Guzzle\Http\Message\Response;
23: use Guzzle\Http\Url;
24: use OpenCloud\ObjectStore\Resource\Container;
25:
26: /**
27: * Class responsible for migrating the contents of one container to another
28: *
29: * @package OpenCloud\ObjectStore\Upload
30: */
31: class ContainerMigration
32: {
33: /** @var \Guzzle\Batch\Batch */
34: protected $readQueue;
35:
36: /** @var \Guzzle\Batch\Batch */
37: protected $writeQueue;
38:
39: /** @var \OpenCloud\ObjectStore\Resource\Container */
40: protected $oldContainer;
41:
42: /** @var \OpenCloud\ObjectStore\Resource\Container */
43: protected $newContainer;
44:
45: /** @var \Guzzle\Common\Collection */
46: protected $options = array();
47:
48: protected $defaults = array(
49: 'read.batchLimit' => 1000,
50: 'read.pageLimit' => 10000,
51: 'write.batchLimit' => 100
52: );
53:
54: /**
55: * @param Container $old Source container
56: * @param Container $new Target container
57: * @param array $options Options that configure process
58: * @return ContainerMigration
59: */
60: public static function factory(Container $old, Container $new, array $options = array())
61: {
62: $migration = new self();
63:
64: $migration->setOldContainer($old);
65: $migration->setNewContainer($new);
66: $migration->setOptions($options);
67:
68: $migration->setupReadQueue();
69: $migration->setupWriteQueue();
70:
71: return $migration;
72: }
73:
74: /**
75: * @param Container $old
76: */
77: public function setOldContainer(Container $old)
78: {
79: $this->oldContainer = $old;
80: }
81:
82: /**
83: * @return Container
84: */
85: public function getOldContainer()
86: {
87: return $this->oldContainer;
88: }
89:
90: /**
91: * @param Container $new
92: */
93: public function setNewContainer(Container $new)
94: {
95: $this->newContainer = $new;
96: }
97:
98: /**
99: * @return Container
100: */
101: public function getNewContainer()
102: {
103: return $this->newContainer;
104: }
105:
106: /**
107: * @param array $options
108: */
109: public function setOptions(array $options)
110: {
111: $this->options = Collection::fromConfig($options, $this->defaults);
112: }
113:
114: /**
115: * @return \Guzzle\Common\Collection
116: */
117: public function getOptions()
118: {
119: return $this->options;
120: }
121:
122: /**
123: * Set the read queue as a {@see \Guzzle\Batch\Batch} queue using the {@see \Guzzle\Batch\BatchBuilder}
124: */
125: public function setupReadQueue()
126: {
127: $this->readQueue = BatchBuilder::factory()
128: ->transferRequests($this->options->get('read.batchLimit'))
129: ->build();
130: }
131:
132: /**
133: * Set the write queue as a {@see \Guzzle\Batch\Batch} queue using the {@see \Guzzle\Batch\BatchBuilder}
134: */
135: public function setupWriteQueue()
136: {
137: $this->writeQueue = BatchBuilder::factory()
138: ->transferRequests($this->options->get('write.batchLimit'))
139: ->build();
140: }
141:
142: /**
143: * @return \Guzzle\Http\ClientInterface
144: */
145: private function getClient()
146: {
147: return $this->newContainer->getService()->getClient();
148: }
149:
150: /**
151: * Create a collection of files to be migrated and add them to the read queue
152: */
153: protected function enqueueGetRequests()
154: {
155: $files = $this->oldContainer->objectList(array(
156: 'limit.total' => false,
157: 'limit.page' => $this->options->get('read.pageLimit')
158: ));
159:
160: foreach ($files as $file) {
161: $this->readQueue->add(
162: $this->getClient()->get($file->getUrl())
163: );
164: }
165: }
166:
167: /**
168: * Send the read queue (in order to gather more information about individual files)
169: *
170: * @return array Responses
171: */
172: protected function sendGetRequests()
173: {
174: $this->enqueueGetRequests();
175:
176: return $this->readQueue->flush();
177: }
178:
179: /**
180: * Create a tailored PUT request for each file
181: *
182: * @param Response $response
183: * @return \Guzzle\Http\Message\EntityEnclosingRequestInterface
184: */
185: protected function createPutRequest(Response $response)
186: {
187: $segments = Url::factory($response->getEffectiveUrl())->getPathSegments();
188: $name = end($segments);
189:
190: // Retrieve content and metadata
191: $file = $this->newContainer->dataObject()->setName($name);
192: $file->setMetadata($response->getHeaders(), true);
193:
194: return $this->getClient()->put(
195: $file->getUrl(),
196: $file::stockHeaders($file->getMetadata()->toArray()),
197: $response->getBody()
198: );
199: }
200:
201: /**
202: * Initiate the transfer process
203: *
204: * @return array PUT responses
205: */
206: public function transfer()
207: {
208: $requests = $this->sendGetRequests();
209: $this->readQueue = null;
210:
211: foreach ($requests as $key => $request) {
212: $this->writeQueue->add(
213: $this->createPutRequest($request->getResponse())
214: );
215: unset($requests[$key]);
216: }
217:
218: return $this->writeQueue->flush();
219: }
220: }
221: