/* * Created on 20.08.2003 * */ package net.sourceforge.ganttproject.document; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; import java.io.IOException; import java.net.URI; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.ganttproject.GPLogger; import net.sourceforge.ganttproject.GanttOptions; import net.sourceforge.ganttproject.IGanttProject; import net.sourceforge.ganttproject.document.webdav.HttpDocument; import net.sourceforge.ganttproject.document.webdav.WebDavResource.WebDavException; import net.sourceforge.ganttproject.document.webdav.WebDavServerDescriptor; import net.sourceforge.ganttproject.document.webdav.WebDavStorageImpl; import net.sourceforge.ganttproject.gui.UIFacade; import net.sourceforge.ganttproject.gui.options.model.GP1XOptionConverter; import net.sourceforge.ganttproject.parser.ParserFactory; import biz.ganttproject.core.option.DefaultStringOption; import biz.ganttproject.core.option.GPOption; import biz.ganttproject.core.option.GPOptionGroup; import biz.ganttproject.core.option.StringOption; import biz.ganttproject.core.table.ColumnList; import biz.ganttproject.core.time.CalendarFactory; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; /** * This is a helper class, to create new instances of Document easily. It * chooses the correct implementation based on the given path. * * @author Michael Haeusler (michael at akatose.de) */ public class DocumentCreator implements DocumentManager { private final IGanttProject myProject; private final UIFacade myUIFacade; private final ParserFactory myParserFactory; private final WebDavStorageImpl myWebDavStorage; private final StringOption myWorkingDirectory = new StringOptionImpl("working-dir", "working-dir", "dir"); private final GPOptionGroup myOptionGroup; private final GPOptionGroup myWebDavOptionGroup; private final Logger myLogger = GPLogger.getLogger(DocumentManager.class); /** List containing the Most Recent Used documents */ private final DocumentsMRU myMRU = new DocumentsMRU(5); public DocumentCreator(IGanttProject project, UIFacade uiFacade, ParserFactory parserFactory) { myProject = project; myUIFacade = uiFacade; myParserFactory = parserFactory; myWebDavStorage = new WebDavStorageImpl(project, uiFacade); myOptionGroup = new GPOptionGroup("", new GPOption[] { myWorkingDirectory, myWebDavStorage.getLegacyLastWebDAVDocumentOption(), myWebDavStorage.getWebDavLockTimeoutOption() }); myWebDavOptionGroup = new GPOptionGroup("webdav", new GPOption[] { myWebDavStorage.getServersOption(), myWebDavStorage.getLastWebDavDocumentOption(), myWebDavStorage.getWebDavReleaseLockOption(), myWebDavStorage.getProxyOption() }); } /** * Creates an HttpDocument if path starts with "http://" or "https://"; * creates a FileDocument otherwise. * * @param path * path to the document * @return an implementation of the interface Document */ private Document createDocument(String path) { return createDocument(path, null, null); } /** * Creates an HttpDocument if path starts with "http://" or "https://"; * creates a FileDocument otherwise. * * @param path * path to the document * @param user * username * @param pass * password * @return an implementation of the interface Document * @throws an * Exception when the specified protocol is not supported */ private Document createDocument(String path, String user, String pass) { assert path != null; path = path.trim(); String lowerPath = path.toLowerCase(); if (lowerPath.startsWith("http://") || lowerPath.startsWith("https://")) { try { if (user == null && pass == null) { WebDavServerDescriptor server = myWebDavStorage.findServer(path); if (server != null) { user = server.getUsername(); pass = server.getPassword(); } } return new HttpDocument(path, user, pass, myWebDavStorage.getProxyOption()); } catch (IOException e) { GPLogger.log(e); return null; } catch (WebDavException e) { GPLogger.log(e); return null; } } else if (lowerPath.startsWith("ftp:")) { return new FtpDocument(path, myFtpUserOption, myFtpPasswordOption); } else if (!lowerPath.startsWith("file://") && path.contains("://")) { // Generate error for unknown protocol throw new RuntimeException("Unknown protocol: " + path.substring(0, path.indexOf("://"))); } return new FileDocument(new File(path)); } @Override public Document getDocument(String path) { Document physicalDocument = createDocument(path); Document proxyDocument = new ProxyDocument(this, physicalDocument, myProject, myUIFacade, getVisibleFields(), getResourceVisibleFields(), getParserFactory()); return proxyDocument; } @Override public Document getProxyDocument(Document physicalDocument) { Document proxyDocument = new ProxyDocument(this, physicalDocument, myProject, myUIFacade, getVisibleFields(), getResourceVisibleFields(), getParserFactory()); return proxyDocument; } @Override public Document newAutosaveDocument() throws IOException { File tempFile = File.createTempFile("_ganttproject_autosave", ".gan"); return getDocument(tempFile.getAbsolutePath()); } public static Runnable createAutosaveCleanup() { long now = CalendarFactory.newCalendar().getTimeInMillis(); final File tempDir = getTempDir(); final long cutoff; try { File optionsFile = GanttOptions.getOptionsFile(); if (!optionsFile.exists()) { return null; } GPLogger.log("Options file:" + optionsFile.getAbsolutePath()); BasicFileAttributes attrs = Files.readAttributes(optionsFile.toPath(), BasicFileAttributes.class); FileTime accessTime = attrs.lastAccessTime(); cutoff = Math.min(accessTime.toMillis(), now); } catch (IOException e) { GPLogger.log(e); return null; } return new Runnable() { @Override public void run() { GPLogger.log("Deleting old auto-save files"); deleteAutosaves(); } private void deleteAutosaves() { // Let's find autosaves created before launch of this GP instance File[] previousAutosaves = tempDir.listFiles(new FileFilter() { @Override public boolean accept(File file) { return file.getName().startsWith("_ganttproject_autosave") && file.lastModified() < cutoff; } }); for (File f : previousAutosaves) { f.deleteOnExit(); } } }; } private FileSystem getAutosaveZipFs() { try { File tempDir = getTempDir(); if (tempDir == null) { return null; } File autosaveFile = new File(tempDir, "_ganttproject_autosave.zip"); if (autosaveFile.exists() && !autosaveFile.canWrite()) { myLogger.warning(String.format( "Autosave file %s is not writable", autosaveFile.getAbsolutePath())); return null; } URI uri = new URI("jar:file:" + autosaveFile.toURI().getPath()); return FileSystems.newFileSystem(uri, ImmutableMap.<String, Object>of("create", "true")); } catch (Throwable e) { myLogger.log(Level.SEVERE, "Failure when creating ZIP FS for autosaves", e); return null; } } private static File getTempDir() { File tempDir = new File(System.getProperty("java.io.tmpdir")); if (tempDir.exists() && tempDir.isDirectory() && tempDir.canWrite()) { return tempDir; } try { File tempFile = File.createTempFile("_ganttproject_autosave", ".empty"); tempDir = tempFile.getParentFile(); if (tempDir.exists() && tempDir.isDirectory() && tempDir.canWrite()) { return tempDir; } } catch (IOException e) { GPLogger.getLogger(DocumentManager.class).log(Level.WARNING, "Can't get parent of the temp file", e); } GPLogger.getLogger(DocumentManager.class).warning("Failed to find temporary directory"); return null; } @Override public Document getLastAutosaveDocument(Document priorTo) throws IOException { File f = File.createTempFile("tmp", ""); File directory = f.getParentFile(); File files[] = directory.listFiles(new FilenameFilter() { @Override public boolean accept(File f, String arg1) { return arg1.startsWith("_ganttproject_autosave"); } }); Arrays.sort(files, new Comparator<File>() { @Override public int compare(File left, File right) { return Long.valueOf(left.lastModified()).compareTo(Long.valueOf(right.lastModified())); } }); if (files.length == 0) { return null; } if (priorTo == null) { return getDocument(files[files.length - 1].getAbsolutePath()); } for (int i = files.length - 1; i >= 0; i--) { if (files[i].getName().equals(priorTo.getFileName())) { return i > 0 ? getDocument(files[i - 1].getAbsolutePath()) : null; } } return null; } protected ColumnList getVisibleFields() { return null; } protected ColumnList getResourceVisibleFields() { return null; } protected ParserFactory getParserFactory() { return myParserFactory; } String createTemporaryFile() throws IOException { File tempFile = File.createTempFile("project", ".gan", getWorkingDirectoryFile()); return tempFile.getAbsolutePath(); } @Override public void changeWorkingDirectory(File directory) { assert directory.isDirectory(); myWorkingDirectory.lock(); myWorkingDirectory.setValue(directory.getAbsolutePath()); myWorkingDirectory.commit(); } @Override public String getWorkingDirectory() { return myWorkingDirectory.getValue(); } private File getWorkingDirectoryFile() { return new File(getWorkingDirectory()); } @Override public GPOptionGroup getOptionGroup() { return myOptionGroup; } @Override public FTPOptions getFTPOptions() { return myFtpOptions; } @Override public GPOptionGroup[] getNetworkOptionGroups() { return new GPOptionGroup[] { myFtpOptions, myOptionGroup, myWebDavOptionGroup }; } @Override public DocumentStorageUi getWebDavStorageUi() { return myWebDavStorage; } private final StringOption myFtpUserOption = new StringOptionImpl("user-name", "ftp", "ftpuser"); private final StringOption myFtpServerNameOption = new StringOptionImpl("server-name", "ftp", "ftpurl"); private final StringOption myFtpDirectoryNameOption = new StringOptionImpl("directory-name", "ftp", "ftpdir"); private final StringOption myFtpPasswordOption = new StringOptionImpl("password", "ftp", "ftppwd"); private final FTPOptions myFtpOptions = new FTPOptions("ftp", new GPOption[] { myFtpUserOption, myFtpServerNameOption, myFtpDirectoryNameOption, myFtpPasswordOption }) { @Override public StringOption getDirectoryName() { return myFtpDirectoryNameOption; } @Override public StringOption getPassword() { return myFtpPasswordOption; } @Override public StringOption getServerName() { return myFtpServerNameOption; } @Override public StringOption getUserName() { return myFtpUserOption; } }; static final String USERNAME_OPTION_ID = "user-name"; static final String SERVERNAME_OPTION_ID = "server-name"; static final String DIRECTORYNAME_OPTION_ID = "directory-name"; static final String PASSWORD_OPTION_ID = "password"; private static class StringOptionImpl extends DefaultStringOption implements GP1XOptionConverter { private final String myLegacyTagName; private final String myLegacyAttrName; private StringOptionImpl(String optionName, String legacyTagName, String legacyAttrName) { super(optionName); myLegacyTagName = legacyTagName; myLegacyAttrName = legacyAttrName; } @Override public String getTagName() { return myLegacyTagName; } @Override public String getAttributeName() { return myLegacyAttrName; } @Override public void loadValue(String legacyValue) { loadPersistentValue(legacyValue); } } @Override public List<String> getRecentDocuments() { return Lists.newArrayList(myMRU.iterator()); } @Override public void addListener(DocumentMRUListener listener) { myMRU.addListener(listener); } @Override public void addToRecentDocuments(Document document) { myMRU.add(document.getPath(), true); } @Override public void addToRecentDocuments(String value) { myMRU.add(value, false); } @Override public void clearRecentDocuments() { myMRU.clear(); } }