Java tutorial
/** * 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"); } }