X-Git-Url: https://git.pterodactylus.net/?a=blobdiff_plain;f=src%2Fde%2Ftodesbaum%2Fjsite%2Fapplication%2FProjectInserter.java;h=db4d514c98c640bd7a1a5d7c01d8b6fa03c8eca7;hb=9a7fb4ee0b03f8af6be48c78d08e19bd89d022ff;hp=c8c5e936bd91b7028693bcfdcd226be0c17b0985;hpb=b1c905d1a826b0fdd2947c58ace76d1347ede54a;p=jSite.git
diff --git a/src/de/todesbaum/jsite/application/ProjectInserter.java b/src/de/todesbaum/jsite/application/ProjectInserter.java
index c8c5e93..db4d514 100644
--- a/src/de/todesbaum/jsite/application/ProjectInserter.java
+++ b/src/de/todesbaum/jsite/application/ProjectInserter.java
@@ -1,6 +1,5 @@
/*
- * jSite - a tool for uploading websites into Freenet
- * Copyright (C) 2006 David Roden
+ * jSite - ProjectInserter.java - Copyright © 2006â2011 David Roden
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,16 +18,13 @@
package de.todesbaum.jsite.application;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
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;
@@ -36,23 +32,19 @@ import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
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;
import de.todesbaum.util.freenet.fcp2.ClientPutComplexDir;
+import de.todesbaum.util.freenet.fcp2.ClientPutDir.ManifestPutter;
import de.todesbaum.util.freenet.fcp2.Connection;
import de.todesbaum.util.freenet.fcp2.DirectFileEntry;
import de.todesbaum.util.freenet.fcp2.FileEntry;
import de.todesbaum.util.freenet.fcp2.Message;
import de.todesbaum.util.freenet.fcp2.RedirectFileEntry;
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 +80,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 +216,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.
*
@@ -243,76 +261,7 @@ public class ProjectInserter implements FileScannerListener, Runnable {
private InputStream createFileInputStream(String filename, FileOption fileOption, int edition, long[] length) throws IOException {
File file = new File(project.getLocalPath(), filename);
length[0] = file.length();
- if (!fileOption.getReplaceEdition()) {
- return new FileInputStream(file);
- }
- ByteArrayOutputStream filteredByteOutputStream = new ByteArrayOutputStream(Math.min(Integer.MAX_VALUE, (int) length[0]));
- ReplacingOutputStream outputStream = new ReplacingOutputStream(filteredByteOutputStream);
- FileInputStream fileInput = new FileInputStream(file);
- try {
- outputStream.addReplacement("$[EDITION]", String.valueOf(edition));
- outputStream.addReplacement("$[URI]", project.getFinalRequestURI(0));
- for (int index = 1; index <= fileOption.getEditionRange(); index++) {
- outputStream.addReplacement("$[URI+" + index + "]", project.getFinalRequestURI(index));
- outputStream.addReplacement("$[EDITION+" + index + "]", String.valueOf(edition + index));
- }
- StreamCopier.copy(fileInput, outputStream, length[0]);
- } finally {
- Closer.close(fileInput);
- Closer.close(outputStream);
- Closer.close(filteredByteOutputStream);
- }
- byte[] filteredBytes = filteredByteOutputStream.toByteArray();
- length[0] = filteredBytes.length;
- return new ByteArrayInputStream(filteredBytes);
- }
-
- /**
- * Creates an input stream for a container.
- *
- * @param containerFiles
- * All container definitions
- * @param containerName
- * The name of the container to create
- * @param edition
- * The current edition
- * @param containerLength
- * An array containing a single long which is used to
- * return the final length of the container stream,
- * after all replacements
- * @return The input stream for the container
- * @throws IOException
- * if an I/O error occurs
- */
- private InputStream createContainerInputStream(Map> containerFiles, String containerName, int edition, long[] containerLength) throws IOException {
- File tempFile = File.createTempFile("jsite", ".zip", (tempDirectory == null) ? null : new File(tempDirectory));
- tempFile.deleteOnExit();
- FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
- ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream);
- try {
- for (String filename : containerFiles.get(containerName)) {
- File dataFile = new File(project.getLocalPath(), filename);
- if (dataFile.exists()) {
- ZipEntry zipEntry = new ZipEntry(filename);
- long[] fileLength = new long[1];
- InputStream wrappedInputStream = createFileInputStream(filename, project.getFileOption(filename), edition, fileLength);
- try {
- zipOutputStream.putNextEntry(zipEntry);
- StreamCopier.copy(wrappedInputStream, zipOutputStream, fileLength[0]);
- } finally {
- zipOutputStream.closeEntry();
- wrappedInputStream.close();
- }
- }
- }
- } finally {
- zipOutputStream.closeEntry();
- Closer.close(zipOutputStream);
- Closer.close(fileOutputStream);
- }
-
- containerLength[0] = tempFile.length();
- return new FileInputStream(tempFile);
+ return new FileInputStream(file);
}
/**
@@ -330,8 +279,6 @@ public class ProjectInserter implements FileScannerListener, Runnable {
private FileEntry createFileEntry(String filename, int edition, Map> containerFiles) {
FileEntry fileEntry = null;
FileOption fileOption = project.getFileOption(filename);
- if (filename.startsWith("/container/:")) {
- String containerName = filename.substring("/container/:".length());
try {
long[] containerLength = new long[1];
InputStream containerInputStream = createContainerInputStream(containerFiles, containerName, edition, containerLength);
@@ -358,33 +305,6 @@ public class ProjectInserter implements FileScannerListener, Runnable {
}
/**
- * Creates container definitions.
- *
- * @param files
- * The list of all files
- * @param containers
- * The list of all containers
- * @param containerFiles
- * Empty map that will be filled with container definitions
- */
- private void createContainers(List files, List containers, Map> containerFiles) {
- for (String filename : new ArrayList(files)) {
- FileOption fileOption = project.getFileOption(filename);
- String containerName = fileOption.getContainer();
- if (!containerName.equals("")) {
- if (!containers.contains(containerName)) {
- containers.add(containerName);
- containerFiles.put(containerName, new ArrayList());
- /* hmm. looks like a hack to me. */
- files.add("/container/:" + containerName);
- }
- containerFiles.get(containerName).add(filename);
- files.remove(filename);
- }
- }
- }
-
- /**
* Validates the given project. The project will be checked for any invalid
* conditions, such as invalid insert or request keys, missing path names,
* missing default file, and so on.
@@ -410,17 +330,14 @@ public class ProjectInserter implements FileScannerListener, Runnable {
}
}
String indexFile = project.getIndexFile();
- boolean hasIndexFile = (indexFile != null);
- if (hasIndexFile && !project.getFileOption(indexFile).getContainer().equals("")) {
- checkReport.addIssue("warning.container-index", false);
- }
+ boolean hasIndexFile = (indexFile != null) && (indexFile.length() > 0);
List allowedIndexContentTypes = Arrays.asList("text/html", "application/xhtml+xml");
if (hasIndexFile && !allowedIndexContentTypes.contains(project.getFileOption(indexFile).getMimeType())) {
checkReport.addIssue("warning.index-not-html", false);
}
Map fileOptions = project.getFileOptions();
Set> fileOptionEntries = fileOptions.entrySet();
- boolean insert = false;
+ boolean insert = fileOptionEntries.isEmpty();
for (Entry fileOptionEntry : fileOptionEntries) {
String fileName = fileOptionEntry.getKey();
FileOption fileOption = fileOptionEntry.getValue();
@@ -435,6 +352,22 @@ public class ProjectInserter implements FileScannerListener, Runnable {
if (!insert) {
checkReport.addIssue("error.no-files-to-insert", true);
}
+ Set fileNames = new HashSet();
+ for (Entry 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 +379,9 @@ public class ProjectInserter implements FileScannerListener, Runnable {
List 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,18 +391,13 @@ public class ProjectInserter implements FileScannerListener, Runnable {
cause = e1;
}
- if (!connected) {
- fireProjectInsertFinished(false, cause);
+ if (!connected || cancelled) {
+ fireProjectInsertFinished(false, cancelled ? new AbortedException() : cause);
return;
}
Client client = new Client(connection);
- /* create containers */
- final List containers = new ArrayList();
- final Map> containerFiles = new HashMap>();
- createContainers(files, containers, containerFiles);
-
/* collect files */
int edition = project.getEdition();
String dirURI = "USK@" + project.getInsertURI() + "/" + project.getPath() + "/" + edition + "/";
@@ -477,6 +407,8 @@ public class ProjectInserter implements FileScannerListener, Runnable {
}
putDir.setVerbosity(Verbosity.ALL);
putDir.setMaxRetries(-1);
+ putDir.setEarlyEncode(false);
+ putDir.setManifestPutter(ManifestPutter.DEFAULT);
for (String filename : files) {
FileEntry fileEntry = createFileEntry(filename, edition, containerFiles);
if (fileEntry != null) {
@@ -491,7 +423,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 +432,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")
@@ -526,13 +454,12 @@ public class ProjectInserter implements FileScannerListener, Runnable {
boolean finalized = Boolean.parseBoolean(message.get("FinalizedTotal"));
fireProjectInsertProgress(succeeded, failed, fatal, total, finalized);
}
- success = "PutSuccessful".equals(messageName);
- finished = success || "PutFailed".equals(messageName) || messageName.endsWith("Error");
+ success |= "PutSuccessful".equals(messageName);
+ finished = (success && (finalURI != null)) || "PutFailed".equals(messageName) || messageName.endsWith("Error");
}
}
/* post-insert work */
- fireProjectInsertFinished(success, disconnected ? new IOException("Connection terminated") : null);
if (success) {
@SuppressWarnings("null")
String editionPart = finalURI.substring(finalURI.lastIndexOf('/') + 1);
@@ -540,6 +467,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 +550,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 David âBombeâ
+ * Roden
+ */
+ 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 David âBombeâ
- * Roden
+ * @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;
}
}