Add project manager as property change listener when loading 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.FileReader;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.io.Reader;
29 import java.io.Writer;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Map.Entry;
36 import java.util.logging.Level;
37 import java.util.logging.Logger;
38
39 import net.pterodactylus.util.collection.MapWriter;
40 import net.pterodactylus.util.io.Closer;
41 import net.pterodactylus.util.logging.Logging;
42
43 /**
44  * Manages projects, taking care of persistence, lifetime statistics, and other
45  * things.
46  *
47  * @author David ‘Bombe’ Roden <bombe@freenetproject.org>
48  */
49 public class ProjectManager implements PropertyChangeListener {
50
51         /** Logger. */
52         private static final Logger logger = Logging.getLogger(ProjectManager.class.getName());
53
54         /** The directory the projects are stored in. */
55         private final String directory;
56
57         /** The node manager. */
58         private NodeManager nodeManager;
59
60         /** All projects. */
61         private final List<Project> projects = Collections.synchronizedList(new ArrayList<Project>());
62
63         /**
64          * Creates a new project manager that saves and restores its state to/from
65          * the given directory.
66          *
67          * @param directory
68          *            The directory to save and restore states to/from
69          */
70         public ProjectManager(String directory) {
71                 this.directory = directory;
72         }
73
74         //
75         // ACCESSORS
76         //
77
78         /**
79          * Returns the directory the projects are loaded from and saved to.
80          *
81          * @return The directory for storing the projects
82          */
83         public String getDirectory() {
84                 return directory;
85         }
86
87         /**
88          * Returns a list of all projects.
89          *
90          * @return A list of all projects
91          */
92         public List<Project> getProjects() {
93                 return Collections.unmodifiableList(new ArrayList<Project>(projects));
94         }
95
96         /**
97          * Sets the node manager to use.
98          *
99          * @param nodeManager
100          *            The node manager to use
101          */
102         public void setNodeManager(NodeManager nodeManager) {
103                 this.nodeManager = nodeManager;
104         }
105
106         //
107         // ACTIONS
108         //
109
110         /**
111          * Loads projects and statistics.
112          *
113          * @throws IOException
114          *             if an I/O error occurs
115          */
116         public void load() throws IOException {
117                 File directoryFile = new File(directory);
118                 File projectFile = new File(directoryFile, "projects.properties");
119                 if (!projectFile.exists() || !projectFile.isFile() || !projectFile.canRead()) {
120                         return;
121                 }
122                 logger.log(Level.INFO, "Loading projects from “" + projectFile + "”...");
123                 Map<String, String> projectProperties;
124                 Reader reader = null;
125                 try {
126                         reader = new FileReader(projectFile);
127                         projectProperties = MapWriter.read(reader);
128                 } finally {
129                         Closer.close(reader);
130                 }
131                 int projectIndex = 0;
132                 while (projectProperties.containsKey("projects." + projectIndex + ".name")) {
133                         String projectPrefix = "projects." + projectIndex;
134                         String projectId = projectProperties.get(projectPrefix + ".id");
135                         String projectName = projectProperties.get(projectPrefix + ".name");
136                         String projectDescription = projectProperties.get(projectPrefix + ".description");
137                         String projectPrivateKey = projectProperties.get(projectPrefix + ".privateKey");
138                         String projectPublicKey = projectProperties.get(projectPrefix + ".publicKey");
139                         String projectBasePath = projectProperties.get(projectPrefix + ".basePath");
140                         String projectDefaultFile = projectProperties.get(projectPrefix + ".defaultFile");
141                         String projectNode = projectProperties.get(projectPrefix + ".node");
142                         Project project = new Project();
143                         project.setId(projectId);
144                         project.setName(projectName);
145                         project.setDescription(projectDescription);
146                         project.setPrivateKey(projectPrivateKey);
147                         project.setPublicKey(projectPublicKey);
148                         project.setBasePath(projectBasePath);
149                         project.setDefaultFile(projectDefaultFile);
150                         if (projectNode != null) {
151                                 Node node = nodeManager.getNode(projectNode);
152                                 project.setNode(node);
153                         }
154                         int overrideIndex = 0;
155                         while (projectProperties.containsKey(projectPrefix + ".overrides." + overrideIndex + ".override")) {
156                                 String filePath = projectProperties.get(projectPrefix + ".overrides." + overrideIndex + ".filePath");
157                                 FileOverride override = FileOverride.valueOf(projectProperties.get(projectPrefix + ".overrides." + overrideIndex + ".override"));
158                                 project.addFileOverride(filePath, override);
159                                 logger.log(Level.FINEST, "read override: " + filePath + ", " + override);
160                                 overrideIndex++;
161                         }
162                         project.addPropertyChangeListener(this);
163                         projects.add(project);
164                         logger.fine("loaded project “" + project.getName() + "”.");
165                         projectIndex++;
166                 }
167         }
168
169         /**
170          * Saves projects and statistics.
171          *
172          * @throws IOException
173          *             if an I/O error occurs
174          */
175         public void save() throws IOException {
176                 File directoryFile = new File(directory);
177                 if (!directoryFile.exists()) {
178                         if (!directoryFile.mkdirs()) {
179                                 throw new IOException("could not create directory: " + directory);
180                         }
181                 }
182                 Map<String, String> projectProperties = new HashMap<String, String>();
183                 int projectIndex = 0;
184                 for (Project project : projects) {
185                         String projectPrefix = "projects." + projectIndex;
186                         projectProperties.put(projectPrefix + ".id", project.getId());
187                         projectProperties.put(projectPrefix + ".name", project.getName());
188                         projectProperties.put(projectPrefix + ".description", project.getDescription());
189                         projectProperties.put(projectPrefix + ".privateKey", project.getPrivateKey());
190                         projectProperties.put(projectPrefix + ".publicKey", project.getPublicKey());
191                         projectProperties.put(projectPrefix + ".basePath", project.getBasePath());
192                         projectProperties.put(projectPrefix + ".defaultFile", project.getDefaultFile());
193                         if (project.getNode() != null) {
194                                 projectProperties.put(projectPrefix + ".node", project.getNode().getId());
195                         }
196                         int overrideIndex = 0;
197                         for (Entry<String, FileOverride> overrideEntry : project.getFileOverrides().entrySet()) {
198                                 projectProperties.put(projectPrefix + ".overrides." + overrideIndex + ".filePath", overrideEntry.getKey());
199                                 projectProperties.put(projectPrefix + ".overrides." + overrideIndex + ".override", overrideEntry.getValue().toString());
200                                 overrideIndex++;
201                         }
202                         projectIndex++;
203                 }
204                 File projectFile = new File(directoryFile, "projects.properties");
205                 Writer writer = null;
206                 try {
207                         writer = new FileWriter(projectFile);
208                         MapWriter.write(writer, projectProperties);
209                 } finally {
210                         Closer.close(writer);
211                 }
212         }
213
214         /**
215          * Creates a new project. The returned {@link Project} will contain a newly
216          * generated key pair.
217          *
218          * @return A newly created project
219          * @throws IOException
220          *             if an I/O error occured communicating with the node
221          * @throws JSiteException
222          *             if there is a problem with the node
223          */
224         public Project createProject() throws IOException, JSiteException {
225                 Project project = new Project();
226                 String[] keyPair = nodeManager.generateKeyPair();
227                 project.setName("");
228                 project.setDescription("");
229                 project.setPrivateKey(keyPair[0]);
230                 project.setPublicKey(keyPair[1]);
231                 project.setBasePath("");
232                 project.setDefaultFile("");
233                 projects.add(project);
234                 project.addPropertyChangeListener(this);
235                 try {
236                         save();
237                 } catch (IOException ioe1) {
238                         /* ignore. */
239                 }
240                 return project;
241         }
242
243         /**
244          * Clones the given project and returns the clone. The clone will be
245          * identical in all user-exposed fields, except for the project’s
246          * {@link Project#getId ID}.
247          *
248          * @param project
249          *            The project to clone
250          * @return The cloned project
251          */
252         public Project cloneProject(Project project) {
253                 Project projectClone = new Project(project);
254                 projects.add(projectClone);
255                 projectClone.addPropertyChangeListener(this);
256                 try {
257                         save();
258                 } catch (IOException ioe1) {
259                         /* ignore. */
260                 }
261                 return projectClone;
262         }
263
264         /**
265          * Removes the given project.
266          *
267          * @param project
268          *            The project to remove
269          */
270         public void removeProject(Project project) {
271                 projects.remove(project);
272                 try {
273                         save();
274                 } catch (IOException ioe1) {
275                         /* ignore. */
276                 }
277         }
278
279         //
280         // INTERFACE PropertyChangeListener
281         //
282
283         /**
284          * {@inheritDoc}
285          */
286         public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
287                 try {
288                         save();
289                 } catch (IOException ioe1) {
290                         /* ignore. */
291                 }
292         }
293
294 }