Package com.rackspacecloud.client.cloudfiles

Source Code of com.rackspacecloud.client.cloudfiles.FilesClient

/*
* See COPYING for license information.
*/

package com.rackspacecloud.client.cloudfiles;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.lang.text.StrTokenizer;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.rackspacecloud.client.cloudfiles.wrapper.RequestEntityWrapper;

/**
*
* A client for Cloud Files.  Here follows a basic example of logging in, creating a container and an
* object, retrieving the object, and then deleting both the object and container.  For more examples,
* see the code in com.rackspacecloud.client.cloudfiles.sample, which contains a series of examples.
*
* <pre>
*
*  //  Create the client object for username "jdoe", password "johnsdogsname".
*   FilesClient myClient = FilesClient("jdoe", "johnsdogsname");
*
*  // Log in (<code>login()</code> will return false if the login was unsuccessful.
*  assert(myClient.login());
*
*  // Make sure there are no containers in the account
*  assert(myClient.listContainers.length() == 0);
*  // Create the container
*  assert(myClient.createContainer("myContainer"));
*  // Now we should have one
*  assert(myClient.listContainers.length() == 1);
*  // Upload the file "alpaca.jpg"
*  assert(myClient.storeObject("myContainer", new File("alapca.jpg"), "image/jpeg"));
*  // Download "alpaca.jpg"
*  FilesObject obj = myClient.getObject("myContainer", "alpaca.jpg");
*  byte data[] = obj.getObject();
*  // Clean up after ourselves.
*  // Note:  Order here is important, you can't delete non-empty containers.
*  assert(myClient.deleteObject("myContainer", "alpaca.jpg"));
*  assert(myClient.deleteContainer("myContainer");
* </pre>
*
* @see com.rackspacecloud.client.cloudfiles.sample.FilesCli
* @see com.rackspacecloud.client.cloudfiles.sample.FilesAuth
* @see com.rackspacecloud.client.cloudfiles.sample.FilesCopy
* @see com.rackspacecloud.client.cloudfiles.sample.FilesList
* @see com.rackspacecloud.client.cloudfiles.sample.FilesRemove
* @see com.rackspacecloud.client.cloudfiles.sample.FilesMakeContainer
*
* @author lvaughn
*/
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 authToken = null;
    private boolean isLoggedin = false;
    private boolean useETag = true;
    private boolean snet = false;
    private List<FilesRegion> regions = new ArrayList<FilesRegion>();
    private FilesRegion currentRegion = null;
    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 = 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() {
              protected HttpParams createHttpParams() {
                  BasicHttpParams params = new BasicHttpParams();
                  org.apache.http.params.HttpConnectionParams.setSoTimeout(params, connectionTimeOut);
                  params.setParameter("http.socket.timeout", connectionTimeOut);
                  return params;
              }

              @Override
                protected ClientConnectionManager createClientConnectionManager() {
                     SchemeRegistry schemeRegistry = new SchemeRegistry();
                     schemeRegistry.register(
                             new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
                     schemeRegistry.register(
                             new Scheme("https", 443, SSLSocketFactory.getSocketFactory()));
                     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).
     *
     * @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
    {
        HttpPost method = new HttpPost(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);
        JSONObject creds = new JSONObject();
        creds.put("username", username);
        creds.put("key", password);
        JSONObject auth = new JSONObject();
        auth.put("credentials", creds);
        String json = JSONValue.toJSONString(auth);
        HttpEntity entity = new ByteArrayEntity(json.getBytes("UTF-8"));
        method.setHeader("Content-type", "application/json");
        method.setEntity(entity);

        FilesResponse response = new FilesResponse(client.execute(method));
       
        if (response.loginSuccess())
        {
            isLoggedin   = true;
            regions.clear();
            regions.addAll(response.getRegions());
            setDefaultRegion();
            authToken = response.getAuthToken();
            //logger.debug("storageURL: " + getStorageURL());
            logger.debug("authToken: " + authToken);
            //logger.debug("cdnManagementURL:" + getCdnManagementURL());
            logger.debug("ConnectionManager:" + client.getConnectionManager());
        }
        method.abort();

        return this.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;
      FilesRegion region = new FilesRegion("", storageURL, cdnManagmentUrl, true);
      regions.clear();
      regions.add(region);
      currentRegion = region;
      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(getStorageURL(), 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(getStorageURL(), parameters) : getStorageURL();
        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 response from server", response.getResponseHeaders(), response.getStatusLine());
        }
      }
      catch (Exception ex) {
        ex.printStackTrace();
        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(getStorageURL() +"/"+sanitizeForURI(container), parameters) : getStorageURL();
           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) {
        logger.error("Error parsing xml", ex);
        throw new FilesException("Error parsing server resposne", 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(getStorageURL());
          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(getStorageURL());
              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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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)
            {
              return;
            }
            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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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  {
            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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL()+"/"+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(getCdnManagementURL(), params) : getCdnManagementURL();
             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 = getCdnManagementURL() + "/" + 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)
      {
        return;
      }
      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 = getCdnManagementURL() + "/" + 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)
      {
        return;
      }
      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(getCdnManagementURL(), params) : getCdnManagementURL();
          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(getStorageURL()+"/"+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(getStorageURL()+"/"+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 IOException, HttpException, FilesException
    {
      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 IOException, HttpException, FilesException
    {
      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 IOException, HttpException, FilesException
    {
      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 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 IOException, HttpException, FilesException
    {
      if (this.isLoggedin())
      {
        if (isValidContainerName(container) && isValidObjectName(name) )
        {
          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(getStorageURL()+"/"+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(getStorageURL()+"/"+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());
                }
            }
            if (response.getStatusCode() == HttpStatus.SC_CREATED)
            {
              return response.getResponseHeader(FilesConstants.E_TAG).getValue();
            }
            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(name)) {
            throw new FilesInvalidNameException(name);
          }
          else {
            throw new FilesInvalidNameException(container);
          }
        }
      }
      else {
           throw new FilesAuthorizationException("You must be logged in", null, null);
      }
    }


    /**
     * 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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL() + "/" + 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(getStorageURL() + "/" + 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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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(getStorageURL()+"/"+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 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(getStorageURL()+"/"+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(getStorageURL()+"/"+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;
    }

    public InputStream getObjectAsRangedStream (String container, String objName, long offset, long length) 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(getStorageURL()+"/"+sanitizeForURI(container)+"/"+sanitizeForURI(objName));
          method.getParams().setIntParameter("http.socket.timeout", connectionTimeOut);
          method.setHeader(FilesConstants.X_AUTH_TOKEN, authToken);
                        if (offset >= 0)
          {
            method.setHeader("Range", "bytes="+offset+"-"+length);
          }
                        else
          {
            method.setHeader("Range", "bytes="+offset+"-");
          }
          FilesResponse response = new FilesResponse(client.execute(method));

            if (response.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
               method.abort();
            login();
            method = new HttpGet(getStorageURL()+"/"+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 = 0;

      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 = 0;
      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 = 0;

        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 (currentRegion == null) ? "" : currentRegion.getStorageUrl(snet);
    }

    /**
     * @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();
  }
 
  public static 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;
  }
        public static 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 (currentRegion == null) ? "" : currentRegion.getCDNManagementURL();
  }
 

    /**
     * @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 = getStorageURL() + "/"+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())) {
             for(String key:metadata.keySet())
               method.setHeader(FilesConstants.X_OBJECT_META+key,
                 FilesClient.sanitizeForURI(metadata.get(key)));
           }
          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())) {
                   for(String key:metadata.keySet())
                     method.setHeader(FilesConstants.X_OBJECT_META+key,
                       FilesClient.sanitizeForURI(metadata.get(key)));
                 }
                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(){
      snet = true;
    }
    public void usePublic(){
      snet = false;
    }
    public boolean usingSnet(){
      return snet;
    }
    private boolean envSnet(){
      if (System.getenv("RACKSPACE_SERVICENET") == null) {
        return false;
      }
      else{
        snet = true;
        return true;
      }
    }
   
  public String[] getRegions()
  {
    String[] result = new String[regions.size()];
    for (int i = 0; i < regions.size(); i++) {
      result[i] = regions.get(i).getRegionId();
    }
    return result;
  }
 
  public String getCurrentRegion()
  {
    return currentRegion == null ? "" : currentRegion.getRegionId();
  }
 
  public void setCurrentRegion(String regionId)
  {
    if (null != regionId) {
      for (FilesRegion region : regions) {
        if (regionId.equals(region.getRegionId())) {
          currentRegion = region;
          return;
        }
      }
    }
    throw new IllegalArgumentException();
  }
 
  public void setDefaultRegion()
  {
    for (FilesRegion region : regions) {
      if (region.isDefault()) {
        currentRegion = region;
        return;
      }
    }
    if (regions.size() > 0) {
      currentRegion = regions.get(0);
      return;
    }
    throw new IllegalArgumentException("No regions");
  }
}
TOP

Related Classes of com.rackspacecloud.client.cloudfiles.FilesClient

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.