diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java index 85bbcfb5f4..1eb9268b1a 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSBlobStore.java @@ -43,15 +43,17 @@ import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.mezeo.pcs2.endpoints.WebDAV; import org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytes; import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; +import org.jclouds.mezeo.pcs2.functions.CreateSubFolderIfNotExistsAndGetResourceId; import org.jclouds.mezeo.pcs2.functions.ContainerAndFileNameToResourceId; import org.jclouds.mezeo.pcs2.functions.ContainerNameToResourceId; import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx; import org.jclouds.mezeo.pcs2.functions.InvalidatePCSKeyCacheAndReturnVoidIf2xx; import org.jclouds.mezeo.pcs2.functions.ReturnFalseIfContainerNotFound; import org.jclouds.mezeo.pcs2.functions.ReturnTrueIfContainerAlreadyExists; -import org.jclouds.mezeo.pcs2.xml.FileListToContainerMetadataListHandler; +import org.jclouds.mezeo.pcs2.xml.CachingFileListToContainerMetadataListHandler; import org.jclouds.mezeo.pcs2.xml.FileListToFileMetadataListHandler; import org.jclouds.mezeo.pcs2.xml.FileMetadataHandler; import org.jclouds.rest.Endpoint; @@ -78,7 +80,7 @@ import org.jclouds.rest.XMLResponseParser; public interface PCSBlobStore extends BlobStore { @GET - @XMLResponseParser(FileListToContainerMetadataListHandler.class) + @XMLResponseParser(CachingFileListToContainerMetadataListHandler.class) @Headers(keys = "X-Cloud-Depth", values = "2") @Path("/contents") @Endpoint(RootContainer.class) @@ -117,8 +119,9 @@ public interface PCSBlobStore extends BlobStore putBlob( - @PathParam("containerResourceId") @ParamParser(ContainerNameToResourceId.class) String containerName, + @PathParam("containerResourceId") + @ParamParser(CreateSubFolderIfNotExistsAndGetResourceId.class) + Future putBlob(String containerName, @EntityParam(PCSFileAsMultipartFormBinder.class) PCSFile object); @DELETE @@ -132,12 +135,10 @@ public interface PCSBlobStore extends BlobStore getBlob(String container, String key); + Future getBlob(@PathParam("container") String container, @PathParam("key") String key); @GET @ExceptionParser(ThrowKeyNotFoundOn404.class) diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java index 75c290c9b0..78dd8f861e 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSConnection.java @@ -77,6 +77,11 @@ public interface PCSConnection { @Endpoint(RootContainer.class) Future createContainer(@EntityParam(CreateContainerBinder.class) String container); + + @POST + @Path("/contents") + Future createContainer(@Endpoint URI parent, @EntityParam(CreateContainerBinder.class) String container); + @DELETE @ExceptionParser(ReturnVoidOnNotFoundOr404.class) Future deleteContainer(@Endpoint URI container); @@ -87,6 +92,12 @@ public interface PCSConnection { @Path("/contents") Future> listFiles(@Endpoint URI container); + @GET + @XMLResponseParser(FileListToContainerMetadataListHandler.class) + @Headers(keys = "X-Cloud-Depth", values = "2") + @Path("/contents") + Future> listContainers(@Endpoint URI container); + @POST @Path("/contents") Future uploadFile(@Endpoint URI container, diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSUtil.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSUtil.java index cf72a55e1e..90924d344a 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSUtil.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/PCSUtil.java @@ -24,18 +24,21 @@ package org.jclouds.mezeo.pcs2; import java.net.URI; +import java.util.concurrent.Future; import javax.ws.rs.GET; import javax.ws.rs.PUT; import org.jclouds.http.filters.BasicAuthentication; -import org.jclouds.http.functions.ReturnFalseOn404; +import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMultiMap; import org.jclouds.rest.Endpoint; import org.jclouds.rest.EntityParam; -import org.jclouds.rest.ExceptionParser; import org.jclouds.rest.RequestFilters; +import org.jclouds.rest.ResponseParser; import org.jclouds.rest.SkipEncoding; +import com.google.common.collect.Multimap; + /** * Provides access to Mezeo PCS v2 via their REST API. *

@@ -50,10 +53,9 @@ import org.jclouds.rest.SkipEncoding; public interface PCSUtil { @PUT - @ExceptionParser(ReturnFalseOn404.class) - boolean put(@Endpoint URI resource, @EntityParam String value); + Future put(@Endpoint URI resource, @EntityParam String value); @GET - String get(@Endpoint URI resource); - + @ResponseParser(AddEntryIntoMultiMap.class) + Future addEntryToMultiMap(Multimap map, String key, @Endpoint URI value); } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/binders/PCSFileAsMultipartFormBinder.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/binders/PCSFileAsMultipartFormBinder.java index e8fccadbdf..10da356211 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/binders/PCSFileAsMultipartFormBinder.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/binders/PCSFileAsMultipartFormBinder.java @@ -35,6 +35,8 @@ import org.jclouds.blobstore.domain.Blob; import org.jclouds.http.HttpRequest; import org.jclouds.http.MultipartForm; import org.jclouds.http.MultipartForm.Part; +import org.jclouds.mezeo.pcs2.functions.Key; +import org.jclouds.mezeo.pcs2.util.PCSUtils; import org.jclouds.rest.binders.EntityBinder; import com.google.common.collect.ImmutableMultimap; @@ -50,12 +52,11 @@ public class PCSFileAsMultipartFormBinder implements EntityBinder { public void addEntityToRequest(Object entity, HttpRequest request) { Blob object = (Blob) entity; - - Multimap partHeaders = ImmutableMultimap.of("Content-Disposition", - String.format("form-data; name=\"%s\"; filename=\"%s\"", object.getKey(), object - .getKey()), HttpHeaders.CONTENT_TYPE, checkNotNull(object.getMetadata() - .getContentType(), "object.metadata.contentType()")); - + Key key = PCSUtils.parseKey(new Key("junk", object.getKey())); + Multimap partHeaders = ImmutableMultimap.of("Content-Disposition", String + .format("form-data; name=\"%s\"; filename=\"%s\"", key.getKey(), key.getKey()), + HttpHeaders.CONTENT_TYPE, checkNotNull(object.getMetadata().getContentType(), + "object.metadata.contentType()")); Object data = checkNotNull(object.getData(), "object.getData()"); Part part; diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java index ea6f023b22..9ccccb0cf7 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/PCSContextModule.java @@ -88,7 +88,7 @@ public class PCSContextModule extends AbstractModule { @Provides @Singleton public ConcurrentMap provideConcurrentMap(FindIdInContainerList finder) { - return new MapMaker().concurrencyLevel(32).expiration(30, TimeUnit.SECONDS).makeComputingMap( + return new MapMaker().expiration(30, TimeUnit.SECONDS).makeComputingMap( finder); } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java index dc9c474743..68cc6940f0 100755 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/config/RestPCSBlobStoreModule.java @@ -52,6 +52,7 @@ import org.jclouds.mezeo.pcs2.endpoints.Recyclebin; import org.jclouds.mezeo.pcs2.endpoints.RootContainer; import org.jclouds.mezeo.pcs2.endpoints.Shares; import org.jclouds.mezeo.pcs2.endpoints.Tags; +import org.jclouds.mezeo.pcs2.endpoints.WebDAV; import org.jclouds.mezeo.pcs2.handlers.PCSClientErrorRetryHandler; import org.jclouds.mezeo.pcs2.reference.PCSConstants; import org.jclouds.rest.RestClientFactory; @@ -99,7 +100,8 @@ public class RestPCSBlobStoreModule extends AbstractModule { @Provides @Singleton - protected BlobStore provideBlobStore(RestClientFactory factory) { + protected BlobStore provideBlobStore( + RestClientFactory factory) { return factory.create(PCSBlobStore.class); } @@ -117,6 +119,13 @@ public class RestPCSBlobStoreModule extends AbstractModule { return URI.create(endpoint); } + @Provides + @Singleton + @WebDAV + protected URI provideWebDAVURI(@Named(PCSConstants.PROPERTY_PCS2_ENDPOINT) String endpoint) { + return URI.create(endpoint.replaceAll("v2", "dav")); + } + @Provides @Singleton @Contacts diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/domain/ContainerMetadata.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/domain/ContainerMetadata.java index 5d09b79ab5..db28892888 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/domain/ContainerMetadata.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/domain/ContainerMetadata.java @@ -35,6 +35,7 @@ import org.joda.time.DateTime; public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMetadata implements PCSObject { private URI url; + private URI parent; private DateTime created; private DateTime lastModified; private DateTime accessed; @@ -46,15 +47,15 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet @Override public String toString() { - return "ContainerMetadata [name=" + name + ", url=" + url + ", accessed=" + accessed - + ", bytes=" + bytes + ", created=" + created + ", isInProject=" + isInProject - + ", isShared=" + isShared + ", lastModified=" + lastModified + ", owner=" + owner - + ", version=" + version + "]"; + return "ContainerMetadata [name=" + name + ", url=" + url + ", parent=" + parent + ", owner=" + + owner + ", version=" + version + ", bytes=" + bytes + ", isInProject=" + + isInProject + ", isShared=" + isShared + ", created=" + created + + ", lastModified=" + lastModified + ", accessed=" + accessed + "]"; } @Override public int hashCode() { - int prime = 31; + final int prime = 31; int result = super.hashCode(); result = prime * result + ((accessed == null) ? 0 : accessed.hashCode()); result = prime * result + (int) (bytes ^ (bytes >>> 32)); @@ -63,6 +64,7 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet result = prime * result + (isShared ? 1231 : 1237); result = prime * result + ((lastModified == null) ? 0 : lastModified.hashCode()); result = prime * result + ((owner == null) ? 0 : owner.hashCode()); + result = prime * result + ((parent == null) ? 0 : parent.hashCode()); result = prime * result + ((url == null) ? 0 : url.hashCode()); result = prime * result + version; return result; @@ -103,6 +105,11 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet return false; } else if (!owner.equals(other.owner)) return false; + if (parent == null) { + if (other.parent != null) + return false; + } else if (!parent.equals(other.parent)) + return false; if (url == null) { if (other.url != null) return false; @@ -112,15 +119,16 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet return false; return true; } - + public ContainerMetadata() { super(); } - - public ContainerMetadata(String name, URI url, DateTime created, DateTime lastModified, - DateTime accessed, String owner, boolean isShared, boolean isInProject, int version, - long bytes) { + + public ContainerMetadata(String name, URI url, URI parent, DateTime created, + DateTime lastModified, DateTime accessed, String owner, boolean isShared, + boolean isInProject, int version, long bytes) { super(name); + this.setParent(parent); this.url = url; this.created = created; this.lastModified = lastModified; @@ -168,4 +176,12 @@ public class ContainerMetadata extends org.jclouds.blobstore.domain.ContainerMet return bytes; } + public void setParent(URI parent) { + this.parent = parent; + } + + public URI getParent() { + return parent; + } + } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/endpoints/WebDAV.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/endpoints/WebDAV.java new file mode 100644 index 0000000000..22ab9dbfb8 --- /dev/null +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/endpoints/WebDAV.java @@ -0,0 +1,44 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.mezeo.pcs2.endpoints; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.inject.Qualifier; + +/** + * Related to a resource of type WebDAV + * + * @author Adrian Cole + * + */ +@Retention(value = RetentionPolicy.RUNTIME) +@Target(value = { ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD }) +@Qualifier +public @interface WebDAV { + +} \ No newline at end of file diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddEntryIntoMultiMap.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddEntryIntoMultiMap.java new file mode 100644 index 0000000000..24f13b2cb4 --- /dev/null +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddEntryIntoMultiMap.java @@ -0,0 +1,77 @@ +package org.jclouds.mezeo.pcs2.functions; + +import static com.google.common.base.Preconditions.checkState; + +import java.lang.reflect.Constructor; + +import javax.inject.Inject; + +import org.jclouds.http.HttpRequest; +import org.jclouds.http.HttpResponse; +import org.jclouds.http.functions.ReturnStringIf200; +import org.jclouds.rest.RestContext; + +import com.google.common.base.Function; +import com.google.common.collect.Multimap; + +/** + * + * @author Adrian Cole + */ +public class AddEntryIntoMultiMap implements Function, RestContext { + ReturnStringIf200 returnIf200; + + @Inject + private AddEntryIntoMultiMap(ReturnStringIf200 returnIf200) { + this.returnIf200 = returnIf200; + } + + private Object[] args; + private HttpRequest request; + + static final Void v; + static { + Constructor cv; + try { + cv = Void.class.getDeclaredConstructor(); + cv.setAccessible(true); + v = cv.newInstance(); + } catch (Exception e) { + throw new Error("Error setting up class", e); + } + } + + @SuppressWarnings("unchecked") + public Void apply(HttpResponse from) + + { + checkState(args != null, "args should be initialized at this point"); + Multimap map = null; + String key = null; + for (Object arg : args) { + if (arg instanceof Multimap) + map = (Multimap) arg; + else if (arg instanceof String) + key = arg.toString(); + } + checkState(map != null, "No Multimap found in args, improper method declarations"); + checkState(key != null, "No String found in args, improper method declarations"); + + map.put(key, returnIf200.apply(from).trim()); + return v; + } + + public Object[] getArgs() { + return args; + } + + public HttpRequest getRequest() { + return request; + } + + public void setContext(HttpRequest request, Object[] args) { + this.request = request; + this.args = args; + } + +} \ No newline at end of file diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytes.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytes.java index 892ec9a2e7..759d033e90 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytes.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytes.java @@ -27,12 +27,18 @@ import static com.google.common.base.Preconditions.checkState; import java.io.IOException; import java.net.URI; +import java.util.Set; import java.util.Map.Entry; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import javax.inject.Inject; +import javax.inject.Named; import javax.ws.rs.core.UriBuilder; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.http.HttpRequest; import org.jclouds.http.HttpResponse; import org.jclouds.http.HttpResponseException; @@ -44,6 +50,7 @@ import org.jclouds.rest.RestContext; import org.jclouds.util.Utils; import com.google.common.base.Function; +import com.google.common.collect.Sets; /** * PCS does not return an eTag header. As such, we'll make one out of the object id. @@ -58,6 +65,13 @@ public class AddMetadataAndParseResourceIdIntoBytes implements Function> puts = Sets.newHashSet(); for (Entry entry : file.getMetadata().getUserMetadata().entries()) { URI key = UriBuilder.fromUri(uri).path(String.format("metadata/%s", entry.getKey())) .build(); - util.put(key, entry.getValue()); + puts.add(util.put(key, entry.getValue())); } + for (Future put : puts) { + try { + put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException("Error putting metadata for file: " + file, e); + } + } + return PCSUtils.getEtag(uri); } catch (IOException e) { throw new HttpResponseException("couldn't parse url from response", null, from, e); diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ContainerAndFileNameToResourceId.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ContainerAndFileNameToResourceId.java index 185dafdc10..85346687e8 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ContainerAndFileNameToResourceId.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/ContainerAndFileNameToResourceId.java @@ -59,9 +59,9 @@ public class ContainerAndFileNameToResourceId implements Function + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.mezeo.pcs2.functions; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; + +import java.net.URI; +import java.util.SortedSet; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.inject.Inject; +import javax.inject.Named; +import javax.inject.Singleton; + +import org.jclouds.blobstore.ContainerNotFoundException; +import org.jclouds.blobstore.domain.Blob; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; +import org.jclouds.mezeo.pcs2.PCSConnection; +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.mezeo.pcs2.util.PCSUtils; +import org.jclouds.util.Utils; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.collect.ComputationException; + +/** + * + * @author Adrian Cole + */ +@Singleton +public class CreateSubFolderIfNotExistsAndGetResourceId implements Function { + + /** + * maximum duration of an blob Request + */ + @Inject(optional = true) + @Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT) + protected long requestTimeoutMilliseconds = 30000; + + private final ConcurrentMap finder; + private final PCSConnection blobStore; + private final URI rootContainer; + + @Inject + public CreateSubFolderIfNotExistsAndGetResourceId(PCSConnection blobStore, + ConcurrentMap finder, @RootContainer URI rootContainer) { + this.finder = finder; + this.blobStore = blobStore; + this.rootContainer = rootContainer; + } + + @SuppressWarnings("unchecked") + public String apply(Object from) { + checkState(checkNotNull(from, "args") instanceof Object[], + "this must be applied to a method!"); + Object[] args = (Object[]) from; + checkArgument(args[0] instanceof String, "arg[0] must be a container name"); + checkArgument(args[1] instanceof Blob, "arg[1] must be a pcsfile"); + Key key = PCSUtils.parseKey(new Key(args[0].toString(), ((Blob) args[1]).getKey())); + try { + return finder.get(key.getContainer()); + } catch (ComputationException e) { + if (e.getCause() instanceof ContainerNotFoundException) { + String[] containerTree = key.getContainer().split("/"); + + SortedSet response = blobStore.listContainers(); + URI containerUri; + try { + containerUri = urlForNameInListOrCreate(rootContainer, containerTree[0], + response); + } catch (Exception e1) { + + Utils. rethrowIfRuntimeOrSameType(e1); + throw new BlobRuntimeException("error creating container at: " + containerTree[0], e1); + } + if (containerTree.length != 1) { + for (int i = 1; i < containerTree.length; i++) { + try { + response = blobStore.listContainers(containerUri).get( + requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + containerUri = urlForNameInListOrCreate(containerUri, containerTree[i], + response); + } catch (Exception e2) { + Utils. rethrowIfRuntimeOrSameType(e2); + throw new BlobRuntimeException("error listing container at: " + containerUri, + e2); + } + } + } + return PCSUtils.getContainerId(containerUri); + } + + Utils. rethrowIfRuntimeOrSameType(e); + throw e; + } + } + + @VisibleForTesting + URI urlForNameInListOrCreate(URI parent, String toFind, + SortedSet containerMetadataList) throws InterruptedException, + ExecutionException, TimeoutException { + for (ContainerMetadata data : containerMetadataList) { + if (toFind.equals(data.getName())) { + return data.getUrl(); + } + } + return blobStore.createContainer(parent, toFind).get(requestTimeoutMilliseconds, + TimeUnit.MILLISECONDS); + } + +} \ No newline at end of file diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java index eadfdec5b1..0d6060b4bf 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerList.java @@ -23,38 +23,76 @@ */ package org.jclouds.mezeo.pcs2.functions; +import java.net.URI; import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import javax.inject.Inject; +import javax.inject.Named; import org.jclouds.blobstore.ContainerNotFoundException; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.mezeo.pcs2.util.PCSUtils; +import org.jclouds.util.Utils; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; public class FindIdInContainerList implements Function { - private PCSConnection connection; + private final PCSConnection connection; + private final URI rootContainerUri; + + /** + * maximum duration of an blob Request + */ + @Inject(optional = true) + @Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT) + protected long requestTimeoutMilliseconds = 30000; @Inject - public FindIdInContainerList(PCSConnection connection) { + public FindIdInContainerList(PCSConnection connection, @RootContainer URI rootContainerURI) { this.connection = connection; + this.rootContainerUri = rootContainerURI; } public String apply(String key) { - SortedSet response = connection.listContainers(); - return idForNameInListOrException(key, response); + String[] containerTree = key.split("/"); + URI containerUri = rootContainerUri; + try { + containerUri = urlForNameInListOrException(containerTree[0], rootContainerUri, connection + .listContainers(rootContainerUri).get(requestTimeoutMilliseconds, + TimeUnit.MILLISECONDS)); + if (containerTree.length != 1) { + for (int i = 1; i < containerTree.length; i++) { + containerUri = urlForNameInListOrException(containerTree[i], containerUri, + listContainers(containerUri)); + } + } + return PCSUtils.getContainerId(containerUri); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException("error listing container at: " + containerUri, e); + } + } + + private SortedSet listContainers(URI containerUri) + throws InterruptedException, ExecutionException, TimeoutException { + return connection.listContainers(containerUri).get(requestTimeoutMilliseconds, + TimeUnit.MILLISECONDS); } @VisibleForTesting - String idForNameInListOrException(String toFind, + URI urlForNameInListOrException(String toFind, URI parent, SortedSet containerMetadataList) { for (ContainerMetadata data : containerMetadataList) { - if (toFind.equals(data.getName())) { - String path = data.getUrl().getPath(); - int indexAfterContainersSlash = path.indexOf("containers/") + "containers/".length(); - return path.substring(indexAfterContainersSlash); + if (toFind.equals(data.getName()) && parent.equals(data.getParent())) { + return data.getUrl(); } } throw new ContainerNotFoundException(toFind); diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java index da579439fc..d698c474ef 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/functions/FindIdInFileList.java @@ -36,6 +36,7 @@ import org.jclouds.http.HttpException; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.jclouds.mezeo.pcs2.util.PCSUtils; import org.jclouds.util.Utils; import com.google.common.annotations.VisibleForTesting; @@ -50,6 +51,7 @@ public class FindIdInFileList implements Function { } public String apply(Key key) { + key = PCSUtils.parseKey(key); SortedSet response; try { response = connection.listBlobs(key.getContainer()).get(10, TimeUnit.SECONDS); diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/util/PCSUtils.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/util/PCSUtils.java index f16e1e6aee..b925b00846 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/util/PCSUtils.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/util/PCSUtils.java @@ -26,6 +26,7 @@ package org.jclouds.mezeo.pcs2.util; import java.net.URI; import org.jclouds.http.HttpUtils; +import org.jclouds.mezeo.pcs2.functions.Key; /** * Utilities for PCS connections. @@ -44,4 +45,20 @@ public class PCSUtils { return eTag; } + public static Key parseKey(Key key) { + + if (key.getKey().indexOf('/') != -1) { + String container = key.getContainer() + '/' + + key.getKey().substring(0, key.getKey().lastIndexOf('/')); + String newKey = key.getKey().substring(key.getKey().lastIndexOf('/')+1); + key = new Key(container.replaceAll("//", "/"), newKey); + } + return key; + } + + public static String getContainerId(URI url) { + String path = url.getPath(); + int indexAfterContainersSlash = path.indexOf("containers/") + "containers/".length(); + return path.substring(indexAfterContainersSlash); + } } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/CachingFileListToContainerMetadataListHandler.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/CachingFileListToContainerMetadataListHandler.java new file mode 100644 index 0000000000..8e6535de3b --- /dev/null +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/CachingFileListToContainerMetadataListHandler.java @@ -0,0 +1,57 @@ +/** + * + * Copyright (C) 2009 Global Cloud Specialists, Inc. + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ +package org.jclouds.mezeo.pcs2.xml; + +import java.util.SortedSet; +import java.util.concurrent.ConcurrentMap; + +import javax.inject.Inject; + +import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.util.PCSUtils; +import org.jclouds.util.DateService; + +/** + * @author Adrian Cole + */ +public class CachingFileListToContainerMetadataListHandler extends + FileListToContainerMetadataListHandler { + private final ConcurrentMap cache; + + @Inject + public CachingFileListToContainerMetadataListHandler(ConcurrentMap cache, + DateService dateParser) { + super(dateParser); + this.cache = cache; + } + + @Override + public SortedSet getResult() { + SortedSet result = super.getResult(); + for (ContainerMetadata md : result) { + cache.put(md.getName(), PCSUtils.getContainerId(md.getUrl())); + } + return result; + } +} diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandler.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandler.java index e95d466794..d062289e24 100755 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandler.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandler.java @@ -26,9 +26,11 @@ package org.jclouds.mezeo.pcs2.xml; import java.net.URI; import java.util.SortedSet; +import javax.annotation.Resource; import javax.inject.Inject; import org.jclouds.http.functions.ParseSax; +import org.jclouds.logging.Logger; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.util.DateService; import org.joda.time.DateTime; @@ -43,6 +45,20 @@ import com.google.common.collect.Sets; public class FileListToContainerMetadataListHandler extends ParseSax.HandlerWithResult> { + @Override + public String toString() { + return "FileListToContainerMetadataListHandler [containerMetadata=" + containerMetadata + + ", currentAccessed=" + currentAccessed + ", currentBytes=" + currentBytes + + ", currentCreated=" + currentCreated + ", currentInproject=" + currentInproject + + ", currentModified=" + currentModified + ", currentName=" + currentName + + ", currentOwner=" + currentOwner + ", currentShared=" + currentShared + + ", currentText=" + currentText + ", currentUrl=" + currentUrl + + ", currentVersion=" + currentVersion + ", dateParser=" + dateParser + "]"; + } + + @Resource + protected Logger logger = Logger.NULL; + private SortedSet containerMetadata = Sets.newTreeSet(); private URI currentUrl; private String currentName; @@ -59,6 +75,11 @@ public class FileListToContainerMetadataListHandler extends private final DateService dateParser; + boolean inContainer = false; + boolean ignore = false; + + private URI currentParent; + @Inject public FileListToContainerMetadataListHandler(DateService dateParser) { this.dateParser = dateParser; @@ -72,16 +93,54 @@ public class FileListToContainerMetadataListHandler extends public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("container")) { + if (inContainer) { + ignore = true; + return; + } + inContainer = true; int index = attributes.getIndex("xlink:href"); if (index != -1) { currentUrl = URI.create(attributes.getValue(index)); } + } else if (qName.equals("parent") && !ignore) { + int index = attributes.getIndex("xlink:href"); + if (index != -1) { + currentParent = URI.create(attributes.getValue(index)); + } } } @Override public void endElement(String uri, String name, String qName) { - if (qName.equals("name")) { + if (ignore) { + currentText = new StringBuilder(); + if (qName.equals("container")) { + ignore = false; + } + return; + } + if (qName.equals("container")) { + inContainer = false; + try { + containerMetadata.add(new ContainerMetadata(currentName, currentUrl, currentParent, + currentCreated, currentModified, currentAccessed, currentOwner, currentShared, + currentInproject, currentVersion, currentBytes)); + } catch (RuntimeException e) { + logger.error(e, "error creating object! current state %s", this); + throw e; + } + currentUrl = null; + currentParent = null; + currentName = null; + currentCreated = null; + currentInproject = false; + currentModified = null; + currentOwner = null; + currentVersion = 0; + currentShared = false; + currentAccessed = null; + currentBytes = 0; + } else if (qName.equals("name")) { currentName = currentText.toString().trim(); } else if (qName.equals("created")) { currentCreated = dateParser.fromSeconds(Long.parseLong(currentText.toString().trim())); @@ -99,21 +158,6 @@ public class FileListToContainerMetadataListHandler extends currentAccessed = dateParser.fromSeconds(Long.parseLong(currentText.toString().trim())); } else if (qName.equals("bytes")) { currentBytes = Long.parseLong(currentText.toString().trim()); - } else if (qName.equals("container")) { - containerMetadata.add(new ContainerMetadata(currentName, currentUrl, currentCreated, - currentModified, currentAccessed, currentOwner, currentShared, currentInproject, - currentVersion, currentBytes)); - currentUrl = null; - currentName = null; - currentCreated = null; - currentInproject = false; - currentModified = null; - currentOwner = null; - currentVersion = 0; - currentShared = false; - currentAccessed = null; - currentBytes = 0; - } currentText = new StringBuilder(); } diff --git a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandler.java b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandler.java index 066129b50f..f4f83cf44c 100644 --- a/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandler.java +++ b/mezeo/pcs2/core/src/main/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandler.java @@ -24,17 +24,25 @@ package org.jclouds.mezeo.pcs2.xml; import java.net.URI; +import java.util.Set; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; +import javax.inject.Named; +import org.jclouds.blobstore.internal.BlobRuntimeException; +import org.jclouds.blobstore.reference.BlobStoreConstants; import org.jclouds.mezeo.pcs2.PCSUtil; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.util.DateService; +import org.jclouds.util.Utils; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; /** * @author Adrian Cole @@ -43,6 +51,13 @@ public class FileMetadataHandler extends BaseFileMetadataHandler { private final PCSUtil util; private FileMetadata fileMetadata = null; protected Multimap userMetadata = HashMultimap.create(); + Set> puts = Sets.newHashSet(); + /** + * maximum duration of an blob Request + */ + @Inject(optional = true) + @Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT) + protected long requestTimeoutMilliseconds = 30000; @Inject public FileMetadataHandler(PCSUtil util, DateService dateParser) { @@ -51,6 +66,14 @@ public class FileMetadataHandler extends BaseFileMetadataHandler { } public FileMetadata getResult() { + for (Future put : puts) { + try { + put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS); + } catch (Exception e) { + Utils. rethrowIfRuntimeOrSameType(e); + throw new BlobRuntimeException("Error getting metadata", e); + } + } return fileMetadata; } @@ -66,8 +89,8 @@ public class FileMetadataHandler extends BaseFileMetadataHandler { int index = attributes.getIndex("xlink:href"); if (index != -1) { String key = attributes.getValue(index).replaceAll(".*/metadata/", ""); - String value = util.get(URI.create(attributes.getValue(index))).trim(); - userMetadata.put(key.toLowerCase(), value); + puts.add(util.addEntryToMultiMap(userMetadata, key.toLowerCase(), URI.create(attributes + .getValue(index)))); } } } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java index 2a8c7151a1..ddf2cf9a7a 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSBlobStoreTest.java @@ -51,15 +51,16 @@ import org.jclouds.http.HttpRequest; import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule; import org.jclouds.http.filters.BasicAuthentication; import org.jclouds.http.functions.ParseSax; -import org.jclouds.http.functions.ReturnFalseOn404; -import org.jclouds.http.functions.ReturnStringIf200; import org.jclouds.http.functions.ReturnTrueIf2xx; +import org.jclouds.http.functions.ReturnVoidIf2xx; import org.jclouds.http.options.GetOptions; import org.jclouds.mezeo.pcs2.binders.PCSFileAsMultipartFormBinderTest; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.mezeo.pcs2.endpoints.RootContainer; +import org.jclouds.mezeo.pcs2.endpoints.WebDAV; +import org.jclouds.mezeo.pcs2.functions.AddEntryIntoMultiMap; import org.jclouds.mezeo.pcs2.functions.AddMetadataAndParseResourceIdIntoBytes; import org.jclouds.mezeo.pcs2.functions.AssembleBlobFromContentAndMetadataCache; import org.jclouds.mezeo.pcs2.functions.InvalidateContainerNameCacheAndReturnTrueIf2xx; @@ -72,7 +73,9 @@ import org.jclouds.util.Utils; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Multimap; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @@ -124,6 +127,8 @@ public class PCSBlobStoreTest { return ImmutableSortedSet .of(new ContainerMetadata( "mycontainer", + URI + .create("https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B"), URI .create("https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B"), dateService.fromSeconds(1254008225), @@ -180,6 +185,14 @@ public class PCSBlobStoreTest { return null; } + public Future> listContainers(URI container) { + throw new UnsupportedOperationException(); + } + + public Future createContainer(URI parent, String container) { + throw new UnsupportedOperationException(); + } + } public void testListContainers() throws SecurityException, NoSuchMethodException { @@ -311,8 +324,7 @@ public class PCSBlobStoreTest { HttpRequest httpMethod = processor.createRequest(method, new Object[] { "mycontainer", "testfile.txt" }); assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); - assertEquals(httpMethod.getEndpoint().getPath(), - "/files/9E4C5AFA-A98B-11DE-8B4C-C3884B4A2DA3/content"); + assertEquals(httpMethod.getEndpoint().getPath(), "/webdav/mycontainer/testfile.txt"); assertEquals(httpMethod.getEndpoint().getQuery(), null); assertEquals(httpMethod.getMethod(), HttpMethod.GET); assertEquals(httpMethod.getHeaders().size(), 0); @@ -372,24 +384,25 @@ public class PCSBlobStoreTest { assertEquals(httpMethod.getHeaders().get(HttpHeaders.CONTENT_TYPE), Collections .singletonList("application/unknown")); assertEquals("bar", httpMethod.getEntity()); - assertEquals(processor.createExceptionParserOrNullIfNotFound(method).getClass(), - ReturnFalseOn404.class); + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnTrueIf2xx.class); + ReturnVoidIf2xx.class); } - public void testGetMetadata() throws SecurityException, NoSuchMethodException { - Method method = PCSUtil.class.getMethod("get", URI.class); + public void testAddEntryToMultiMap() throws SecurityException, NoSuchMethodException { + Method method = PCSUtil.class.getMethod("addEntryToMultiMap", Multimap.class, String.class, + URI.class); - HttpRequest httpMethod = utilProcessor.createRequest(method, new Object[] { - URI.create("http://localhost/pow"), "foo" }); + HttpRequest httpMethod = utilProcessor + .createRequest(method, new Object[] { ImmutableMultimap.of("key", "value"), + "newkey", URI.create("http://localhost/pow") }); assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); assertEquals(httpMethod.getEndpoint().getPath(), "/pow"); assertEquals(httpMethod.getMethod(), HttpMethod.GET); assertEquals(httpMethod.getHeaders().size(), 0); assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), - ReturnStringIf200.class); + AddEntryIntoMultiMap.class); } JaxrsAnnotationProcessor processor; @@ -406,6 +419,9 @@ public class PCSBlobStoreTest { URI.create("http://localhost:8080")); bind(URI.class).annotatedWith(RootContainer.class).toInstance( URI.create("http://localhost:8080/root")); + bind(URI.class).annotatedWith(WebDAV.class).toInstance( + URI.create("http://localhost:8080/webdav")); + bind(PCSConnection.class).to(StubPCSConnection.class).asEagerSingleton(); } @SuppressWarnings("unused") @@ -414,12 +430,13 @@ public class PCSBlobStoreTest { public PCSUtil getPCSUtil() { return new PCSUtil() { - public String get(URI resource) { + public Future put(URI resource, String value) { return null; } - public boolean put(URI resource, String value) { - return true; + public Future addEntryToMultiMap(Multimap map, + String key, URI value) { + return null; } }; diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java index 9f8798cd6a..952dc79e12 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionLiveTest.java @@ -102,14 +102,14 @@ public class PCSConnectionLiveTest { public void testObjectOperations() throws Exception { String containerName = containerPrefix + ".testObjectOperations"; String data = "Here is my data"; - + URI container = connection.createContainer(containerName).get(10, TimeUnit.SECONDS); // Test PUT with string data, ETag hash, and a piece of metadata PCSFile object = new PCSFile("object"); object.setData(data); object.setContentLength(data.length()); - URI objectURI = connection.uploadFile(container, object).get(10, TimeUnit.SECONDS); + URI objectURI = connection.uploadFile(container, object).get(30, TimeUnit.SECONDS); try { connection.downloadFile(UriBuilder.fromUri(objectURI).path("sad").build()).get(10, diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java index c369989187..4ed95726d3 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/PCSConnectionTest.java @@ -33,6 +33,7 @@ import java.net.URI; import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Future; import javax.inject.Singleton; import javax.ws.rs.HttpMethod; @@ -58,6 +59,7 @@ import org.jclouds.util.Utils; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.google.common.collect.Multimap; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @@ -141,6 +143,23 @@ public class PCSConnectionTest { assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); } + public void testListContainersURI() throws SecurityException, NoSuchMethodException { + Method method = PCSConnection.class.getMethod("listContainers", URI.class); + + HttpRequest httpMethod = processor.createRequest(method, new Object[] { URI + .create("http://localhost/mycontainer") }); + assertEquals(httpMethod.getEndpoint().getHost(), "localhost"); + assertEquals(httpMethod.getEndpoint().getPath(), "/mycontainer/contents"); + assertEquals(httpMethod.getEndpoint().getQuery(), null); + assertEquals(httpMethod.getMethod(), HttpMethod.GET); + assertEquals(httpMethod.getHeaders().size(), 1); + assertEquals(httpMethod.getHeaders().get("X-Cloud-Depth"), Collections.singletonList("2")); + assertEquals(processor.createResponseParser(method, httpMethod, null).getClass(), + ParseSax.class); + // TODO check generic type of response parser + assertEquals(processor.createExceptionParserOrNullIfNotFound(method), null); + } + public void testUploadFile() throws SecurityException, NoSuchMethodException, IOException { Method method = PCSConnection.class.getMethod("uploadFile", URI.class, PCSFile.class); @@ -214,12 +233,13 @@ public class PCSConnectionTest { public PCSUtil getPCSUtil() { return new PCSUtil() { - public String get(URI resource) { + public Future put(URI resource, String value) { return null; } - public boolean put(URI resource, String value) { - return true; + public Future addEntryToMultiMap(Multimap map, + String key, URI value) { + return null; } }; diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java index 9c64f3f844..b68f6f0ea6 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/config/StubPCSBlobStoreModule.java @@ -35,6 +35,7 @@ import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; import org.jclouds.mezeo.pcs2.domain.FileMetadata; import org.jclouds.mezeo.pcs2.domain.PCSFile; +import org.jclouds.mezeo.pcs2.endpoints.RootContainer; import org.jclouds.mezeo.pcs2.internal.StubPCSConnection; import com.google.inject.AbstractModule; @@ -58,6 +59,7 @@ public class StubPCSBlobStoreModule extends AbstractModule { }).asEagerSingleton(); bind(PCSConnection.class).to(StubPCSConnection.class).asEagerSingleton(); bind(URI.class).annotatedWith(PCS.class).toInstance(URI.create("https://localhost/pcsblob")); + bind(URI.class).annotatedWith(RootContainer.class).toInstance( + URI.create("http://localhost/root")); } - } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytesTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytesTest.java index 97bb97ea1b..52a0e9658e 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytesTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/AddMetadataAndParseResourceIdIntoBytesTest.java @@ -31,6 +31,7 @@ import static org.easymock.classextension.EasyMock.verify; import static org.testng.Assert.assertEquals; import java.net.URI; +import java.util.concurrent.Future; import javax.ws.rs.ext.RuntimeDelegate; @@ -55,20 +56,22 @@ public class AddMetadataAndParseResourceIdIntoBytesTest { } HttpResponse response = new HttpResponse(); + @SuppressWarnings("unchecked") PCSUtil createPCSUtil() { PCSUtil connection = createMock(PCSUtil.class); + final Future voidF = createMock(Future.class); expect( connection .put( eq(URI .create("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B/metadata/foo")), - eq("bar"))).andReturn(true); + eq("bar"))).andReturn(voidF); expect( connection .put( eq(URI .create("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B/metadata/biz")), - eq("baz"))).andReturn(true); + eq("baz"))).andReturn(voidF); replay(connection); return connection; } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java index c22d0a2fb3..3cf9126c5a 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/functions/FindIdInContainerListTest.java @@ -23,14 +23,21 @@ */ package org.jclouds.mezeo.pcs2.functions; -import static org.easymock.classextension.EasyMock.createNiceMock; import static org.testng.Assert.assertEquals; +import java.io.InputStream; import java.net.URI; +import java.util.SortedSet; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.jclouds.blobstore.ContainerNotFoundException; import org.jclouds.mezeo.pcs2.PCSConnection; import org.jclouds.mezeo.pcs2.domain.ContainerMetadata; +import org.jclouds.mezeo.pcs2.domain.FileMetadata; +import org.jclouds.mezeo.pcs2.domain.PCSFile; import org.jclouds.util.DateService; import org.testng.annotations.Test; @@ -44,25 +51,104 @@ import com.google.common.collect.ImmutableSortedSet; @Test(groups = "unit", testName = "pcs2.FindIdInContainerList") public class FindIdInContainerListTest { private DateService dateService = new DateService(); + private final PCSConnection connection = new PCSConnection() { - private final ImmutableSortedSet OF = ImmutableSortedSet - .of(new ContainerMetadata( - "test1", - URI - .create("https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B"), - dateService.fromSeconds(1254008225), dateService.fromSeconds(1254008226), - dateService.fromSeconds(1254008227), "adrian@jclouds.org", true, false, 1, - 1024)); + public Future createContainer(String container) { + return null; + } + + public Future createContainer(URI parent, String container) { + return null; + } + + public Future deleteContainer(URI container) { + return null; + } + + public Future deleteFile(URI file) { + return null; + } + + public Future downloadFile(URI file) { + return null; + } + + public SortedSet listContainers() { + return null; + } + + public Future> listContainers(URI container) { + if (container.equals(URI.create("https://localhost/root"))) { + return createFuture(ImmutableSortedSet.of(new ContainerMetadata("apple", URI + .create("https://localhost/containers/rootapple"), URI + .create("https://localhost/root"), dateService.fromSeconds(1254008225), + dateService.fromSeconds(1254008226), dateService.fromSeconds(1254008227), + "adrian@jclouds.org", true, false, 1, 1024))); + } else if (container.equals(URI.create("https://localhost/containers/rootapple"))) { + return createFuture(ImmutableSortedSet.of(new ContainerMetadata("apple", URI + .create("https://localhost/containers/appleapple"), URI + .create("https://localhost/containers/rootapple"), dateService + .fromSeconds(1254008225), dateService.fromSeconds(1254008226), dateService + .fromSeconds(1254008227), "adrian@jclouds.org", true, false, 1, 1024))); + } else { + return createFuture(ImmutableSortedSet. of()); + } + } + + Future createFuture(final T data) { + return new Future() { + + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + public T get() throws InterruptedException, ExecutionException { + return data; + } + + public T get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { + return data; + } + + public boolean isCancelled() { + return false; + } + + public boolean isDone() { + return false; + } + + }; + } + + public Future> listFiles(URI container) { + return null; + } + + public Future uploadFile(URI container, PCSFile object) { + return null; + } + + }; + FindIdInContainerList binder = new FindIdInContainerList(connection, URI + .create("https://localhost/root")); @Test(expectedExceptions = ContainerNotFoundException.class) public void testBad() { - FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSConnection.class)); - binder.idForNameInListOrException("hello", OF); + binder.apply("hello"); + } + + @Test(expectedExceptions = ContainerNotFoundException.class) + public void testBad2() { + binder.apply("apple/hello"); } public void testGood() { - FindIdInContainerList binder = new FindIdInContainerList(createNiceMock(PCSConnection.class)); - assertEquals(binder.idForNameInListOrException("test1", OF), - "7F143552-AAF5-11DE-BBB0-0BC388ED913B"); + assertEquals(binder.apply("apple"), "rootapple"); + } + + public void testSub() { + assertEquals(binder.apply("apple/apple"), "appleapple"); } } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java index 0b710dd8cb..3f8510e7e6 100755 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSBlobStoreIntegrationTest.java @@ -50,7 +50,7 @@ public class PCSBlobStoreIntegrationTest extends assertEquals(metadata.getSize(), TEST_STRING.length()); assertEquals(metadata.getUserMetadata().get("adrian"), Collections .singletonList("powderpuff")); - // Content-MD5 not supported http://code.google.com/p/jclouds/issues/detail?id=105 + // Issue 105 // assertEquals(metadata.getContentMD5(), HttpUtils.md5(TEST_STRING.getBytes())); } @@ -58,62 +58,64 @@ public class PCSBlobStoreIntegrationTest extends @Test(enabled = false) public void testGetIfMatch() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // etag not supported http://code.google.com/p/jclouds/issues/detail?id=105 + // Issue 105 } @Override @Test(enabled = false) public void testGetIfModifiedSince() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported + // Issue 105 } @Override @Test(enabled = false) public void testGetIfNoneMatch() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // etag not supported http://code.google.com/p/jclouds/issues/detail?id=105 + // Issue 105 } @Override @Test(enabled = false) public void testGetIfUnmodifiedSince() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported + // Issue 105 } @Override @Test(enabled = false) public void testGetRange() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported http://code.google.com/p/jclouds/issues/detail?id=106 + // Issue 106, Mezeo defect 2644 } @Override @Test(enabled = false) public void testGetStartAt() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported http://code.google.com/p/jclouds/issues/detail?id=106 + // Issue 106, Mezeo defect 2644 } @Override @Test(enabled = false) public void testGetTail() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported http://code.google.com/p/jclouds/issues/detail?id=106 + // Issue 106, Mezeo defect 2644 } @Override @Test(enabled = false) public void testGetTwoRanges() throws InterruptedException, ExecutionException, TimeoutException, IOException { - // unsupported http://code.google.com/p/jclouds/issues/detail?id=106 + // Issue 106, Mezeo defect 2644 } @DataProvider(name = "delete") @Override - public Object[][] createData() { - return new Object[][] { { "normal" } }; + // normal constraints: The characters \ / : * ? " < > and | cannot be used in names. + public Object[][] createData() {// unicode Issue 110, Mezeo defect: 2675 + // slashes are supported, as they are a part of filepaths which we use nested container to + // create. + return new Object[][] { { "normal" }, { "sp ace" }, { "path/foo" } }; } - } \ No newline at end of file diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java index b90c6cef63..7006218c23 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/integration/PCSTestInitializer.java @@ -48,7 +48,7 @@ public class PCSTestInitializer extends @Override protected PCSContext createLiveContext(Module configurationModule, String url, String app, String account, String key) { - return new PCSContextBuilder(URI.create(url), account, key).relaxSSLHostname().withModules( + return new PCSContextBuilder(URI.create(url), account, key).relaxSSLHostname().withSaxDebug().withModules( configurationModule, new Log4JLoggingModule()).buildContext(); } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java index 387312b3af..6ca6b64bc5 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/internal/StubPCSConnection.java @@ -41,31 +41,48 @@ import org.jclouds.mezeo.pcs2.domain.PCSFile; */ public class StubPCSConnection implements PCSConnection { + public Future createContainer(String container) { + throw new UnsupportedOperationException(); + + } + + public Future createContainer(URI parent, String container) { + throw new UnsupportedOperationException(); + + } + public Future deleteContainer(URI container) { throw new UnsupportedOperationException(); + } public Future deleteFile(URI file) { throw new UnsupportedOperationException(); + } public Future downloadFile(URI file) { throw new UnsupportedOperationException(); - } - public Future> listFiles(URI container) { - throw new UnsupportedOperationException(); - } - - public Future uploadFile(URI container, PCSFile object) { - throw new UnsupportedOperationException(); - } - - public Future createContainer(String container) { - throw new UnsupportedOperationException(); } public SortedSet listContainers() { throw new UnsupportedOperationException(); + + } + + public Future> listContainers(URI container) { + throw new UnsupportedOperationException(); + + } + + public Future> listFiles(URI container) { + throw new UnsupportedOperationException(); + + } + + public Future uploadFile(URI container, PCSFile object) { + throw new UnsupportedOperationException(); + } } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/util/PCSUtilsTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/util/PCSUtilsTest.java index c0bf9f2056..5afb4ce9cb 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/util/PCSUtilsTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/util/PCSUtilsTest.java @@ -28,6 +28,7 @@ import static org.testng.Assert.assertEquals; import java.net.URI; import org.jclouds.http.HttpUtils; +import org.jclouds.mezeo.pcs2.functions.Key; import org.testng.annotations.Test; /** @@ -44,4 +45,17 @@ public class PCSUtilsTest { .create("http://localhost/contents/7F143552-AAF5-11DE-BBB0-0BC388ED913B")); assertEquals(eTag, expected); } + + public void testParseKey() { + Key key = PCSUtils.parseKey(new Key("container", "key")); + assertEquals(key.getContainer(), "container"); + assertEquals(key.getKey(), "key"); + key = PCSUtils.parseKey(new Key("container", "container/key")); + assertEquals(key.getContainer(), "container/container"); + assertEquals(key.getKey(), "key"); + key = PCSUtils.parseKey(new Key("container", "/container/key")); + assertEquals(key.getContainer(), "container/container"); + assertEquals(key.getKey(), "key"); + + } } diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandlerTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandlerTest.java index 2cc589493d..27ff0edde6 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandlerTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileListToContainerMetadataListHandlerTest.java @@ -63,6 +63,8 @@ public class FileListToContainerMetadataListHandlerTest extends BaseHandlerTest "test1", URI .create("https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B"), + URI + .create("https://pcsbeta.mezeo.net/v2/containers/0B5C8F50-8E72-11DE-A1D4-D73479DA6257"), dateService.fromSeconds(1254008225), dateService.fromSeconds(1254008226), dateService.fromSeconds(1254008227), "adrian@jclouds.org", true, false, 1, 1024)); diff --git a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandlerTest.java b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandlerTest.java index 2176d94213..10a3c38dff 100644 --- a/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandlerTest.java +++ b/mezeo/pcs2/core/src/test/java/org/jclouds/mezeo/pcs2/xml/FileMetadataHandlerTest.java @@ -23,14 +23,12 @@ */ package org.jclouds.mezeo.pcs2.xml; -import static org.easymock.EasyMock.expect; -import static org.easymock.EasyMock.replay; -import static org.easymock.EasyMock.verify; import static org.easymock.classextension.EasyMock.createMock; import static org.testng.Assert.assertEquals; import java.io.InputStream; import java.net.URI; +import java.util.concurrent.Future; import javax.inject.Singleton; import javax.ws.rs.core.MediaType; @@ -42,6 +40,7 @@ import org.jclouds.util.DateService; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; +import com.google.common.collect.Multimap; import com.google.inject.AbstractModule; import com.google.inject.Provides; @@ -54,7 +53,6 @@ import com.google.inject.Provides; public class FileMetadataHandlerTest extends BaseHandlerTest { private DateService dateService; - private PCSUtil util; @BeforeTest @Override @@ -66,19 +64,23 @@ public class FileMetadataHandlerTest extends BaseHandlerTest { protected void configure() { } - @SuppressWarnings("unused") + @SuppressWarnings( { "unused", "unchecked" }) @Singleton @Provides PCSUtil provideUtil() { - util = createMock(PCSUtil.class); - // Note that we should convert uppercase to lowercase! - expect( - util - .get(URI - .create("https://pcsbeta.mezeo.net/v2/files/9E4C5AFA-A98B-11DE-8B4C-C3884B4A2DA3/metadata/Foo"))) - .andReturn("bar"); - replay(util); - return util; + final Future voidF = createMock(Future.class); + return new PCSUtil() { + public Future addEntryToMultiMap(Multimap map, String key, + URI value) { + map.put(key.toLowerCase(), "bar"); + return voidF; + } + + public Future put(URI resource, String value) { + return null; + } + + }; } }); @@ -96,10 +98,8 @@ public class FileMetadataHandlerTest extends BaseHandlerTest { MediaType.TEXT_PLAIN, false); // Note that we should convert uppercase to lowercase, since most clouds do anyway expects.getUserMetadata().put("foo", "bar"); - FileMetadata result = (FileMetadata) factory.create( injector.getInstance(FileMetadataHandler.class)).parse(is); - verify(util); assertEquals(result, expects); } diff --git a/mezeo/pcs2/core/src/test/resources/test_file_list.xml b/mezeo/pcs2/core/src/test/resources/test_file_list.xml index ca3cbc0698..8ad5e14fc0 100644 --- a/mezeo/pcs2/core/src/test/resources/test_file_list.xml +++ b/mezeo/pcs2/core/src/test/resources/test_file_list.xml @@ -15,7 +15,11 @@ 1024 + xlink:type="simple" count="0" start="0" total="0"> + +