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