content: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  package com.cloud.hypervisor.vmware.util;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.BufferedReader;
  import java.io.ByteArrayOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.io.OutputStream;
  import java.io.UnsupportedEncodingException;
  import java.net.HttpURLConnection;
  import java.net.URL;
  import java.net.URLEncoder;
  import java.nio.charset.Charset;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicLong;
  
  import javax.net.ssl.HostnameVerifier;
  import javax.net.ssl.HttpsURLConnection;
  import javax.net.ssl.SSLSession;
  import javax.xml.ws.soap.SOAPFaultException;
  
  import org.apache.cloudstack.utils.security.SSLUtils;
  import org.apache.cloudstack.utils.security.SecureSSLSocketFactory;
  import org.apache.logging.log4j.Logger;
  import org.apache.logging.log4j.LogManager;
  
  import com.cloud.hypervisor.vmware.mo.DatacenterMO;
  import com.cloud.hypervisor.vmware.mo.DatastoreFile;
  import com.cloud.utils.ActionDelegate;
  import com.cloud.utils.StringUtils;
  import com.vmware.pbm.PbmPortType;
  import com.vmware.pbm.PbmServiceInstanceContent;
  import com.vmware.vim25.ManagedObjectReference;
  import com.vmware.vim25.ObjectContent;
  import com.vmware.vim25.ObjectSpec;
  import com.vmware.vim25.PropertyFilterSpec;
  import com.vmware.vim25.PropertySpec;
  import com.vmware.vim25.ServiceContent;
  import com.vmware.vim25.TaskInfo;
  import com.vmware.vim25.TraversalSpec;
  import com.vmware.vim25.VimPortType;
  
  public class VmwareContext {
      protected static Logger LOGGER = LogManager.getLogger(VmwareContext.class);
  
      private static final int MAX_CONNECT_RETRY = 5;
      private static final int CONNECT_RETRY_INTERVAL = 1000;
  
      private static final int ChunkSize = 1 * 1024 * 1024;        // 1M
  
      private final VmwareClient _vimClient;
      private final String _serverAddress;
  
      private final Map<String, Object> _stockMap = new HashMap<String, Object>();
  
      private VmwareContextPool _pool;
      private String _poolKey;
  
      private static volatile int s_outstandingCount = 0;
  
      static {
          try {
              javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[1];
              javax.net.ssl.TrustManager tm = new TrustAllManager();
              trustAllCerts[0] = tm;
              javax.net.ssl.SSLContext sc = SSLUtils.getSSLContext();
              sc.init(null, trustAllCerts, null);
              javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(new SecureSSLSocketFactory(sc));
  
              HostnameVerifier hv = new HostnameVerifier() {
                  @Override
                  public boolean verify(String urlHostName, SSLSession session) {
                      return true;
                  }
              };
              HttpsURLConnection.setDefaultHostnameVerifier(hv);
          } catch (Exception e) {
              LOGGER.error("Unexpected exception ", e);
          }
      }
  
      public VmwareContext(VmwareClient client, String address) {
          assert (client != null) : "Invalid parameter in constructing VmwareContext object";
  
          _vimClient = client;
          _serverAddress = address;
  
          registerOutstandingContext();
          if (LOGGER.isInfoEnabled())
              LOGGER.info("New VmwareContext object, current outstanding count: " + getOutstandingContextCount());
      }
  
      public boolean validate() {
          return _vimClient.validate();
      }
  
      public void registerStockObject(String name, Object obj) {
          synchronized (_stockMap) {
              _stockMap.put(name, obj);
          }
      }
  
      public void uregisterStockObject(String name) {
          synchronized (_stockMap) {
              _stockMap.remove(name);
          }
      }
  
      public void clearStockObjects() {
          synchronized (_stockMap) {
              _stockMap.clear();
          }
      }
  
      @SuppressWarnings("unchecked")
      public <T> T getStockObject(String name) {
          synchronized (_stockMap) {
              return (T)_stockMap.get(name);
          }
      }
  
      public String getServerAddress() {
          return _serverAddress;
      }
  
      public VimPortType getService() {
          return _vimClient.getService();
      }
  
      public ServiceContent getServiceContent() {
          return _vimClient.getServiceContent();
      }
  
      public PbmPortType getPbmService() {
          return _vimClient.getPbmService();
      }
  
      public PbmServiceInstanceContent getPbmServiceContent() {
          return _vimClient.getPbmServiceContent();
      }
  
      public ManagedObjectReference getPropertyCollector() {
          return _vimClient.getPropCol();
      }
  
      public ManagedObjectReference getRootFolder() {
          return _vimClient.getRootFolder();
      }
  
      public VmwareClient getVimClient() {
          return _vimClient;
      }
  
      public void setPoolInfo(VmwareContextPool pool, String poolKey) {
          _pool = pool;
          _poolKey = poolKey;
      }
  
      public VmwareContextPool getPool() {
          return _pool;
      }
  
      public String getPoolKey() {
          return _poolKey;
      }
  
      public void idleCheck() throws Exception {
          getRootFolder();
      }
  
      public static int getOutstandingContextCount() {
          return s_outstandingCount;
      }
  
      public static void registerOutstandingContext() {
          s_outstandingCount++;
      }
  
      public static void unregisterOutstandingContext() {
          s_outstandingCount--;
      }
  
      public ManagedObjectReference getHostMorByPath(String inventoryPath) throws Exception {
          assert (inventoryPath != null);
  
          String[] tokens;
          if (inventoryPath.startsWith("/"))
              tokens = inventoryPath.substring(1).split("/");
          else
              tokens = inventoryPath.split("/");
  
          ManagedObjectReference mor = getRootFolder();
          for (int i = 0; i < tokens.length; i++) {
              String token = tokens[i];
              List<ObjectContent> ocs;
              PropertySpec pSpec = null;
              ObjectSpec oSpec = null;
              if (mor.getType().equalsIgnoreCase("Datacenter")) {
                  pSpec = new PropertySpec();
                  pSpec.setAll(false);
                  pSpec.setType("ManagedEntity");
                  pSpec.getPathSet().add("name");
  
                  TraversalSpec dcHostFolderTraversal = new TraversalSpec();
                  dcHostFolderTraversal.setType("Datacenter");
                  dcHostFolderTraversal.setPath("hostFolder");
                  dcHostFolderTraversal.setName("dcHostFolderTraversal");
  
                  oSpec = new ObjectSpec();
                  oSpec.setObj(mor);
                  oSpec.setSkip(Boolean.TRUE);
                  oSpec.getSelectSet().add(dcHostFolderTraversal);
  
              } else if (mor.getType().equalsIgnoreCase("Folder")) {
                  pSpec = new PropertySpec();
                  pSpec.setAll(false);
                  pSpec.setType("ManagedEntity");
                  pSpec.getPathSet().add("name");
  
                  TraversalSpec folderChildrenTraversal = new TraversalSpec();
                  folderChildrenTraversal.setType("Folder");
                  folderChildrenTraversal.setPath("childEntity");
                  folderChildrenTraversal.setName("folderChildrenTraversal");
  
                  oSpec = new ObjectSpec();
                  oSpec.setObj(mor);
                  oSpec.setSkip(Boolean.TRUE);
                  oSpec.getSelectSet().add(folderChildrenTraversal);
  
              } else if (mor.getType().equalsIgnoreCase("ClusterComputeResource")) {
                  pSpec = new PropertySpec();
                  pSpec.setType("ManagedEntity");
                  pSpec.getPathSet().add("name");
  
                  TraversalSpec clusterHostTraversal = new TraversalSpec();
                  clusterHostTraversal.setType("ClusterComputeResource");
                  clusterHostTraversal.setPath("host");
                  clusterHostTraversal.setName("folderChildrenTraversal");
  
                  oSpec = new ObjectSpec();
                  oSpec.setObj(mor);
                  oSpec.setSkip(Boolean.TRUE);
                  oSpec.getSelectSet().add(clusterHostTraversal);
  
              } else {
                  LOGGER.error("Invalid inventory path, path element can only be datacenter and folder");
                  return null;
              }
  
              PropertyFilterSpec pfSpec = new PropertyFilterSpec();
              pfSpec.getPropSet().add(pSpec);
              pfSpec.getObjectSet().add(oSpec);
              List<PropertyFilterSpec> pfSpecArr = new ArrayList<PropertyFilterSpec>();
              pfSpecArr.add(pfSpec);
              ocs = getService().retrieveProperties(getPropertyCollector(), pfSpecArr);
  
              if (ocs != null && ocs.size() > 0) {
                  boolean found = false;
                  for (ObjectContent oc : ocs) {
                      String name = oc.getPropSet().get(0).getVal().toString();
                      if (name.equalsIgnoreCase(token) || name.equalsIgnoreCase("host")) {
                          mor = oc.getObj();
                          found = true;
                          if (name.equalsIgnoreCase("host"))
                              i--;
                          break;
                      }
                  }
                  if (!found) {
                      LOGGER.error("Path element points to an un-existing inventory entity");
                      return null;
                  }
              } else {
                  LOGGER.error("Path element points to an un-existing inventory entity");
                  return null;
              }
          }
          return mor;
      }
  
      // path in format of <datacenter name>/<datastore name>
      public ManagedObjectReference getDatastoreMorByPath(String inventoryPath) throws Exception {
          assert (inventoryPath != null);
  
          String[] tokens;
          if (inventoryPath.startsWith("/"))
              tokens = inventoryPath.substring(1).split("/");
          else
              tokens = inventoryPath.split("/");
  
          if (tokens == null || tokens.length != 2) {
              LOGGER.error("Invalid datastore inventory path. path: " + inventoryPath);
              return null;
          }
  
          DatacenterMO dcMo = new DatacenterMO(this, tokens[0]);
          if (dcMo.getMor() == null) {
              LOGGER.error("Unable to locate the datacenter specified in path: " + inventoryPath);
              return null;
          }
  
          return dcMo.findDatastore(tokens[1]);
      }
  
      // path in format of <datacenter name>/<datastore name>
      public String getDatastoreNameFromPath(String inventoryPath) throws Exception {
          assert (inventoryPath != null);
  
          String[] tokens;
          if (inventoryPath.startsWith("/"))
              tokens = inventoryPath.substring(1).split("/");
          else
              tokens = inventoryPath.split("/");
  
          if (tokens == null || tokens.length != 2) {
              LOGGER.error("Invalid datastore inventory path. path: " + inventoryPath);
              return null;
          }
  
          return tokens[1];
      }
  
      public void waitForTaskProgressDone(ManagedObjectReference morTask) throws Exception {
          while (true) {
              TaskInfo tinfo = (TaskInfo)_vimClient.getDynamicProperty(morTask, "info");
              Integer progress = tinfo.getProgress();
              if (progress == null)
                  break;
  
              if (progress.intValue() >= 100)
                  break;
  
              Thread.sleep(1000);
          }
      }
  
      public void getFile(String urlString, String localFileFullName) throws Exception {
          HttpURLConnection conn = getHTTPConnection(urlString);
  
          InputStream in = conn.getInputStream();
          OutputStream out = new FileOutputStream(new File(localFileFullName));
          byte[] buf = new byte[ChunkSize];
          int len = 0;
          while ((len = in.read(buf)) > 0) {
              out.write(buf, 0, len);
          }
          in.close();
          out.close();
      }
  
      public void uploadFile(String httpMethod, String urlString, String localFileName) throws Exception {
          HttpURLConnection conn = getRawHTTPConnection(urlString);
  
          conn.setDoOutput(true);
          conn.setUseCaches(false);
  
          conn.setChunkedStreamingMode(ChunkSize);
          conn.setRequestMethod(httpMethod);
          conn.setRequestProperty("Connection", "Keep-Alive");
          String contentType = "application/octet-stream";
          conn.setRequestProperty("content-type", contentType);
          conn.setRequestProperty("content-length", Long.toString(new File(localFileName).length()));
          connectWithRetry(conn);
          OutputStream out = null;
          InputStream in = null;
          BufferedReader br = null;
  
          try {
              out = conn.getOutputStream();
              in = new FileInputStream(localFileName);
              byte[] buf = new byte[ChunkSize];
              int len = 0;
              while ((len = in.read(buf)) > 0) {
                  out.write(buf, 0, len);
              }
              out.flush();
          } finally {
              if (in != null)
                  in.close();
  
              if (out != null)
                  out.close();
  
              if (br != null)
                  br.close();
              conn.disconnect();
          }
      }
  
      private Charset getCharSetFromConnection(HttpURLConnection conn) {
          String charsetName = conn.getContentEncoding();
          Charset charset;
          try {
              charset = Charset.forName(charsetName);
          } catch (IllegalArgumentException e) {
              LOGGER.warn("Illegal/unsupported/null charset name from connection. charsetname from connection is " + charsetName);
              charset = StringUtils.getPreferredCharset();
          }
          return charset;
      }
  
      public void uploadVmdkFile(String httpMethod, String urlString, String localFileName, long totalBytesUpdated, ActionDelegate<Long> progressUpdater) throws Exception {
  
          HttpURLConnection conn = getRawHTTPConnection(urlString);
  
          conn.setDoOutput(true);
          conn.setUseCaches(false);
  
          conn.setChunkedStreamingMode(ChunkSize);
          conn.setRequestMethod(httpMethod);
          if (urlString.endsWith(".iso")) {
              conn.setRequestProperty("Overwrite", "t");
          }
          conn.setRequestProperty("Connection", "Keep-Alive");
          String contentType = urlString.endsWith(".iso") ?
                  "application/octet-stream" :
                  "application/x-vnd.vmware-streamVmdk";
          conn.setRequestProperty("content-type", contentType);
          conn.setRequestProperty("content-length", Long.toString(new File(localFileName).length()));
          connectWithRetry(conn);
  
          BufferedOutputStream bos = null;
          BufferedInputStream is = null;
          try {
              bos = new BufferedOutputStream(conn.getOutputStream());
              is = new BufferedInputStream(new FileInputStream(localFileName));
              int bufferSize = ChunkSize;
              byte[] buffer = new byte[bufferSize];
              while (true) {
                  int bytesRead = is.read(buffer, 0, bufferSize);
                  if (bytesRead == -1) {
                      break;
                  }
                  bos.write(buffer, 0, bytesRead);
                  totalBytesUpdated += bytesRead;
                  bos.flush();
                  if (progressUpdater != null)
                      progressUpdater.action(new Long(totalBytesUpdated));
              }
              bos.flush();
          } finally {
              if (is != null)
                  is.close();
              if (bos != null)
                  bos.close();
  
              conn.disconnect();
          }
      }
  
      public long downloadVmdkFile(String urlString, String localFileName, AtomicLong totalBytesDownloaded, ActionDelegate<Long> progressUpdater) throws Exception {
          HttpURLConnection conn = getRawHTTPConnection(urlString);
  
          String cookie = _vimClient.getServiceCookie();
          if (cookie == null) {
              LOGGER.error("No cookie is found in vwware web service request context!");
              throw new Exception("No cookie is found in vmware web service request context!");
          }
          conn.addRequestProperty("Cookie", cookie);
          conn.setDoInput(true);
          conn.setDoOutput(true);
          conn.setAllowUserInteraction(true);
          connectWithRetry(conn);
  
          long bytesWritten = 0;
          InputStream in = null;
          OutputStream out = null;
          try {
              in = conn.getInputStream();
              out = new FileOutputStream(new File(localFileName));
  
              byte[] buf = new byte[ChunkSize];
              int len = 0;
              while ((len = in.read(buf)) > 0) {
                  out.write(buf, 0, len);
                  bytesWritten += len;
                  totalBytesDownloaded.addAndGet(len);
  
                  if (progressUpdater != null)
                      progressUpdater.action(new Long(totalBytesDownloaded.get()));
              }
          } finally {
              if (in != null)
                  in.close();
              if (out != null)
                  out.close();
  
              conn.disconnect();
          }
          return bytesWritten;
      }
  
      public byte[] getResourceContent(String urlString) throws Exception {
          HttpURLConnection conn = getHTTPConnection(urlString);
          InputStream in = conn.getInputStream();
  
          ByteArrayOutputStream out = new ByteArrayOutputStream();
          byte[] buf = new byte[ChunkSize];
          int len = 0;
          while ((len = in.read(buf)) > 0) {
              out.write(buf, 0, len);
          }
          in.close();
          out.close();
          return out.toByteArray();
      }
  
      public void uploadResourceContent(String urlString, byte[] content) throws Exception {
          // vSphere does not support POST
          HttpURLConnection conn = getHTTPConnection(urlString, "PUT");
  
          OutputStream out = conn.getOutputStream();
          out.write(content);
          out.flush();
  
          BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), getCharSetFromConnection(conn)));
          String line;
          while ((in.ready()) && (line = in.readLine()) != null) {
              if (LOGGER.isTraceEnabled())
                  LOGGER.trace("Upload " + urlString + " response: " + line);
          }
          out.close();
          in.close();
      }
  
      /*
       * Sample content returned by query a datastore directory
       *
       * Url for the query
       *     https://vsphere-1.lab.vmops.com/folder/Fedora-clone-test?dcPath=cupertino&dsName=NFS+datastore
       *
       * Returned content from vSphere
       *
          <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
          <html>
            <head>
              <meta http-equiv="content-type" content="text/html; charset=utf-8">
              <title>Index of Fedora-clone-test on datastore NFS datastore in datacenter cupertino</title></head>
            <body>
          <h1>Index of Fedora-clone-test on datastore NFS datastore in datacenter cupertino</h1>
          <table>
              <tr><th>Name</th><th>Last modified</th><th>Size</th></tr><tr><th colspan="3"><hr></th></tr>
              <tr><td><a href="/folder?dcPath=cupertino&amp;dsName=NFS%20datastore">Parent Directory</a></td><td>&nbsp;</td><td align="right">  - </td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2da2013465%2ehlog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test-a2013465.hlog</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">1</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2da2013465%2evswp?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test-a2013465.vswp</a></td><td align="right">14-Aug-2010 23:01</td><td align="right">402653184</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2dflat%2evmdk?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test-flat.vmdk</a></td><td align="right">26-Aug-2010 18:43</td><td align="right">17179869184</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2envram?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test.nvram</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">8684</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2evmdk?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test.vmdk</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">479</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2evmsd?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test.vmsd</a></td><td align="right">14-Aug-2010 16:59</td><td align="right">0</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2evmx?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test.vmx</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">3500</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/Fedora%2dclone%2dtest%2evmxf?dcPath=cupertino&amp;dsName=NFS%20datastore">
              Fedora-clone-test.vmxf</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">272</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/test%2etxt?dcPath=cupertino&amp;dsName=NFS%20datastore">test.txt</a></td>
              <td align="right">24-Aug-2010 01:03</td><td align="right">12</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d2%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-2.log</a></td><td align="right">14-Aug-2010 16:51</td><td align="right">80782</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d3%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-3.log</a></td><td align="right">14-Aug-2010 19:07</td><td align="right">58573</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d4%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-4.log</a></td><td align="right">14-Aug-2010 23:00</td><td align="right">49751</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d5%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-5.log</a></td><td align="right">15-Aug-2010 00:04</td><td align="right">64024</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d6%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-6.log</a></td><td align="right">15-Aug-2010 00:11</td><td align="right">59742</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2d7%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware-7.log</a></td><td align="right">15-Aug-2010 00:13</td><td align="right">59859</td></tr>
              <tr><td><a href="/folder/Fedora%2dclone%2dtest/vmware%2elog?dcPath=cupertino&amp;dsName=NFS%20datastore">
              vmware.log</a></td><td align="right">15-Aug-2010 00:23</td><td align="right">47157</td></tr>
              <tr><th colspan="5"><hr></th></tr>
          </table>
            </body>
          </html>
       */
      public String[] listDatastoreDirContent(String urlString) throws Exception {
          List<String> fileList = new ArrayList<String>();
          String content = new String(getResourceContent(urlString),"UTF-8");
          String marker = "</a></td><td ";
          int parsePos = -1;
          do {
              parsePos = content.indexOf(marker, parsePos < 0 ? 0 : parsePos);
              if (parsePos > 0) {
                  int beginPos = content.lastIndexOf('>', parsePos - 1);
                  if (beginPos < 0)
                      beginPos = 0;
  
                  fileList.add((content.substring(beginPos + 1, parsePos)));
                  parsePos += marker.length();
              } else {
                  break;
              }
          } while (parsePos > 0);
          return fileList.toArray(new String[0]);
      }
  
      public String composeDatastoreBrowseUrl(String dcName, String fullPath) {
          DatastoreFile dsFile = new DatastoreFile(fullPath);
          return composeDatastoreBrowseUrl(dcName, dsFile.getDatastoreName(), dsFile.getRelativePath());
      }
  
      public String composeDatastoreBrowseUrl(String dcName, String datastoreName, String relativePath) {
          assert (relativePath != null);
          assert (datastoreName != null);
  
          StringBuffer sb = new StringBuffer();
          sb.append("https://");
          sb.append(_serverAddress);
          sb.append("/folder/");
          try {
              sb.append(URLEncoder.encode(relativePath, "UTF-8"));
              sb.append("?dcPath=").append(URLEncoder.encode(dcName, "UTF-8"));
              sb.append("&dsName=").append(URLEncoder.encode(datastoreName, "UTF-8"));
          } catch (UnsupportedEncodingException e) {
              LOGGER.error(String.format("Unable to encode URL. relativePath : %s, dcPath : %s, dsName : %s", relativePath, dcName, datastoreName), e);
          }
          return sb.toString();
      }
  
      public HttpURLConnection getHTTPConnection(String urlString) throws Exception {
          return getHTTPConnection(urlString, "GET");
      }
  
      public HttpURLConnection getHTTPConnection(String urlString, String httpMethod) throws Exception {
          String cookie = _vimClient.getServiceCookie();
          if (cookie == null) {
              LOGGER.error("No cookie is found in vmware web service request context!");
              throw new Exception("No cookie is found in vmware web service request context!");
          }
          URL url = new URL(urlString);
          HttpURLConnection conn = (HttpURLConnection)url.openConnection();
  
          conn.setDoInput(true);
          conn.setDoOutput(true);
          conn.setAllowUserInteraction(true);
          conn.addRequestProperty("Cookie", cookie);
          conn.setRequestMethod(httpMethod);
          connectWithRetry(conn);
          return conn;
      }
  
      public HttpURLConnection getRawHTTPConnection(String urlString) throws Exception {
          URL url = new URL(urlString);
          return (HttpURLConnection)url.openConnection();
      }
  
      private static void connectWithRetry(HttpURLConnection conn) throws Exception {
          boolean connected = false;
          for (int i = 0; i < MAX_CONNECT_RETRY && !connected; i++) {
              try {
                  conn.connect();
                  connected = true;
                  LOGGER.info("Connected, conn: " + conn.toString() + ", retry: " + i);
              } catch (Exception e) {
                  LOGGER.warn("Unable to connect, conn: " + conn.toString() + ", message: " + e.toString() + ", retry: " + i);
  
                  try {
                      Thread.sleep(CONNECT_RETRY_INTERVAL);
                  } catch (InterruptedException ex) {
                      LOGGER.debug("[ignored] interrupted while connecting.");
                  }
              }
          }
  
          if (!connected)
              throw new Exception("Unable to connect to " + conn.toString());
      }
  
      public void close() {
          clearStockObjects();
          try {
              LOGGER.info("Disconnecting VMware session");
              _vimClient.disconnect();
          } catch(SOAPFaultException sfe) {
              LOGGER.debug("Tried to disconnect a session that is no longer valid");
          } catch (Exception e) {
              LOGGER.warn("Unexpected exception: ", e);
          } finally {
              if (_pool != null) {
                  _pool.unregisterContext(this);
              }
              unregisterOutstandingContext();
          }
      }
  
      public static class TrustAllManager implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager {
          @Override
          public java.security.cert.X509Certificate[] getAcceptedIssuers() {
              return null;
          }
  
          public boolean isServerTrusted(java.security.cert.X509Certificate[] certs) {
              return true;
          }
  
          public boolean isClientTrusted(java.security.cert.X509Certificate[] certs) {
              return true;
          }
  
          @Override
          public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
              return;
          }
  
          @Override
          public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException {
              return;
          }
      }
  }
  