Package org.apache.oodt.cas.pushpull.protocol

Source Code of org.apache.oodt.cas.pushpull.protocol.ProtocolHandler

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.oodt.cas.pushpull.protocol;

//OODT imports
import org.apache.oodt.cas.protocol.Protocol;
import org.apache.oodt.cas.protocol.ProtocolFactory;
import org.apache.oodt.cas.protocol.ProtocolFile;
import org.apache.oodt.cas.pushpull.protocol.RemoteSiteFile;
import org.apache.oodt.cas.pushpull.config.ProtocolInfo;
import org.apache.oodt.cas.protocol.auth.BasicAuthentication;
import org.apache.oodt.cas.protocol.exceptions.ProtocolException;
import org.apache.oodt.cas.protocol.util.ProtocolFileFilter;
import org.apache.oodt.cas.pushpull.exceptions.RemoteConnectionException;


//JDK imports
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* This class is responsible for creating the appropriate Protocol for the given
* RemoteSites. The boolean argument 'allowReuse' allows for one unique protocol
* for each URL. That is, if allowReuse is set to true, then if no Protocol has
* been created for the current site, the Protocol created will be saved and
* then returned for any later called with allowReuse equals true. This is to
* allow for the same Protocol object to be used by several classes. The
* Protocol class has been synchronized so this is thread-safe. If you set
* 'allowReuse' to false then a new Protocol object will be created and
* returned.<br>
* <br>
*
* @author bfoster
*/
public class ProtocolHandler {

  private final HashMap<URL, ProtocolFactory> urlAndProtocolFactory;

  private final HashMap<URL, Protocol> reuseProtocols;

  private final HashMap<RemoteSiteFile, PagingInfo> pageInfos;

  private final HashMap<RemoteSiteFile, List<RemoteSiteFile>> pathAndFileListMap;

  private final ProtocolInfo pi;

  private static final Logger LOG = Logger.getLogger(ProtocolHandler.class
      .getName());

  /**
   * Creates a new ProtocolHandler for the given Config object
   *
   * @param config
   *          The Config object that guides this ProtocolHandler in making class
   *          instanciations
   */
  public ProtocolHandler(ProtocolInfo pi) {
    this.pi = pi;
    urlAndProtocolFactory = new HashMap<URL, ProtocolFactory>();
    reuseProtocols = new HashMap<URL, Protocol>();
    pageInfos = new HashMap<RemoteSiteFile, PagingInfo>();
    pathAndFileListMap = new HashMap<RemoteSiteFile, List<RemoteSiteFile>>();
  }

  /**
   * Returns the appropriate protocol for the given Path
   *
   * @param ProtocolPath
   *          Used to determine the appropriate Protocol to be returned and the
   *          path to navigate on if navigateToPathLoc is set to true.
   * @param allowReuse
   *          Set to true if you would like ProtocolHandler to take care of the
   *          protocol returned (i.e. reuseable protocols may be returned by
   *          this method again, if it is the appropriate protocol type for a
   *          given Path. Also ProtocolHandler will take care of disconnecting
   *          the reuseable protocols)
   * @param navigateToPathLoc
   *          If true, will navigate the to the end of the Path location
   *          specified
   * @return Protocol for the given Path
   * @throws RemoteCommunicationException
   *           If there is an error creating the protocol
   */
  public Protocol getAppropriateProtocol(RemoteSiteFile pFile,
      boolean allowReuse, boolean navigateToPathLoc)
      throws RemoteConnectionException {
    try {
      Protocol protocol = getAppropriateProtocol(pFile, allowReuse);
      if (protocol != null && navigateToPathLoc) {
        if (pFile.isDir())
          this.cd(protocol, pFile);
        else if (pFile.getParent() != null)
          this.cd(protocol, new RemoteSiteFile(pFile.getParent(), pFile.getSite()));
      }
      return protocol;
    } catch (Exception e) {
      throw new RemoteConnectionException(
          "Failed to get appropriate protocol for " + pFile + " : "
              + e.getMessage(), e);
    }
  }

  private Protocol getAppropriateProtocol(RemoteSiteFile pFile,
      boolean allowReuse) throws ProtocolException, MalformedURLException {
    return this.getAppropriateProtocolBySite(pFile.getSite(), allowReuse);
  }

  public Protocol getAppropriateProtocolBySite(RemoteSite remoteSite,
      boolean allowReuse) throws ProtocolException {
    Protocol protocol = null;
    if ((allowReuse && ((protocol = reuseProtocols.get(remoteSite.getURL())) == null))
        || !allowReuse) {
      ProtocolFactory protocolFactory = this.urlAndProtocolFactory
          .get(remoteSite.getURL());
      if (protocolFactory == null) {
        LinkedList<Class<ProtocolFactory>> protocolClasses = pi
            .getProtocolClassesForProtocolType(remoteSite.getURL()
                .getProtocol());
        for (Class<ProtocolFactory> clazz : protocolClasses) {
          try {
            if ((protocol = (protocolFactory = clazz.newInstance())
                .newInstance()) != null) {
              if (!connect(protocol, remoteSite, true)) {
                LOG.log(
                    Level.WARNING,
                    "ProtocolFactory "
                        + protocolFactory.getClass().getCanonicalName()
                        + " is not compatible with server at "
                        + remoteSite.getURL());
                protocol = null;
              } else {
                this.urlAndProtocolFactory.put(remoteSite.getURL(),
                    protocolFactory);
                break;
              }
            }
          } catch (Exception e) {
            LOG.log(Level.WARNING, "Failed to instanciate protocol " + clazz
                + " for " + remoteSite.getURL());
          }
        }
        if (protocol == null)
          throw new ProtocolException("Failed to get appropriate protocol for "
              + remoteSite);
      } else {
        connect(protocol = protocolFactory.newInstance(), remoteSite, false);
      }
      if (allowReuse)
        this.reuseProtocols.put(remoteSite.getURL(), protocol);
    }
    return protocol;
  }

  public synchronized List<RemoteSiteFile> nextPage(RemoteSite site, Protocol protocol)
      throws RemoteConnectionException, ProtocolException {
    return nextPage(site, protocol, null);
  }

  /**
   * @param protocol
   * @return
   * @throws RemoteConnectionException
   * @throws ProtocolException
   */
  public synchronized List<RemoteSiteFile> nextPage(RemoteSite site, Protocol protocol,
      ProtocolFileFilter filter) throws RemoteConnectionException,
      ProtocolException {

    PagingInfo pgInfo = this.getPagingInfo(this.pwd(site, protocol));
    try {
      System.out.println("PageSize: " + pi.getPageSize() + " PageLoc: "
          + pgInfo.getPageLoc());
      List<RemoteSiteFile> fileList = this.ls(site, protocol);
      System.out.println("FileList size: " + fileList.size());

      if (this.getDynamicFileList(site, protocol) == null
          && !this.passesDynamicDetection(pgInfo, fileList)) {
        LOG.log(
            Level.SEVERE,
            "Remote directory '"
                + this.pwd(site, protocol)
                + "' file list size has changed -- setting directory as dynamic and resetting page location");
        this.putDynamicFileList(site, protocol, fileList);
        pgInfo.updatePageInfo(0, fileList);
      }

      List<RemoteSiteFile> page = new LinkedList<RemoteSiteFile>();
      int curLoc = pgInfo.getPageLoc();
      for (; page.size() < pi.getPageSize() && curLoc < fileList.size(); curLoc++) {
        if (filter == null || filter.accept(fileList.get(curLoc)))
          page.add(fileList.get(curLoc));
      }
      pgInfo.updatePageInfo(curLoc, fileList);

      return page;
    } catch (Exception e) {
      e.printStackTrace();
      throw new RemoteConnectionException(
          "Failed getting next page for protocol " + protocol + "-- pgStart = "
              + pgInfo.getPageLoc() + " pgSize = " + pi.getPageSize() + " : "
              + e.getMessage());
    }

  }

  private boolean passesDynamicDetection(PagingInfo pgInfo,
      List<RemoteSiteFile> newLS) throws MalformedURLException,
      ProtocolException {
    if (pgInfo.getSizeOfLastLS() != -1
        && (pgInfo.getSizeOfLastLS() != newLS.size() || (newLS.size() != 0
            && pgInfo.getPageLoc() < newLS.size() && (newLS.get(pgInfo
            .getPageLoc()) == null || !newLS.get(pgInfo.getPageLoc()).equals(
            pgInfo.getRemoteSiteFileAtPageLoc()))))) {
      return false;
    } else {
      return true;
    }
  }

  public void download(Protocol protocol, RemoteSiteFile fromFile, File toFile,
      boolean delete) throws RemoteConnectionException {

    // rename file for download
    File downloadFile = new File(
        String.format("%s/Downloading_%s", toFile.getParent(), toFile.getName()));
    toFile.renameTo(downloadFile);

    LOG.log(Level.INFO, "Starting to download " + fromFile);
    try {
      // try to download the file
      protocol.get(fromFile, downloadFile);

      // rename file back to original name
      if (!downloadFile.renameTo(toFile)) {
        throw new IOException(
            String.format("Failed to rename file %s to %s", downloadFile, toFile));
      }

      // delete file is specified
      if (delete) {
        if (!this.delete(protocol, fromFile))
          LOG.log(Level.WARNING, "Failed to delete file '" + fromFile
              + "' from server '" + fromFile.getSite() + "'");
        else
          LOG.log(Level.INFO, "Successfully deleted file '" + fromFile
              + "' from server '" + fromFile.getSite() + "'");
      }

      LOG.log(Level.INFO, "Finished downloading " + fromFile + " to " + toFile);

    } catch (Exception e) {
      downloadFile.delete();
      throw new RemoteConnectionException("Failed to download file " + fromFile
          + " : " + e.getMessage(), e);
    }
  }

  /**
   * Connects the given Protocol to the given URL
   *
   * @param protocol
   *          The Protocol that will be connected
   * @param url
   *          The server to which the Protocol will connect
   * @throws RemoteConnectionException
   *           If connection fails
   * @throws RemoteLoginException
   *           If login fails
   */
  public boolean connect(Protocol protocol, RemoteSite remoteSite, boolean test) {
    for (int tries = 0; tries < 3; tries++) {

      // wait for 5 secs before next retry
      if (tries > 0) {
        LOG.log(Level.INFO, "Will retry connecting to " + remoteSite
            + " in 5 seconds");
        synchronized (this) {
          try {
            System.out.print("Waiting");
            for (int i = 0; i < 5; i++) {
              System.out.print(" .");
              wait(1000);
            }
            System.out.println();
          } catch (Exception e) {
          }
        }
      }

      try {
        // make sure protocol is disconnected
        try {
          protocol.close();
        } catch (Exception e) {
        }

        // try connecting Protocol
        protocol.connect(
            remoteSite.getURL().getHost(),
            new BasicAuthentication(remoteSite.getUsername(), remoteSite
                .getPassword()));

        // check connection
        if (protocol.connected()
            && (!test || isOkProtocol(protocol, remoteSite))) {
          LOG.log(Level.INFO,
              "Successfully connected to " + remoteSite.getURL()
                  + " with protocol '" + protocol.getClass().getCanonicalName()
                  + "' and username '" + remoteSite.getUsername() + "'");
          return true;
        } else
          return false;

      } catch (Exception e) {
        LOG.log(Level.WARNING, "Error occurred while connecting to "
            + remoteSite + " : " + e.getMessage(), e);
      }

    }
    return false;
  }

  private boolean isOkProtocol(Protocol protocol, RemoteSite remoteSite) {
    try {
      LOG.log(Level.INFO, "Testing protocol "
          + protocol.getClass().getCanonicalName()
          + " . . . this may take a few minutes . . .");
      // test ls, cd, and pwd
      this.cdToHOME(protocol);
      RemoteSiteFile home = this.pwd(remoteSite, protocol);
      this.ls(remoteSite, protocol);
      if (remoteSite.getCdTestDir() != null)
        this.cd(protocol, new RemoteSiteFile(home, remoteSite.getCdTestDir(),
            true, remoteSite));
      else
        this.cdToROOT(protocol);
      this.cdToHOME(protocol);
      if (home == null || !home.equals(this.pwd(remoteSite, protocol)))
        throw new ProtocolException("Home directory not the same after cd");
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "Protocol "
          + protocol.getClass().getCanonicalName()
          + " failed compatibility test : " + e.getMessage(), e);
      return false;
    }
    return true;
  }

  public void cdToROOT(Protocol protocol) throws ProtocolException {
    protocol.cdRoot();
  }

  public void cdToHOME(Protocol protocol) throws ProtocolException {
    protocol.cdHome();
  }

  public boolean isProtocolConnected(Protocol protocol)
      throws ProtocolException {
    return protocol.connected();
  }

  public void cd(Protocol protocol, RemoteSiteFile file)
      throws ProtocolException {
    protocol.cd(file);
  }

  public RemoteSiteFile getProtocolFileFor(RemoteSite site, Protocol protocol, String file,
      boolean isDir) throws ProtocolException {
    return this.getProtocolFileByProtocol(site, protocol, file, isDir);
  }

  public synchronized boolean delete(Protocol protocol, RemoteSiteFile file)
      throws MalformedURLException, ProtocolException {
    try {
      PagingInfo pgInfo = this.getPagingInfo(file.getRemoteParent());
      List<RemoteSiteFile> fileList = this.ls(protocol, file.getRemoteParent());
      int indexOfFile = fileList.indexOf(file);
      if (indexOfFile != -1) {
        protocol.delete(file);
        fileList.remove(indexOfFile);
        System.out.println("IndexOfFile: " + indexOfFile + " PageIndex: "
            + pgInfo.getPageLoc());
        if (indexOfFile < pgInfo.getPageLoc()
            || indexOfFile == fileList.size() - 1)
          pgInfo.updatePageInfo(pgInfo.getPageLoc() - 1, fileList);
        else
          pgInfo.updatePageInfo(pgInfo.getPageLoc(), fileList);
        return true;
      } else {
        return false;
      }
    } catch (Exception e) {
      LOG.log(Level.SEVERE, "Failed to delete file", e);
      return false;
    }
  }

  private synchronized void putPgInfo(PagingInfo pgInfo, RemoteSiteFile pFile) {
    this.pageInfos.put(pFile, pgInfo);
  }

  private synchronized PagingInfo getPagingInfo(RemoteSiteFile pFile) {
    PagingInfo pgInfo = this.pageInfos.get(pFile);
    if (pgInfo == null)
      this.putPgInfo(pgInfo = new PagingInfo(), pFile);
    return pgInfo;
  }

  public RemoteSiteFile pwd(RemoteSite site, Protocol protocol) throws ProtocolException {
    return new RemoteSiteFile(protocol.pwd(), site);
  }

  public List<RemoteSiteFile> ls(Protocol protocol, RemoteSiteFile dir)
      throws ProtocolException {
    List<RemoteSiteFile> fileList = this.getDynamicFileList(dir.getSite(), protocol);
    if (fileList == null) {
      protocol.cd(dir);
      fileList = toRemoteSiteFiles(protocol.ls(), dir.getSite());
    }
    return fileList;
  }

  public List<RemoteSiteFile> ls(RemoteSite site, Protocol protocol) throws ProtocolException {
    List<RemoteSiteFile> fileList = this.getDynamicFileList(site, protocol);
    if (fileList == null)
      fileList = toRemoteSiteFiles(protocol.ls(), site);
    return fileList;
  }

  public List<RemoteSiteFile> ls(RemoteSite site, Protocol protocol, ProtocolFileFilter filter)
      throws ProtocolException {
    List<RemoteSiteFile> fileList = this.getDynamicFileList(site, protocol);
    if (fileList == null)
      fileList = toRemoteSiteFiles(protocol.ls(filter), site);
    return fileList;
  }

  private synchronized List<RemoteSiteFile> getDynamicFileList(RemoteSite site, Protocol protocol)
      throws ProtocolException {
    return (List<RemoteSiteFile>) (List<?>) this.pathAndFileListMap.get(this
        .pwd(site, protocol));
  }

  private synchronized void putDynamicFileList(RemoteSite site, Protocol protocol,
      List<RemoteSiteFile> fileList) throws ProtocolException {
    this.pathAndFileListMap.put(this.pwd(site, protocol), fileList);
  }

  public synchronized RemoteSiteFile getHomeDir(RemoteSite site, Protocol protocol) {
    try {
      protocol.cdHome();
      return new RemoteSiteFile(protocol.pwd(), site);
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  public String getAbsPathFor(Protocol protocol, String path, boolean isDir) {
    try {
      protocol.cd(new ProtocolFile(path, isDir));
      return protocol.pwd().getAbsoluteFile().getPath();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  /**
   * Disconnects and logs out the given Protocol
   *
   * @param protocol
   *          The Protocol to be logout out and disconnected
   * @throws RemoteConnectionException
   */
  public void disconnect(Protocol protocol) throws RemoteConnectionException {
    try {
      LOG.log(Level.INFO, "Disconnecting protocol " + protocol.getClass().getName());
      protocol.close();
    } catch (Exception e) {
      throw new RemoteConnectionException("Error disconnecting " + protocol.getClass().getName()
          + " : " + e.getMessage());
    }
  }

  /**
   * Disconnects all waiting Protocols and clears the waiting lists. Also clears
   * the current Protocol
   *
   * @throws RemoteConnectionException
   */
  public void close() throws RemoteConnectionException {
    Set<Entry<URL, Protocol>> entries = reuseProtocols.entrySet();
    for (Entry<URL, Protocol> entry : entries) {
      disconnect(entry.getValue());
    }
    this.reuseProtocols.clear();
    this.urlAndProtocolFactory.clear();
    this.pageInfos.clear();
    this.pathAndFileListMap.clear();
  }

  private synchronized RemoteSiteFile getProtocolFileByProtocol(
      RemoteSite site, Protocol protocol, String file, boolean isDir) throws ProtocolException {
    try {
      if (!file.startsWith("/")) {
        protocol.cdHome();
        file = protocol.pwd().getPath() + "/" + file;
      }
      return new RemoteSiteFile(file, isDir, site);
    } catch (Exception e) {
      throw new ProtocolException("Failed to create protocol for " + file
          + " : " + e.getMessage());
    }
  }

  private List<RemoteSiteFile> toRemoteSiteFiles(List<ProtocolFile> files, RemoteSite site) {
    List<RemoteSiteFile> newFiles = new Vector<RemoteSiteFile>();
    if (files != null) {
      for (ProtocolFile file : files) {
        newFiles.add(new RemoteSiteFile(file, site));
      }
    }
    return newFiles;
  }

  class PagingInfo {

    private int pageLoc;

    private int sizeOfLastLS;

    private RemoteSiteFile pFileAtPageLoc;

    PagingInfo() {
      this.pageLoc = 0;
      this.sizeOfLastLS = -1;
      this.pFileAtPageLoc = null;
    }

    synchronized void updatePageInfo(int newPageLoc, List<RemoteSiteFile> ls)
        throws MalformedURLException, ProtocolException {
      this.sizeOfLastLS = ls.size();
      this.pageLoc = newPageLoc < 0 ? 0 : newPageLoc;
      this.pFileAtPageLoc = (this.sizeOfLastLS > 0 && newPageLoc < ls.size()) ? ls
          .get(newPageLoc) : null;
    }

    synchronized int getPageLoc() {
      return this.pageLoc;
    }

    synchronized int getSizeOfLastLS() {
      return this.sizeOfLastLS;
    }

    synchronized RemoteSiteFile getRemoteSiteFileAtPageLoc() {
      return this.pFileAtPageLoc;
    }

  }

}
TOP

Related Classes of org.apache.oodt.cas.pushpull.protocol.ProtocolHandler

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.