You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by wo...@apache.org on 2010/09/19 00:16:14 UTC

svn commit: r998560 - in /incubator/libcloud/sandbox/java/trunk: ./ lib/ src/base/connection/ src/simplecloud/storage/ src/simplecloud/storage/interfaces/ src/simplecloud/storage/providers/amazon/ src/simplecloud/storage/providers/nirvanix/

Author: woodser
Date: Sat Sep 18 22:16:13 2010
New Revision: 998560

URL: http://svn.apache.org/viewvc?rev=998560&view=rev
Log:
*Nirvanix adapter added to SimpleCloud*

simplecloud > storage > Example.java illustrates how to interact with SimpleCloud APIs

In support of this driver, some structural changes were made:
- NirvanixAdapter, NirvanixConnection, and NirvanixHandler classes added
- HttpClient libraries updated to 4.1-alpha2 to support multipart-form posts for Nirvanix storeItem()
- IStorageAdapter interface changed to support generic Map<Object, Object> options parameter

Note: Cleanup work in ConnectionKey will soon follow to better support data posts and generic encodeData(Object)

Added:
    incubator/libcloud/sandbox/java/trunk/lib/httpclient-4.1-alpha2.jar   (with props)
    incubator/libcloud/sandbox/java/trunk/lib/httpclient-cache-4.1-alpha2.jar   (with props)
    incubator/libcloud/sandbox/java/trunk/lib/httpcore-4.1-beta2.jar   (with props)
    incubator/libcloud/sandbox/java/trunk/lib/httpcore-nio-4.1-beta2.jar   (with props)
    incubator/libcloud/sandbox/java/trunk/lib/httpmime-4.1-alpha2.jar   (with props)
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixAdapter.java
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixConnection.java
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixHandler.java
Removed:
    incubator/libcloud/sandbox/java/trunk/lib/httpclient-4.0.1.jar
    incubator/libcloud/sandbox/java/trunk/lib/httpcore-4.0.1.jar
    incubator/libcloud/sandbox/java/trunk/lib/httpmime-4.0.1.jar
Modified:
    incubator/libcloud/sandbox/java/trunk/.classpath
    incubator/libcloud/sandbox/java/trunk/src/base/connection/ConnectionKey.java
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/Example.java
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/interfaces/IStorageAdapter.java
    incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/amazon/S3Adapter.java

Modified: incubator/libcloud/sandbox/java/trunk/.classpath
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/.classpath?rev=998560&r1=998559&r2=998560&view=diff
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/.classpath (original)
+++ incubator/libcloud/sandbox/java/trunk/.classpath Sat Sep 18 22:16:13 2010
@@ -3,15 +3,17 @@
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
 	<classpathentry kind="lib" path="lib/apache-mime4j-0.6.jar"/>
+	<classpathentry kind="lib" path="lib/commons-codec-1.4.jar"/>
 	<classpathentry kind="lib" path="lib/commons-logging-1.1.1.jar"/>
-	<classpathentry kind="lib" path="lib/httpclient-4.0.1.jar"/>
-	<classpathentry kind="lib" path="lib/httpcore-4.0.1.jar"/>
-	<classpathentry kind="lib" path="lib/httpmime-4.0.1.jar"/>
+	<classpathentry kind="lib" path="lib/httpclient-4.1-alpha2.jar"/>
+	<classpathentry kind="lib" path="lib/httpclient-cache-4.1-alpha2.jar"/>
+	<classpathentry kind="lib" path="lib/httpcore-4.1-beta2.jar"/>
+	<classpathentry kind="lib" path="lib/httpcore-nio-4.1-beta2.jar"/>
+	<classpathentry kind="lib" path="lib/httpmime-4.1-alpha2.jar"/>
 	<classpathentry kind="lib" path="lib/resolver.jar"/>
 	<classpathentry kind="lib" path="lib/serializer.jar"/>
 	<classpathentry kind="lib" path="lib/xercesImpl.jar"/>
 	<classpathentry kind="lib" path="lib/xercesSamples.jar"/>
 	<classpathentry kind="lib" path="lib/xml-apis.jar"/>
-	<classpathentry kind="lib" path="lib/commons-codec-1.4.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>

Added: incubator/libcloud/sandbox/java/trunk/lib/httpclient-4.1-alpha2.jar
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/lib/httpclient-4.1-alpha2.jar?rev=998560&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/libcloud/sandbox/java/trunk/lib/httpclient-4.1-alpha2.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/libcloud/sandbox/java/trunk/lib/httpclient-cache-4.1-alpha2.jar
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/lib/httpclient-cache-4.1-alpha2.jar?rev=998560&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/libcloud/sandbox/java/trunk/lib/httpclient-cache-4.1-alpha2.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/libcloud/sandbox/java/trunk/lib/httpcore-4.1-beta2.jar
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/lib/httpcore-4.1-beta2.jar?rev=998560&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/libcloud/sandbox/java/trunk/lib/httpcore-4.1-beta2.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/libcloud/sandbox/java/trunk/lib/httpcore-nio-4.1-beta2.jar
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/lib/httpcore-nio-4.1-beta2.jar?rev=998560&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/libcloud/sandbox/java/trunk/lib/httpcore-nio-4.1-beta2.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Added: incubator/libcloud/sandbox/java/trunk/lib/httpmime-4.1-alpha2.jar
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/lib/httpmime-4.1-alpha2.jar?rev=998560&view=auto
==============================================================================
Binary file - no diff available.

Propchange: incubator/libcloud/sandbox/java/trunk/lib/httpmime-4.1-alpha2.jar
------------------------------------------------------------------------------
    svn:mime-type = application/octet-stream

Modified: incubator/libcloud/sandbox/java/trunk/src/base/connection/ConnectionKey.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/base/connection/ConnectionKey.java?rev=998560&r1=998559&r2=998560&view=diff
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/base/connection/ConnectionKey.java (original)
+++ incubator/libcloud/sandbox/java/trunk/src/base/connection/ConnectionKey.java Sat Sep 18 22:16:13 2010
@@ -34,7 +34,7 @@ import base.interfaces.IItem;
 /*
  * Manages a connection with a secret access key.
  */
-public abstract class ConnectionKey implements IConnection {
+public class ConnectionKey implements IConnection {
 
 	protected String host;
 
@@ -99,6 +99,19 @@ public abstract class ConnectionKey impl
 		}
 	}
 
+	/*
+	 * TODO: I think some drivers could be simplified if request() was
+	 * overloaded to take in a full URL, including host & action.  This is
+	 * especially true for drivers that require multiple requests to retrieve
+	 * e.g. a session token or DownloadNode for downloading content.  This
+	 * would prevent need to re-connect() for each of these requests.
+	 * 
+	 * TODO: Rewrite in support of:
+	 * 	string body content
+	 * 	inputstream body content (use Item?)
+	 * 	multipart form posts
+	 * 	change to encodeData(Object data) for generic processing
+	 */
 	public Response request(String method, String path,
 			Map<String, String> headers, Map<String, String> params, Object data) {
 		this.method = method;

Modified: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/Example.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/Example.java?rev=998560&r1=998559&r2=998560&view=diff
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/Example.java (original)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/Example.java Sat Sep 18 22:16:13 2010
@@ -9,31 +9,57 @@ import java.util.HashMap;
 import java.util.Map;
 
 import simplecloud.storage.providers.amazon.S3Adapter;
+import simplecloud.storage.providers.nirvanix.NirvanixAdapter;
 import base.types.Item;
 
 public class Example {
-
-	public static void main(String [] args) {
-		S3Adapter adapter = new S3Adapter("your access key id", "your access key", "s3.amazonaws.com");
-		Map<String, String> options = new HashMap<String, String>();
-		
-		// Upload something
+	
+	public static void main(String[] args) {
+		// Nirvanix
 		try {
-			String localPath = "/Users/Eric/libcloud_demo.jpg";
+			NirvanixAdapter nirvanix = new NirvanixAdapter("your application name", "your application key", "your username", "your password");
+			Map<Object, Object> options = new HashMap<Object, Object>();
+			String localPath = "/Users/Eric/Desktop/frozen.jpg";
 			String contentType = "image/jpeg";
 			File file = new File(localPath);
 			InputStream stream = new BufferedInputStream(new FileInputStream(file));
 			Item item = new Item(stream, contentType, null, file.length());
-			options.put(S3Adapter.Type.SRC_BUCKET.toString(), "ericlibcloud");
-			adapter.storeItem("/libcloud_demo_stored.jpg", item, options);
+			if (nirvanix.storeItem("/toby.jpg", item, options)) {
+				System.out.println("Upload succeeded");
+			} else {
+				System.out.println("Upload failed");
+			}
+			System.out.println("Nirvanix root: ");
+			options.put(NirvanixAdapter.Type.PAGE_NUMBER, "1");
+			options.put(NirvanixAdapter.Type.PAGE_SIZE, "10");
+			for (String name : nirvanix.listItems("/", options)) {
+				System.out.println('\t' + name);
+			}
 		} catch (FileNotFoundException e) {
 			e.printStackTrace();
 		}
 		
-//		options.put(S3Adapter.Type.SRC_BUCKET.toString(), "ericlibcloud");
-//		for (String item : adapter.listItems("/", options)) {
-//			System.out.println(item);
-//		}
-//		adapter.renameItem("/libcloud_demo.jpg", "libcloud_demo2.jpg", options);
+		// Amazon
+		try {
+			S3Adapter amazon = new S3Adapter("your access key id", "your access key", "s3.amazonaws.com");
+			Map<Object, Object> options = new HashMap<Object, Object>();
+			String localPath = "/Users/Eric/Desktop/frozen.jpg";
+			String contentType = "image/jpeg";
+			File file = new File(localPath);
+			InputStream stream = new BufferedInputStream(new FileInputStream(file));
+			Item item = new Item(stream, contentType, null, file.length());
+			options.put(S3Adapter.Type.SRC_BUCKET, "ericlibcloud");
+			if (amazon.storeItem("/toby.jpg", item, options)) {
+				System.out.println("Upload succeeded");
+			} else {
+				System.out.println("Upload failed");
+			}
+			System.out.println("Amazon root:");
+			for (String name : amazon.listItems("/", options)) {
+				System.out.println('\t' + name);
+			}
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
 	}
 }

Modified: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/interfaces/IStorageAdapter.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/interfaces/IStorageAdapter.java?rev=998560&r1=998559&r2=998560&view=diff
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/interfaces/IStorageAdapter.java (original)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/interfaces/IStorageAdapter.java Sat Sep 18 22:16:13 2010
@@ -10,26 +10,27 @@ import base.interfaces.IItem;
  * 
  * TODO: Comments
  * TODO: For every return void, why not return status? PHP doesn't.
+ * TODO: listItems() return List<Map<String, Object>> for attributes instead of names?
  */
 public interface IStorageAdapter {
 	
-	public IItem fetchItem(String path, Map<String, String> options);
+	public IItem fetchItem(String path, Map<Object, Object> options);
 	
-	public boolean storeItem(String destinationPath, IItem item, Map<String, String> options);
+	public boolean storeItem(String destinationPath, IItem item, Map<Object, Object> options);
 	
-	public void deleteItem(String path, Map<String, String> options);
+	public void deleteItem(String path, Map<Object, Object> options);
 	
-	public void copyItem(String sourcePath, String destinationPath, Map<String, String> options);
+	public void copyItem(String sourcePath, String destinationPath, Map<Object, Object> options);
 	
-	public void moveItem(String sourcePath, String destinationPath, Map<String, String> options);
+	public void moveItem(String sourcePath, String destinationPath, Map<Object, Object> options);
 	
-	public void renameItem(String path, String name, Map<String, String> options);
+	public void renameItem(String path, String name, Map<Object, Object> options);
 
-	public List<String> listItems(String path, Map<String, String> options);
+	public List<String> listItems(String path, Map<Object, Object> options);
 	
-	public Map<String, String> fetchMetadata(String path, Map<String, String> options);
+	public Map<String, String> fetchMetadata(String path, Map<Object, Object> options);
 	
-	public void storeMetadata(String destinationPath, Map<String, String> metadata, Map<String, String> options);
+	public void storeMetadata(String destinationPath, Map<String, String> metadata, Map<Object, Object> options);
 	
-	public void deleteMetadata(String path, Map<String, String> options);
+	public void deleteMetadata(String path, Map<Object, Object> options);
 }

Modified: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/amazon/S3Adapter.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/amazon/S3Adapter.java?rev=998560&r1=998559&r2=998560&view=diff
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/amazon/S3Adapter.java (original)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/amazon/S3Adapter.java Sat Sep 18 22:16:13 2010
@@ -25,25 +25,21 @@ import base.interfaces.IResponse;
 
 /*
  * Adapter for interacting with Amazon S3.
+ * 
+ * TODO: different locations
+ * TODO: comment required option parameters
+ * TODO: general comments
  */
 public class S3Adapter extends StorageAdapter {
 	
 	private final String HOST;
 	
 	/*
-	 * TEMP TODO: see if this is of value.
+	 * Constant keys for options.
 	 */
 	public enum Type {
-		SRC_BUCKET("srcBucket"),
-		DEST_BUCKET("destBucket");
-		
-		private String value;
-		private Type(String value) {
-			this.value = value;
-		}
-		public String toString() {
-			return this.value;
-		}
+		SRC_BUCKET,
+		DEST_BUCKET;
 	}
 	
 	/*
@@ -58,32 +54,32 @@ public class S3Adapter extends StorageAd
 		connection = new S3Connection(accessId, accessKey, host, this);
 	}
 
-	public IItem fetchItem(String path, Map<String, String> options) {
-		String bucket = options.get(Type.SRC_BUCKET.toString());
+	public IItem fetchItem(String path, Map<Object, Object> options) {
+		String bucket = (String)options.get(Type.SRC_BUCKET);
 		connection.connect(bucket + '.' + HOST);
 		IResponse response = connection.request("GET", path, null, null, null);
 		S3Handler rh = new S3Handler(response);
 		return (IItem)rh.getParsedObject();
 	}
 
-	public boolean storeItem(String destinationPath, IItem item, Map<String, String> options) {
-		String bucket = options.get(Type.SRC_BUCKET.toString());
+	public boolean storeItem(String destinationPath, IItem item, Map<Object, Object> options) {
+		String bucket = (String)options.get(Type.SRC_BUCKET);
 		connection.connect(bucket + '.' + HOST);
 		IResponse response = connection.request("PUT", destinationPath, null, null, item);
 		new S3Handler(response);
 		return response.getStatus() == 200;
 	}
 	
-	public void deleteItem(String path, Map<String, String> options) {
-		String bucket = options.get(Type.SRC_BUCKET.toString());
+	public void deleteItem(String path, Map<Object, Object> options) {
+		String bucket = (String)options.get(Type.SRC_BUCKET);
 		connection.connect(bucket + '.' + HOST);
 		IResponse response = connection.request("DELETE", path, null, null, null);
 		new S3Handler(response);
 	}
 
-	public void copyItem(String sourcePath, String destinationPath, Map<String, String> options) {
-		String srcBucket = options.get(Type.SRC_BUCKET.toString());
-		String destBucket = options.get(Type.DEST_BUCKET.toString());
+	public void copyItem(String sourcePath, String destinationPath, Map<Object, Object> options) {
+		String srcBucket = (String)options.get(Type.SRC_BUCKET);
+		String destBucket = (String)options.get(Type.DEST_BUCKET);
 		connection.connect(destBucket + '.' + HOST);
 		Map<String, String> headers = new HashMap<String, String>();
 		headers.put("x-amz-copy-source", '/' + srcBucket + sourcePath);
@@ -91,11 +87,10 @@ public class S3Adapter extends StorageAd
 		new S3Handler(response);
 	}
 
-	public void moveItem(String sourcePath, String destinationPath,
-			Map<String, String> options) {
+	public void moveItem(String sourcePath, String destinationPath, Map<Object, Object> options) {
 		// Copy the item
-		String srcBucket = options.get(Type.SRC_BUCKET.toString());
-		String destBucket = options.get(Type.DEST_BUCKET.toString());
+		String srcBucket = (String)options.get(Type.SRC_BUCKET);
+		String destBucket = (String)options.get(Type.DEST_BUCKET);
 		connection.connect(destBucket + '.' + HOST);
 		Map<String, String> headers = new HashMap<String, String>();
 		headers.put("x-amz-copy-source", '/' + srcBucket + sourcePath);
@@ -108,14 +103,14 @@ public class S3Adapter extends StorageAd
 		new S3Handler(response);
 	}
 
-	public void renameItem(String path, String name, Map<String, String> options) {
+	public void renameItem(String path, String name, Map<Object, Object> options) {
 		String destPath = path.substring(0, path.lastIndexOf('/') + 1) + name;
-		options.put(Type.DEST_BUCKET.toString(), options.get(Type.SRC_BUCKET.toString()));
+		options.put(Type.DEST_BUCKET, options.get(Type.SRC_BUCKET));
 		moveItem(path, destPath, options);
 	}
 
-	public List<String> listItems(String path, Map<String, String> options) {
-		String bucket = options.get(Type.SRC_BUCKET.toString());
+	public List<String> listItems(String path, Map<Object, Object> options) {
+		String bucket = (String)options.get(Type.SRC_BUCKET);
 		connection.connect(bucket + '.' + HOST);
 		IResponse response = connection.request("GET", path, null, null, null);
 		S3Handler rh = new S3Handler(response);
@@ -136,21 +131,19 @@ public class S3Adapter extends StorageAd
 	}
 
 	@Override
-	public Map<String, String> fetchMetadata(String path,
-			Map<String, String> options) {
+	public Map<String, String> fetchMetadata(String path, Map<Object, Object> options) {
 		// TODO Auto-generated method stub
 		return null;
 	}
 
 	@Override
-	public void storeMetadata(String destinationPath,
-			Map<String, String> metadata, Map<String, String> options) {
+	public void storeMetadata(String destinationPath, Map<String, String> metadata, Map<Object, Object> options) {
 		// TODO Auto-generated method stub
 		
 	}
 
 	@Override
-	public void deleteMetadata(String path, Map<String, String> options) {
+	public void deleteMetadata(String path, Map<Object, Object> options) {
 		// TODO Auto-generated method stub
 		
 	}

Added: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixAdapter.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixAdapter.java?rev=998560&view=auto
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixAdapter.java (added)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixAdapter.java Sat Sep 18 22:16:13 2010
@@ -0,0 +1,312 @@
+package simplecloud.storage.providers.nirvanix;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.utils.URIUtils;
+import org.apache.http.entity.mime.MultipartEntity;
+import org.apache.http.entity.mime.content.InputStreamBody;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import simplecloud.storage.interfaces.IStorageAdapter;
+import base.Driver;
+import base.connection.ConnectionKey;
+import base.connection.Response;
+import base.interfaces.IItem;
+import base.interfaces.IResponse;
+
+/*
+ * TODO: comment required option parameters
+ * TODO: general comments
+ * TODO: test url encoding of paths
+ */
+public class NirvanixAdapter extends Driver implements IStorageAdapter {
+	
+	private static final String HOST = "services.nirvanix.com";
+	
+	private String sessionToken;
+	
+	private String appName;
+	
+	private String account;
+	
+	/*
+	 * Constant keys for option parameters.
+	 */
+	public enum Type {
+		PAGE_NUMBER,
+		PAGE_SIZE,
+		ITEM_TYPE,
+		FILE,
+		FOLDER;
+	}
+	
+	public NirvanixAdapter(String appName, String appKey, String username, String password) {
+		this.connection = new NirvanixConnection(appKey, username, password, HOST, this);
+		this.sessionToken = ((NirvanixConnection)connection).getSessionToken();
+		this.appName = appName;
+		this.account = username;
+	}
+
+	public IItem fetchItem(String path, Map<Object, Object> options) {
+		// First get the DownloadNode
+		Map<String, String> nodeParams = new HashMap<String, String>();
+		nodeParams.put("sessionToken", sessionToken);
+		nodeParams.put("filePath", path);
+		IResponse nodeResponse = connection.request("GET", "/ws/IMFS/GetDownloadNodes.ashx", null, nodeParams, null);
+		NirvanixHandler handler = new NirvanixHandler(nodeResponse);
+		String node = parseDownloadNodes((Document)handler.getParsedObject()).get(0);
+		
+		// Download content from node
+		Map<String, String> downloadParams = new HashMap<String, String>();
+		downloadParams.put("sessionToken", sessionToken);
+		ConnectionKey downloadConnection = new ConnectionKey(null, true, node, this);
+		String action;
+		try {
+			action = '/' + appName + '/' + account + URLEncoder.encode(path, "UTF-8");
+			IResponse downloadResponse = downloadConnection.request("GET", action, null, downloadParams, null);
+			if (downloadResponse.getStatus() == 200) return downloadResponse.getItem();
+			new NirvanixHandler(downloadResponse);
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return null;	// Error
+	}
+
+	/*
+	 * TODO: This was hacked together because of lacking multipart-form posts in ConnectionKey.
+	 */
+	public boolean storeItem(String destinationPath, IItem item, Map<Object, Object> options) {
+		// First get the StorageNode
+		Map<String, String> nodeParams = new HashMap<String, String>();
+		nodeParams.put("sessionToken", sessionToken);
+		nodeParams.put("sizeBytes", String.valueOf(item.getContentLength()));
+		IResponse nodeResponse = connection.request("GET", "/ws/IMFS/GetStorageNode.ashx", null, nodeParams, null);
+		NirvanixHandler handler = new NirvanixHandler(nodeResponse);
+		String uploadHost = parseUploadHost((Document)handler.getParsedObject());
+		String uploadToken = parseUploadToken((Document)handler.getParsedObject());
+		
+		// Parse directory & filename
+		String directory = destinationPath.substring(0, destinationPath.lastIndexOf('/') + 1);
+		String filename = destinationPath.substring(destinationPath.lastIndexOf('/'), destinationPath.length());
+		
+		// Create URI endpoint
+		Map<String, String> uploadParams = new HashMap<String, String>();
+		uploadParams.put("uploadToken", uploadToken);
+		uploadParams.put("destFolderPath", directory);
+		URI uri = null;
+		try {
+			uri = URIUtils.createURI("http", uploadHost, -1, "/Upload.ashx", ConnectionKey.urlEncodeMap(uploadParams), null);
+		} catch (URISyntaxException e) {
+			e.printStackTrace();
+			return false;
+		}
+		HttpPost httppost = new HttpPost(uri);
+		
+		// Build request entity
+		MultipartEntity entity = new MultipartEntity();
+		InputStreamBodyLength isbl = new InputStreamBodyLength(item.getContent(), item.getContentType(), filename, item.getContentLength());
+		entity.addPart("fileContent", isbl);
+		httppost.setEntity(entity);
+		
+		// POST file to the server
+		HttpClient httpclient = new DefaultHttpClient();
+		try {
+			IResponse response = new Response(httpclient.execute(httppost));
+			new NirvanixHandler(response);
+			return true;
+		} catch (ClientProtocolException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return false;	// Error
+	}
+
+	public void deleteItem(String path, Map<Object, Object> options) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("sessionToken", sessionToken);
+		Type itemType = (Type)options.get(Type.ITEM_TYPE);
+		IResponse response = null;
+		if (itemType == Type.FOLDER) {
+			params.put("folderPath", path);
+			response = connection.request("GET", "/ws/IMFS/DeleteFolders.ashx", null, params, null);
+		} else {
+			params.put("filePath", path);
+			response = connection.request("GET", "/ws/IMFS/DeleteFiles.ashx", null, params, null);
+		}
+		new NirvanixHandler(response);
+	}
+
+	public void copyItem(String sourcePath, String destinationPath, Map<Object, Object> options) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("sessionToken", sessionToken);
+		Type itemType = (Type)options.get(Type.ITEM_TYPE);
+		IResponse response = null;
+		if (itemType == Type.FOLDER) {
+			params.put("srcFolderPath", sourcePath);
+			params.put("destFolderPath", destinationPath);
+			response = connection.request("GET", "/ws/IMFS/CopyFolders.ashx", null, params, null);
+		} else {
+			params.put("srcFilePath", sourcePath);
+			params.put("destFolderPath", destinationPath);
+			response = connection.request("GET", "/ws/IMFS/CopyFiles.ashx", null, params, null);
+		}
+		new NirvanixHandler(response);
+	}
+
+	public void moveItem(String sourcePath, String destinationPath, Map<Object, Object> options) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("sessionToken", sessionToken);
+		Type itemType = (Type)options.get(Type.ITEM_TYPE);
+		IResponse response = null;
+		if (itemType == Type.FOLDER) {
+			params.put("srcFolderPath", sourcePath);
+			params.put("destFolderPath", destinationPath);
+			response = connection.request("GET", "/ws/IMFS/MoveFolders.ashx", null, params, null);
+		} else {
+			params.put("srcFilePath", sourcePath);
+			params.put("destFolderPath", destinationPath);
+			response = connection.request("GET", "/ws/IMFS/MoveFiles.ashx", null, params, null);
+		}
+		new NirvanixHandler(response);
+	}
+
+	public void renameItem(String path, String name, Map<Object, Object> options) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("sessionToken", sessionToken);
+		Type itemType = (Type)options.get(Type.ITEM_TYPE);
+		IResponse response = null;
+		if (itemType == Type.FOLDER) {
+			params.put("folderPath", path);
+			params.put("newFolderName", name);
+			response = connection.request("GET", "/ws/IMFS/RenameFolder.ashx", null, params, null);
+		} else {
+			params.put("filePath", path);
+			params.put("newFileName", name);
+			response = connection.request("GET", "/ws/IMFS/RenameFile.ashx", null, params, null);
+		}
+		new NirvanixHandler(response);
+	}
+
+	public List<String> listItems(String path, Map<Object, Object> options) {
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("sessionToken", sessionToken);
+		params.put("folderPath", path);
+		params.put("pageNumber", (String)options.get(Type.PAGE_NUMBER));
+		params.put("pageSize", (String)options.get(Type.PAGE_SIZE));
+		IResponse response = connection.request("GET", "/ws/IMFS/ListFolder.ashx" + path, null, params, null);
+		NirvanixHandler rh = new NirvanixHandler(response);
+		return parseItems((Document)rh.getParsedObject());
+	}
+
+	@Override
+	public Map<String, String> fetchMetadata(String path, Map<Object, Object> options) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	@Override
+	public void storeMetadata(String destinationPath, Map<String, String> metadata, Map<Object, Object> options) {
+		// TODO Auto-generated method stub
+
+	}
+
+	@Override
+	public void deleteMetadata(String path, Map<Object, Object> options) {
+		// TODO Auto-generated method stub
+
+	}
+	
+	private List<String> parseDownloadNodes(Document doc) {
+		try {
+			Element elem = doc.getDocumentElement();
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			List<String> nodes = new ArrayList<String>();
+			NodeList downloadNodes = (NodeList)xpath.evaluate("/Response/DownloadNode", elem, XPathConstants.NODESET);
+			for (int i = 0; i < downloadNodes.getLength(); i++) {
+				nodes.add(downloadNodes.item(i).getTextContent());
+			}
+			return nodes;
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+		return null;	// Error
+	}
+	
+	private String parseUploadHost(Document doc) {
+		try {
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			return xpath.evaluate("/Response/GetStorageNode/UploadHost", doc.getDocumentElement());
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+		return null;	// Error
+	}
+	
+	private String parseUploadToken(Document doc) {
+		try {
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			return xpath.evaluate("/Response/GetStorageNode/UploadToken", doc.getDocumentElement());
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+		return null;	// Error
+	}
+
+	private List<String> parseItems(Document doc) {
+		try {
+			Element elem = doc.getDocumentElement();
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			List<String> items = new ArrayList<String>();
+			NodeList folders = (NodeList)xpath.evaluate("/Response/ListFolder/Folder/Name", elem, XPathConstants.NODESET);
+			for (int i = 0; i < folders.getLength(); i++) {
+				items.add(folders.item(i).getTextContent());
+			}
+			NodeList files = (NodeList)xpath.evaluate("/Response/ListFolder/File/Name", elem, XPathConstants.NODESET);
+			for (int i = 0; i < files.getLength(); i++) {
+				items.add(files.item(i).getTextContent());
+			}
+			return items;
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+		return null;	// Error
+	}
+	
+	/*
+	 * Simple extension of InputStreamBody to support length.
+	 * 
+	 * TODO: this is simply a hack until HttpClient-alpha2 matures
+	 */
+	private class InputStreamBodyLength extends InputStreamBody {
+		private long length;
+		public InputStreamBodyLength(InputStream in, String mimeType,
+				String filename, long length) {
+			super(in, mimeType, filename);
+			this.length = length;
+		}
+		public long getContentLength() {
+			return length;
+		}
+	}
+}

Added: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixConnection.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixConnection.java?rev=998560&view=auto
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixConnection.java (added)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixConnection.java Sat Sep 18 22:16:13 2010
@@ -0,0 +1,76 @@
+package simplecloud.storage.providers.nirvanix;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.xml.sax.InputSource;
+
+import base.Driver;
+import base.connection.ConnectionUserAndKey;
+import base.interfaces.IResponse;
+
+/*
+ * Manages a connection to the Nirvanix storage cloud.
+ */
+public class NirvanixConnection extends ConnectionUserAndKey {
+	
+	private String appKey;
+	
+	private String sessionToken;
+	
+	private static final boolean SECURE = true;
+
+	/*
+	 * Constructs the Nirvanix connection.
+	 * 
+	 * @param appKey is necessary to authenticate with the API
+	 * @param accessId identifies the user connecting to Nirvanix
+	 * @param accessKey is the user's secret key
+	 * @param host defines Nirvanix's host server
+	 * @param driver references the driver that owns this connection
+	 */
+	public NirvanixConnection(String appKey, String accessId, String accessKey,
+			String host, Driver driver) {
+		super(accessId, accessKey, SECURE, host, driver);
+		this.appKey = appKey;
+		this.sessionToken = null;
+		refreshSessionToken();
+	}
+	
+	/* 
+	 * Returns the session token that authenticates calls to the Nirvanix API.
+	 * If this token expires, a new token must be issued using
+	 * refreshSessionToken().
+	 * 
+	 * @return String is the session token to authenticate API calls
+	 */
+	public String getSessionToken() {
+		if (sessionToken == null) refreshSessionToken();
+		return sessionToken;
+	}
+	
+	/*
+	 * Refreshes the session token.
+	 */
+	public void refreshSessionToken() {
+		// Request authentication
+		Map<String, String> params = new HashMap<String, String>();
+		params.put("appKey", appKey);
+		params.put("username", accessId);
+		params.put("password", accessKey);
+		IResponse response = request("GET", "/ws/Authentication/Login.ashx", null, params, null);
+		
+		// Parse response
+		XPath xpath = XPathFactory.newInstance().newXPath();
+		try {
+			InputSource input = new InputSource(response.getItem().getContent());
+			sessionToken = xpath.evaluate("Response/SessionToken", input);
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+	}
+}

Added: incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixHandler.java
URL: http://svn.apache.org/viewvc/incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixHandler.java?rev=998560&view=auto
==============================================================================
--- incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixHandler.java (added)
+++ incubator/libcloud/sandbox/java/trunk/src/simplecloud/storage/providers/nirvanix/NirvanixHandler.java Sat Sep 18 22:16:13 2010
@@ -0,0 +1,74 @@
+package simplecloud.storage.providers.nirvanix;
+
+import java.io.IOException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import base.connection.ResponseHandler;
+import base.interfaces.IResponse;
+
+public class NirvanixHandler extends ResponseHandler {
+	
+	private Document document;
+	private String responseCode;
+	private String phrase;
+
+	public NirvanixHandler(IResponse response) {
+		super(response);
+		this.responseCode = null;
+		this.phrase = null;
+	}
+	
+	protected void parseBody() {
+		this.object = document;
+	}
+	
+	protected String parseError() {
+		if (responseCode == null || phrase == null) {
+			return super.parseError();
+		}
+		return responseCode + ": " + phrase;
+	}
+	
+	protected boolean success() {
+		// Simple error occurred
+		if (response.getStatus() != 200) {
+			return super.success();
+		}
+		
+		// Further investigation needed; look at the body... bum bum bum.
+		try {
+			// Parse out document for future consumption
+			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+			DocumentBuilder db = dbf.newDocumentBuilder();
+			document = db.parse(response.getItem().getContent());
+			
+			// Parse response for status
+			Element elem = document.getDocumentElement();
+			XPath xpath = XPathFactory.newInstance().newXPath();
+			responseCode = xpath.evaluate("/Response/ResponseCode", elem);
+			if (!responseCode.equals("0")) {
+				phrase = xpath.evaluate("/Response/ErrorMessage", elem);
+				return false;
+			}
+		} catch (ParserConfigurationException e) {
+			e.printStackTrace();
+		} catch (SAXException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} catch (XPathExpressionException e) {
+			e.printStackTrace();
+		}
+		return true;	// Not guilty!
+	}
+}