src/main/java/com/rackspacecloud/client/cloudfiles/FilesContainer.java
public class FilesContainer
{
    private String name;
    private List<FilesObject> objects = null;
    private FilesClient client = null;
    private static Logger logger = Logger.getLogger(FilesContainer.class);

    /**
     * Create a new container (Note, this does not actually create a container on the server)
     *  
     * @param name    The name of the container
     * @param objs    The objects in that container
     * @param client  The client we are currently using
     */
    public FilesContainer(String name, List<FilesObject> objs, FilesClient client)
    {
        this.name = name;
        this.objects = objs;
        this.client = client;
    }

    /**
     * @param name The name of the container
     * @param client A logged in client
     */
    public FilesContainer(String name, FilesClient client)
    {
        this.name = name;
        this.client = client;
    }

    /**
     * Get the name of the container
     * 
     * @return The name of this container
     */
    public String getName()
    {
        return name;
    }

    /**
     * Set the name of the container
     * 
     * @param name The new name
     */
    public void setName(String name)
    {
        this.name = name;
    }

    /**
     * Returns the contents of this container
     * 
     * @return A list of the contents
     * @throws HttpException There was a problem communicating with the server
     * @throws IOException There was a problem communicating with the server
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public List<FilesObject> getObjects() throws IOException, FilesAuthorizationException, FilesException
    {
    
        if (client != null)
        {
            if(objects == null) {
            	objects = client.listObjects(this.name);            
            }
            return objects;
        }
        else
        {
            logger.fatal("This Container has no FilesClient defined !");
        }
        return null;
    }

    /**
     * Returns the contents of this container
     * 
     * @param path Limit the results to files under the following path
     * 
     * @return A list of the contents
     * @throws HttpException There was a problem communicating with the server
     * @throws IOException There was a problem communicating with the server
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public List<FilesObject> getObjects(String path) throws HttpException, IOException, FilesAuthorizationException, FilesException
    {
    
        if (client != null)
        {
            if(objects == null) {
            	objects = client.listObjects(this.name, path);            
            }
            return objects;
        }
        else
        {
            logger.fatal("This Container has no FilesClient defined !");
        }
        return null;
    }

    /**
     * Get useful information on this container
     * 
     * @return The container info
     * @throws HttpException There was a problem communicating with the server
     * @throws IOException There was a problem communicating with the server
     * @throws FilesException 
     */
    public FilesContainerInfo getInfo() throws HttpException, IOException, FilesException
    {
        if (client != null)
        {
            return client.getContainerInfo(this.name);
        }
        else
        {
            logger.fatal("This container does not have a valid client !");
        }
        return null;
    }

    /**
     * Returns the instance of the client we're using
     * 
     * @return The FilesClient
     */
    public FilesClient getClient()
    {
        return this.client;                
    }

    /**
     * Adds a new object to the container
     * 
     * @param f    The file for this object
     * @param mimeType It's MIME type
     * @return The return code from the server
     * @throws NoSuchAlgorithmException  The MD5 implementation is not installed in the client
     * @throws IOException
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public boolean addObject (File f, String mimeType) throws NoSuchAlgorithmException, IOException, FilesAuthorizationException, FilesException
    {
        FilesObject obj = new FilesObject(f, mimeType, this);

        if (objects != null)
            objects.add(obj);
        else
        {
           objects = getObjects();
           if (objects != null)
              return objects.add (obj);
        }
        logger.fatal("Could not add Object, it seems something is wrong with this Container or FilesClient"); 
        return false;
    }

    /**
     * Creates the container represented by this instance on the server
     *
     * @return Either FilesConstants.CONTAINER_CREATED or FilesConstants.CONTAINER_EXISTED or -1 if the client has not been set 
     * @throws HttpException
     * @throws IOException
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public void createContainer () throws HttpException, IOException, FilesAuthorizationException, FilesException
    {
        if (client != null)
        {
        	client.createContainer(this.name);
        }
        else
            logger.fatal("This Container has no FilesClient defined !");

    }
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesUtil.java
public class FilesUtil
{
    private static Logger logger = Logger.getLogger(FilesUtil.class);

    /**
     * The name of the properties file we're looking for
     */
    private final static String file = "cloudfiles.properties";

    /**
     * A cache of the properties
     */
    private static Properties props = null;


    /**
     * Find the properties file in the class path and load it.
     *
     * @throws IOException
     */
    private static synchronized void loadPropertiesFromClasspath() throws IOException
    {
        props = new Properties();
        InputStream io = FilesUtil.class.getClassLoader().getResourceAsStream(file);
        if (io == null)
        {
            throw new FileNotFoundException("Property file '" + file + "' not found in the classpath.");
        }
        loadProperties(io);
    }


    /**
     * Loads properties from input stream.
     *
     * @param io
     * @throws IOException
     */
    public static void loadProperties(final InputStream io) throws IOException
    {
        if (null == io)
        {
            throw new IllegalArgumentException("Input stream cannot be null.");
        }
        props = new Properties();
        props.load(io);
    }


    /**
     * Look up a property from the properties file.
     *
     * @param key The name of the property to be found
     * @return The value of the property
     */
    public static String getProperty(final String key)
    {
        if (props == null)
        {
            try
            {
                loadPropertiesFromClasspath();
            }
            catch (Exception e)
            {
                logger.warn("Unable to load properties file.", e);
            }
        }
        return props.getProperty(key);
    }


    /**
     * Look up a property from the properties file.
     *
     * @param key The name of the property to be found
     * @return The value of the property
     */
    public static String getProperty(final String key, final String defaultValue)
    {
        if (props == null)
        {
            try
            {
                loadPropertiesFromClasspath();
            }
            catch (Exception e)
            {
                logger.warn("Unable to load properties file.", e);
            }
        }
        return props.getProperty(key, defaultValue);
    }


    /**
     * Looks up the value of a key from the properties file and converts it to an integer.
     *
     * @param key
     * @return The value of that key
     */
    public static int getIntProperty(final String key)
    {
        final String property = getProperty(key);
        if (property == null)
        {
            logger.warn("Could not load integer property " + key + ".");
            return -1;
        }
        try
        {
            return Integer.parseInt(property);
        }
        catch (NumberFormatException e)
        {
            logger.warn("Invalid format for a number in properties file: " + property + ".", e);
            return -1;
        }
    }
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesCDNContainer.java
public class FilesCDNContainer {
	private boolean enabled;
	private String userAgentACL;
	private String referrerACL;
	private int ttl;
	private String cdnURL;
	private String sslURL = null;
	private String streamingURL = null;
	private String name;
	private boolean retainLogs;
	
	/**
	 * @return the retainLogs
	 */
	public boolean getRetainLogs() {
		return retainLogs;
	}

	/**
	 * @param retainLogs the retainLogs to set
	 */
	public void setRetainLogs(boolean retainLogs) {
		this.retainLogs = retainLogs;
	}

	public FilesCDNContainer() {
	}
	
	public FilesCDNContainer(String cdnURL) {
		this.cdnURL = cdnURL;
	}
	
	public FilesCDNContainer(String cdnURL, String sslUrl) {
		this.cdnURL = cdnURL;
		this.sslURL = sslUrl;
	}
	
	public FilesCDNContainer(String cdnURL, String sslUrl, String streamingUrl) {
		this.cdnURL = cdnURL;
		this.sslURL = sslUrl;
		this.streamingURL = streamingUrl;
	}
	
	public FilesCDNContainer(String name, boolean enabled, int ttl, boolean retainLogs) {
		this.enabled = enabled;
		this.ttl = ttl;
		this.name = name;
		this.retainLogs = retainLogs;
	}
	

	
	/**
	 * @return Is this container CDN enabled
	 */
	public boolean isEnabled() {
		return enabled;
	}
	/**
	 * @param enabled the enabled to set
	 */
	public void setEnabled(boolean enabled) {
		this.enabled = enabled;
	}
	/**
	 * @return the userAgentACL
	 */
	public String getUserAgentACL() {
		return userAgentACL;
	}
	/**
	 * @param userAgentACL the userAgentACL to set
	 */
	public void setUserAgentACL(String userAgentACL) {
		this.userAgentACL = "".equals(userAgentACL) ? null : userAgentACL;
	}
	/**
	 * @return the refererACL
	 */
	public String getReferrerACL() {
		return referrerACL;
	}
	/**
	 * @param refererACL the refererACL to set
	 */
	public void setReferrerACL(String referrerACL) {
		this.referrerACL = "".equals(referrerACL) ? null : referrerACL;
	}
	/**
	 * @return the ttl
	 */
	public int getTtl() {
		return ttl;
	}
	/**
	 * @param ttl the ttl to set
	 */
	public void setTtl(int ttl) {
		this.ttl = ttl;
	}

	/**
	 * @return the cdnURL
	 */
	public String getCdnURL() {
		return cdnURL;
	}

	/**
	 * @param cdnURL the cdnURL to set
	 */
	public void setCdnURL(String cdnURL) {
		this.cdnURL = cdnURL;
	}
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return The URL for accessing content in this container via the CDN
	 */
	public String getSSLURL() {
		return sslURL;
	}

	/**
	 * 
	 * @param sslURL the sslURL to set
	 */
	void setSSLURL(String sslURL) {
		this.sslURL = sslURL;
	}
	
	/**
	 * @return The  Streaming URL for accessing content in this container via the CDN
	 */
	public String getStreamingURL() {
		return this.streamingURL;
	}

	/**
	 * 
	 * @param sstreamingURL the streamingURL to set
	 */
	void setStreamingURL(String streamingURL) {
		this.streamingURL = streamingURL;
	}
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesConstants.java
public class FilesConstants
{
    private static Logger logger = Logger.getLogger(FilesConstants.class);
    
    public static final String USER_AGENT = "java-cloudfiles/1.9.2";
    
    
    //MODULE TORRENT
    public static final String X_CHECK_TORRENT_FILEID  = "X-checkTorrentFileId";
    public static final String X_CHECK_TORRENT_VERSION  = "X-checkTorrentVersion";
    public static final String X_CHECK_TORRENT_WORKSPACEID  = "X-checkTorrentWorkSpaceId";
    public static final String X_CHECK_TORRENT_KEYFILE = "X-checkTorrentKeyFile";
    public static final String X_AUTHENTICATION_URL = "X-authenticationURL";
    
    
    /** HTTP Header token that identifies the username to Cloud Files **/
    public static final String X_STORAGE_USER_DEFAULT   = "x-auth-user";
    /** HTTP header token that identifies the password to Cloud Files **/
    public static final String X_STORAGE_PASS_DEFAULT   = "x-auth-key";
    /** HTTP header token that identifies the Storage URL after a successful user login to Cloud Files **/
    public static final String X_STORAGE_URL    = "X-Storage-Url";
    /** HTTP header that identifies the CDN Management URL after a successful login to Cloud Files **/
    public static final String X_CDN_MANAGEMENT_URL = "X-CDN-Management-URL";
    /** HTTP header token that identifies the Storage Token after a successful user login to Cloud Files **/
    public static final String X_AUTH_TOKEN  = "X-Auth-Token";
    /** HTTP header token that is returned on a HEAD request against a Container.  The value of this header is the number of Objects in the Container **/
    public static final String X_CONTAINER_OBJECT_COUNT = "X-Container-Object-Count";
    /** HTTP header token that is returned on a HEAD request against a Container.  The value of this header is the number of Objects in the Container **/
    public static final String X_CONTAINER_BYTES_USED = "X-Container-Bytes-Used";
    /** HTTP header token that is returned on a HEAD request against an Account.  The value of this header is the number of Containers in the Account **/
    public static final String X_ACCOUNT_CONTAINER_COUNT = "X-Account-Container-Count";
    /** HTTP header token that is returned on a HEAD request against an Account.  The value of this header is the total size of the Objects in the Account **/
    public static final String X_ACCOUNT_BYTES_USED = "X-Account-Bytes-Used";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_URI = "X-CDN-URI";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_SSL_URI = "X-CDN-SSL-URI";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_Streaming_URI = "X-CDN-Streaming-URI";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_TTL = "X-TTL";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_RETAIN_LOGS = "X-Log-Retention";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_ENABLED = "X-CDN-Enabled";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_USER_AGENT_ACL = "X-User-Agent-ACL";
    /** HTTP header token that is returned by calls to the CDN Management API **/
    public static final String X_CDN_REFERRER_ACL = "X-Referrer-ACL ";
    /** HTTP header used by Cloud Files for the source of an object being copied. The value of this header is /<sourcecontainer>/<sourceobject> **/
    public static final String X_COPY_FROM = "X-Copy-From";
    /** HTTP Header used by Cloud Files for the MD5Sum of the object being created in a Container **/
    public static final String E_TAG = "ETag";
    /** HTTP Header used for Object Manifest **/
    public static final String MANIFEST_HEADER = "X-Object-Manifest";
    /** These constants are used for performing queries on the content of a container **/
    public static final String LIST_CONTAINER_NAME_QUERY = "prefix";
    public static final String LIST_CONTAINER_LIMIT_OBJ_COUNT_QUERY = "limit";
    public static final String LIST_CONTAINER_START_OFFSET_QUERY = "offset";
    
    public static final int CONTAINER_NAME_LENGTH = 256;
    public static final int OBJECT_NAME_LENGTH = 1024;
    public static final int METADATA_NAME_LENGTH = 1024;
    public static final int METADATA_VALUE_LENGTH = 1024;
    
    public static final String X_PURGE_EMAIL = "X-Purge-Email";
    
    /** Prefix Cloud Files expects on all Meta data headers on Objects **/
    public static final String X_OBJECT_META = "X-Object-Meta-";

    public static Properties MIMETYPES = new Properties ();
    
    static {
    	try
        {
    		MIMETYPES.load (FilesConstants.class.getResourceAsStream("MIME.types"));
        }
        catch (IOException err)
        {
            logger.warn ("Could not load MIME.types all refrences to FilesConstants.MIMETYPES will return null.", err);
        }
    }

    /**
     * Convenience method to get a MIME Type.  If none is found it will return "application/octet-stream"
     * 
     * @param fileExt
     * @return The suggested MIME type for the file extention.  
     */
    public static String getMimetype (String fileExt)
    {
    	return FilesConstants.MIMETYPES.getProperty(fileExt.toLowerCase(), "application/octet-stream");
    }
}
src/main/java/com/rackspacecloud/client/cloudfiles/expections/FilesInvalidNameException.java
public class FilesInvalidNameException extends FilesException {
	/**
	 * 
	 */
	private static final long serialVersionUID = -9043382616400647532L;

	public FilesInvalidNameException(String name) {
		super("Invalid name: " + name, null, null);
	}
	
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesObject.java
public class FilesObject
{
    private String container;
    private String name;
    private String md5sum = null;
    private long size = -1;
    private String mimeType = null;
    private String lastModified = null;
    private FilesClient client = null;

    private static Logger logger = Logger.getLogger(FilesObject.class);

    /**
     * Constructs a new FilesObject (from the file system)
     * 
     * @param obj        A file representing the object
     * @param mimeType   Its MIME type
     * @param container  The container it lives in
     * @throws NullPointerException      A null parameter was passed in
     * @throws NoSuchAlgorithmException  MD5 was not installed on the client.
     * @throws IOException               There was an I/O error talking to the server
     */
    FilesObject (File obj, String mimeType, FilesContainer container) throws NullPointerException, NoSuchAlgorithmException, IOException
    {
    	if (obj != null)
        {
            if (obj.exists())
            {
                if (!obj.isDirectory())
                {
                    setName (obj.getName());
                    setMd5sum(FilesClient.md5Sum(obj));
                    setSize(obj.length());
                    setMimeType(mimeType);
                    setClient(container.getClient ());
                    setContainer(container.getName());
                }
                else
                {
                	logger.fatal("Can not create Directories as FSObjects create a FilesContainer for this object");
                	throw new NullPointerException("File Object was a directory !");
                }
            }
            else
            {
                logger.fatal("File object must exist so we can create an MD5SUM for it !");
                throw new NullPointerException ("The file object provided does not exist.");
            }
        }
    	else
        {
            logger.fatal("Not possible to create a FilesObject from a null File.");
            throw new NullPointerException ("File Object passed was null !");
        }
    }

    /**
     * Creates a new FilesObject with data from the server
     * 
     * @param name      The name of the object
     * @param container The name of it's container
     * @param client    The client it can be accessed through.
     */
    FilesObject(String name, String container, FilesClient client )
    {
        this.name = name;
        this.container = container;
        this.client = client;
    }

    /**
     * @return The object's name on the server
     */
    public String getName()
    {
        return name;
    }

    /**
     * Set's the objects name (up to a maximum of 128 characters)
     * 
     * @param name The new name
     */
    public void setName(String name)
    {
        if (name.length() > FilesConstants.OBJECT_NAME_LENGTH)
        {
            logger.warn("Object name larger than " + FilesConstants.OBJECT_NAME_LENGTH + " characters truncating from: "+name);
            this.name = name.substring(0, FilesConstants.OBJECT_NAME_LENGTH);
            logger.warn("Object name truncated to : "+name);
        }
        this.name = name;
    }

    /**
     * @return The MIME type of the object, pulled from the server
     * @throws HttpException
     * @throws IOException
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public String getMimeType() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
        if (mimeType == null) {
        	getMetaData();
        }
        return mimeType;
    }

    /**
     * @param mimeType The new MIME type for this object
     */
    public void setMimeType(String mimeType)
    {
        this.mimeType = mimeType;
    }

    /**
     * Get's the MD5 Checksum for this object 
     * 
     * @return The MD5 checksum, returned as a base 16 encoded string
     * @throws HttpException
     * @throws IOException
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public String getMd5sum() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
        if (md5sum == null) {
        	getMetaData();
        }
        return md5sum;
    }

    /**
     * 
     * @param md5sum The MD5 sum of the file (as a hex-encoded string)
     */
    public void setMd5sum(String md5sum)
    {
        this.md5sum = md5sum;
    }

    /**
     * Returns the size of the object, in bytes
     * 
     * @return The size of the object in bytes
     * @throws HttpException
     * @throws IOException
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public long getSize() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
        if (size == -1) {
        	getMetaData();
        }
        return size;
    }

    /**
     * Set the size of the object
     * 
     * @param size The size, in bytes
     */
    public void setSize(long size)
    {
        this.size = size;
    }

    /**
     * Download the object and write it to a local file
     * 
     * @param localFile The file
     * @return The number of bytes written
     * @throws FileNotFoundException Could not find the local file (does the path to it exist?)
     * @throws HttpException There was an error communicating with the server
     * @throws IOException There was an I/O exception communicating with the server or writing the file.
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public long writeObjectToFile (File localFile) throws FileNotFoundException, HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
    	long bytesWritten = 0;
    	FileOutputStream out = new FileOutputStream(localFile);
    	InputStream in =  getObjectAsStream();
        byte[] data = new byte[1024];
        
        int ret = in.read (data, 0, data.length);
        if (ret == -1)
        {
        	out.write(data, 0, data.length);
        	out.flush();
            out.close();
            in.close ();
        	return data.length;
        }

        while (ret != -1)
        {
        	bytesWritten += ret;
            out.write(data, 0, ret);
            ret = in.read (data, 0, data.length);
        }
        out.flush();
        out.close();
        in.close ();

    	return bytesWritten;
    }

    /**
     * Returns an inputStream with the contents of the object
     * 
     * @return An inputStream that will return the contents of the object
     * @throws HttpException There was an error communicating with the server
     * @throws IOException There was an I/O exception communicating with the server or writing the file.
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public InputStream getObjectAsStream() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
    	return client.getObjectAsStream(container, name);
    }

    /**
     * Download the contents of the object
     * 
     * @return The content of the object
     * @throws HttpException There was an error communicating with the server
     * @throws IOException There was an I/O exception communicating with the server or writing the file.
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     * @throws FilesNotFoundException 
     */
    public byte[] getObject() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException, FilesNotFoundException
    {
    	return client.getObject(container, name);
    }

    /**
     * Return any metadata associated with this object
     * 
     * @return The metadata
     * @throws HttpException There was an error communicating with the server
     * @throws IOException There was an I/O exception communicating with the server or writing the file.
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public FilesObjectMetaData getMetaData() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
    	FilesObjectMetaData metaData = client.getObjectMetaData(container, name);
        if (metaData != null)
        {
            setMd5sum(metaData.getETag());
            setSize(Long.parseLong (metaData.getContentLength()));
            setMimeType(metaData.getMimeType());
            setLastModified(metaData.getLastModified());
        }
        return metaData;
    }

    /**
     * @param client The new client for this object
     */
    void setClient(FilesClient client)
    {
        this.client = client;
    }

    /**
     * @param container The new container
     */
    void setContainer (String container)
    {
        this.container = container;
    }
    
    public static FilesObject uploadObject(File obj, String mimeType, FilesContainer container)  throws IOException, FilesException {
		return uploadObject(obj, mimeType, container, null);
    }

    public static FilesObject uploadObject(File obj, String mimeType, FilesContainer container, IFilesTransferCallback callback) throws IOException, FilesException {
    	FilesObject result = null;
    	try {
    		result = new FilesObject(obj, mimeType, container);
    		FilesClient client = container.getClient();
    		client.storeObjectAs(container.getName(), obj, mimeType, obj.getName(), callback);
    	}
    	catch (NoSuchAlgorithmException nsae) {
    		// This should never happen
    		logger.fatal("Install doesn't have MD5, can't upload files", nsae);
    	}
    	catch (HttpException ex) {
    		throw new FilesException("Error in network operation", ex);
    	}
    	
		return result;
    }

    /**
     * Returns the size as a human readable string, rounding to the nearest KB/MB/GB
     * 
     * @return The size of the object as a human readable string.
     * @throws HttpException There was an error communicating with the server
     * @throws IOException There was an I/O exception communicating with the server or writing the file.
     * @throws FilesAuthorizationException 
     * @throws FilesInvalidNameException 
     */
    public String getSizeString () throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
    {
        long kb = 1024;
        long mb = kb*1024;
        long gb = mb*1024;

        //Make sure the size is correct.
        getMetaData();

            //KB
        if (size > gb)
            return (size/gb) + " GB";
        else if (size > mb)
            return (size/mb)+" MB";
        else if (size > kb)
            return (size/kb) +" KB";
        else
            return getSize ()+" Bytes";

    }

	/**
	 * @return the lastModified
	 */
	public String getLastModified() throws HttpException, IOException, FilesAuthorizationException, FilesInvalidNameException
	{
		if (lastModified == null) { 
			getMetaData();
		}
		return lastModified;
	}

	/**
	 * @param lastModified the lastModified to set
	 */
	public void setLastModified(String lastModified) {
		this.lastModified = lastModified;
	}
	
	/**
	 * 
	 * @return True if the "object" is really a directory.
	 */
	public boolean isDirectory() {
		return this.size == 0 && "application/directory".equals(this.mimeType);
	}
	
	/**
	 * 
	 * @return The CDN url for the object (if its container has been CDN enabled), null if 
	 *         the container hasn't been CDN enabled.
	 * @throws HttpException 
	 * @throws IOException 
	 * @throws FilesException 
	 */
	public String getCDNURL () throws FilesException, IOException, HttpException  {
		try {
			FilesCDNContainer c = client.getCDNContainerInfo(container);
			return c.getCdnURL() + "/" + name;
		}
		catch (FilesNotFoundException fnfe) {
			// Not enabled
			return null;
		}
		
	}
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesObjectMetaData.java
public class FilesObjectMetaData
{
    private String mimeType;
    private String contentLength;
    private String eTag;
    private String lastModified;
    private Map<String, String> metaData = new HashMap<String, String>();

    /**
     * An object storing the metadata for an FS Cloud object
     * 
     * @param mimeType      The MIME type for the object
     * @param contentLength The content-length (e.g., size) of the object
     * @param eTag          The MD5 check-sum of the object's contents
     * @param lastModified  The last time the object was modified.
     */
    public FilesObjectMetaData(String mimeType, String contentLength, String eTag, String lastModified)
    {
        this.mimeType = mimeType;
        this.contentLength = contentLength;
        this.eTag = eTag;
        this.lastModified = lastModified;
    }

    /**
     * An object storing the metadata for an FS Cloud object
     * 
     * @param mimeType      The MIME type for the object
     * @param contentLength The content-length (e.g., size) of the object
     * @param lastModified  The last time the object was modified.
     */
    public FilesObjectMetaData(String mimeType, String contentLength, String lastModified)
    {
        this.mimeType = mimeType;
        this.contentLength = contentLength;
        this.lastModified = lastModified;
    }

    /**
     * The last time the object was modified
     * 
     * @return The last modification date
     */
    public String getLastModified()
    {
        return lastModified;
    }

    /**
     * Set the last time the object was modified 
     */
   void setLastModified(String lastModified)
    {
        this.lastModified = lastModified;
    }

    /**
     * The MIME type of the object
     * 
     * @return The MIME type of the object
     */
    public String getMimeType()
    {
        return mimeType;
    }

    /**
     * Set's the MIME type of the object
     * 
     * @param mimeType
     */
    void setMimeType(String mimeType)
    {
        this.mimeType = mimeType;
    }

    /**
     * The size of the object, in bytes
     * 
     * @return The size of the object
     */
    public String getContentLength()
    {
        return contentLength;
    }

    /**
     * Set the size of the object
     * 
     * @param contentLength The new content length
     */
    void setContentLength(String contentLength)
    {
        this.contentLength = contentLength;
    }

    /**
     * The MD5 checksum represented in a hex-encoded string
     * 
     * @return The eTag
     */
    public String getETag()
    {
        return eTag;
    }

    /**
     * Set the MD5 checksum for this object
     * 
     * @param eTag The new eTag
     */
    void setETag(String eTag)
    {
        this.eTag = eTag;
    }

    /**
     * The metadata associated with this object.
     * 
     * @return The object's metadata
     */
    public Map<String, String> getMetaData()
    {
        return metaData;
    }

    /**
     * Set new metatdata for this object.  Warning, this metadata clears out all old metadata.  To add new fields, use 
     * <code>setMetaData</code> instead.
     * 
     * @param metaData The new metadata
     */
    public void setMetaData(Map<String, String> metaData)
    {
        this.metaData = metaData;
    }

    /**
     * Add a new meta-data entry (or overwrite an old one)
     * 
     * @param key   The key for this key-value pair
     * @param value The value for this key-value pair
     */
    public void addMetaData (String key, String value)
    {
        metaData.put(FilesConstants.X_OBJECT_META.concat(key), value);
    }

    /** Constructs a new header for a given metadata key
     * @param key The key
     * @return The name of a header for this key
     */
    public String getMetaKey (String key)
    {
        return metaData.get(FilesConstants.X_OBJECT_META.concat(key));
    }

    /**
     * Tests to see if a given metadata key is present for this object
     * 
     * @param key The key to check for
     * @return True if it's present, false otherwise.
     */
    public boolean containesKey (String key)
    {
        return metaData.containsKey(FilesConstants.X_OBJECT_META.concat(key));
    }
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesAccountInfo.java
public class FilesAccountInfo {
	private long bytesUsed;
	private int containerCount;
	
	public FilesAccountInfo(long bytes, int containers) {
		bytesUsed = bytes;
		containerCount = containers;
	}
	/**
	 * Returns the total number of bytes used by all objects in a given account.
	 * 
	 * @return the bytesUsed
	 */
	public long getBytesUsed() {
		return bytesUsed;
	}
	/**
	 * @param bytesUsed The number of bytes in the account
	 */
	public void setBytesUsed(long bytesUsed) {
		this.bytesUsed = bytesUsed;
	}
	/**
	 * The number of containers in a given account.
	 * 
	 * @return the containerCount
	 */
	public int getContainerCount() {
		return containerCount;
	}
	/**
	 * @param containerCount the containerCount to set
	 */
	public void setContainerCount(int containerCount) {
		this.containerCount = containerCount;
	}
	
	
}
src/main/java/com/rackspacecloud/client/cloudfiles/IFilesTransferCallback.java
public interface IFilesTransferCallback {
	public void progress(long n);

}
src/main/java/com/rackspacecloud/client/cloudfiles/expections/FilesContainerExistsException.java
public class FilesContainerExistsException extends FilesException {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7282149064519490145L;

	/**
	 * @param message
	 * @param httpHeaders
	 * @param httpStatusLine
	 */
	public FilesContainerExistsException(String message, Header[] httpHeaders,
			StatusLine httpStatusLine) {
		super(message, httpHeaders, httpStatusLine);
	}

}
src/main/java/com/rackspacecloud/client/cloudfiles/expections/FilesException.java
@SuppressWarnings("serial")
public class FilesException extends HttpException {
    private Header [] httpHeaders;
    private StatusLine httpStatusLine;

    /**
     * An exception generated when a client tries to do something they aren't authorized to do.  
     * 
     * @param message        The message
     * @param httpHeaders    The returned HTTP headers
     * @param httpStatusLine The HTTP Status lined returned
     */
    public FilesException(String message, Header [] httpHeaders, StatusLine httpStatusLine)
    {
    	super (message);
    	this.httpHeaders = httpHeaders;
    	this.httpStatusLine = httpStatusLine;
    }
    
    public FilesException(String message, Throwable cause) {
    	super(message, cause);
    }

    /**
     * @return The HTTP headers returned by the server
     */
    public Header[] getHttpHeaders ()
    {
        return httpHeaders;
    }

    /**
     * @return The HTTP Headers returned by the server in a human-readable string.
     */
    public String getHttpHeadersAsString ()
    {
    	if (httpHeaders == null) return "";
    	
        StringBuffer httpHeaderString = new StringBuffer();
        for (Header h: httpHeaders)
            httpHeaderString.append(h.getName() + ": " + h.getValue() + "\n"); 

        return httpHeaderString.toString();
    }

    /**
     * @return The HTTP status line from the server
     */
    public StatusLine getHttpStatusLine ()
    {
        return httpStatusLine;
    }

    /**
     * @return The numeric HTTP status code from the server
     */
    public int getHttpStatusCode ()
    {
        return httpStatusLine == null? -1 : httpStatusLine.getStatusCode();
    }

    /**
     * @return The HTTP status message from the server
     */
    public String getHttpStatusMessage ()
    {
        return httpStatusLine == null ? null : httpStatusLine.getReasonPhrase();
    }

    /**
     * @return The version of HTTP used.
     */
    public String getHttpVersion ()
    {
        return httpStatusLine == null ? null : httpStatusLine.getProtocolVersion().toString(); 
    }

}
src/main/java/com/rackspacecloud/client/cloudfiles/expections/FilesNotFoundException.java
public class FilesNotFoundException extends FilesException {

	/**
	 * 
	 */
	private static final long serialVersionUID = 111718445621236026L;

	/**
	 * @param message
	 * @param httpHeaders
	 * @param httpStatusLine
	 */
	public FilesNotFoundException(String message, Header[] httpHeaders,
			StatusLine httpStatusLine) {
		super(message, httpHeaders, httpStatusLine);
	}

}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesClient.java
public class FilesClient {

    public static final String VERSION = "v1";
    private String username = null;
    private String password = null;
    private String account = null;
    private String authenticationURL;
    private int connectionTimeOut;
    private String storageURL = null;
    private String cdnManagementURL = null;
    private String authToken = null;
    private boolean isLoggedin = false;
    private boolean useETag = true;
    private boolean snet = false;
    private String snetAddr = "https://snet-";
    private HttpClient client = null;
    private static Logger logger = Logger.getLogger(FilesClient.class);

    /**
     * @param client The HttpClient to talk to Swift
     * @param username The username to log in to
     * @param password The password
     * @param account The Cloud Files account to use
     * @param connectionTimeOut The connection timeout, in ms.
     */
    public FilesClient(HttpClient client, String username, String password,
            String authUrl, String account, int connectionTimeOut) {

        this.client = WebClientDevWrapper.wrapClient(client);
        this.username = username;
        this.password = password;
        this.account = account;
        if (authUrl == null) {
            authUrl = FilesUtil.getProperty("auth_url");
        }
        if (account != null && account.length() > 0) {
            this.authenticationURL = authUrl + VERSION + "/" + account
                    + FilesUtil.getProperty("auth_url_post");
        } else {
            this.authenticationURL = authUrl;
        }
        this.connectionTimeOut = connectionTimeOut;

        setUserAgent(FilesConstants.USER_AGENT);

        if (logger.isDebugEnabled()) {
            logger.debug("UserName: " + this.username);
            logger.debug("AuthenticationURL: " + this.authenticationURL);
            logger.debug("ConnectionTimeOut: " + this.connectionTimeOut);
        }
    }

    /**
     * @param username The username to log in to
     * @param password The password
     * @param account The Cloud Files account to use
     * @param connectionTimeOut The connection timeout, in ms.
     */
    public FilesClient(String username, String password, String authUrl,
            String account, final int connectionTimeOut) {
        this(new DefaultHttpClient() {
            @Override
            protected HttpParams createHttpParams() {
                BasicHttpParams params = new BasicHttpParams();
                org.apache.http.params.HttpConnectionParams.setSoTimeout(
                        params, connectionTimeOut);
                params.setParameter("http.socket.timeout", connectionTimeOut);
                return params;
            }

            @SuppressWarnings("deprecation")
            @Override
            protected ClientConnectionManager createClientConnectionManager() {
                SchemeRegistry schemeRegistry = new SchemeRegistry();
                schemeRegistry.register(new Scheme("http", PlainSocketFactory
                        .getSocketFactory(), 80));
                schemeRegistry.register(new Scheme("https", SSLSocketFactory
                        .getSocketFactory(), 443));
                return new ThreadSafeClientConnManager(createHttpParams(),
                        schemeRegistry);
            }
        }, username, password, authUrl, account, connectionTimeOut);

    }

    /**
     * This method uses the default connection time out of CONNECTON_TIMEOUT. If
     * <code>account</code> is null, "Mosso Style" authentication is assumed,
     * otherwise standard Cloud Files authentication is used.
     *
     * @param username
     * @param password
     * @param authUrl
     */
    public FilesClient(String username, String password, String authUrl) {
        this(username, password, authUrl, null, FilesUtil.getIntProperty("connection_timeout"));
    }

    /**
     * Mosso-style authentication (No accounts). https://10.30.230.191:8080
     *
     * @param username Your CloudFiles username
     * @param apiAccessKey Your CloudFiles API Access Key
     */
    public FilesClient(String username, String apiAccessKey) {
        this(username, apiAccessKey, null, null, FilesUtil
                .getIntProperty("connection_timeout"));
        // lConnectionManagerogger.warn("LGV");
        // logger.debug("LGV:" + client.getHttpConnectionManager());
    }

    /**
     * This method uses the default connection time out of CONNECTON_TIMEOUT and
     * username, password, and account from FilesUtil
     */
    public FilesClient() {
        this(FilesUtil.getProperty("username"), FilesUtil
                .getProperty("password"), null, FilesUtil
                .getProperty("account"), FilesUtil
                .getIntProperty("connection_timeout"));
    }

    /**
     * Returns the Account associated with the URL
     *
     * @return The account name
     */
    public String getAccount() {
        return account;
    }

    /**
     * Set the Account value and reassemble the Authentication URL.
     *
     * @param account
     */
    public void setAccount(String account) {
        this.account = account;
        if (account != null && account.length() > 0) {
            this.authenticationURL = FilesUtil.getProperty("auth_url")
                    + VERSION + "/" + account
                    + FilesUtil.getProperty("auth_url_post");
        } else {
            this.authenticationURL = FilesUtil.getProperty("auth_url");
        }
    }

    /**
     * Log in to CloudFiles. This method performs the authentication and sets up
     * the client's internal state.
     *
     * @return true if the login was successful, false otherwise.
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     */
    public boolean login() throws IOException, HttpException {

        isLoggedin = false;
        HttpGet method = new HttpGet(authenticationURL);
        method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);

        method.setHeader(FilesUtil.getProperty("auth_user_header", FilesConstants.X_STORAGE_USER_DEFAULT), username);
        method.setHeader(FilesUtil.getProperty("auth_pass_header", FilesConstants.X_STORAGE_PASS_DEFAULT), password);

        FilesResponse response = new FilesResponse(client.execute(method));

        if (response.loginSuccess()) {
            isLoggedin = true;
            if (usingSnet() || envSnet()) {
                storageURL = snetAddr + response.getStorageURL().substring(8);
            } else {
                storageURL = response.getStorageURL();
            }
            cdnManagementURL = response.getCDNManagementURL();
            authToken = response.getAuthToken();
            logger.debug("storageURL: " + storageURL);
            logger.debug("authToken: " + authToken);
            logger.debug("cdnManagementURL:" + cdnManagementURL);
            logger.debug("ConnectionManager:" + client.getConnectionManager());
        }
        method.abort();

        return this.isLoggedin;
    }

    public boolean loginKeystone() throws UnknownHostException, SocketException, IOException, UnauthorizeException {
        isLoggedin = false;

        String tenantNameKeystone = "";
        String userNameKeystone = username;
        if (username.split(":").length >= 2) {
            tenantNameKeystone = username.split(":")[0];
            userNameKeystone = username.split(":")[1];
        }

        String content = "{\"auth\": {\"passwordCredentials\": {\"username\": \"" + userNameKeystone + "\", \"password\": \"" + password + "\"}, \"tenantName\":\"" + tenantNameKeystone + "\"}}";

        HttpPost method = new HttpPost(authenticationURL);
        method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
        method.setHeader("Content-type", "application/json");

        InputStream stream = new ByteArrayInputStream(content.getBytes("UTF-8"));
        InputStreamEntity entity = new InputStreamEntity(stream, content.getBytes("UTF-8").length);
        method.setEntity(entity);

        stream.close();

        FilesResponse response = new FilesResponse(client.execute(method));

        if (response.getStatusCode() == 401) {
            throw new UnauthorizeException("Incorrect user/password.", response.getResponseHeaders(), response.getStatusLine());
        }

        InputStream in = response.getResponseBodyAsStream();
        String outString = IOUtils.toString(in, "UTF-8");
        in.close();

        JsonElement jelement = new JsonParser().parse(outString);
        JsonObject jobject = jelement.getAsJsonObject();

        jobject = jobject.getAsJsonObject("access");
        jobject = jobject.getAsJsonObject("token");
        authToken = jobject.get("id").getAsString();

        jobject = jelement.getAsJsonObject();
        jobject = jobject.getAsJsonObject("access");

        JsonArray jarray = jobject.getAsJsonArray("serviceCatalog");

        for (JsonElement serviceCatalog : jarray) {
            JsonObject jsonLineItem = serviceCatalog.getAsJsonObject();

            String value = jsonLineItem.get("type").getAsString();

            if (value.compareTo("object-store") == 0) {
                JsonArray jarray2 = jsonLineItem.getAsJsonArray("endpoints");
                JsonElement endpoint = jarray2.get(0);
                storageURL = endpoint.getAsJsonObject().get("publicURL").getAsString();

                isLoggedin = true;
                cdnManagementURL = "";

                break;
            }
        }

        logger.debug("storageURL: " + storageURL);
        logger.debug("authToken: " + authToken);
        logger.debug("cdnManagementURL:" + cdnManagementURL);
        logger.debug("ConnectionManager:" + client.getConnectionManager());

        return isLoggedin;
    }
    
    public boolean loginKeystoneV3() throws UnknownHostException, SocketException, IOException, UnauthorizeException {
        isLoggedin = false;

        String tenantNameKeystone = "";
        String userNameKeystone = username;
        if (username.split(":").length >= 2) {
            tenantNameKeystone = username.split(":")[0];
            userNameKeystone = username.split(":")[1];
        }

        String content = "{\"auth\": {\"identity\": {\"methods\": [\"password\"],\"password\": {\"user\":{\"domain\": {\"name\": \"d_Stacksync\"},\"name\": \""+userNameKeystone+"\",\"password\": \""+password+"\"}}},\"scope\": {\"project\": {\"domain\": {\"name\": \"d_Stacksync\"},\"name\": \""+tenantNameKeystone+"\"}}}}";
        //String content = "{\"auth\": {\"passwordCredentials\": {\"username\": \"" + userNameKeystone + "\", \"password\": \"" + password + "\"}, \"tenantName\":\"" + tenantNameKeystone + "\"}}";

        HttpPost method = new HttpPost(authenticationURL);
        method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
        method.setHeader("Content-type", "application/json");

        InputStream stream = new ByteArrayInputStream(content.getBytes("UTF-8"));
        InputStreamEntity entity = new InputStreamEntity(stream, content.getBytes("UTF-8").length);
        method.setEntity(entity);

        stream.close();

        FilesResponse response = new FilesResponse(client.execute(method));

        if (response.getStatusCode() == 401) {
            throw new UnauthorizeException("Incorrect user/password.", response.getResponseHeaders(), response.getStatusLine());
        }

        InputStream in = response.getResponseBodyAsStream();
        String outString = IOUtils.toString(in, "UTF-8");
        in.close();
        
        Header header = response.getResponseHeaders("X-Subject-Token")[0];
        authToken = header.getValue();

        JsonElement jelement = new JsonParser().parse(outString);
        JsonObject jobject = jelement.getAsJsonObject();

        //jobject = jelement.getAsJsonObject();
        jobject = jobject.getAsJsonObject("token");

        JsonArray jarray = jobject.getAsJsonArray("catalog");

        for (JsonElement serviceCatalog : jarray) {
            JsonObject jsonLineItem = serviceCatalog.getAsJsonObject();

            String value = jsonLineItem.get("type").getAsString();

            if (value.compareTo("object-store") == 0) {
                JsonArray jarray2 = jsonLineItem.getAsJsonArray("endpoints");
                
                for (JsonElement endpoint : jarray2) {
                    JsonObject endpointObject = endpoint.getAsJsonObject();
                    String publicURL = endpointObject.get("interface").getAsString();
                    if (publicURL.compareTo("public") == 0) {
                        storageURL = endpointObject.get("url").getAsString();
                        isLoggedin = true;
                        cdnManagementURL = "";

                        break;
                    }
                }
            }
        }

        logger.debug("storageURL: " + storageURL);
        logger.debug("authToken: " + authToken);
        logger.debug("cdnManagementURL:" + cdnManagementURL);
        logger.debug("ConnectionManager:" + client.getConnectionManager());

        return isLoggedin;
    }

    /**
     * Log in to CloudFiles. This method performs the authentication and sets up
     * the client's internal state.
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     */
    public boolean login(String authToken, String storageURL,
            String cdnManagmentUrl) throws IOException, HttpException {
        isLoggedin = true;
        this.storageURL = storageURL;
        this.cdnManagementURL = cdnManagmentUrl;
        this.authToken = authToken;
        return true;
    }

    /**
     * List all of the containers available in an account, ordered by container
     * name.
     *
     * @return null if the user is not logged in or the Account is not found. A
     * List of FSContainers with all of the containers in the account. if there
     * are no containers in the account, the list will be zero length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainerInfo> listContainersInfo() throws IOException,
            HttpException, FilesAuthorizationException, FilesException {
        return listContainersInfo(-1, null);
    }

    /**
     * List the containers available in an account, ordered by container name.
     *
     * @param limit The maximum number of containers to return. -1 returns an
     * unlimited number.
     *
     * @return null if the user is not logged in or the Account is not found. A
     * List of FSContainers with all of the containers in the account. if there
     * are no containers in the account, the list will be zero length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainerInfo> listContainersInfo(int limit)
            throws IOException, HttpException, FilesAuthorizationException, FilesException {
        return listContainersInfo(limit, null);
    }

    /**
     * List the containers available in an account, ordered by container name.
     *
     * @param limit The maximum number of containers to return. -1 returns an
     * unlimited number.
     * @param marker Return containers that occur after this lexicographically.
     *
     * @return null if the user is not logged in or the Account is not found. A
     * List of FSContainers with all of the containers in the account. if there
     * are no containers in the account, the list will be zero length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainerInfo> listContainersInfo(int limit, String marker)
            throws IOException, HttpException, FilesAuthorizationException, FilesException {
        if (!this.isLoggedin()) {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        HttpGet method = null;
        try {
            LinkedList<NameValuePair> parameters = new LinkedList<NameValuePair>();
            if (limit > 0) {
                parameters.add(new BasicNameValuePair("limit", String.valueOf(limit)));
            }
            if (marker != null) {
                parameters.add(new BasicNameValuePair("marker", marker));
            }
            parameters.add(new BasicNameValuePair("format", "xml"));
            String uri = makeURI(storageURL, parameters);
            method = new HttpGet(uri);
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.removeHeaders(FilesConstants.X_AUTH_TOKEN);
                if (login()) {
                    method = new HttpGet(uri);
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            }

            if (response.getStatusCode() == HttpStatus.SC_OK) {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document document = builder.parse(response.getResponseBodyAsStream());

                NodeList nodes = document.getChildNodes();
                Node accountNode = nodes.item(0);
                if (!"account".equals(accountNode.getNodeName())) {
                    logger.error("Got unexpected type of XML");
                    return null;
                }
                ArrayList<FilesContainerInfo> containerList = new ArrayList<FilesContainerInfo>();
                NodeList containerNodes = accountNode.getChildNodes();
                for (int i = 0; i < containerNodes.getLength(); ++i) {
                    Node containerNode = containerNodes.item(i);
                    if (!"container".equals(containerNode.getNodeName())) {
                        continue;
                    }
                    String name = null;
                    int count = -1;
                    long size = -1;
                    NodeList objectData = containerNode.getChildNodes();
                    for (int j = 0; j < objectData.getLength(); ++j) {
                        Node data = objectData.item(j);
                        if ("name".equals(data.getNodeName())) {
                            name = data.getTextContent();
                        } else if ("bytes".equals(data.getNodeName())) {
                            size = Long.parseLong(data.getTextContent());
                        } else if ("count".equals(data.getNodeName())) {
                            count = Integer.parseInt(data.getTextContent());
                        } else {
                            logger.debug("Unexpected container-info tag:"
                                    + data.getNodeName());
                        }
                    }
                    if (name != null) {
                        FilesContainerInfo obj = new FilesContainerInfo(name, count, size);
                        containerList.add(obj);
                    }
                }
                return containerList;
            } else if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                return new ArrayList<FilesContainerInfo>();
            } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                throw new FilesNotFoundException("Account not Found",
                        response.getResponseHeaders(), response.getStatusLine());
            } else {
                throw new FilesException("Unexpected Return Code",
                        response.getResponseHeaders(), response.getStatusLine());
            }
        } catch (Exception ex) {
            throw new FilesException("Unexpected problem, probably in parsing Server XML", ex);
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }

    /**
     * List the containers available in an account.
     *
     * @return null if the user is not logged in or the Account is not found. A
     * List of FilesContainer with all of the containers in the account. if
     * there are no containers in the account, the list will be zero length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainer> listContainers() throws IOException,
            HttpException, FilesAuthorizationException, FilesException {
        return listContainers(-1, null);
    }

    /**
     * List the containers available in an account.
     *
     * @param limit The maximum number of containers to return. -1 denotes no
     * limit.
     *
     * @return null if the user is not logged in or the Account is not found. A
     * List of FilesContainer with all of the containers in the account. if
     * there are no containers in the account, the list will be zero length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainer> listContainers(int limit) throws IOException,
            HttpException, FilesAuthorizationException, FilesException {
        return listContainers(limit, null);
    }

    /**
     * List the containers available in an account.
     *
     * @param limit The maximum number of containers to return. -1 denotes no
     * limit.
     * @param marker Only return containers after this container. Null denotes
     * starting at the beginning (lexicographically).
     *
     * @return A List of FilesContainer with all of the containers in the
     * account. if there are no containers in the account, the list will be zero
     * length.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesContainer> listContainers(int limit, String marker)
            throws IOException, HttpException, FilesException {
        if (!this.isLoggedin()) {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }
        HttpGet method = null;
        try {
            LinkedList<NameValuePair> parameters = new LinkedList<NameValuePair>();

            if (limit > 0) {
                parameters.add(new BasicNameValuePair("limit", String.valueOf(limit)));
            }
            if (marker != null) {
                parameters.add(new BasicNameValuePair("marker", marker));
            }

            String uri = parameters.size() > 0 ? makeURI(storageURL, parameters) : storageURL;
            method = new HttpGet(uri);
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpGet(uri);
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            }

            if (response.getStatusCode() == HttpStatus.SC_OK) {
                // logger.warn(method.getResponseCharSet());
                StrTokenizer tokenize = new StrTokenizer(response.getResponseBodyAsString());
                tokenize.setDelimiterString("\n");
                String[] containers = tokenize.getTokenArray();
                ArrayList<FilesContainer> containerList = new ArrayList<FilesContainer>();
                for (String container : containers) {
                    containerList.add(new FilesContainer(container, this));
                }
                return containerList;
            } else if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                return new ArrayList<FilesContainer>();
            } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                throw new FilesNotFoundException("Account was not found",
                        response.getResponseHeaders(), response.getStatusLine());
            } else {
                throw new FilesException("Unexpected resposne from server",
                        response.getResponseHeaders(), response.getStatusLine());
            }
        } catch (Exception ex) {
            ex.toString();
            throw new FilesException("Unexpected error, probably parsing Server XML", ex);
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }

    /**
     * List all of the objects in a container with the given starting string.
     *
     * @param container The container name
     * @param startsWith The string to start with
     * @param path Only look for objects in this path
     * @param limit Return at most <code>limit</code> objects
     * @param marker Returns objects lexicographically greater than
     * <code>marker</code>. Used in conjunction with <code>limit</code> to
     * paginate the list.
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjectsStartingWith(String container,
            String startsWith, String path, int limit, String marker)
            throws IOException, FilesException {
        return listObjectsStartingWith(container, startsWith, path, limit, marker, null);
    }

    /**
     * List all of the objects in a container with the given starting string.
     *
     * @param container The container name
     * @param startsWith The string to start with
     * @param path Only look for objects in this path
     * @param limit Return at most <code>limit</code> objects
     * @param marker Returns objects lexicographically greater than
     * <code>marker</code>. Used in conjunction with <code>limit</code> to
     * paginate the list.
     * @param delimter Use this argument as the delimiter that separates
     * "directories"
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjectsStartingWith(String container,
            String startsWith, String path, int limit, String marker,
            Character delimiter) throws IOException, FilesException {
        if (!this.isLoggedin()) {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }
        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(container);
        }
        HttpGet method = null;
        try {
            LinkedList<NameValuePair> parameters = new LinkedList<NameValuePair>();
            parameters.add(new BasicNameValuePair("format", "xml"));
            if (startsWith != null) {
                parameters.add(new BasicNameValuePair(FilesConstants.LIST_CONTAINER_NAME_QUERY, startsWith));
            }
            if (path != null) {
                parameters.add(new BasicNameValuePair("path", path));
            }
            if (limit > 0) {
                parameters.add(new BasicNameValuePair("limit", String.valueOf(limit)));
            }
            if (marker != null) {
                parameters.add(new BasicNameValuePair("marker", marker));
            }
            if (delimiter != null) {
                parameters.add(new BasicNameValuePair("delimiter", delimiter.toString()));
            }

            String uri = parameters.size() > 0 ? makeURI(storageURL + "/"
                    + sanitizeForURI(container), parameters) : storageURL;
            method = new HttpGet(uri);
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.removeHeaders(FilesConstants.X_AUTH_TOKEN);
                if (login()) {
                    method = new HttpGet(uri);
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            }

            if (response.getStatusCode() == HttpStatus.SC_OK) {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document document = builder.parse(response.getResponseBodyAsStream());

                NodeList nodes = document.getChildNodes();
                Node containerList = nodes.item(0);
                if (!"container".equals(containerList.getNodeName())) {
                    logger.error("Got unexpected type of XML");
                    return null;
                }
                ArrayList<FilesObject> objectList = new ArrayList<FilesObject>();
                NodeList objectNodes = containerList.getChildNodes();
                for (int i = 0; i < objectNodes.getLength(); ++i) {
                    Node objectNode = objectNodes.item(i);
                    String nodeName = objectNode.getNodeName();
                    if (!("object".equals(nodeName) || "subdir".equals(nodeName))) {
                        continue;
                    }
                    String name = null;
                    String eTag = null;
                    long size = -1;
                    String mimeType = null;
                    String lastModified = null;
                    NodeList objectData = objectNode.getChildNodes();
                    if ("subdir".equals(nodeName)) {
                        size = 0;
                        mimeType = "application/directory";
                        name = objectNode.getAttributes().getNamedItem("name").getNodeValue();
                    }
                    for (int j = 0; j < objectData.getLength(); ++j) {
                        Node data = objectData.item(j);
                        if ("name".equals(data.getNodeName())) {
                            name = data.getTextContent();
                        } else if ("content_type".equals(data.getNodeName())) {
                            mimeType = data.getTextContent();
                        } else if ("hash".equals(data.getNodeName())) {
                            eTag = data.getTextContent();
                        } else if ("bytes".equals(data.getNodeName())) {
                            size = Long.parseLong(data.getTextContent());
                        } else if ("last_modified".equals(data.getNodeName())) {
                            lastModified = data.getTextContent();
                        } else {
                            logger.warn("Unexpected tag:" + data.getNodeName());
                        }
                    }
                    if (name != null) {
                        FilesObject obj = new FilesObject(name, container, this);
                        if (eTag != null) {
                            obj.setMd5sum(eTag);
                        }
                        if (mimeType != null) {
                            obj.setMimeType(mimeType);
                        }
                        if (size >= 0) {
                            obj.setSize(size);
                        }
                        if (lastModified != null) {
                            obj.setLastModified(lastModified);
                        }
                        objectList.add(obj);
                    }
                }
                return objectList;
            } else if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                logger.debug("Container " + container + " has no Objects");
                return new ArrayList<FilesObject>();
            } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                throw new FilesNotFoundException("Container was not found",
                        response.getResponseHeaders(), response.getStatusLine());
            } else {
                throw new FilesException("Unexpected Server Result",
                        response.getResponseHeaders(), response.getStatusLine());
            }
        } catch (FilesNotFoundException fnfe) {
            throw fnfe;
        } catch (Exception ex) {
            String errorMessage = "Error parsing server resposne " + ex.getMessage();
            logger.error(errorMessage);
            throw new FilesException(errorMessage, ex);
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjects(String container) throws IOException,
            FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, null, -1, null, null);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param delimter Use this argument as the delimiter that separates
     * "directories"
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjects(String container, Character delimiter)
            throws IOException, FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, null, -1, null, delimiter);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param limit Return at most <code>limit</code> objects
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjects(String container, int limit)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesException {
        return listObjectsStartingWith(container, null, null, limit, null, null);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param path Only look for objects in this path
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     */
    public List<FilesObject> listObjects(String container, String path)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesException {
        return listObjectsStartingWith(container, null, path, -1, null, null);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param path Only look for objects in this path
     * @param delimter Use this argument as the delimiter that separates
     * "directories"
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     */
    public List<FilesObject> listObjects(String container, String path,
            Character delimiter) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, path, -1, null, delimiter);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param path Only look for objects in this path
     * @param limit Return at most <code>limit</code> objects
     * @param marker Returns objects lexicographically greater than
     * <code>marker</code>. Used in conjunction with <code>limit</code> to
     * paginate the list.
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjects(String container, String path,
            int limit) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, path, limit, null);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param path Only look for objects in this path
     * @param limit Return at most <code>limit</code> objects
     * @param marker Returns objects lexicographically greater than
     * <code>marker</code>. Used in conjunction with <code>limit</code> to
     * paginate the list.
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     */
    public List<FilesObject> listObjects(String container, String path,
            int limit, String marker) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, path, limit, marker);
    }

    /**
     * List the objects in a container in lexicographic order.
     *
     * @param container The container name
     * @param limit Return at most <code>limit</code> objects
     * @param marker Returns objects lexicographically greater than
     * <code>marker</code>. Used in conjunction with <code>limit</code> to
     * paginate the list.
     *
     * @return A list of FilesObjects starting with the given string
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public List<FilesObject> listObjects(String container, int limit,
            String marker) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        return listObjectsStartingWith(container, null, null, limit, marker);
    }

    /**
     * Convenience method to test for the existence of a container in Cloud
     * Files.
     *
     * @param container
     * @return true if the container exists. false otherwise.
     * @throws IOException
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     */
    public boolean containerExists(String container) throws IOException,
            HttpException {
        try {
            this.getContainerInfo(container);
            return true;
        } catch (FilesException fnfe) {
            return false;
        }
    }

    /**
     * Gets information for the given account.
     *
     * @return The FilesAccountInfo with information about the number of
     * containers and number of bytes used by the given account.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was another error in the request to the
     * server.
     * @throws FilesAuthorizationException The client's login was invalid.
     */
    public FilesAccountInfo getAccountInfo() throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        if (this.isLoggedin()) {
            HttpHead method = null;

            try {
                method = new HttpHead(storageURL);
                method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                FilesResponse response = new FilesResponse(client.execute(method));
                if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    method.removeHeaders(FilesConstants.X_AUTH_TOKEN);
                    if (login()) {
                        method.abort();
                        method = new HttpHead(storageURL);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        response = new FilesResponse(client.execute(method));
                    } else {
                        throw new FilesAuthorizationException(
                                "Re-login failed",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                }

                if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                    int nContainers = response.getAccountContainerCount();
                    long totalSize = response.getAccountBytesUsed();
                    return new FilesAccountInfo(totalSize, nContainers);
                } else {
                    throw new FilesException("Unexpected return from server",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            } finally {
                if (method != null) {
                    method.abort();
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }
    }

    /**
     * Get basic information on a container (number of items and the total
     * size).
     *
     * @param container The container to get information for
     * @return ContainerInfo object of the container is present or null if its
     * not present
     * @throws IOException There was a socket level exception while talking to
     * CloudFiles
     * @throws HttpException There was an protocol level exception while talking
     * to Cloudfiles
     * @throws FilesNotFoundException The container was not found
     * @throws FilesAuthorizationException The client was not logged in or the
     * log in expired.
     */
    public FilesContainerInfo getContainerInfo(String container)
            throws IOException, HttpException, FilesException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container)) {

                HttpHead method = null;
                try {
                    method = new HttpHead(storageURL + "/"
                            + sanitizeForURI(container));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.removeHeaders(FilesConstants.X_AUTH_TOKEN);
                        if (login()) {
                            method = new HttpHead(storageURL + "/"
                                    + sanitizeForURI(container));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        int objCount = response.getContainerObjectCount();
                        long objSize = response.getContainerBytesUsed();
                        return new FilesContainerInfo(container, objCount,
                                objSize);
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Container not found: " + container,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException(
                                "Unexpected result from server",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                throw new FilesInvalidNameException(container);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Creates a container
     *
     * @param name The name of the container to be created
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException The client was not property logged in
     * @throws FilesInvalidNameException The container name was invalid
     */
    public void createContainer(String name) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        if (this.isLoggedin()) {
            if (isValidContainerName(name)) {
                HttpPut method = new HttpPut(storageURL + "/"
                        + sanitizeForURI(name));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);

                try {
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpPut(storageURL + "/"
                                    + sanitizeForURI(name));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        // Fressen
                    } else if (response.getStatusCode() == HttpStatus.SC_ACCEPTED) {
                        throw new FilesContainerExistsException(name,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException("Unexpected Response",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                throw new FilesInvalidNameException(name);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }
    }

    /**
     * Deletes a container
     *
     * @param name The name of the container
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException The user is not Logged in
     * @throws FilesInvalidNameException The container name is invalid
     * @throws FilesNotFoundException The container doesn't exist
     * @throws FilesContainerNotEmptyException The container was not empty
     */
    public boolean deleteContainer(String name) throws IOException,
            HttpException, FilesAuthorizationException,
            FilesInvalidNameException, FilesNotFoundException,
            FilesContainerNotEmptyException {
        if (this.isLoggedin()) {
            if (isValidContainerName(name)) {
                HttpDelete method = new HttpDelete(storageURL + "/"
                        + sanitizeForURI(name));
                try {
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpDelete(storageURL + "/"
                                    + sanitizeForURI(name));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        logger.debug("Container Deleted : " + name);
                        return true;
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        logger.debug("Container does not exist !");
                        throw new FilesNotFoundException(
                                "You can't delete an non-empty container",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_CONFLICT) {
                        logger.debug("Container is not empty, can not delete a none empty container !");
                        throw new FilesContainerNotEmptyException(
                                "You can't delete an non-empty container",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                throw new FilesInvalidNameException(name);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return false;
    }

    /**
     * Enables access of files in this container via the Content Delivery
     * Network.
     *
     * @param name The name of the container to enable
     * @return The CDN Url of the container
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was an error talking to the CDN Server.
     */
    public String cdnEnableContainer(String name) throws IOException,
            HttpException, FilesException {
        String returnValue = null;
        if (this.isLoggedin()) {
            if (isValidContainerName(name)) {
                HttpPut method = null;
                try {
                    method = new HttpPut(cdnManagementURL + "/"
                            + sanitizeForURI(name));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpPut(cdnManagementURL + "/"
                                    + sanitizeForURI(name));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED
                            || response.getStatusCode() == HttpStatus.SC_ACCEPTED) {
                        returnValue = response.getCdnUrl();
                    } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        logger.warn("Unauthorized access");
                        throw new FilesAuthorizationException(
                                "User not Authorized!",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException("Unexpected Server Response",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                throw new FilesInvalidNameException(name);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return returnValue;
    }

    public String cdnUpdateContainer(String name, int ttl, boolean enabled,
            boolean retainLogs) throws IOException, HttpException,
            FilesException {
        return cdnUpdateContainer(name, ttl, enabled, null, null, retainLogs);
    }

    /**
     * Enables access of files in this container via the Content Delivery
     * Network.
     *
     * @param name The name of the container to enable
     * @param ttl How long the CDN can use the content before checking for an
     * update. A negative value will result in this not being changed.
     * @param enabled True if this container should be accessible, false
     * otherwise
     * @param retainLogs True if cdn access logs should be kept for this
     * container, false otherwise
     * @return The CDN Url of the container
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was an error talking to the CDN Service
     */
    /*
     * @param referrerAcl Unused for now
     * 
     * @param userAgentACL Unused for now
     */
    private String cdnUpdateContainer(String name, int ttl, boolean enabled,
            String referrerAcl, String userAgentACL, boolean retainLogs)
            throws IOException, HttpException, FilesException {
        String returnValue = null;
        if (this.isLoggedin()) {
            if (isValidContainerName(name)) {
                HttpPost method = null;
                try {
                    method = new HttpPost(cdnManagementURL + "/"
                            + sanitizeForURI(name));

                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    // TTL
                    if (ttl > 0) {
                        method.setHeader(FilesConstants.X_CDN_TTL,
                                Integer.toString(ttl));
                    }
                    // Enabled
                    method.setHeader(FilesConstants.X_CDN_ENABLED,
                            Boolean.toString(enabled));

                    // Log Retention
                    method.setHeader(FilesConstants.X_CDN_RETAIN_LOGS,
                            Boolean.toString(retainLogs));

                    // Referrer ACL
                    if (referrerAcl != null) {
                        method.setHeader(FilesConstants.X_CDN_REFERRER_ACL,
                                referrerAcl);
                    }

                    // User Agent ACL
                    if (userAgentACL != null) {
                        method.setHeader(FilesConstants.X_CDN_USER_AGENT_ACL,
                                userAgentACL);
                    }
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            new HttpPost(cdnManagementURL + "/"
                                    + sanitizeForURI(name));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            // TTL
                            if (ttl > 0) {
                                method.setHeader(FilesConstants.X_CDN_TTL,
                                        Integer.toString(ttl));
                            }
                            // Enabled
                            method.setHeader(FilesConstants.X_CDN_ENABLED,
                                    Boolean.toString(enabled));
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_ACCEPTED) {
                        returnValue = response.getCdnUrl();
                    } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        logger.warn("Unauthorized access");
                        throw new FilesAuthorizationException(
                                "User not Authorized!",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException("Unexpected Server Response",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                throw new FilesInvalidNameException(name);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return returnValue;
    }

    /**
     * Gets current CDN sharing status of the container
     *
     * @param name The name of the container to enable
     * @return Information on the container
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was an error talking to the CloudFiles
     * Server
     * @throws FilesNotFoundException The Container has never been CDN enabled
     */
    public FilesCDNContainer getCDNContainerInfo(String container)
            throws IOException, FilesNotFoundException, HttpException,
            FilesException {
        if (isLoggedin()) {
            if (isValidContainerName(container)) {
                HttpHead method = null;
                try {
                    method = new HttpHead(cdnManagementURL + "/"
                            + sanitizeForURI(container));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpHead(cdnManagementURL + "/"
                                    + sanitizeForURI(container));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        FilesCDNContainer result = new FilesCDNContainer(
                                response.getCdnUrl());
                        result.setName(container);
                        result.setSSLURL(response.getCdnSslUrl());
                        result.setStreamingURL(response.getCdnStreamingUrl());
                        for (Header hdr : response.getResponseHeaders()) {
                            String name = hdr.getName().toLowerCase();
                            if ("x-cdn-enabled".equals(name)) {
                                result.setEnabled(Boolean.valueOf(hdr
                                        .getValue()));
                            } else if ("x-log-retention".equals(name)) {
                                result.setRetainLogs(Boolean.valueOf(hdr
                                        .getValue()));
                            } else if ("x-ttl".equals(name)) {
                                result.setTtl(Integer.parseInt(hdr.getValue()));
                            } else if ("x-referrer-acl".equals(name)) {
                                result.setReferrerACL(hdr.getValue());
                            } else if ("x-user-agent-acl".equals(name)) {
                                result.setUserAgentACL(hdr.getValue());
                            }
                        }
                        return result;
                    } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        logger.warn("Unauthorized access");
                        throw new FilesAuthorizationException(
                                "User not Authorized!",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Container is not CDN enabled",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException(
                                "Unexpected result from server: ",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                throw new FilesInvalidNameException(container);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Gets current CDN sharing status of the container
     *
     * @param name The name of the container to enable
     * @return Information on the container
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException There was an error talking to the CloudFiles
     * Server
     * @throws FilesNotFoundException The Container has never been CDN enabled
     */
    public boolean isCDNEnabled(String container) throws IOException,
            HttpException, FilesException {
        if (isLoggedin()) {
            if (isValidContainerName(container)) {
                HttpHead method = null;
                try {
                    method = new HttpHead(cdnManagementURL + "/"
                            + sanitizeForURI(container));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpHead(cdnManagementURL + "/"
                                    + sanitizeForURI(container));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        for (Header hdr : response.getResponseHeaders()) {
                            String name = hdr.getName().toLowerCase();
                            if ("x-cdn-enabled".equals(name)) {
                                return Boolean.valueOf(hdr.getValue());
                            }
                        }
                        throw new FilesException(
                                "Server did not return X-CDN-Enabled header: ",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        logger.warn("Unauthorized access");
                        throw new FilesAuthorizationException(
                                "User not Authorized!",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        return false;
                    } else {
                        throw new FilesException(
                                "Unexpected result from server: ",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                throw new FilesInvalidNameException(container);
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Creates a path (but not any of the sub portions of the path)
     *
     * @param container The name of the container.
     * @param path The name of the Path
     * @throws HttpException There was an error at the protocol layer while
     * talking to CloudFiles
     * @throws IOException There was an error at the socket layer while talking
     * to CloudFiles
     * @throws FilesException There was another error while taking to the
     * CloudFiles server
     */
    public void createPath(String container, String path) throws HttpException,
            IOException, FilesException {

        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(container);
        }
        if (!isValidObjectName(path)) {
            throw new FilesInvalidNameException(path);
        }
        storeObject(container, new byte[0], "application/directory", path,
                new HashMap<String, String>());
    }

    /**
     * Create all of the path elements for the entire tree for a given path.
     * Thus,
     * <code>createFullPath("myContainer", "foo/bar/baz")</code> creates the
     * paths "foo", "foo/bar" and "foo/bar/baz".
     *
     * @param container The name of the container
     * @param path The full name of the path
     * @throws HttpException There was an error at the protocol layer while
     * talking to CloudFiles
     * @throws IOException There was an error at the socket layer while talking
     * to CloudFiles
     * @throws FilesException There was another error while taking to the
     * CloudFiles server
     */
    public void createFullPath(String container, String path)
            throws HttpException, IOException, FilesException {
        String parts[] = path.split("/");

        for (int i = 0; i < parts.length; ++i) {
            StringBuilder sb = new StringBuilder();
            for (int j = 0; j <= i; ++j) {
                if (sb.length() != 0) {
                    sb.append("/");
                }
                sb.append(parts[j]);
            }
            createPath(container, sb.toString());
        }

    }

    /**
     * Gets the names of all of the containers associated with this account.
     *
     * @param limit The maximum number of container names to return
     * @return A list of container names
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<String> listCdnContainers(int limit) throws IOException,
            HttpException, FilesException {
        return listCdnContainers(limit, null);
    }

    /**
     * Gets the names of all of the containers associated with this account.
     *
     * @return A list of container names
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<String> listCdnContainers() throws IOException, HttpException,
            FilesException {
        return listCdnContainers(-1, null);
    }

    /**
     * Gets the names of all of the containers associated with this account.
     *
     * @param limit The maximum number of container names to return
     * @param marker All of the results will come after <code>marker</code>
     * lexicographically.
     * @return A list of container names
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<String> listCdnContainers(int limit, String marker)
            throws IOException, HttpException, FilesException {
        if (this.isLoggedin()) {
            HttpGet method = null;
            try {
                LinkedList<NameValuePair> params = new LinkedList<NameValuePair>();
                if (limit > 0) {
                    params.add(new BasicNameValuePair("limit", String
                            .valueOf(limit)));
                }
                if (marker != null) {
                    params.add(new BasicNameValuePair("marker", marker));
                }
                String uri = (params.size() > 0) ? makeURI(cdnManagementURL,
                        params) : cdnManagementURL;
                method = new HttpGet(uri);
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                FilesResponse response = new FilesResponse(
                        client.execute(method));

                if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    method.abort();
                    if (login()) {
                        method = new HttpGet(uri);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        response = new FilesResponse(client.execute(method));
                    } else {
                        throw new FilesAuthorizationException(
                                "Re-login failed",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                }

                if (response.getStatusCode() == HttpStatus.SC_OK) {
                    StrTokenizer tokenize = new StrTokenizer(
                            response.getResponseBodyAsString());
                    tokenize.setDelimiterString("\n");
                    String[] containers = tokenize.getTokenArray();
                    List<String> returnValue = new ArrayList<String>();
                    for (String containerName : containers) {
                        returnValue.add(containerName);
                    }
                    return returnValue;
                } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    logger.warn("Unauthorized access");
                    throw new FilesAuthorizationException(
                            "User not Authorized!",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                } else {
                    throw new FilesException("Unexpected server response",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            } finally {
                if (method != null) {
                    method.abort();
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Purges all items from a given container from the CDN
     *
     * @param container The name of the container
     * @param emailAddresses An optional comma separated list of email addresses
     * to be notified when the purge is complete. <code>null</code> if desired.
     * @throws IOException Error talking to the cdn management server
     * @throws HttpException Error with HTTP
     * @throws FilesAuthorizationException Log in was not successful, or account
     * is suspended
     * @throws FilesException Other error
     */
    public void purgeCDNContainer(String container, String emailAddresses)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesException {
        if (!isLoggedin) {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(container);
        }
        HttpDelete method = null;
        try {
            String deleteUri = cdnManagementURL + "/"
                    + sanitizeForURI(container);
            method = new HttpDelete(deleteUri);
            method.getParams().setIntParameter("http.socket.timeout",
                    connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            if (emailAddresses != null) {
                method.setHeader(FilesConstants.X_PURGE_EMAIL, emailAddresses);
            }

            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpDelete(deleteUri);
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (emailAddresses != null) {
                        method.setHeader(FilesConstants.X_PURGE_EMAIL,
                                emailAddresses);
                    }
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            }

            if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                // fressen
            } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                throw new FilesAuthorizationException("User not Authorized!",
                        response.getResponseHeaders(), response.getStatusLine());
            } else {
                throw new FilesException("Unexpected server response",
                        response.getResponseHeaders(), response.getStatusLine());
            }
        } finally {
            if (method != null) {
                method.abort();
            }
        }

    }

    /**
     * Purges all items from a given container from the CDN
     *
     * @param container The name of the container
     * @param object The name of the object
     * @param emailAddresses An optional comma separated list of email addresses
     * to be notified when the purge is complete. <code>null</code> if desired.
     * @throws IOException Error talking to the cdn management server
     * @throws HttpException Error with HTTP
     * @throws FilesAuthorizationException Log in was not successful, or account
     * is suspended
     * @throws FilesException Other error
     */
    public void purgeCDNObject(String container, String object,
            String emailAddresses) throws IOException, HttpException,
            FilesAuthorizationException, FilesException {
        if (!isLoggedin) {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(container);
        }
        HttpDelete method = null;
        try {
            String deleteUri = cdnManagementURL + "/"
                    + sanitizeForURI(container) + "/"
                    + sanitizeAndPreserveSlashes(object);
            method = new HttpDelete(deleteUri);
            method.getParams().setIntParameter("http.socket.timeout",
                    connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            if (emailAddresses != null) {
                method.setHeader(FilesConstants.X_PURGE_EMAIL, emailAddresses);
            }

            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpDelete(deleteUri);
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (emailAddresses != null) {
                        method.setHeader(FilesConstants.X_PURGE_EMAIL,
                                emailAddresses);
                    }
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            }

            if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) { 
                // fressen
            } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                throw new FilesAuthorizationException("User not Authorized!",
                        response.getResponseHeaders(), response.getStatusLine());
            } else {
                System.out.println(response.getStatusLine());
                throw new FilesException("Unexpected server response",
                        response.getResponseHeaders(), response.getStatusLine());
            }
        } finally {
            if (method != null) {
                method.abort();
            }
        }

    }

    /**
     * Gets list of all of the containers associated with this account.
     *
     * @return A list of containers
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<FilesCDNContainer> listCdnContainerInfo() throws IOException,
            HttpException, FilesException {
        return listCdnContainerInfo(-1, null);
    }

    /**
     * Gets list of all of the containers associated with this account.
     *
     * @param limit The maximum number of container names to return
     * @return A list of containers
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<FilesCDNContainer> listCdnContainerInfo(int limit)
            throws IOException, HttpException, FilesException {
        return listCdnContainerInfo(limit, null);
    }

    /**
     * Gets list of all of the containers associated with this account.
     *
     * @param limit The maximum number of container names to return
     * @param marker All of the names will come after <code>marker</code>
     * lexicographically.
     * @return A list of containers
     *
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public List<FilesCDNContainer> listCdnContainerInfo(int limit, String marker)
            throws IOException, HttpException, FilesException {
        if (this.isLoggedin()) {
            HttpGet method = null;
            try {
                LinkedList<NameValuePair> params = new LinkedList<NameValuePair>();
                params.add(new BasicNameValuePair("format", "xml"));
                if (limit > 0) {
                    params.add(new BasicNameValuePair("limit", String
                            .valueOf(limit)));
                }
                if (marker != null) {
                    params.add(new BasicNameValuePair("marker", marker));
                }
                String uri = params.size() > 0 ? makeURI(cdnManagementURL,
                        params) : cdnManagementURL;
                method = new HttpGet(uri);
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);

                FilesResponse response = new FilesResponse(
                        client.execute(method));

                if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    method.abort();
                    if (login()) {
                        method = new HttpGet(uri);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);

                        response = new FilesResponse(client.execute(method));
                    } else {
                        throw new FilesAuthorizationException(
                                "Re-login failed",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                }

                if (response.getStatusCode() == HttpStatus.SC_OK) {
                    DocumentBuilderFactory factory = DocumentBuilderFactory
                            .newInstance();
                    DocumentBuilder builder = factory.newDocumentBuilder();
                    Document document = builder.parse(response
                            .getResponseBodyAsStream());

                    NodeList nodes = document.getChildNodes();
                    Node accountNode = nodes.item(0);
                    if (!"account".equals(accountNode.getNodeName())) {
                        logger.error("Got unexpected type of XML");
                        return null;
                    }
                    ArrayList<FilesCDNContainer> containerList = new ArrayList<FilesCDNContainer>();
                    NodeList containerNodes = accountNode.getChildNodes();
                    for (int i = 0; i < containerNodes.getLength(); ++i) {
                        Node containerNode = containerNodes.item(i);
                        if (!"container".equals(containerNode.getNodeName())) {
                            continue;
                        }
                        FilesCDNContainer container = new FilesCDNContainer();
                        NodeList objectData = containerNode.getChildNodes();
                        for (int j = 0; j < objectData.getLength(); ++j) {
                            Node data = objectData.item(j);
                            if ("name".equals(data.getNodeName())) {
                                container.setName(data.getTextContent());
                            } else if ("cdn_url".equals(data.getNodeName())) {
                                container.setCdnURL(data.getTextContent());
                            } else if ("cdn_ssl_url".equals(data.getNodeName())) {
                                container.setSSLURL(data.getTextContent());
                            } else if ("cdn_streaming_url".equals(data
                                    .getNodeName())) {
                                container
                                        .setStreamingURL(data.getTextContent());
                            } else if ("cdn_enabled".equals(data.getNodeName())) {
                                container.setEnabled(Boolean.parseBoolean(data
                                        .getTextContent()));
                            } else if ("log_retention".equals(data
                                    .getNodeName())) {
                                container.setRetainLogs(Boolean
                                        .parseBoolean(data.getTextContent()));
                            } else if ("ttl".equals(data.getNodeName())) {
                                container.setTtl(Integer.parseInt(data
                                        .getTextContent()));
                            } else if ("referrer_acl"
                                    .equals(data.getNodeName())) {
                                container.setReferrerACL(data.getTextContent());
                            } else if ("useragent_acl".equals(data
                                    .getNodeName())) {
                                container
                                        .setUserAgentACL(data.getTextContent());
                            } else {
                                // logger.warn("Unexpected container-info tag:"
                                // + data.getNodeName());
                            }
                        }
                        if (container.getName() != null) {
                            containerList.add(container);
                        }
                    }
                    return containerList;
                } else if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    logger.warn("Unauthorized access");
                    throw new FilesAuthorizationException(
                            "User not Authorized!",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                } else {
                    throw new FilesException("Unexpected server response",
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            } catch (SAXException ex) {
                // probably a problem parsing the XML
                throw new FilesException("Problem parsing XML", ex);
            } catch (ParserConfigurationException ex) {
                // probably a problem parsing the XML
                throw new FilesException("Problem parsing XML", ex);
            } finally {
                if (method != null) {
                    method.abort();
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Create a manifest on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param manifest Set manifest content here
     * @param callback The object to which any callbacks will be sent (null if
     * you don't want callbacks)
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public boolean createManifestObject(String container, String contentType,
            String name, String manifest, IFilesTransferCallback callback)
            throws IOException, HttpException, FilesException {
        return createManifestObject(container, contentType, name, manifest,
                new HashMap<String, String>(), callback);
    }

    /**
     * Create a manifest on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param manifest Set manifest content here
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public boolean createManifestObject(String container, String contentType,
            String name, String manifest, Map<String, String> metadata)
            throws IOException, HttpException, FilesException {
        return createManifestObject(container, contentType, name, manifest,
                metadata, null);
    }

    /**
     * Create a manifest on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param manifest Set manifest content here
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @param callback The object to which any callbacks will be sent (null if
     * you don't want callbacks)
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public boolean createManifestObject(String container, String contentType,
            String name, String manifest, Map<String, String> metadata,
            IFilesTransferCallback callback) throws IOException, HttpException,
            FilesException {
        byte[] arr = new byte[0];
        if (this.isLoggedin()) {
            String objName = name;
            if (isValidContainerName(container) && isValidObjectName(objName)) {

                HttpPut method = null;
                try {
                    method = new HttpPut(storageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    method.setHeader(FilesConstants.MANIFEST_HEADER, manifest);
                    ByteArrayEntity entity = new ByteArrayEntity(arr);
                    entity.setContentType(contentType);
                    method.setEntity(new RequestEntityWrapper(entity, callback));
                    for (String key : metadata.keySet()) {
                        // logger.warn("Key:" + key + ":" +
                        // sanitizeForURI(metadata.get(key)));
                        method.setHeader(FilesConstants.X_OBJECT_META + key,
                                sanitizeForURI(metadata.get(key)));
                    }

                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpPut(storageURL + "/"
                                    + sanitizeForURI(container) + "/"
                                    + sanitizeForURI(objName));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            if (manifest != null) {
                                method.setHeader(
                                        FilesConstants.MANIFEST_HEADER,
                                        manifest);
                            }
                            entity = new ByteArrayEntity(arr);
                            entity.setContentType(contentType);
                            method.setEntity(new RequestEntityWrapper(entity,
                                    callback));
                            for (String key : metadata.keySet()) {
                                method.setHeader(FilesConstants.X_OBJECT_META
                                        + key,
                                        sanitizeForURI(metadata.get(key)));
                            }
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        return true;
                    } else if (response.getStatusCode() == HttpStatus.SC_PRECONDITION_FAILED) {
                        throw new FilesException("Etag missmatch",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_LENGTH_REQUIRED) {
                        throw new FilesException("Length miss-match",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException("Unexpected Server Response",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Store a file on the server
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeObjectAs(String container, File obj, String contentType,
            String name) throws OverQuotaException, FilesException, IOException,
            HttpException {
        return storeObjectAs(container, obj, contentType, name,
                new HashMap<String, String>(), null);
    }

    /**
     * Store a file on the server
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeObjectAs(String container, File obj, String contentType,
            String name, IFilesTransferCallback callback) throws OverQuotaException,
            FilesException, IOException, HttpException {
        return storeObjectAs(container, obj, contentType, name,
                new HashMap<String, String>(), callback);
    }

    /**
     * Store a file on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException
     */
    public String storeObjectAs(String container, File obj, String contentType,
            String name, Map<String, String> metadata) throws OverQuotaException,
            FilesException, IOException, HttpException {
        return storeObjectAs(container, obj, contentType, name, metadata, null);
    }

    /**
     * Store a file on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @param metadata The callback object that will be called as the data is
     * sent
     * @return The ETAG if the save was successful, null otherwise
     * @throws OverQuotaException
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeObjectAs(String container, File obj, String contentType, String name, Map<String, String> metadata,
            IFilesTransferCallback callback) throws OverQuotaException, FilesException, IOException, HttpException {

        if (!this.isLoggedin()) {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }

        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(name);
        }

        if (!isValidObjectName(name)) {
            throw new FilesInvalidNameException(container);
        }

        if (!obj.exists()) {
            throw new FileNotFoundException(name + " does not exist");
        }

        if (obj.isDirectory()) {
            throw new IOException("The alleged file was a directory");
        }


        HttpPut method = null;
        try {
            method = new HttpPut(storageURL + "/"
                    + sanitizeForURI(container) + "/"
                    + sanitizeForURI(name));
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            if (useETag) {
                method.setHeader(FilesConstants.E_TAG, md5Sum(obj));
            }
            method.setEntity(new RequestEntityWrapper(new FileEntity(obj, contentType), callback));
            for (String key : metadata.keySet()) {
                method.setHeader(FilesConstants.X_OBJECT_META + key, sanitizeForURI(metadata.get(key)));
            }
            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpPut(storageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(name));
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (useETag) {
                        method.setHeader(FilesConstants.E_TAG, md5Sum(obj));
                    }
                    method.setEntity(new RequestEntityWrapper(
                            new FileEntity(obj, contentType), callback));
                    for (String key : metadata.keySet()) {
                        method.setHeader(FilesConstants.X_OBJECT_META + key, sanitizeForURI(metadata.get(key)));
                    }
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed", response.getResponseHeaders(), response.getStatusLine());
                }
            }
            int statusCode = response.getStatusCode();

            switch (statusCode) {
                case HttpStatus.SC_CREATED:
                    return response.getResponseHeader(FilesConstants.E_TAG).getValue();
                case HttpStatus.SC_PRECONDITION_FAILED:
                    throw new FilesException("Etag missmatch", response.getResponseHeaders(), response.getStatusLine());
                case HttpStatus.SC_LENGTH_REQUIRED:
                    throw new FilesException("Length miss-match", response.getResponseHeaders(), response.getStatusLine());
                case HttpStatus.SC_REQUEST_TOO_LONG:
                    throw new OverQuotaException("Quota excedeed", response.getResponseHeaders(), response.getStatusLine());
                default:
                    throw new FilesException("Unexpected Server Response", response.getResponseHeaders(), response.getStatusLine());
            }
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }
    
    /**
     * Store a file on the server
     *
     * @param sharedStorageURL
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeSharedObjectAs(String sharedStorageURL, String container, 
            File obj, String contentType, String name) throws OverQuotaException,
            FilesException, IOException, HttpException {
        
        if (!this.isLoggedin()) {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }

        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(name);
        }

        if (!isValidObjectName(name)) {
            throw new FilesInvalidNameException(container);
        }

        if (!obj.exists()) {
            throw new FileNotFoundException(name + " does not exist");
        }

        if (obj.isDirectory()) {
            throw new IOException("The alleged file was a directory");
        }


        HttpPut method = null;
        try {
            method = new HttpPut(sharedStorageURL + "/"
                    + sanitizeForURI(container) + "/"
                    + sanitizeForURI(name));
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            if (useETag) {
                method.setHeader(FilesConstants.E_TAG, md5Sum(obj));
            }
            method.setEntity(new RequestEntityWrapper(new FileEntity(obj, contentType), null));
            FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpPut(sharedStorageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(name));
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (useETag) {
                        method.setHeader(FilesConstants.E_TAG, md5Sum(obj));
                    }
                    method.setEntity(new RequestEntityWrapper(new FileEntity(obj, contentType), null));
                    response = new FilesResponse(client.execute(method));
                } else {
                    throw new FilesAuthorizationException("Re-login failed", response.getResponseHeaders(), response.getStatusLine());
                }
            }
            int statusCode = response.getStatusCode();

            switch (statusCode) {
                case HttpStatus.SC_CREATED:
                    return response.getResponseHeader(FilesConstants.E_TAG).getValue();
                case HttpStatus.SC_PRECONDITION_FAILED:
                    throw new FilesException("Etag missmatch", response.getResponseHeaders(), response.getStatusLine());
                case HttpStatus.SC_LENGTH_REQUIRED:
                    throw new FilesException("Length miss-match", response.getResponseHeaders(), response.getStatusLine());
                case HttpStatus.SC_REQUEST_TOO_LONG:
                    throw new OverQuotaException("Quota excedeed", response.getResponseHeaders(), response.getStatusLine());
                default:
                    throw new FilesException("Unexpected Server Response", response.getResponseHeaders(), response.getStatusLine());
            }
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }

    /**
     * Copies the file to Cloud Files, keeping the original file name in Cloud
     * Files.
     *
     * @param container The name of the container to place the file in
     * @param obj The File to transfer
     * @param contentType The file's MIME type
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeObject(String container, File obj, String contentType)
            throws IOException, HttpException, FilesException {
        return storeObjectAs(container, obj, contentType, obj.getName());
    }

    /**
     * Store a file on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public boolean storeObject(String container, byte obj[],
            String contentType, String name, Map<String, String> metadata)
            throws IOException, HttpException, FilesException {
        return storeObject(container, obj, contentType, name, metadata, null);
    }

    /**
     * Store a file on the server, including metadata
     *
     * @param container The name of the container
     * @param obj The File containing the file to copy over
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @param callback The object to which any callbacks will be sent (null if
     * you don't want callbacks)
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public boolean storeObject(String container, byte obj[],
            String contentType, String name, Map<String, String> metadata,
            IFilesTransferCallback callback) throws IOException, HttpException,
            FilesException {
        if (this.isLoggedin()) {
            String objName = name;
            if (isValidContainerName(container) && isValidObjectName(objName)) {

                HttpPut method = null;
                try {
                    method = new HttpPut(storageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (useETag) {
                        method.setHeader(FilesConstants.E_TAG, md5Sum(obj));
                    }
                    ByteArrayEntity entity = new ByteArrayEntity(obj);
                    entity.setContentType(contentType);
                    method.setEntity(new RequestEntityWrapper(entity, callback));
                    for (String key : metadata.keySet()) {
                        // logger.warn("Key:" + key + ":" +
                        // sanitizeForURI(metadata.get(key)));
                        method.setHeader(FilesConstants.X_OBJECT_META + key,
                                sanitizeForURI(metadata.get(key)));
                    }

                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        if (login()) {
                            method = new HttpPut(storageURL + "/"
                                    + sanitizeForURI(container) + "/"
                                    + sanitizeForURI(objName));
                            method.getParams().setIntParameter(
                                    "http.socket.timeout", connectionTimeOut);
                            method.setHeader(FilesConstants.X_AUTH_TOKEN,
                                    authToken);
                            if (useETag) {
                                method.setHeader(FilesConstants.E_TAG,
                                        md5Sum(obj));
                            }
                            entity = new ByteArrayEntity(obj);
                            entity.setContentType(contentType);
                            method.setEntity(new RequestEntityWrapper(entity,
                                    callback));
                            for (String key : metadata.keySet()) {
                                method.setHeader(FilesConstants.X_OBJECT_META
                                        + key,
                                        sanitizeForURI(metadata.get(key)));
                            }
                            response = new FilesResponse(client.execute(method));
                        } else {
                            throw new FilesAuthorizationException(
                                    "Re-login failed",
                                    response.getResponseHeaders(),
                                    response.getStatusLine());
                        }
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        return true;
                    } else if (response.getStatusCode() == HttpStatus.SC_PRECONDITION_FAILED) {
                        throw new FilesException("Etag missmatch",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else if (response.getStatusCode() == HttpStatus.SC_LENGTH_REQUIRED) {
                        throw new FilesException("Length miss-match",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException("Unexpected Server Response",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Store a file on the server, including metadata, with the contents coming
     * from an input stream. This allows you to not know the entire length of
     * your content when you start to write it. Nor do you have to hold it
     * entirely in memory at the same time.
     *
     * @param container The name of the container
     * @param data Any object that implements InputStream
     * @param contentType The MIME type of the file
     * @param name The name of the file on the server
     * @param metadata A map with the metadata as key names and values as the
     * metadata values
     * @param callback The object to which any callbacks will be sent (null if
     * you don't want callbacks)
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public String storeStreamedObject(String container, InputStream data,
            String contentType, String name, Map<String, String> metadata)
            throws IOException, HttpException, FilesException {
        if (this.isLoggedin()) {
            String objName = name;
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpPut method = new HttpPut(storageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                InputStreamEntity entity = new InputStreamEntity(data, -1);
                entity.setChunked(true);
                entity.setContentType(contentType);
                method.setEntity(entity);
                for (String key : metadata.keySet()) {
                    // logger.warn("Key:" + key + ":" +
                    // sanitizeForURI(metadata.get(key)));
                    method.setHeader(FilesConstants.X_OBJECT_META + key,
                            sanitizeForURI(metadata.get(key)));
                }
                method.removeHeaders("Content-Length");

                try {
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        return response.getResponseHeader(FilesConstants.E_TAG)
                                .getValue();
                    } else {
                        logger.error(response.getStatusLine());
                        throw new FilesException("Unexpected result",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     *
     *
     * @param container The name of the container
     * @param name The name of the object
     * @param entity The name of the request entity (make sure to set the
     * Content-Type
     * @param metadata The metadata for the object
     * @param md5sum The 32 character hex encoded MD5 sum of the data
     * @return The ETAG if the save was successful, null otherwise
     * @throws IOException There was a socket level exception talking to
     * CloudFiles
     * @throws HttpException There was a protocol level error talking to
     * CloudFiles
     * @throws FilesException There was an error talking to CloudFiles.
     */
    public String storeObjectAs(String container, String name,
            HttpEntity entity, Map<String, String> metadata, String md5sum)
            throws IOException, HttpException, FilesException {
        if (this.isLoggedin()) {
            String objName = name;
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpPut method = new HttpPut(storageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                method.setEntity(entity);
                if (useETag && md5sum != null) {
                    method.setHeader(FilesConstants.E_TAG, md5sum);
                }
                method.setHeader(entity.getContentType());

                for (String key : metadata.keySet()) {
                    method.setHeader(FilesConstants.X_OBJECT_META + key,
                            sanitizeForURI(metadata.get(key)));
                }

                try {
                    FilesResponse response = new FilesResponse(
                            client.execute(method));
                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        login();
                        method = new HttpPut(storageURL + "/"
                                + sanitizeForURI(container) + "/"
                                + sanitizeForURI(objName));
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        method.setEntity(entity);
                        method.setHeader(entity.getContentType());
                        for (String key : metadata.keySet()) {
                            method.setHeader(
                                    FilesConstants.X_OBJECT_META + key,
                                    sanitizeForURI(metadata.get(key)));
                        }
                        response = new FilesResponse(client.execute(method));
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        return response.getResponseHeader(FilesConstants.E_TAG)
                                .getValue();
                    } else {
                        logger.debug(response.getStatusLine());
                        throw new FilesException("Unexpected result",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * This method copies the object found in the source container with the
     * source object name to the destination container with the destination
     * object name.
     *
     * @param sourceContainer of object to copy
     * @param sourceObjName of object to copy
     * @param destContainer where object copy will be copied
     * @param destObjName of object copy
     * @return ETAG if successful, else null
     * @throws IOException indicates a socket level error talking to CloudFiles
     * @throws HttpException indicates a protocol level error talking to
     * CloudFiles
     * @throws FilesException indicates an error talking to CloudFiles
     */
    public String copyObject(String sourceContainer, String sourceObjName,
            String destContainer, String destObjName) throws HttpException,
            IOException {
        String etag = null;
        if (this.isLoggedin()) {

            if (isValidContainerName(sourceContainer)
                    && isValidObjectName(sourceObjName)
                    && isValidContainerName(destContainer)
                    && isValidObjectName(destObjName)) {

                HttpPut method = null;
                try {
                    String sourceURI = sanitizeForURI(sourceContainer) + "/"
                            + sanitizeForURI(sourceObjName);
                    String destinationURI = sanitizeForURI(destContainer) + "/"
                            + sanitizeForURI(destObjName);

                    method = new HttpPut(storageURL + "/" + destinationURI);
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    method.setHeader(FilesConstants.X_COPY_FROM, sourceURI);

                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();

                        login();
                        method = new HttpPut(storageURL + "/" + destinationURI);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        method.setHeader(FilesConstants.X_COPY_FROM, sourceURI);

                        response = new FilesResponse(client.execute(method));
                    }

                    if (response.getStatusCode() == HttpStatus.SC_CREATED) {
                        etag = response.getResponseHeader(FilesConstants.E_TAG)
                                .getValue();

                    } else {
                        throw new FilesException(
                                "Unexpected status from server",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }

                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                if (!isValidContainerName(sourceContainer)) {
                    throw new FilesInvalidNameException(sourceContainer);
                } else if (!isValidObjectName(sourceObjName)) {
                    throw new FilesInvalidNameException(sourceObjName);
                } else if (!isValidContainerName(destContainer)) {
                    throw new FilesInvalidNameException(destContainer);
                } else {
                    throw new FilesInvalidNameException(destObjName);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }

        return etag;
    }

    /**
     * Delete the given object from it's container.
     *
     * @param container The container name
     * @param objName The object name
     * @return FilesConstants.OBJECT_DELETED
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public void deleteObject(String container, String objName)
            throws IOException, FilesNotFoundException, HttpException,
            FilesException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpDelete method = null;
                try {
                    method = new HttpDelete(storageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        login();
                        method = new HttpDelete(storageURL + "/"
                                + sanitizeForURI(container) + "/"
                                + sanitizeForURI(objName));
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        response = new FilesResponse(client.execute(method));
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        logger.debug("Object Deleted : " + objName);
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Object was not found " + objName,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException(
                                "Unexpected status from server",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }
    
    /**
     * Delete the given object from it's container.
     *
     * @param container The container name
     * @param objName The object name
     * @return FilesConstants.OBJECT_DELETED
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesException
     */
    public void deleteSharedObject(String sharedStorageURL, String container, String objName)
            throws IOException, FilesNotFoundException, HttpException,
            FilesException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpDelete method = null;
                try {
                    method = new HttpDelete(sharedStorageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        login();
                        method = new HttpDelete(sharedStorageURL + "/"
                                + sanitizeForURI(container) + "/"
                                + sanitizeForURI(objName));
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        response = new FilesResponse(client.execute(method));
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT) {
                        logger.debug("Object Deleted : " + objName);
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Object was not found " + objName,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException(
                                "Unexpected status from server",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    if (method != null) {
                        method.abort();
                    }
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Get an object's metadata
     *
     * @param container The name of the container
     * @param objName The name of the object
     * @return The object's metadata
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException The Client's Login was invalid.
     * @throws FilesInvalidNameException The container or object name was not
     * valid
     * @throws FilesNotFoundException The file was not found
     */
    public FilesObjectMetaData getObjectMetaData(String container,
            String objName) throws IOException, FilesNotFoundException,
            HttpException, FilesAuthorizationException,
            FilesInvalidNameException {
        FilesObjectMetaData metaData;
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpHead method = new HttpHead(storageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                try {
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                        method.abort();
                        login();
                        method.getParams().setIntParameter(
                                "http.socket.timeout", connectionTimeOut);
                        method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        response = new FilesResponse(client.execute(method));
                    }

                    if (response.getStatusCode() == HttpStatus.SC_NO_CONTENT
                            || response.getStatusCode() == HttpStatus.SC_OK) {
                        logger.debug("Object metadata retreived  : " + objName);
                        String mimeType = response.getContentType();
                        String lastModified = response.getLastModified();
                        String eTag = response.getETag();
                        String contentLength = response.getContentLength();

                        metaData = new FilesObjectMetaData(mimeType,
                                contentLength, eTag, lastModified);

                        Header[] headers = response.getResponseHeaders();
                        HashMap<String, String> headerMap = new HashMap<String, String>();

                        for (Header h : headers) {
                            if (h.getName().startsWith(
                                    FilesConstants.X_OBJECT_META)) {
                                headerMap.put(
                                        h.getName().substring(
                                        FilesConstants.X_OBJECT_META
                                        .length()),
                                        unencodeURI(h.getValue()));
                            }
                        }
                        if (headerMap.size() > 0) {
                            metaData.setMetaData(headerMap);
                        }

                        return metaData;
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Container: " + container
                                + " did not have object " + objName,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    } else {
                        throw new FilesException(
                                "Unexpected Return Code from Server",
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
    }

    /**
     * Get the content of the given object
     *
     * @param container The name of the container
     * @param objName The name of the object
     * @return The content of the object
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException
     * @throws FilesInvalidNameException
     * @throws FilesNotFoundException
     */
    public byte[] getObject(String container, String objName)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesInvalidNameException, FilesNotFoundException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                HttpGet method = new HttpGet(storageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);

                try {
                    FilesResponse response = new FilesResponse(
                            client.execute(method));

                    if (response.getStatusCode() == HttpStatus.SC_OK) {
                        logger.debug("Object data retreived  : " + objName);
                        return response.getResponseBody();
                    } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                        throw new FilesNotFoundException(
                                "Container: " + container
                                + " did not have object " + objName,
                                response.getResponseHeaders(),
                                response.getStatusLine());
                    }
                } finally {
                    method.abort();
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return null;
    }
    
    /**
     * Get's the given shared object's content as a stream
     *
     * @param container The name of the container
     * @param objName The name of the object
     * @return An input stream that will give the objects content when read
     * from.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException
     * @throws FilesNotFoundException The container does not exist
     * @throws FilesInvalidNameException
     */
    public InputStream getSharedObjectAsStream(String sharedStorageURL, String container, String objName)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesInvalidNameException, FilesNotFoundException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                if (objName.length() > FilesConstants.OBJECT_NAME_LENGTH) {
                    logger.warn("Object Name supplied was truncated to Max allowed of "
                            + FilesConstants.OBJECT_NAME_LENGTH
                            + " characters !");
                    objName = objName.substring(0,
                            FilesConstants.OBJECT_NAME_LENGTH);
                    logger.warn("Truncated Object Name is: " + objName);
                }

                HttpGet method = new HttpGet(sharedStorageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                FilesResponse response = new FilesResponse(
                        client.execute(method));

                if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    method.abort();
                    login();
                    method = new HttpGet(sharedStorageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    response = new FilesResponse(client.execute(method));
                }

                if (response.getStatusCode() == HttpStatus.SC_OK) {
                    logger.info("Object data retreived  : " + objName);
                    // DO NOT RELEASE THIS CONNECTION
                    return response.getResponseBodyAsStream();
                } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                    method.abort();
                    throw new FilesNotFoundException("Container: " + container
                            + " did not have object " + objName,
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return null;
    }

    /**
     * Get's the given object's content as a stream
     *
     * @param container The name of the container
     * @param objName The name of the object
     * @return An input stream that will give the objects content when read
     * from.
     * @throws IOException There was an IO error doing network communication
     * @throws HttpException There was an error with the http protocol
     * @throws FilesAuthorizationException
     * @throws FilesNotFoundException The container does not exist
     * @throws FilesInvalidNameException
     */
    public InputStream getObjectAsStream(String container, String objName)
            throws IOException, HttpException, FilesAuthorizationException,
            FilesInvalidNameException, FilesNotFoundException {
        if (this.isLoggedin()) {
            if (isValidContainerName(container) && isValidObjectName(objName)) {
                if (objName.length() > FilesConstants.OBJECT_NAME_LENGTH) {
                    logger.warn("Object Name supplied was truncated to Max allowed of "
                            + FilesConstants.OBJECT_NAME_LENGTH
                            + " characters !");
                    objName = objName.substring(0,
                            FilesConstants.OBJECT_NAME_LENGTH);
                    logger.warn("Truncated Object Name is: " + objName);
                }

                HttpGet method = new HttpGet(storageURL + "/"
                        + sanitizeForURI(container) + "/"
                        + sanitizeForURI(objName));
                method.getParams().setIntParameter("http.socket.timeout",
                        connectionTimeOut);
                method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                FilesResponse response = new FilesResponse(
                        client.execute(method));

                if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    method.abort();
                    login();
                    method = new HttpGet(storageURL + "/"
                            + sanitizeForURI(container) + "/"
                            + sanitizeForURI(objName));
                    method.getParams().setIntParameter("http.socket.timeout",
                            connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    response = new FilesResponse(client.execute(method));
                }

                if (response.getStatusCode() == HttpStatus.SC_OK) {
                    logger.info("Object data retreived  : " + objName);
                    // DO NOT RELEASE THIS CONNECTION
                    return response.getResponseBodyAsStream();
                } else if (response.getStatusCode() == HttpStatus.SC_NOT_FOUND) {
                    method.abort();
                    throw new FilesNotFoundException("Container: " + container
                            + " did not have object " + objName,
                            response.getResponseHeaders(),
                            response.getStatusLine());
                }
            } else {
                if (!isValidObjectName(objName)) {
                    throw new FilesInvalidNameException(objName);
                } else {
                    throw new FilesInvalidNameException(container);
                }
            }
        } else {
            throw new FilesAuthorizationException("You must be logged in",
                    null, null);
        }
        return null;
    }

    /**
     * Utility function to write an InputStream to a file
     *
     * @param is
     * @param f
     * @throws IOException
     */
    static void writeInputStreamToFile(InputStream is, File f)
            throws IOException {
        BufferedOutputStream bf = new BufferedOutputStream(
                new FileOutputStream(f));
        byte[] buffer = new byte[1024];
        int read;

        while ((read = is.read(buffer)) > 0) {
            bf.write(buffer, 0, read);
        }

        is.close();
        bf.flush();
        bf.close();
    }

    /**
     * Reads an input stream into a stream
     *
     * @param is The input stream
     * @return The contents of the stream stored in a string.
     * @throws IOException
     */
    static String inputStreamToString(InputStream stream, String encoding)
            throws IOException {
        char buffer[] = new char[4096];
        StringBuilder sb = new StringBuilder();
        InputStreamReader isr = new InputStreamReader(stream, "utf-8"); // For
        // now,
        // assume
        // utf-8
        // to
        // work
        // around
        // server
        // bug

        int nRead;
        while ((nRead = isr.read(buffer)) >= 0) {
            sb.append(buffer, 0, nRead);
        }
        isr.close();

        return sb.toString();
    }

    /**
     * Calculates the MD5 checksum of a file, returned as a hex encoded string
     *
     * @param f The file
     * @return The MD5 checksum, as a base 16 encoded string
     * @throws IOException
     */
    public static String md5Sum(File f) throws IOException {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
            InputStream is = new FileInputStream(f);
            byte[] buffer = new byte[1024];
            int read;

            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }

            is.close();

            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);

            // Front load any zeros cut off by BigInteger
            String md5 = bigInt.toString(16);
            while (md5.length() != 32) {
                md5 = "0" + md5;
            }
            return md5;
        } catch (NoSuchAlgorithmException e) {
            logger.fatal("The JRE is misconfigured on this computer", e);
            return null;
        }
    }

    /**
     * Calculates the MD5 checksum of an array of data
     *
     * @param data The data to checksum
     * @return The checksum, represented as a base 16 encoded string.
     * @throws IOException
     */
    public static String md5Sum(byte[] data) throws IOException {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] md5sum = digest.digest(data);
            BigInteger bigInt = new BigInteger(1, md5sum);

            // Front load any zeros cut off by BigInteger
            String md5 = bigInt.toString(16);
            while (md5.length() != 32) {
                md5 = "0" + md5;
            }
            return md5;
        } catch (NoSuchAlgorithmException nsae) {
            logger.fatal("Major problems with your Java configuration", nsae);
            return null;
        }

    }

    /**
     * Encode any unicode characters that will cause us problems.
     *
     * @param str
     * @return The string encoded for a URI
     */
    public static String sanitizeForURI(String str) {
        URLCodec codec = new URLCodec();
        try {
            return codec.encode(str).replaceAll("\\+", "%20");
        } catch (EncoderException ee) {
            logger.warn("Error trying to encode string for URI", ee);
            return str;
        }
    }

    public static String sanitizeAndPreserveSlashes(String str) {
        URLCodec codec = new URLCodec();
        try {
            return codec.encode(str).replaceAll("\\+", "%20")
                    .replaceAll("%2F", "/");
        } catch (EncoderException ee) {
            logger.warn("Error trying to encode string for URI", ee);
            return str;
        }
    }

    public static String unencodeURI(String str) {
        URLCodec codec = new URLCodec();
        try {
            return codec.decode(str);
        } catch (DecoderException ee) {
            logger.warn("Error trying to encode string for URI", ee);
            return str;
        }

    }

    /**
     * @return The connection timeout used for communicating with the server (in
     * milliseconds)
     */
    public int getConnectionTimeOut() {
        return connectionTimeOut;
    }

    /**
     * The timeout we will use for communicating with the server (in
     * milliseconds)
     *
     * @param connectionTimeOut The new timeout for this connection
     */
    public void setConnectionTimeOut(int connectionTimeOut) {
        this.connectionTimeOut = connectionTimeOut;
    }

    /**
     * @return The storage URL on the other end of the ReST api
     */
    public String getStorageURL() {
        return storageURL;
    }

    /**
     * @return Get's our storage token.
     */
    @Deprecated
    public String getStorageToken() {
        return authToken;
    }

    /**
     * @return Get's our storage token.
     */
    public String getAuthToken() {
        return authToken;
    }

    /**
     * Has this instance of the client authenticated itself? Note, this does not
     * mean that a call right now will work, if the auth token has timed out,
     * you will need to re-auth.
     *
     * @return True if we logged in, false otherwise.
     */
    public boolean isLoggedin() {
        return isLoggedin;
    }

    /**
     * The username we are logged in with.
     *
     * @return The username
     */
    public String getUserName() {
        return username;
    }

    /**
     * Set's the username for this client. Note, setting this after login has no
     * real impact unless the
     * <code>login()</code> method is called again.
     *
     * @param userName the username
     */
    public void setUserName(String userName) {
        this.username = userName;
    }

    /**
     * The password the client will use for the login.
     *
     * @return The password
     */
    public String getPassword() {
        return password;
    }

    /**
     * Set's the password for this client. Note, setting this after login has no
     * real impact unless the
     * <code>login()</code> method is called again.
     *
     * @param password The new password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * The URL we will use for Authentication
     *
     * @return The URL (represented as a string)
     */
    public String getAuthenticationURL() {
        return authenticationURL;
    }

    /**
     * Changes the URL of the authentication service. Note, if one is logged in,
     * this doesn't have an effect unless one calls login again.
     *
     * @param authenticationURL The new authentication URL
     */
    public void setAuthenticationURL(String authenticationURL) {
        this.authenticationURL = authenticationURL;
    }

    /**
     * @return the useETag
     */
    public boolean getUseETag() {
        return useETag;
    }

    /**
     * @param useETag the useETag to set
     */
    public void setUseETag(boolean useETag) {
        this.useETag = useETag;
    }

    public void setUserAgent(String userAgent) {
        client.getParams().setParameter(HTTP.USER_AGENT, userAgent);
    }

    public String getUserAgent() {
        return client.getParams().getParameter(HTTP.USER_AGENT).toString();
    }

    private boolean isValidContainerName(String name) {
        if (name == null) {
            return false;
        }
        int length = name.length();
        if (length == 0 || length > FilesConstants.CONTAINER_NAME_LENGTH) {
            return false;
        }
        if (name.indexOf('/') != -1) {
            return false;
        }
        // if (name.indexOf('?') != -1) return false;
        return true;
    }

    private boolean isValidObjectName(String name) {
        if (name == null) {
            return false;
        }
        int length = name.length();
        if (length == 0 || length > FilesConstants.OBJECT_NAME_LENGTH) {
            return false;
        }
        // if (name.indexOf('?') != -1) return false;
        return true;
    }

    /**
     * @return the cdnManagementURL
     */
    public String getCdnManagementURL() {
        return cdnManagementURL;
    }

    /**
     * @param config
     */
    public boolean updateObjectManifest(String container, String object,
            String manifest) throws FilesAuthorizationException, HttpException,
            IOException, FilesInvalidNameException {
        return updateObjectMetadataAndManifest(container, object,
                new HashMap<String, String>(), manifest);
    }

    /**
     * @param config
     */
    public boolean updateObjectMetadata(String container, String object,
            Map<String, String> metadata) throws FilesAuthorizationException,
            HttpException, IOException, FilesInvalidNameException {
        return updateObjectMetadataAndManifest(container, object, metadata,
                null);
    }

    /**
     * @param config
     */
    public boolean updateObjectMetadataAndManifest(String container,
            String object, Map<String, String> metadata, String manifest)
            throws FilesAuthorizationException, HttpException, IOException, FilesInvalidNameException {
        FilesResponse response;

        if (!isLoggedin) {
            throw new FilesAuthorizationException("You must be logged in", null, null);
        }
        if (!isValidContainerName(container)) {
            throw new FilesInvalidNameException(container);
        }
        if (!isValidObjectName(object)) {
            throw new FilesInvalidNameException(object);
        }

        String postUrl = storageURL + "/" + FilesClient.sanitizeForURI(container) + "/" + FilesClient.sanitizeForURI(object);
        HttpPost method = null;
        try {
            method = new HttpPost(postUrl);
            if (manifest != null) {
                method.setHeader(FilesConstants.MANIFEST_HEADER, manifest);
            }
            method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
            method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
            if (!(metadata == null || metadata.isEmpty())) {
                /// GGIPART ///
                //for (String key: metadata.keySet())
                //method.setHeader(FilesConstants.X_OBJECT_META + key,
                //		FilesClient.sanitizeForURI(metadata.get(key)));

                for (String key : metadata.keySet()) {
                    method.setHeader(key, metadata.get(key));
                }

                /// GGIENDPART ///
            }
            HttpResponse resp = client.execute(method);
            response = new FilesResponse(resp);
            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                method.abort();
                if (login()) {
                    method = new HttpPost(postUrl);
                    method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
                    method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                    if (!(metadata == null || metadata.isEmpty())) {
                        /// GGIPART ///
                        //for (String key: metadata.keySet())
                        //method.setHeader(FilesConstants.X_OBJECT_META + key,
                        //		FilesClient.sanitizeForURI(metadata.get(key)));

                        for (String key : metadata.keySet()) {
                            method.setHeader(key, metadata.get(key));
                        }

                        /// GGIENDPART ///
                    }
                    client.execute(method);
                }
            }

            return true;
        } finally {
            if (method != null) {
                method.abort();
            }
        }
    }

    private String makeURI(String base, List<NameValuePair> parameters) {
        return base + "?" + URLEncodedUtils.format(parameters, "UTF-8");
    }

    /*
     * 
     * private void setQueryParameters(HttpRequestBase method,
     * List<NameValuePair> parameters) throws FilesException{ URI oldURI =
     * method.getURI(); try { URI newURI =
     * URIUtils.createURI(oldURI.getScheme(), oldURI.getHost(), -1,
     * URLEncoder.encode(oldURI.getPath(), "UTF-8"),
     * URLEncodedUtils.format(parameters, "UTF-8"), null);
     * logger.warn("Old Path: " + oldURI.getPath()); logger.warn("New URI: " +
     * newURI); method.setURI(newURI); } catch (UnsupportedEncodingException
     * uee) {
     * logger.error("Somehow, we don't have UTF-8, this is quite a surprise",
     * uee); throw new
     * FilesException("Somehow, we don't have UTF-8, this is quite a surprise",
     * uee); } catch (URISyntaxException use) { logger.error("Bad Syntax", use);
     * throw new FilesException("Bad URL Syntax", use); } }
     */
    public void useSnet() {
        if (snet) {
        } else {
            snet = true;
            if (storageURL != null) {
                storageURL = snetAddr + storageURL.substring(8);
            }
        }
    }

    public void usePublic() {
        if (!snet) {
        } else {
            snet = false;
            if (storageURL != null) {
                storageURL = "https://"
                        + storageURL.substring(snetAddr.length());
            }
        }
    }

    public boolean usingSnet() {
        return snet;
    }

    private boolean envSnet() {
        if (System.getenv("RACKSPACE_SERVICENET") == null) {
            return false;
        } else {
            snet = true;
            return true;
        }
    }

    public HttpClient getClientHTTP() {
        return client;
    }
}
src/main/java/com/rackspacecloud/client/cloudfiles/FilesContainerInfo.java
public class FilesContainerInfo
{
    private int objectCount;
    private long totalSize;
    private String name;

    /**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
     * @param containerCount The number of objects in the container
     * @param totalSize      The total size of the container (in bytes)
     */
    FilesContainerInfo(String name, int containerCount, long totalSize)
    {
    	this.name = name;
        this.objectCount = containerCount;
        this.totalSize = totalSize;
    }

    /**
     * Returns the number of objects in the container
     * 
     * @return The number of objects
     */
    public int getObjectCount()
    {
        return objectCount;
    }

    /**
     * @return The total size of the objects in the container (in bytes)
     */
    public long getTotalSize()
    {
    	return totalSize;
    }

}


Question: package com.rackspacecloud.client.cloudfiles.FilesClientTest;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.log4j.Logger;
import com.rackspacecloud.client.cloudfiles.FilesAccountInfo;
import com.rackspacecloud.client.cloudfiles.FilesCDNContainer;
import com.rackspacecloud.client.cloudfiles.FilesClient;
import com.rackspacecloud.client.cloudfiles.FilesConstants;
import com.rackspacecloud.client.cloudfiles.FilesContainer;
import com.rackspacecloud.client.cloudfiles.FilesContainerInfo;
import com.rackspacecloud.client.cloudfiles.FilesObject;
import com.rackspacecloud.client.cloudfiles.FilesObjectMetaData;
import com.rackspacecloud.client.cloudfiles.FilesUtil;
import com.rackspacecloud.client.cloudfiles.IFilesTransferCallback;
import com.rackspacecloud.client.cloudfiles.expections.FilesContainerExistsException;
import com.rackspacecloud.client.cloudfiles.expections.FilesException;
import com.rackspacecloud.client.cloudfiles.expections.FilesInvalidNameException;
import com.rackspacecloud.client.cloudfiles.expections.FilesNotFoundException;
import junit.framework.TestCase;






public class SnetFilesClientTestCase extends TestCase {
    private static Logger logger = Logger.getLogger(FilesClientTestCase.class);
	private static File SYSTEM_TMP = SystemUtils.getJavaIoTmpDir();
	private static int NUMBER_RANDOM_BYTES = 513;
	
	public void testConstructor() {
		FilesClient client = new FilesClient("foo", "bar", "baz");
		
		assertNotNull(client);
		assertEquals("foo", client.getUserName());
		assertEquals("bar", client.getPassword());
		assertEquals("baz", client.getAccount());
				
	}

	public void testNoArgConstructor() {
		FilesClient client = new FilesClient();
		
		assertNotNull(client);
		assertEquals(FilesUtil.getProperty("username"), client.getUserName());
		assertEquals(FilesUtil.getProperty("password"), client.getPassword());
		assertEquals(FilesUtil.getProperty("account"), client.getAccount());
	}

	public void testLogin() {
		FilesClient client = new FilesClient();
		
		try {
			client.useSnet();
			assertTrue(client.usingSnet());
			assertTrue(client.login());
		} catch (Exception e) {
			fail(e.getMessage());
		} 
		
		// Now try a failed login
		client = new FilesClient(FilesUtil.getProperty("username"), 
				 	   		  FilesUtil.getProperty("password") + " this is a bogus password", 
				 	   		  FilesUtil.getProperty("account"));
		try {
			
			assertFalse(client.login());
		} catch (Exception e) {
			fail(e.getMessage());
		} 
	}
	
	public void testAccountInfo() {
		String containerName = createTempContainerName("byte-array");
		String filename = makeFileName("accountinfo");
		FilesClient client = new FilesClient();
		try {
			client.useSnet();
			assertTrue(client.usingSnet());
			assertTrue(client.login());
			
			// Make sure it's not there
			assertFalse(client.containerExists(containerName));
			
			// Add it
			client.createContainer(containerName);
			
			// See that it's there
			assertTrue(client.containerExists(containerName));
			assertNotNull(client.getContainerInfo(containerName));
			
			// Add some data
			byte randomData[] = makeRandomBytes();
			assertTrue(client.storeObject(containerName, randomData, "application/octet-stream", filename, new HashMap<String,String>()));
			
			// Do the test if we have an account
			if (FilesUtil.getProperty("account") != null) { 
				FilesAccountInfo info = client.getAccountInfo();
				assertTrue(info.getContainerCount() > 0);
				assertTrue(info.getBytesUsed() >= randomData.length);
			}
			
			// Clean up 
			client.deleteObject(containerName, filename);
			assertTrue(client.deleteContainer(containerName));
		} catch (Exception e) {
			e.printStackTrace();
			fail(e.getMessage());
		} 		
	}
	
	public void testMultipleFilesNotThere() {
	    // Tests to make sure we're releasing connections with 404's
		FilesClient client = new FilesClient();
		String filename = makeFileName("random");
		String fullPath = FilenameUtils.concat(SYSTEM_TMP.getAbsolutePath(), filename);
		try {
			byte[] content = makeRandomFile(fullPath);
			client.useSnet();
			assertTrue(client.usingSnet());
			assertTrue(client.login());
			String containerName = createTempContainerName("file-not-there");
			client.createContainer(containerName);
			
			String[] names =  new String[10];
			for(int i=0; i < 10; ++i) names[i] = "File" + (i + 1) + ".txt";
			for(int i=0; i < 5; ++i) 			
				assertNotNull(client.storeObjectAs(containerName, new File(fullPath), "application/octet-stream", names[i]));

            for (int i = 0; i < 10; i++) {
                String fileName = names[i];

                byte[] retrievedContent = null;
                try {
                    retrievedContent = client.getObject(containerName, fileName);
                    assertArrayEquals(content, retrievedContent);
Answer:                 } catch(FilesNotFoundException ex) {