Issue 97: webdav compatible mapping. when slashes are present in blob names, create subfolders accordingly

git-svn-id: http://jclouds.googlecode.com/svn/trunk@1949 3d8758e0-26b5-11de-8745-db77d3ebf521
This commit is contained in:
adrian.f.cole 2009-10-08 00:08:03 +00:00
parent c228add243
commit 22e949343e
31 changed files with 814 additions and 140 deletions

View File

@ -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<ContainerMetadata, FileMetadata, PCSFile> {
@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<ContainerMetadata, FileMetadata,
@Path("/containers/{containerResourceId}/contents")
@Endpoint(PCS.class)
@ResponseParser(AddMetadataAndParseResourceIdIntoBytes.class)
Future<byte[]> putBlob(
@PathParam("containerResourceId") @ParamParser(ContainerNameToResourceId.class) String containerName,
@PathParam("containerResourceId")
@ParamParser(CreateSubFolderIfNotExistsAndGetResourceId.class)
Future<byte[]> putBlob(String containerName,
@EntityParam(PCSFileAsMultipartFormBinder.class) PCSFile object);
@DELETE
@ -132,12 +135,10 @@ public interface PCSBlobStore extends BlobStore<ContainerMetadata, FileMetadata,
@GET
@ExceptionParser(ThrowKeyNotFoundOn404.class)
@Path("/files/{resourceId}/content")
@PathParam("resourceId")
@Endpoint(PCS.class)
@ParamParser(ContainerAndFileNameToResourceId.class)
@Path("{container}/{key}")
@Endpoint(WebDAV.class)
@ResponseParser(AssembleBlobFromContentAndMetadataCache.class)
Future<PCSFile> getBlob(String container, String key);
Future<PCSFile> getBlob(@PathParam("container") String container, @PathParam("key") String key);
@GET
@ExceptionParser(ThrowKeyNotFoundOn404.class)

View File

@ -77,6 +77,11 @@ public interface PCSConnection {
@Endpoint(RootContainer.class)
Future<URI> createContainer(@EntityParam(CreateContainerBinder.class) String container);
@POST
@Path("/contents")
Future<URI> createContainer(@Endpoint URI parent, @EntityParam(CreateContainerBinder.class) String container);
@DELETE
@ExceptionParser(ReturnVoidOnNotFoundOr404.class)
Future<Void> deleteContainer(@Endpoint URI container);
@ -87,6 +92,12 @@ public interface PCSConnection {
@Path("/contents")
Future<? extends SortedSet<FileMetadata>> listFiles(@Endpoint URI container);
@GET
@XMLResponseParser(FileListToContainerMetadataListHandler.class)
@Headers(keys = "X-Cloud-Depth", values = "2")
@Path("/contents")
Future<? extends SortedSet<ContainerMetadata>> listContainers(@Endpoint URI container);
@POST
@Path("/contents")
Future<URI> uploadFile(@Endpoint URI container,

View File

@ -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.
* <p/>
@ -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<Void> put(@Endpoint URI resource, @EntityParam String value);
@GET
String get(@Endpoint URI resource);
@ResponseParser(AddEntryIntoMultiMap.class)
Future<Void> addEntryToMultiMap(Multimap<String, String> map, String key, @Endpoint URI value);
}

View File

@ -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<String, String> 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<String, String> 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;

View File

@ -88,7 +88,7 @@ public class PCSContextModule extends AbstractModule {
@Provides
@Singleton
public ConcurrentMap<String, String> provideConcurrentMap(FindIdInContainerList finder) {
return new MapMaker().concurrencyLevel(32).expiration(30, TimeUnit.SECONDS).makeComputingMap(
return new MapMaker().expiration(30, TimeUnit.SECONDS).makeComputingMap(
finder);
}

View File

@ -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<ContainerMetadata, FileMetadata, PCSFile> provideBlobStore(RestClientFactory factory) {
protected BlobStore<ContainerMetadata, FileMetadata, PCSFile> 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

View File

@ -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;
}
}

View File

@ -0,0 +1,44 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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 {
}

View File

@ -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<HttpResponse, Void>, RestContext {
ReturnStringIf200 returnIf200;
@Inject
private AddEntryIntoMultiMap(ReturnStringIf200 returnIf200) {
this.returnIf200 = returnIf200;
}
private Object[] args;
private HttpRequest request;
static final Void v;
static {
Constructor<Void> 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<String, String> map = null;
String key = null;
for (Object arg : args) {
if (arg instanceof Multimap)
map = (Multimap<String, String>) 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;
}
}

View File

@ -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<HttpResp
private Object[] args;
private HttpRequest request;
/**
* maximum duration of an blob Request
*/
@Inject(optional = true)
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT)
protected long requestTimeoutMilliseconds = 30000;
@Inject
public AddMetadataAndParseResourceIdIntoBytes(PCSUtil util) {
this.util = util;
@ -77,11 +91,22 @@ public class AddMetadataAndParseResourceIdIntoBytes implements Function<HttpResp
String toParse = Utils.toStringAndClose(from.getContent());
logger.trace("%s: received the following response: %s", from, toParse);
URI uri = URI.create(toParse.trim());
Set<Future<Void>> puts = Sets.newHashSet();
for (Entry<String, String> 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<Void> put : puts) {
try {
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<BlobRuntimeException> 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);

View File

@ -59,9 +59,9 @@ public class ContainerAndFileNameToResourceId implements Function<Object, String
Object[] args = (Object[]) from;
checkArgument(args[0] instanceof String, "arg[0] must be a container name");
checkArgument(args[1] instanceof String, "arg[1] must be a pcsfile name (key)");
String container = args[0].toString();
String key = args[1].toString();
try {
return cachedFinder.get(new Key(container, key));
} catch (ComputationException e) {

View File

@ -0,0 +1,140 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<Object, String> {
/**
* maximum duration of an blob Request
*/
@Inject(optional = true)
@Named(BlobStoreConstants.PROPERTY_BLOBSTORE_TIMEOUT)
protected long requestTimeoutMilliseconds = 30000;
private final ConcurrentMap<String, String> finder;
private final PCSConnection blobStore;
private final URI rootContainer;
@Inject
public CreateSubFolderIfNotExistsAndGetResourceId(PCSConnection blobStore,
ConcurrentMap<String, String> 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<ContainerMetadata> response = blobStore.listContainers();
URI containerUri;
try {
containerUri = urlForNameInListOrCreate(rootContainer, containerTree[0],
response);
} catch (Exception e1) {
Utils.<ContainerNotFoundException> 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.<ContainerNotFoundException> rethrowIfRuntimeOrSameType(e2);
throw new BlobRuntimeException("error listing container at: " + containerUri,
e2);
}
}
}
return PCSUtils.getContainerId(containerUri);
}
Utils.<ContainerNotFoundException> rethrowIfRuntimeOrSameType(e);
throw e;
}
}
@VisibleForTesting
URI urlForNameInListOrCreate(URI parent, String toFind,
SortedSet<ContainerMetadata> 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);
}
}

View File

@ -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<String, String> {
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<ContainerMetadata> 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.<ContainerNotFoundException> rethrowIfRuntimeOrSameType(e);
throw new BlobRuntimeException("error listing container at: " + containerUri, e);
}
}
private SortedSet<ContainerMetadata> 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<ContainerMetadata> 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);

View File

@ -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<Key, String> {
}
public String apply(Key key) {
key = PCSUtils.parseKey(key);
SortedSet<FileMetadata> response;
try {
response = connection.listBlobs(key.getContainer()).get(10, TimeUnit.SECONDS);

View File

@ -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);
}
}

View File

@ -0,0 +1,57 @@
/**
*
* Copyright (C) 2009 Global Cloud Specialists, Inc. <info@globalcloudspecialists.com>
*
* ====================================================================
* 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<String, String> cache;
@Inject
public CachingFileListToContainerMetadataListHandler(ConcurrentMap<String, String> cache,
DateService dateParser) {
super(dateParser);
this.cache = cache;
}
@Override
public SortedSet<ContainerMetadata> getResult() {
SortedSet<ContainerMetadata> result = super.getResult();
for (ContainerMetadata md : result) {
cache.put(md.getName(), PCSUtils.getContainerId(md.getUrl()));
}
return result;
}
}

View File

@ -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<SortedSet<ContainerMetadata>> {
@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> 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();
}

View File

@ -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<FileMetadata> {
private final PCSUtil util;
private FileMetadata fileMetadata = null;
protected Multimap<String, String> userMetadata = HashMultimap.create();
Set<Future<Void>> 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<FileMetadata> {
}
public FileMetadata getResult() {
for (Future<Void> put : puts) {
try {
put.get(requestTimeoutMilliseconds, TimeUnit.MILLISECONDS);
} catch (Exception e) {
Utils.<BlobRuntimeException> rethrowIfRuntimeOrSameType(e);
throw new BlobRuntimeException("Error getting metadata", e);
}
}
return fileMetadata;
}
@ -66,8 +89,8 @@ public class FileMetadataHandler extends BaseFileMetadataHandler<FileMetadata> {
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))));
}
}
}

View File

@ -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<? extends SortedSet<ContainerMetadata>> listContainers(URI container) {
throw new UnsupportedOperationException();
}
public Future<URI> 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<PCSBlobStore> 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<Void> put(URI resource, String value) {
return null;
}
public boolean put(URI resource, String value) {
return true;
public Future<Void> addEntryToMultiMap(Multimap<String, String> map,
String key, URI value) {
return null;
}
};

View File

@ -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,

View File

@ -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<Void> put(URI resource, String value) {
return null;
}
public boolean put(URI resource, String value) {
return true;
public Future<Void> addEntryToMultiMap(Multimap<String, String> map,
String key, URI value) {
return null;
}
};

View File

@ -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"));
}
}

View File

@ -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<Void> 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;
}

View File

@ -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<ContainerMetadata> 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<URI> createContainer(String container) {
return null;
}
public Future<URI> createContainer(URI parent, String container) {
return null;
}
public Future<Void> deleteContainer(URI container) {
return null;
}
public Future<Void> deleteFile(URI file) {
return null;
}
public Future<InputStream> downloadFile(URI file) {
return null;
}
public SortedSet<ContainerMetadata> listContainers() {
return null;
}
public Future<? extends SortedSet<ContainerMetadata>> 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.<ContainerMetadata> of());
}
}
<T> Future<T> createFuture(final T data) {
return new Future<T>() {
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<? extends SortedSet<FileMetadata>> listFiles(URI container) {
return null;
}
public Future<URI> 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");
}
}

View File

@ -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" } };
}
}

View File

@ -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();
}

View File

@ -41,31 +41,48 @@ import org.jclouds.mezeo.pcs2.domain.PCSFile;
*/
public class StubPCSConnection implements PCSConnection {
public Future<URI> createContainer(String container) {
throw new UnsupportedOperationException();
}
public Future<URI> createContainer(URI parent, String container) {
throw new UnsupportedOperationException();
}
public Future<Void> deleteContainer(URI container) {
throw new UnsupportedOperationException();
}
public Future<Void> deleteFile(URI file) {
throw new UnsupportedOperationException();
}
public Future<InputStream> downloadFile(URI file) {
throw new UnsupportedOperationException();
}
public Future<? extends SortedSet<FileMetadata>> listFiles(URI container) {
throw new UnsupportedOperationException();
}
public Future<URI> uploadFile(URI container, PCSFile object) {
throw new UnsupportedOperationException();
}
public Future<URI> createContainer(String container) {
throw new UnsupportedOperationException();
}
public SortedSet<ContainerMetadata> listContainers() {
throw new UnsupportedOperationException();
}
public Future<? extends SortedSet<ContainerMetadata>> listContainers(URI container) {
throw new UnsupportedOperationException();
}
public Future<? extends SortedSet<FileMetadata>> listFiles(URI container) {
throw new UnsupportedOperationException();
}
public Future<URI> uploadFile(URI container, PCSFile object) {
throw new UnsupportedOperationException();
}
}

View File

@ -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");
}
}

View File

@ -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));

View File

@ -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<Void> voidF = createMock(Future.class);
return new PCSUtil() {
public Future<Void> addEntryToMultiMap(Multimap<String, String> map, String key,
URI value) {
map.put(key.toLowerCase(), "bar");
return voidF;
}
public Future<Void> 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);
}

View File

@ -15,7 +15,11 @@
<bytes>1024</bytes>
<contents
xlink:href="https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B/contents"
xlink:type="simple" count="0" start="0" total="0" />
xlink:type="simple" count="0" start="0" total="0">
<container
xlink:href="https://eval.mezeo.net/v2/containers/D3256E66-B29F-11DE-9597-BF70124CE977"
xlink:type="simple" />
</contents>
<tags
xlink:href="https://pcsbeta.mezeo.net/v2/containers/7F143552-AAF5-11DE-BBB0-0BC388ED913B/tags"
xlink:type="simple" />