move project directory
[jSite2.git] / src / net / pterodactylus / jsite / core / project / ProjectManager.java
diff --git a/src/net/pterodactylus/jsite/core/project/ProjectManager.java b/src/net/pterodactylus/jsite/core/project/ProjectManager.java
new file mode 100644 (file)
index 0000000..62e8f8e
--- /dev/null
@@ -0,0 +1,307 @@
+/*
+ * jSite2 - ProjectManager.java -
+ * Copyright © 2008 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
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+package net.pterodactylus.jsite.core.project;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.Random;
+import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.pterodactylus.jsite.core.JSiteException;
+import net.pterodactylus.jsite.core.NodeManager;
+import net.pterodactylus.util.io.Closer;
+import net.pterodactylus.util.logging.Logging;
+import net.pterodactylus.util.number.Hex;
+
+/**
+ * Manages projects, taking care of persistence, lifetime statistics, and other
+ * things.
+ *
+ * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
+ */
+public class ProjectManager implements PropertyChangeListener {
+
+       /** Logger. */
+       private static final Logger logger = Logging.getLogger(ProjectManager.class.getName());
+
+       /** The RNG used to create project IDs. */
+       private static final Random random = new Random();
+
+       /** The directory the projects are stored in. */
+       private final String directory;
+
+       /** The node manager. */
+       private NodeManager nodeManager;
+
+       /** All projects. */
+       private final List<Project> projects = Collections.synchronizedList(new ArrayList<Project>());
+
+       /**
+        * Creates a new project manager that saves and restores its state to/from
+        * the given directory.
+        *
+        * @param directory
+        *            The directory to save and restore states to/from
+        */
+       public ProjectManager(String directory) {
+               this.directory = directory;
+       }
+
+       //
+       // ACCESSORS
+       //
+
+       /**
+        * Returns the directory the projects are loaded from and saved to.
+        *
+        * @return The directory for storing the projects
+        */
+       public String getDirectory() {
+               return directory;
+       }
+
+       /**
+        * Returns a list of all projects.
+        *
+        * @return A list of all projects
+        */
+       public List<Project> getProjects() {
+               return Collections.unmodifiableList(new ArrayList<Project>(projects));
+       }
+
+       /**
+        * Sets the node manager to use.
+        *
+        * @param nodeManager
+        *            The node manager to use
+        */
+       public void setNodeManager(NodeManager nodeManager) {
+               this.nodeManager = nodeManager;
+       }
+
+       //
+       // ACTIONS
+       //
+
+       /**
+        * Loads projects and statistics.
+        *
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void load() throws IOException {
+               File directoryFile = new File(directory);
+               File projectFile = new File(directoryFile, "projects.properties");
+               if (!projectFile.exists() || !projectFile.isFile() || !projectFile.canRead()) {
+                       return;
+               }
+               Properties projectProperties = new Properties();
+               InputStream projectInputStream = null;
+               try {
+                       projectInputStream = new FileInputStream(projectFile);
+                       projectProperties.load(projectInputStream);
+               } finally {
+                       Closer.close(projectInputStream);
+               }
+               int projectIndex = 0;
+               while (projectProperties.containsKey("projects." + projectIndex + ".name")) {
+                       String projectPrefix = "projects." + projectIndex;
+                       String projectId = projectProperties.getProperty(projectPrefix + ".id");
+                       String projectName = projectProperties.getProperty(projectPrefix + ".name");
+                       String projectDescription = projectProperties.getProperty(projectPrefix + ".description");
+                       String projectPrivateKey = projectProperties.getProperty(projectPrefix + ".privateKey");
+                       String projectPublicKey = projectProperties.getProperty(projectPrefix + ".publicKey");
+                       String projectBasePath = projectProperties.getProperty(projectPrefix + ".basePath");
+                       String projectDefaultFile = projectProperties.getProperty(projectPrefix + ".defaultFile");
+                       Project project = new Project();
+                       project.setId(projectId);
+                       project.setName(projectName);
+                       project.setDescription(projectDescription);
+                       project.setPrivateKey(projectPrivateKey);
+                       project.setPublicKey(projectPublicKey);
+                       project.setBasePath(projectBasePath);
+                       project.setDefaultFile(projectDefaultFile);
+                       int overrideIndex = 0;
+                       while (projectProperties.containsKey(projectPrefix + ".overrides." + overrideIndex + ".override")) {
+                               String filePath = projectProperties.getProperty(projectPrefix + ".overrides." + overrideIndex + ".filePath");
+                               FileOverride override = FileOverride.valueOf(projectProperties.getProperty(projectPrefix + ".overrides." + overrideIndex + ".override"));
+                               project.addFileOverride(filePath, override);
+                               logger.log(Level.FINEST, "read override: " + filePath + ", " + override);
+                               overrideIndex++;
+                       }
+                       projects.add(project);
+                       logger.fine("loaded project “" + project.getName() + "”.");
+                       projectIndex++;
+               }
+       }
+
+       /**
+        * Saves projects and statistics.
+        *
+        * @throws IOException
+        *             if an I/O error occurs
+        */
+       public void save() throws IOException {
+               File directoryFile = new File(directory);
+               if (!directoryFile.exists()) {
+                       if (!directoryFile.mkdirs()) {
+                               throw new IOException("could not create directory: " + directory);
+                       }
+               }
+               Properties projectProperties = new Properties();
+               int projectIndex = 0;
+               for (Project project : projects) {
+                       String projectPrefix = "projects." + projectIndex;
+                       projectProperties.setProperty(projectPrefix + ".id", project.getId());
+                       projectProperties.setProperty(projectPrefix + ".name", project.getName());
+                       projectProperties.setProperty(projectPrefix + ".description", project.getDescription());
+                       projectProperties.setProperty(projectPrefix + ".privateKey", project.getPrivateKey());
+                       projectProperties.setProperty(projectPrefix + ".publicKey", project.getPublicKey());
+                       projectProperties.setProperty(projectPrefix + ".basePath", project.getBasePath());
+                       projectProperties.setProperty(projectPrefix + ".defaultFile", project.getDefaultFile());
+                       int overrideIndex = 0;
+                       for (Entry<String, FileOverride> overrideEntry : project.getFileOverrides().entrySet()) {
+                               projectProperties.setProperty(projectPrefix + ".overrides." + overrideIndex + ".filePath", overrideEntry.getKey());
+                               projectProperties.setProperty(projectPrefix + ".overrides." + overrideIndex + ".override", overrideEntry.getValue().toString());
+                               overrideIndex++;
+                       }
+                       projectIndex++;
+               }
+               File projectFile = new File(directoryFile, "projects.properties");
+               OutputStream projectOutputStream = null;
+               try {
+                       projectOutputStream = new FileOutputStream(projectFile);
+                       projectProperties.store(projectOutputStream, "jSite projects");
+               } finally {
+                       Closer.close(projectOutputStream);
+               }
+       }
+
+       /**
+        * Creates a new project. The returned {@link Project} will contain a newly
+        * generated key pair.
+        *
+        * @return A newly created project
+        * @throws IOException
+        *             if an I/O error occured communicating with the node
+        * @throws JSiteException
+        *             if there is a problem with the node
+        */
+       public Project createProject() throws IOException, JSiteException {
+               Project project = new Project();
+               String[] keyPair = nodeManager.generateKeyPair();
+               project.setId(generateId());
+               project.setName("");
+               project.setDescription("");
+               project.setPrivateKey(keyPair[0]);
+               project.setPublicKey(keyPair[1]);
+               project.setBasePath("");
+               project.setDefaultFile("");
+               projects.add(project);
+               project.addPropertyChangeListener(this);
+               try {
+                       save();
+               } catch (IOException ioe1) {
+                       /* ignore. */
+               }
+               return project;
+       }
+
+       /**
+        * Clones the given project and returns the clone. The clone will be
+        * identical in all user-exposed fields, except for the project’s
+        * {@link Project#getId ID}.
+        *
+        * @param project
+        *            The project to clone
+        * @return The cloned project
+        */
+       public Project cloneProject(Project project) {
+               Project projectClone = new Project(project);
+               projects.add(projectClone);
+               projectClone.setId(generateId());
+               projectClone.addPropertyChangeListener(this);
+               try {
+                       save();
+               } catch (IOException ioe1) {
+                       /* ignore. */
+               }
+               return projectClone;
+       }
+
+       /**
+        * Removes the given project.
+        *
+        * @param project
+        *            The project to remove
+        */
+       public void removeProject(Project project) {
+               projects.remove(project);
+               try {
+                       save();
+               } catch (IOException ioe1) {
+                       /* ignore. */
+               }
+       }
+
+       //
+       // PRIVATE METHODS
+       //
+
+       /**
+        * Generates a new random ID, consisting of 16 random bytes converted to a
+        * hexadecimal number.
+        *
+        * @return The new ID
+        */
+       private static String generateId() {
+               byte[] idBytes = new byte[16];
+               random.nextBytes(idBytes);
+               return Hex.toHex(idBytes);
+       }
+
+       //
+       // INTERFACE PropertyChangeListener
+       //
+
+       /**
+        * {@inheritDoc}
+        */
+       public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
+               try {
+                       save();
+               } catch (IOException ioe1) {
+                       /* ignore. */
+               }
+       }
+
+}