|
31 | 31 | import java.io.IOException; |
32 | 32 | import java.io.InputStream; |
33 | 33 | import java.util.ArrayList; |
| 34 | +import java.util.List; |
34 | 35 | import java.util.UUID; |
35 | 36 | import java.util.concurrent.Callable; |
36 | 37 | import java.util.concurrent.ExecutionException; |
|
41 | 42 | import java.util.concurrent.TimeoutException; |
42 | 43 | import java.util.logging.Level; |
43 | 44 | import java.util.logging.Logger; |
| 45 | + |
| 46 | +import com.google.common.base.Strings; |
44 | 47 | import org.threeten.bp.Duration; |
45 | 48 |
|
46 | 49 | /** |
@@ -121,8 +124,29 @@ public void run() { |
121 | 124 | */ |
122 | 125 | public static Boolean forceDelete(Storage storage, String bucket, long timeout, TimeUnit unit) |
123 | 126 | throws InterruptedException, ExecutionException { |
| 127 | + return forceDelete(storage, bucket, timeout, unit, ""); |
| 128 | + } |
| 129 | + |
| 130 | + /** |
| 131 | + * Deletes a bucket, even if non-empty. Objects in the bucket are listed and deleted until bucket |
| 132 | + * deletion succeeds or {@code timeout} expires. To allow for the timeout, this method uses a |
| 133 | + * separate thread to send the delete requests. Use |
| 134 | + * {@link #forceDelete(Storage storage, String bucket)} if spawning an additional thread is |
| 135 | + * undesirable, such as in the App Engine production runtime. |
| 136 | + * |
| 137 | + * @param storage the storage service to be used to issue requests |
| 138 | + * @param bucket the bucket to be deleted |
| 139 | + * @param timeout the maximum time to wait |
| 140 | + * @param unit the time unit of the timeout argument |
| 141 | + * @param userProject the project to bill for requester-pays buckets (or "") |
| 142 | + * @return true if deletion succeeded, false if timeout expired |
| 143 | + * @throws InterruptedException if the thread deleting the bucket is interrupted while waiting |
| 144 | + * @throws ExecutionException if an exception was thrown while deleting bucket or bucket objects |
| 145 | + */ |
| 146 | + public static Boolean forceDelete(Storage storage, String bucket, long timeout, TimeUnit unit, String userProject) |
| 147 | + throws InterruptedException, ExecutionException { |
124 | 148 | ExecutorService executor = Executors.newSingleThreadExecutor(); |
125 | | - Future<Boolean> future = executor.submit(new DeleteBucketTask(storage, bucket)); |
| 149 | + Future<Boolean> future = executor.submit(new DeleteBucketTask(storage, bucket, userProject)); |
126 | 150 | try { |
127 | 151 | return future.get(timeout, unit); |
128 | 152 | } catch (TimeoutException ex) { |
@@ -210,26 +234,52 @@ private static RetrySettings retrySettings() { |
210 | 234 |
|
211 | 235 | private static class DeleteBucketTask implements Callable<Boolean> { |
212 | 236 |
|
213 | | - private Storage storage; |
214 | | - private String bucket; |
| 237 | + private final Storage storage; |
| 238 | + private final String bucket; |
| 239 | + private final String userProject; |
215 | 240 |
|
216 | 241 | public DeleteBucketTask(Storage storage, String bucket) { |
217 | 242 | this.storage = storage; |
218 | 243 | this.bucket = bucket; |
| 244 | + this.userProject = ""; |
| 245 | + } |
| 246 | + |
| 247 | + public DeleteBucketTask(Storage storage, String bucket, String userProject) { |
| 248 | + this.storage = storage; |
| 249 | + this.bucket = bucket; |
| 250 | + this.userProject = userProject; |
219 | 251 | } |
220 | 252 |
|
221 | 253 | @Override |
222 | 254 | public Boolean call() { |
223 | 255 | while (true) { |
224 | 256 | ArrayList<BlobId> ids = new ArrayList<>(); |
225 | | - for (BlobInfo info : storage.list(bucket, BlobListOption.versions(true)).getValues()) { |
| 257 | + Page<Blob> listedBlobs; |
| 258 | + if (Strings.isNullOrEmpty(userProject)) { |
| 259 | + listedBlobs = storage.list(bucket, BlobListOption.versions(true)); |
| 260 | + } else { |
| 261 | + listedBlobs = storage.list(bucket, BlobListOption.versions(true), BlobListOption.userProject(userProject)); |
| 262 | + } |
| 263 | + for (BlobInfo info : listedBlobs.getValues()) { |
226 | 264 | ids.add(info.getBlobId()); |
227 | 265 | } |
228 | 266 | if (!ids.isEmpty()) { |
229 | | - storage.delete(ids); |
| 267 | + List<Boolean> results = storage.delete(ids); |
| 268 | + if (!Strings.isNullOrEmpty(userProject)) { |
| 269 | + for (int i=0; i<results.size(); i++) { |
| 270 | + if (!results.get(i)) { |
| 271 | + // deleting that blob failed. Let's try in a different way. |
| 272 | + storage.delete(bucket, ids.get(i).getName(), Storage.BlobSourceOption.userProject(userProject)); |
| 273 | + } |
| 274 | + } |
| 275 | + } |
230 | 276 | } |
231 | 277 | try { |
232 | | - storage.delete(bucket); |
| 278 | + if (Strings.isNullOrEmpty(userProject)) { |
| 279 | + storage.delete(bucket); |
| 280 | + } else { |
| 281 | + storage.delete(bucket, Storage.BucketSourceOption.userProject(userProject)); |
| 282 | + } |
233 | 283 | return true; |
234 | 284 | } catch (StorageException e) { |
235 | 285 | if (e.getCode() == 409) { |
|
0 commit comments