load and save nodes in projects
[jSite2.git] / src / net / pterodactylus / jsite / core / ProjectManager.java
1 /*
2  * jSite2 - ProjectManager.java -
3  * Copyright © 2008 David Roden
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 package net.pterodactylus.jsite.core;
21
22 import java.beans.PropertyChangeEvent;
23 import java.beans.PropertyChangeListener;
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Properties;
34 import java.util.Map.Entry;
35 import java.util.logging.Level;
36 import java.util.logging.Logger;
37
38 import net.pterodactylus.util.io.Closer;
39 import net.pterodactylus.util.logging.Logging;
40
41 /**
42  * Manages projects, taking care of persistence, lifetime statistics, and other
43  * things.
44  * 
45  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
46  */
47 public class ProjectManager implements PropertyChangeListener {
48
49         /** Logger. */
50         private static final Logger logger = Logging.getLogger(ProjectManager.class.getName());
51
52         /** The directory the projects are stored in. */
53         private final String directory;
54
55         /** The node manager. */
56         private NodeManager nodeManager;
57
58         /** All projects. */
59         private final List<Project> projects = Collections.synchronizedList(new ArrayList<Project>());
60
61         /**
62          * Creates a new project manager that saves and restores its state to/from
63          * the given directory.
64          * 
65          * @param directory
66          *            The directory to save and restore states to/from
67          */
68         public ProjectManager(String directory) {
69                 this.directory = directory;
70         }
71
72         //
73         // ACCESSORS
74         //
75
76         /**
77          * Returns the directory the projects are loaded from and saved to.
78          * 
79          * @return The directory for storing the projects
80          */
81         public String getDirectory() {
82                 return directory;
83         }
84
85         /**
86          * Returns a list of all projects.
87          * 
88          * @return A list of all projects
89          */
90         public List<Project> getProjects() {
91                 return Collections.unmodifiableList(new ArrayList<Project>(projects));
92         }
93
94         /**
95          * Sets the node manager to use.
96          * 
97          * @param nodeManager
98          *            The node manager to use
99          */
100         public void setNodeManager(NodeManager nodeManager) {
101                 this.nodeManager = nodeManager;
102         }
103
104         //
105         // ACTIONS
106         //
107
108         /**
109          * Loads projects and statistics.
110          * 
111          * @throws IOException
112          *             if an I/O error occurs
113          */
114         public void load() throws IOException {
115                 File directoryFile = new File(directory);
116                 File projectFile = new File(directoryFile, "projects.properties");
117                 if (!projectFile.exists() || !projectFile.isFile() || !projectFile.canRead()) {
118                         return;
119                 }
120                 Properties projectProperties = new Properties();
121                 InputStream projectInputStream = null;
122                 try {
123                         projectInputStream = new FileInputStream(projectFile);
124                         projectProperties.load(projectInputStream);
125                 } finally {
126                         Closer.close(projectInputStream);
127                 }
128                 int projectIndex = 0;
129                 while (projectProperties.containsKey("projects." + projectIndex + ".name")) {
130                         String projectPrefix = "projects." + projectIndex;
131                         String projectId = projectProperties.getProperty(projectPrefix + ".id");
132                         String projectName = projectProperties.getProperty(projectPrefix + ".name");
133                         String projectDescription = projectProperties.getProperty(projectPrefix + ".description");
134                         String projectPrivateKey = projectProperties.getProperty(projectPrefix + ".privateKey");
135                         String projectPublicKey = projectProperties.getProperty(projectPrefix + ".publicKey");
136                         String projectBasePath = projectProperties.getProperty(projectPrefix + ".basePath");
137                         String projectDefaultFile = projectProperties.getProperty(projectPrefix + ".defaultFile");
138                         String projectNode = projectProperties.getProperty(projectPrefix + ".node");
139                         Project project = new Project();
140                         project.setId(projectId);
141                         project.setName(projectName);
142                         project.setDescription(projectDescription);
143                         project.setPrivateKey(projectPrivateKey);
144                         project.setPublicKey(projectPublicKey);
145                         project.setBasePath(projectBasePath);
146                         project.setDefaultFile(projectDefaultFile);
147                         if (projectNode != null) {
148                                 Node node = nodeManager.getNode(projectNode);
149                                 project.setNode(node);
150                         }
151                         int overrideIndex = 0;
152                         while (projectProperties.containsKey(projectPrefix + ".overrides." + overrideIndex + ".override")) {
153                                 String filePath = projectProperties.getProperty(projectPrefix + ".overrides." + overrideIndex + ".filePath");
154                                 FileOverride override = FileOverride.valueOf(projectProperties.getProperty(projectPrefix + ".overrides." + overrideIndex + ".override"));
155                                 project.addFileOverride(filePath, override);
156                                 logger.log(Level.FINEST, "read override: " + filePath + ", " + override);
157                                 overrideIndex++;
158                         }
159                         projects.add(project);
160                         logger.fine("loaded project “" + project.getName() + "”.");
161                         projectIndex++;
162                 }
163         }
164
165         /**
166          * Saves projects and statistics.
167          * 
168          * @throws IOException
169          *             if an I/O error occurs
170          */
171         public void save() throws IOException {
172                 File directoryFile = new File(directory);
173                 if (!directoryFile.exists()) {
174                         if (!directoryFile.mkdirs()) {
175                                 throw new IOException("could not create directory: " + directory);
176                         }
177                 }
178                 Properties projectProperties = new Properties();
179                 int projectIndex = 0;
180                 for (Project project : projects) {
181                         String projectPrefix = "projects." + projectIndex;
182                         projectProperties.setProperty(projectPrefix + ".id", project.getId());
183                         projectProperties.setProperty(projectPrefix + ".name", project.getName());
184                         projectProperties.setProperty(projectPrefix + ".description", project.getDescription());
185                         projectProperties.setProperty(projectPrefix + ".privateKey", project.getPrivateKey());
186                         projectProperties.setProperty(projectPrefix + ".publicKey", project.getPublicKey());
187                         projectProperties.setProperty(projectPrefix + ".basePath", project.getBasePath());
188                         projectProperties.setProperty(projectPrefix + ".defaultFile", project.getDefaultFile());
189                         if (project.getNode() != null) {
190                                 projectProperties.setProperty(projectPrefix + ".node", project.getNode().getId());
191                         }
192                         int overrideIndex = 0;
193                         for (Entry<String, FileOverride> overrideEntry : project.getFileOverrides().entrySet()) {
194                                 projectProperties.setProperty(projectPrefix + ".overrides." + overrideIndex + ".filePath", overrideEntry.getKey());
195                                 projectProperties.setProperty(projectPrefix + ".overrides." + overrideIndex + ".override", overrideEntry.getValue().toString());
196                                 overrideIndex++;
197                         }
198                         projectIndex++;
199                 }
200                 File projectFile = new File(directoryFile, "projects.properties");
201                 OutputStream projectOutputStream = null;
202                 try {
203                         projectOutputStream = new FileOutputStream(projectFile);
204                         projectProperties.store(projectOutputStream, "jSite projects");
205                 } finally {
206                         Closer.close(projectOutputStream);
207                 }
208         }
209
210         /**
211          * Creates a new project. The returned {@link Project} will contain a newly
212          * generated key pair.
213          * 
214          * @return A newly created project
215          * @throws IOException
216          *             if an I/O error occured communicating with the node
217          * @throws JSiteException
218          *             if there is a problem with the node
219          */
220         public Project createProject() throws IOException, JSiteException {
221                 Project project = new Project();
222                 String[] keyPair = nodeManager.generateKeyPair();
223                 project.setName("");
224                 project.setDescription("");
225                 project.setPrivateKey(keyPair[0]);
226                 project.setPublicKey(keyPair[1]);
227                 project.setBasePath("");
228                 project.setDefaultFile("");
229                 projects.add(project);
230                 project.addPropertyChangeListener(this);
231                 try {
232                         save();
233                 } catch (IOException ioe1) {
234                         /* ignore. */
235                 }
236                 return project;
237         }
238
239         /**
240          * Clones the given project and returns the clone. The clone will be
241          * identical in all user-exposed fields, except for the project’s
242          * {@link Project#getId ID}.
243          * 
244          * @param project
245          *            The project to clone
246          * @return The cloned project
247          */
248         public Project cloneProject(Project project) {
249                 Project projectClone = new Project(project);
250                 projects.add(projectClone);
251                 projectClone.addPropertyChangeListener(this);
252                 try {
253                         save();
254                 } catch (IOException ioe1) {
255                         /* ignore. */
256                 }
257                 return projectClone;
258         }
259
260         /**
261          * Removes the given project.
262          * 
263          * @param project
264          *            The project to remove
265          */
266         public void removeProject(Project project) {
267                 projects.remove(project);
268                 try {
269                         save();
270                 } catch (IOException ioe1) {
271                         /* ignore. */
272                 }
273         }
274
275         //
276         // INTERFACE PropertyChangeListener
277         //
278
279         /**
280          * {@inheritDoc}
281          */
282         public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
283                 try {
284                         save();
285                 } catch (IOException ioe1) {
286                         /* ignore. */
287                 }
288         }
289
290 }