Package org.springframework.integration.smb.session

Source Code of org.springframework.integration.smb.session.SmbSession

/**
* Copyright 2002-2012 the original author or authors.
*
* Licensed 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.springframework.integration.smb.session;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.NestedIOException;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.util.Assert;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;

/**
* Implementation of the {@link Session} interface for Server Message Block (SMB)
* also known as Common Internet File System (CIFS). The Samba project set out to
* create non-Windows implementations of SMB. Often Samba is thus used synonymously to SMB.
*
* SMB is an application-layer network protocol that manages shared access to files, printers
* and other networked resources.
*
* See <a href="http://en.wikipedia.org/wiki/Server_Message_Block">Server Message Block</a>
* for more details.
*
* Inspired by the spring-integration-ftp implementation done by Mark Fisher
* and Oleg Zhurakousky.
*
* @author Markus Spann
* @author Mark Fisher
* @author Oleg Zhurakousky
*
* @since 1.0
*/
public class SmbSession implements Session<SmbFile> {

  private final Log           logger         = LogFactory.getLog(SmbSession.class);
  private static final String FILE_SEPARATOR = System.getProperty("file.separator");
  private static final String SMB_FILE_SEPARATOR = "/";

  static {
    configureJcifs();
  }

  private final SmbShare smbShare;

  /**
   * Constructor for an SMB session.
   * @param _host server NetBIOS name, DNS name, or IP address, case-insensitive
   * @param _port port
   * @param _domain case-sensitive domain name
   * @param _user case-sensitive user name
   * @param _password case-sensitive password
   * @param _shareAndDir server root SMB directory
   * @param _replaceFile replace existing files if true
   * @param _useTempFile make use temporary files when writing
   * @throws IOException in case of I/O errors
   */
  SmbSession(String _host, int _port, String _domain, String _user, String _password, String _shareAndDir, boolean _replaceFile, boolean _useTempFile) throws IOException {
    this(new SmbShare(new SmbConfig(_host, _port, _domain, _user, _password, _shareAndDir)));

    smbShare.setReplaceFile(_replaceFile);
    smbShare.setUseTempFile(_useTempFile);
  }

  /**
   * Constructor for an SMB session.
   * @param _smbShare SMB share resource
   */
  public SmbSession(SmbShare _smbShare) {
    Assert.notNull(_smbShare, "smbShare must not be null");
    smbShare = _smbShare;
    logger.debug("New " + getClass().getName() + " created.");
  }

  /**
   * Deletes the file or directory at the specified path.
   * @param _path path to a remote file or directory
   * @return true if delete successful, false if resource is non-existent
   * @throws IOException on error conditions returned by a CIFS server
   * @see org.springframework.integration.file.remote.session.Session#remove(java.lang.String)
   */
  public boolean remove(String _path) throws IOException {
    Assert.hasText(_path, "path must not be empty");

    boolean removed = false;
    SmbFile removeFile = createSmbFileObject(_path);

    if (removeFile.exists()) {
      removeFile.delete();
      removed = true;
    }
    if (!removed) {
      logger.info("Could not remove non-existing resource [" + _path + "].");
    } else if (logger.isInfoEnabled()) {
      logger.info("Successfully removed resource [" + _path + "].");
    }
    return removed;
  }

  /**
   * Returns the contents of the specified SMB resource as an array of SmbFile objects.
   * In case the remote resource does not exist, an empty array is returned.
   * @param _path path to a remote directory
   * @return array of SmbFile objects
   * @throws IOException on error conditions returned by a CIFS server or if the remote resource is not a directory.
   * @see org.springframework.integration.file.remote.session.Session#list(java.lang.String)
   */
  public SmbFile[] list(String _path) throws IOException {
    SmbFile[] files = new SmbFile[0];
    try {
      SmbFile smbDir = createSmbDirectoryObject(_path);
      if (!smbDir.exists()) {
        logger.warn("Remote directory [" + _path + "] does not exist. Cannot list resources.");
        return files;
      } else if (!smbDir.isDirectory()) {
        throw new NestedIOException("Resource [" + _path + "] is not a directory. Cannot list resources.");
      }

      files = smbDir.listFiles();

    } catch (SmbException _ex) {
      throw new NestedIOException("Failed to list resources in [" + _path + "].", _ex);
    }
    String msg = "Successfully listed " + files.length + " resource(s) in [" + _path + "]";
    if (logger.isDebugEnabled()) {
      logger.debug(msg + ": " + Arrays.toString(files));
    } else {
      logger.info(msg + ".");
    }

    return files;
  }

  /**
   * Reads the remote resource specified by path and copies its contents to the specified
   * {@link OutputStream}.
   * @param _path path to a remote file
   * @param _outputStream output stream
   * @throws IOException on error conditions returned by a CIFS server or if the remote resource is not a file.
   * @see org.springframework.integration.file.remote.session.Session#read(java.lang.String, java.io.OutputStream)
   */
  public void read(String _path, OutputStream _outputStream) throws IOException {
    Assert.hasText(_path, "path must not be empty");
    Assert.notNull(_outputStream, "outputStream must not be null");

    try {

      SmbFile remoteFile = createSmbFileObject(_path);
      if (!remoteFile.isFile()) {
        throw new NestedIOException("Resource [" + _path + "] is not a file.");
      }
      FileCopyUtils.copy(remoteFile.getInputStream(), _outputStream);

    } catch (SmbException _ex) {
      throw new NestedIOException("Failed to read resource [" + _path + "].", _ex);
    }
    logger.info("Successfully read resource [" + _path + "].");
  }

  /**
   * Writes contents of the specified {@link InputStream} to the remote resource
   * specified by path. Remote directories are created implicitely as required.
   * @param _inputStream input stream
   * @param _path remote path (of a file) to write to
   * @throws IOException on error conditions returned by a CIFS server
   * @see org.springframework.integration.file.remote.session.Session#write(java.io.InputStream, java.lang.String)
   */
  public void write(InputStream _inputStream, String _path) throws IOException {
    Assert.notNull(_inputStream, "inputStream must not be empty");
    Assert.hasText(_path, "path must not be null");

    try {

      mkdirs(_path);

      SmbFile targetFile = createSmbFileObject(_path);

      if (smbShare.isUseTempFile()) {

        String tempFileName = _path + smbShare.newTempFileSuffix();
        SmbFile tempFile = createSmbFileObject(tempFileName);
        tempFile.createNewFile();
        Assert.isTrue(tempFile.canWrite(), "Temporary file [" + tempFileName + "] is not writable.");

        FileCopyUtils.copy(_inputStream, tempFile.getOutputStream());

        if (targetFile.exists() && smbShare.isReplaceFile()) {
          targetFile.delete();
        }
        tempFile.renameTo(targetFile);

      } else {

        FileCopyUtils.copy(_inputStream, targetFile.getOutputStream());

      }

    } catch (SmbException _ex) {
      throw new NestedIOException("Failed to write resource [" + _path + "].", _ex);
    }
    logger.info("Successfully wrote remote file [" + _path + "].");
  }

  /**
   * Convenience method to write a local file object to a remote location.
   * @see org.springframework.integration.smb.session.SmbSession#write(InputStream, String)
   */
  public SmbFile write(File _file, String _path) throws IOException {
    return writeAndClose(new FileInputStream(_file), _path);
  }

  /**
   * Convenience method to write a byte array to a remote location.
   * @see org.springframework.integration.smb.session.SmbSession#write(InputStream, String)
   */
  public SmbFile write(byte[] _contents, String _path) throws IOException {
    return writeAndClose(new ByteArrayInputStream(_contents), _path);
  }

  /**
   * Creates the specified remote path if not yet exists.
   * If the specified resource is a file rather than a path, creates all directories leading
   * to that file.
   * @param _path remote path to create
   * @return always true (error states are express by exceptions)
   * @throws IOException on error conditions returned by a CIFS server
   * @see org.springframework.integration.file.remote.session.Session#mkdir(java.lang.String)
   */
  public boolean mkdir(String _path) throws IOException {
    try {
      SmbFile dir = createSmbDirectoryObject(_path);
      if (!dir.exists()) {
        dir.mkdirs();
        logger.info("Successfully created remote directory [" + _path + "] in share [" + smbShare + "].");
      } else {
        logger.info("Remote directory [" + _path + "] exists in share [" + smbShare + "].");
      }
      return true;
    } catch (SmbException _ex) {
      throw new NestedIOException("Failed to create directory [" + _path + "].", _ex);
    }
  }

  /**
   * Checks whether the remote resource exists.
   * @param _path remote path
   * @return true if exists, false otherwise
   * @throws IOException  on error conditions returned by a CIFS server
   * @see org.springframework.integration.file.remote.session.Session#exists(java.lang.String)
   */
  public boolean exists(String _path) throws IOException {
    return createSmbFileObject(_path).exists();
  }

  /**
   * Checks whether the remote resource is a file.
   * @param _path remote path
   * @return true if resource is a file, false otherwise
   * @throws IOException on error conditions returned by a CIFS server
   */
  public boolean isFile(String _path) throws IOException {
    SmbFile resource = createSmbFileObject(_path);
    return resource.exists() && resource.isFile();
  }

  /**
   * Checks whether the remote resource is a directory.
   * @param _path remote path
   * @return true if resource is a directory, false otherwise
   * @throws IOException on error conditions returned by a CIFS server
   */
  public boolean isDirectory(String _path) throws IOException {
    SmbFile resource = createSmbFileObject(_path);
    return resource.exists() && resource.isDirectory();
  }

  /**
   * Create all directories in the given remote path reference.
   * @param _path path remote path, may be a file in which case the file name is ignored
   * @return the path created or null
   * @throws IOException on error conditions returned by a CIFS server
   */
  String mkdirs(String _path) throws IOException {
    int idxPath = _path.lastIndexOf(FILE_SEPARATOR);
    if (idxPath > -1) {
      String path = _path.substring(0, idxPath + 1);
      mkdir(path);
      return path;
    }
    return null;
  }

  /**
   * Renames a remote resource.
   * @param _pathFrom remote source path
   * @param _pathTo remote target path
   * @throws IOException on error conditions returned by a CIFS server
   * @see org.springframework.integration.file.remote.session.Session#rename(java.lang.String, java.lang.String)
   */
  public void rename(String _pathFrom, String _pathTo) throws IOException {
    try {

      SmbFile smbFileFrom = createSmbFileObject(_pathFrom);
      SmbFile smbFileTo = createSmbFileObject(_pathTo);
      if (smbShare.isReplaceFile() && smbFileTo.exists()) {
        smbFileTo.delete();
      }
      smbFileFrom.renameTo(smbFileTo);

    } catch (SmbException _ex) {
      throw new NestedIOException("Failed to rename [" + _pathFrom + "] to [" + _pathTo + "].", _ex);
    }
    logger.info("Successfully renamed remote resource [" + _pathFrom + "] to [" + _pathTo + "].");

  }

  /**
   * Closes this SMB session.
   * @see org.springframework.integration.file.remote.session.Session#close()
   */
  public void close() {
    smbShare.doClose();
  }

  /**
   * Checks with this SMB session is open and ready for work by attempting
   * to list remote files and checking for error conditions..
   * @return true if the session is open, false otherwise
   * @see org.springframework.integration.file.remote.session.Session#isOpen()
   */
  public boolean isOpen() {
    if (!smbShare.isOpened()) {
      return false;
    }
    try {
      smbShare.listFiles();
    } catch (Exception _ex) {
      close();
    }
    return smbShare.isOpened();
  }

  /**
   * Convenience method to write the specified input stream to a remote path and return
   * the path as an SMB file object.
   * @param _inputStream input stream
   * @param _path remote path (of a file) to write to
   * @return SMB file object
   * @throws IOException on error conditions returned by a CIFS server
   */
  SmbFile writeAndClose(InputStream _inputStream, String _path) throws IOException {
    write(_inputStream, _path);
    _inputStream.close();
    return createSmbFileObject(_path);
  }

  /**
   * Factory method for new SmbFile objects under this session's share for the specified path.
   * @param path remote path
   * @param isDirectory Boolean object to indicate the path is a directory, may be null
   * @return SmbFile object for path
   * @throws IOException in case of I/O errors
   */
  private SmbFile createSmbFileObject(String path, Boolean isDirectory) throws IOException {

    final String cleanedPath = StringUtils.cleanPath(path);

    if (!StringUtils.hasText(cleanedPath)) {
      return smbShare;
    }

    SmbFile smbFile = new SmbFile(smbShare, cleanedPath);

    boolean appendFileSeparator = !cleanedPath.endsWith(SMB_FILE_SEPARATOR);
    if (appendFileSeparator) {
      try {
          appendFileSeparator = smbFile.isDirectory() || (isDirectory != null && isDirectory);
      } catch (SmbException ex) {
          appendFileSeparator = false;
      }
    }
    if (appendFileSeparator) {
      smbFile = createSmbFileObject(cleanedPath + SMB_FILE_SEPARATOR);
    }
    if (logger.isDebugEnabled()) {
      logger.debug("Created new " + SmbFile.class.getName() + "[" + smbFile + "] for path [" + path + "].");
    }
    return smbFile;
  }

  /**
   * Creates an SMB file object pointing to a remote file.
   */
  public SmbFile createSmbFileObject(String _path) throws IOException {
    return createSmbFileObject(_path, null);
  }

  /**
   * Creates an SMB file object pointing to a remote directory.
   */
  public SmbFile createSmbDirectoryObject(String _path) throws IOException {
    return createSmbFileObject(_path, true);
  }

  /**
   * Static configuration of the JCIFS library.
   * The log level of this class is mapped to a suitable <code>jcifs.util.loglevel</code>
   */
  static void configureJcifs() {
    // TODO jcifs.Config.setProperty("jcifs.smb.client.useExtendedSecurity", "false");
    // TODO jcifs.Config.setProperty("jcifs.smb.client.disablePlainTextPasswords", "false");

    // set JCIFS SMB client library' log level unless already configured by system property
    final String sysPropLogLevel = "jcifs.util.loglevel";

    if (jcifs.Config.getProperty(sysPropLogLevel) == null) {
      // set log level according to this class' logger's log level.
      Log log = LogFactory.getLog(SmbSession.class);
      if (log.isTraceEnabled()) {
        jcifs.Config.setProperty(sysPropLogLevel, "N");
      } else if (log.isDebugEnabled()) {
        jcifs.Config.setProperty(sysPropLogLevel, "3");
      } else {
        jcifs.Config.setProperty(sysPropLogLevel, "1");
      }
    }
  }

  @Override
  public String[] listNames(String path) throws IOException {
    throw new UnsupportedOperationException("Not implemented yet");
  }

}
TOP

Related Classes of org.springframework.integration.smb.session.SmbSession

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.