View Javadoc

1   /**
2    *
3    * Copyright (C) 2011 Cloud Conscious, LLC. <info@cloudconscious.com>
4    *
5    * ====================================================================
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   * ====================================================================
18   */
19  package org.jclouds.blobstore.internal;
20  
21  import static com.google.common.base.Preconditions.checkNotNull;
22  import static org.jclouds.blobstore.options.ListContainerOptions.Builder.recursive;
23  
24  import java.util.Set;
25  import java.util.concurrent.Callable;
26  import java.util.concurrent.ExecutorService;
27  
28  import javax.inject.Inject;
29  import javax.inject.Named;
30  
31  import org.jclouds.Constants;
32  import org.jclouds.blobstore.AsyncBlobStore;
33  import org.jclouds.blobstore.BlobStoreContext;
34  import org.jclouds.blobstore.ContainerNotFoundException;
35  import org.jclouds.blobstore.domain.Blob;
36  import org.jclouds.blobstore.domain.BlobBuilder;
37  import org.jclouds.blobstore.domain.PageSet;
38  import org.jclouds.blobstore.domain.StorageMetadata;
39  import org.jclouds.blobstore.options.ListContainerOptions;
40  import org.jclouds.blobstore.util.BlobUtils;
41  import org.jclouds.blobstore.util.internal.BlobUtilsImpl;
42  import org.jclouds.collect.Memoized;
43  import org.jclouds.domain.Location;
44  import org.jclouds.util.Assertions;
45  
46  import com.google.common.base.Supplier;
47  import com.google.common.util.concurrent.Futures;
48  import com.google.common.util.concurrent.ListenableFuture;
49  
50  /**
51   * 
52   * @author Adrian Cole
53   */
54  public abstract class BaseAsyncBlobStore implements AsyncBlobStore {
55  
56     protected final BlobStoreContext context;
57     protected final BlobUtils blobUtils;
58     protected final ExecutorService service;
59     protected final Supplier<Location> defaultLocation;
60     protected final Supplier<Set<? extends Location>> locations;
61  
62     @Inject
63     protected BaseAsyncBlobStore(BlobStoreContext context, BlobUtils blobUtils,
64              @Named(Constants.PROPERTY_USER_THREADS) ExecutorService service, Supplier<Location> defaultLocation,
65              @Memoized Supplier<Set<? extends Location>> locations) {
66        this.context = checkNotNull(context, "context");
67        this.blobUtils = checkNotNull(blobUtils, "blobUtils");
68        this.service = checkNotNull(service, "service");
69        this.defaultLocation = checkNotNull(defaultLocation, "defaultLocation");
70        this.locations = checkNotNull(locations, "locations");
71     }
72  
73     @Override
74     public BlobStoreContext getContext() {
75        return context;
76     }
77  
78     /**
79      * invokes {@link BlobUtilsImpl#newBlob }
80      */
81     @Override
82     public Blob newBlob(String name) {
83        return blobUtils.newBlob(name);
84     }
85  
86     /**
87      * invokes {@link BlobUtilsImpl#blobBuilder }
88      */
89     @Override
90     public BlobBuilder blobBuilder(String name) {
91        return blobUtils.blobBuilder().name(name);
92     }
93  
94     /**
95      * This implementation invokes
96      * {@link #list(String,org.jclouds.blobstore.options.ListContainerOptions)}
97      * 
98      * @param container
99      *           container name
100     */
101    @Override
102    public ListenableFuture<PageSet<? extends StorageMetadata>> list(String container) {
103       return this.list(container, org.jclouds.blobstore.options.ListContainerOptions.NONE);
104    }
105 
106    /**
107     * This implementation invokes {@link #countBlobs} with the
108     * {@link ListContainerOptions#recursive} option.
109     * 
110     * @param container
111     *           container name
112     */
113    @Override
114    public ListenableFuture<Long> countBlobs(String container) {
115       return countBlobs(container, recursive());
116    }
117 
118    /**
119     * This implementation invokes {@link BlobUtilsImpl#countBlobs}
120     * 
121     * @param container
122     *           container name
123     */
124    @Override
125    public ListenableFuture<Long> countBlobs(final String containerName, final ListContainerOptions options) {
126       return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Long>() {
127          public Long call() throws Exception {
128             return blobUtils.countBlobs(containerName, options);
129          }
130 
131          @Override
132          public String toString() {
133             return "countBlobs(" + containerName + ")";
134          }
135       }), service);
136    }
137 
138    /**
139     * This implementation invokes {@link #clearContainer} with the
140     * {@link ListContainerOptions#recursive} option.
141     * 
142     * @param container
143     *           container name
144     */
145    @Override
146    public ListenableFuture<Void> clearContainer(final String container) {
147       return clearContainer(container, recursive());
148    }
149 
150    /**
151     * This implementation invokes {@link BlobUtilsImpl#clearContainer}
152     * 
153     * @param container
154     *           container name
155     */
156    @Override
157    public ListenableFuture<Void> clearContainer(final String containerName, final ListContainerOptions options) {
158       return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
159 
160          public Void call() throws Exception {
161             blobUtils.clearContainer(containerName, options);
162             return null;
163          }
164 
165          @Override
166          public String toString() {
167             return "clearContainer(" + containerName + ")";
168          }
169       }), service);
170    }
171 
172    /**
173     * This implementation invokes {@link BlobUtilsImpl#deleteDirectory}.
174     * 
175     * @param container
176     *           container name
177     */
178    @Override
179    public ListenableFuture<Void> deleteDirectory(final String containerName, final String directory) {
180       return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
181 
182          public Void call() throws Exception {
183             blobUtils.deleteDirectory(containerName, directory);
184             return null;
185          }
186 
187          @Override
188          public String toString() {
189             return "deleteDirectory(" + containerName + "," + directory + ")";
190          }
191       }), service);
192    }
193 
194    /**
195     * This implementation invokes {@link BlobUtilsImpl#directoryExists}
196     * 
197     * @param container
198     *           container name
199     * @param directory
200     *           virtual path
201     */
202    public ListenableFuture<Boolean> directoryExists(final String containerName, final String directory) {
203       return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Boolean>() {
204 
205          public Boolean call() throws Exception {
206             return blobUtils.directoryExists(containerName, directory);
207          }
208 
209          @Override
210          public String toString() {
211             return "directoryExists(" + containerName + "," + directory + ")";
212          }
213       }), service);
214    }
215 
216    /**
217     * This implementation invokes {@link BlobUtilsImpl#createDirectory}
218     * 
219     * @param container
220     *           container name
221     * @param directory
222     *           virtual path
223     */
224 
225    public ListenableFuture<Void> createDirectory(final String containerName, final String directory) {
226 
227       return blobUtils.directoryExists(containerName, directory) ? Futures.immediateFuture((Void) null)
228                : org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
229                   public Void call() throws Exception {
230                      blobUtils.createDirectory(containerName, directory);
231                      return null;
232                   }
233 
234                   @Override
235                   public String toString() {
236                      return "createDirectory(" + containerName + "," + directory + ")";
237                   }
238                }), service);
239    }
240 
241    /**
242     * This implementation invokes
243     * {@link #getBlob(String,String,org.jclouds.blobstore.options.GetOptions)}
244     * 
245     * @param container
246     *           container name
247     * @param key
248     *           blob key
249     */
250    @Override
251    public ListenableFuture<Blob> getBlob(String container, String key) {
252       return getBlob(container, key, org.jclouds.blobstore.options.GetOptions.NONE);
253    }
254 
255    /**
256     * This implementation invokes {@link #deleteAndEnsurePathGone}
257     * 
258     * @param container
259     *           bucket name
260     */
261    @Override
262    public ListenableFuture<Void> deleteContainer(final String container) {
263       return org.jclouds.concurrent.Futures.makeListenable(service.submit(new Callable<Void>() {
264 
265          public Void call() throws Exception {
266             deleteAndEnsurePathGone(container);
267             return null;
268          }
269 
270          @Override
271          public String toString() {
272             return "deleteContainer(" + container + ")";
273          }
274       }), service);
275    }
276 
277    protected void deleteAndEnsurePathGone(final String container) {
278       try {
279          if (!Assertions.eventuallyTrue(new Supplier<Boolean>() {
280             public Boolean get() {
281                try {
282                   clearContainer(container, recursive());
283                   return deleteAndVerifyContainerGone(container);
284                } catch (ContainerNotFoundException e) {
285                   return true;
286                }
287             }
288 
289          }, 30000)) {
290             throw new IllegalStateException(container + " still exists after deleting!");
291          }
292       } catch (InterruptedException e) {
293          new IllegalStateException(container + " interrupted during deletion!", e);
294       }
295    }
296 
297    @Override
298    public ListenableFuture<Set<? extends Location>> listAssignableLocations() {
299       return Futures.<Set<? extends Location>> immediateFuture(locations.get());
300    }
301 
302    protected abstract boolean deleteAndVerifyContainerGone(String container);
303 
304 }