package ru.kkey.core; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.net.ftp.FTPClient; import org.apache.commons.net.ftp.FTPFile; /** * Data source for ftp server navigation * * @author anstarovoyt */ public class FTPSource implements Source { public static int RETRY_COUNT = 1; public static final SourceFactory FACTORY = new SourceFactory() { @Override public Source create(String path) { return FTPSource.create(path); } }; private static final Logger logger = Logger.getAnonymousLogger(); private static FTPSource create(String fullPath) { try { String prefix = ""; if (!fullPath.startsWith("ftp://")) { prefix = "ftp://"; } return new FTPSource(new URL(prefix + fullPath)); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } private volatile FTPClient client; private final URL url; private final List<String> paths = new ArrayList<>(); public FTPSource(URL url) { this.url = url; try { connect(); String path = url.getPath(); if (null != path && !path.isEmpty()) { client.changeWorkingDirectory(path); addToPath(path); } } catch (IOException e) { throw new RuntimeException(e.getMessage(), e); } } @Override public void destroy() { try { client.disconnect(); } catch (Exception e) { logger.log(Level.WARNING, e.getMessage(), e); } } @Override public synchronized byte[] getFile(FileItem item) { return getFile(item, 0); } @Override public Source getSourceFor(FileItem item) { return null; } @Override public synchronized FileItem goBack() { return goBack(0); } @Override public synchronized void goInto(FileItem item) { goInto(item, 0); } @Override public synchronized List<FileItem> listFiles() { return listFiles(0); } private void addToPath(String path) { if (path == null || path.isEmpty()) { return; } String[] subPaths = path.split("/"); for (String sub : subPaths) { if (null != sub && !sub.isEmpty()) { paths.add(sub); } } } private void checkConnection() { boolean isAvailable = false; try { isAvailable = client.isAvailable(); } catch (Exception e) { logger.log(Level.FINE, e.getMessage(), e); } if (!isAvailable) { reconnect(); } } private void connect() { try { client = new FTPClient(); String userWithPass = url.getUserInfo(); String login = null == userWithPass ? "anonymous" : userWithPass; String pass = ""; int port = url.getPort() == -1 ? 21 : url.getPort(); if (null != userWithPass && userWithPass.contains(":")) { String[] splitUserPass = userWithPass.split(":"); login = splitUserPass[0]; pass = splitUserPass[1]; } client.connect(url.getHost(), port); client.login(login, pass); if (!paths.isEmpty()) { client.changeWorkingDirectory(Utils.joinPath(paths, "/")); } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } private byte[] getFile(FileItem item, int retry) { try { checkConnection(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); if (client.retrieveFile(item.getName(), buffer)) { buffer.flush(); return buffer.toByteArray(); } if (retry < RETRY_COUNT) { reconnect(); return getFile(item, ++retry); } throw new RuntimeException("Cannot loading file"); } catch (IOException e) { if (retry < RETRY_COUNT) { logger.log(Level.WARNING, e.getMessage(), e); reconnect(); return getFile(item, ++retry); } throw new RuntimeException(e.getMessage(), e); } } private FileItem goBack(int retry) { try { checkConnection(); boolean result = client.changeToParentDirectory(); if (result && paths.size() > 0) { String prev = paths.remove(paths.size() - 1); return new FileItem(prev, true); } if (!result) { //some problems. Try reconnect reconnect(); return null; } return null; } catch (IOException e) { if (retry < RETRY_COUNT) { logger.log(Level.WARNING, e.getMessage(), e); reconnect(); return goBack(++retry); } throw new RuntimeException(e.getMessage(), e); } } private void goInto(FileItem item, int retry) { try { checkConnection(); if (client.changeWorkingDirectory(item.getName())) { paths.add(item.getName()); } else { //some problems. Try reconnect reconnect(); } } catch (Exception e) { if (retry < RETRY_COUNT) { logger.log(Level.WARNING, e.getMessage(), e); reconnect(); goInto(item, ++retry); } throw new RuntimeException(e.getMessage(), e); } } private List<FileItem> listFiles(int retry) { List<FileItem> result = new ArrayList<>(); try { checkConnection(); result.addAll(toFileItems(client.listFiles())); return result; } catch (IOException e) { if (retry < RETRY_COUNT) { logger.log(Level.WARNING, e.getMessage(), e); reconnect(); return listFiles(++retry); } throw new RuntimeException(e.getMessage(), e); } } private void reconnect() { try { client.disconnect(); } catch (Exception e) { } connect(); } private List<FileItem> toFileItems(FTPFile[] files) { List<FileItem> result = new ArrayList<>(); for (FTPFile file : files) { result.add(new FileItem(file.getName(), file.isDirectory())); } Collections.sort(result); return result; } }