Add javadoc.
[jSite.git] / src / de / todesbaum / jsite / application / ProjectInserter.java
index c8c5e93..e52587d 100644 (file)
@@ -29,6 +29,7 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -39,7 +40,6 @@ import java.util.logging.Logger;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipOutputStream;
 
-import de.todesbaum.jsite.application.ProjectInserter.CheckReport.Issue;
 import de.todesbaum.jsite.gui.FileScanner;
 import de.todesbaum.jsite.gui.FileScannerListener;
 import de.todesbaum.util.freenet.fcp2.Client;
@@ -53,6 +53,7 @@ import de.todesbaum.util.freenet.fcp2.Verbosity;
 import de.todesbaum.util.io.Closer;
 import de.todesbaum.util.io.ReplacingOutputStream;
 import de.todesbaum.util.io.StreamCopier;
+import de.todesbaum.util.io.StreamCopier.ProgressListener;
 
 /**
  * Manages project inserts.
@@ -88,6 +89,15 @@ public class ProjectInserter implements FileScannerListener, Runnable {
        /** The temp directory. */
        private String tempDirectory;
 
+       /** The current connection. */
+       private Connection connection;
+
+       /** Whether the insert is cancelled. */
+       private volatile boolean cancelled = false;
+
+       /** Progress listener for payload transfers. */
+       private ProgressListener progressListener;
+
        /**
         * Adds a listener to the list of registered listeners.
         *
@@ -215,14 +225,31 @@ public class ProjectInserter implements FileScannerListener, Runnable {
 
        /**
         * Starts the insert.
+        *
+        * @param progressListener
+        *            Listener to notify on progress events
         */
-       public void start() {
+       public void start(ProgressListener progressListener) {
+               cancelled = false;
+               this.progressListener = progressListener;
                fileScanner = new FileScanner(project);
                fileScanner.addFileScannerListener(this);
                new Thread(fileScanner).start();
        }
 
        /**
+        * Stops the current insert.
+        */
+       public void stop() {
+               cancelled = true;
+               synchronized (lockObject) {
+                       if (connection != null) {
+                               connection.disconnect();
+                       }
+               }
+       }
+
+       /**
         * Creates an input stream that delivers the given file, replacing edition
         * tokens in the file’s content, if necessary.
         *
@@ -410,7 +437,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                        }
                }
                String indexFile = project.getIndexFile();
-               boolean hasIndexFile = (indexFile != null);
+               boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0);
                if (hasIndexFile && !project.getFileOption(indexFile).getContainer().equals("")) {
                        checkReport.addIssue("warning.container-index", false);
                }
@@ -420,7 +447,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                }
                Map<String, FileOption> fileOptions = project.getFileOptions();
                Set<Entry<String, FileOption>> fileOptionEntries = fileOptions.entrySet();
-               boolean insert = false;
+               boolean insert = fileOptionEntries.isEmpty();
                for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
                        String fileName = fileOptionEntry.getKey();
                        FileOption fileOption = fileOptionEntry.getValue();
@@ -435,6 +462,22 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                if (!insert) {
                        checkReport.addIssue("error.no-files-to-insert", true);
                }
+               Set<String> fileNames = new HashSet<String>();
+               for (Entry<String, FileOption> fileOptionEntry : fileOptionEntries) {
+                       FileOption fileOption = fileOptionEntry.getValue();
+                       if (!fileOption.isInsert() && !fileOption.isInsertRedirect()) {
+                               logger.log(Level.FINEST, "Ignoring {0}.", fileOptionEntry.getKey());
+                               continue;
+                       }
+                       String fileName = fileOptionEntry.getKey();
+                       if (fileOption.hasChangedName()) {
+                               fileName = fileOption.getChangedName();
+                       }
+                       logger.log(Level.FINEST, "Adding “{0}” for {1}.", new Object[] { fileName, fileOptionEntry.getKey() });
+                       if (!fileNames.add(fileName)) {
+                               checkReport.addIssue("error.duplicate-file", true, fileName);
+                       }
+               }
                return checkReport;
        }
 
@@ -446,7 +489,9 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                List<String> files = fileScanner.getFiles();
 
                /* create connection to node */
-               Connection connection = freenetInterface.getConnection("project-insert-" + random + counter++);
+               synchronized (lockObject) {
+                       connection = freenetInterface.getConnection("project-insert-" + random + counter++);
+               }
                connection.setTempDirectory(tempDirectory);
                boolean connected = false;
                Throwable cause = null;
@@ -456,8 +501,8 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                        cause = e1;
                }
 
-               if (!connected) {
-                       fireProjectInsertFinished(false, cause);
+               if (!connected || cancelled) {
+                       fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause);
                        return;
                }
 
@@ -477,6 +522,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                }
                putDir.setVerbosity(Verbosity.ALL);
                putDir.setMaxRetries(-1);
+               putDir.setEarlyEncode(false);
                for (String filename : files) {
                        FileEntry fileEntry = createFileEntry(filename, edition, containerFiles);
                        if (fileEntry != null) {
@@ -491,7 +537,8 @@ public class ProjectInserter implements FileScannerListener, Runnable {
 
                /* start request */
                try {
-                       client.execute(putDir);
+                       client.execute(putDir, progressListener);
+                       fireProjectUploadFinished();
                } catch (IOException ioe1) {
                        fireProjectInsertFinished(false, ioe1);
                        return;
@@ -499,17 +546,12 @@ public class ProjectInserter implements FileScannerListener, Runnable {
 
                /* parse progress and success messages */
                String finalURI = null;
-               boolean firstMessage = true;
                boolean success = false;
                boolean finished = false;
                boolean disconnected = false;
-               while (!finished) {
+               while (!finished && !cancelled) {
                        Message message = client.readMessage();
                        finished = (message == null) || (disconnected = client.isDisconnected());
-                       if (firstMessage) {
-                               fireProjectUploadFinished();
-                               firstMessage = false;
-                       }
                        logger.log(Level.FINE, "Received message: " + message);
                        if (!finished) {
                                @SuppressWarnings("null")
@@ -532,7 +574,6 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                }
 
                /* post-insert work */
-               fireProjectInsertFinished(success, disconnected ? new IOException("Connection terminated") : null);
                if (success) {
                        @SuppressWarnings("null")
                        String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1);
@@ -540,6 +581,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                        project.setEdition(newEdition);
                        project.setLastInsertionTime(System.currentTimeMillis());
                }
+               fireProjectInsertFinished(success, cancelled ? new AbortedException() : (disconnected ? new IOException("Connection terminated") : null));
        }
 
        //
@@ -622,72 +664,72 @@ public class ProjectInserter implements FileScannerListener, Runnable {
                        return issues.size();
                }
 
+       }
+
+       /**
+        * Container class for a single issue. An issue contains an error key
+        * that describes the error, and a fatality flag that determines whether
+        * the insert has to be aborted (if the flag is {@code true}) or if it
+        * can still be performed and only a warning should be generated (if the
+        * flag is {@code false}).
+        *
+        * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
+        *         Roden</a>
+        */
+       public static class Issue {
+
+               /** The error key. */
+               private final String errorKey;
+
+               /** The fatality flag. */
+               private final boolean fatal;
+
+               /** Additional parameters. */
+               private String[] parameters;
+
                /**
-                * Container class for a single issue. An issue contains an error key
-                * that describes the error, and a fatality flag that determines whether
-                * the insert has to be aborted (if the flag is {@code true}) or if it
-                * can still be performed and only a warning should be generated (if the
-                * flag is {@code false}).
+                * Creates a new issue.
                 *
-                * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’
-                *         Roden</a>
+                * @param errorKey
+                *            The error key
+                * @param fatal
+                *            The fatality flag
+                * @param parameters
+                *            Any additional parameters
                 */
-               public static class Issue {
-
-                       /** The error key. */
-                       private final String errorKey;
-
-                       /** The fatality flag. */
-                       private final boolean fatal;
-
-                       /** Additional parameters. */
-                       private String[] parameters;
-
-                       /**
-                        * Creates a new issue.
-                        *
-                        * @param errorKey
-                        *            The error key
-                        * @param fatal
-                        *            The fatality flag
-                        * @param parameters
-                        *            Any additional parameters
-                        */
-                       protected Issue(String errorKey, boolean fatal, String... parameters) {
-                               this.errorKey = errorKey;
-                               this.fatal = fatal;
-                               this.parameters = parameters;
-                       }
-
-                       /**
-                        * Returns the key of the encountered error.
-                        *
-                        * @return The error key
-                        */
-                       public String getErrorKey() {
-                               return errorKey;
-                       }
+               protected Issue(String errorKey, boolean fatal, String... parameters) {
+                       this.errorKey = errorKey;
+                       this.fatal = fatal;
+                       this.parameters = parameters;
+               }
 
-                       /**
-                        * Returns whether the issue is fatal and the insert has to be
-                        * aborted. Otherwise only a warning should be shown.
-                        *
-                        * @return {@code true} if the insert needs to be aborted, {@code
-                        *         false} otherwise
-                        */
-                       public boolean isFatal() {
-                               return fatal;
-                       }
+               /**
+                * Returns the key of the encountered error.
+                *
+                * @return The error key
+                */
+               public String getErrorKey() {
+                       return errorKey;
+               }
 
-                       /**
-                        * Returns any additional parameters.
-                        *
-                        * @return The additional parameters
-                        */
-                       public String[] getParameters() {
-                               return parameters;
-                       }
+               /**
+                * Returns whether the issue is fatal and the insert has to be
+                * aborted. Otherwise only a warning should be shown.
+                *
+                * @return {@code true} if the insert needs to be aborted, {@code
+                *         false} otherwise
+                */
+               public boolean isFatal() {
+                       return fatal;
+               }
 
+               /**
+                * Returns any additional parameters.
+                *
+                * @return The additional parameters
+                */
+               public String[] getParameters() {
+                       return parameters;
                }
 
        }