View Javadoc

1   /*
2    * Created on Dec 9, 2004 by Justin Sher
3    *
4    *  ginp - Java Web Application for Viewing Photo Collections
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19   */
20  package net.sf.ginp.setup;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.File;
24  import java.io.FileNotFoundException;
25  import java.io.FileOutputStream;
26  import java.io.IOException;
27  import java.io.InputStream;
28  import java.io.StringReader;
29  import java.io.UnsupportedEncodingException;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.Iterator;
34  import java.util.List;
35  import java.util.prefs.BackingStoreException;
36  import java.util.prefs.Preferences;
37  
38  import javax.xml.transform.TransformerException;
39  
40  import net.sf.ginp.setup.data.DirectoryPref;
41  import net.sf.ginp.setup.data.SetupVisit;
42  import net.sf.ginp.util.GinpUtil;
43  import net.sf.ginp.config.Configuration;
44  
45  import org.apache.commons.logging.Log;
46  import org.apache.commons.logging.LogFactory;
47  import org.dom4j.Document;
48  import org.dom4j.DocumentException;
49  import org.dom4j.Element;
50  import org.dom4j.io.OutputFormat;
51  import org.dom4j.io.SAXReader;
52  import org.dom4j.io.XMLWriter;
53  import org.dom4j.tree.DefaultDocumentType;
54  import org.xml.sax.InputSource;
55  
56  import com.thoughtworks.xstream.XStream;
57  
58  /**
59   * Implementation of Setup Manager
60   * @author Justin Sher
61   * @see net.sf.ginp.setup.SetupManager
62   **/
63  public class SetupManagerImpl implements SetupManager {
64  	private Log log = LogFactory.getLog(SetupManagerImpl.class);
65  	private static final String TROUBLE_PARSING = "troubleParsing";
66  	/**
67  	 * @param configUrl the URL identifying the config to check
68  	 * @return true or false 
69  	 * @see net.sf.ginp.setup.SetupManager#configExists(URL configUrl)
70  	 */
71  	public boolean configExists(URL configUrl) {
72  		//String file= this.getConfiguration(configUrl);
73  		//if (file==null) { return false; }
74  		File fileTest = new File(GinpUtil.getConfigFileRealPath());
75  		if (!fileTest.canRead()) {
76  			return false;
77  		}
78  		return true;
79  	}
80  
81  	/**
82  	 * @see net.sf.ginp.setup.SetupManager#deleteConfiguration(java.net.URL)
83  	 */
84  	public void deleteConfiguration(URL configUrl) {
85  		Preferences preferences = Preferences.userNodeForPackage(SetupManagerImpl.class);
86  		preferences.remove(configUrl.toExternalForm());
87  		try {
88  			preferences.flush();
89  		} catch (BackingStoreException e) {
90              log.error("No big deal if we couldn't delete", e);
91  		}
92  	}
93  
94  	/**
95  	 * @throws SetupException
96  	 * @see net.sf.ginp.setup.SetupManager#createConfiguration(java.net.URL, java.lang.String)
97  	 */
98  	public void createConfiguration(URL configUrl, String configuration) throws SetupException {
99  		storeConfig(configUrl, configuration);
100 	}
101 
102 	/**
103 	 * @param configUrl
104 	 * @param configuration
105 	 * @return the saves prefs object
106 	 */
107 	private Preferences storeConfig(URL configUrl, String configuration) {
108 		Preferences preferences = Preferences.userNodeForPackage(SetupManagerImpl.class);
109 		preferences.put(configUrl.toExternalForm(),configuration);
110 		return preferences;
111 	}
112 
113 	/**
114 	 * @see net.sf.ginp.setup.SetupManager#validConfigLoc(java.lang.String)
115 	 */
116 	public Boolean validConfigLoc(String path) {
117 		File pathDir=new File(path);
118 		if (!pathDir.isDirectory()) { return new Boolean(false); } 
119 		if (!pathDir.canWrite()) { return new Boolean(false);  }
120 		if (!pathDir.canRead()) { return new Boolean(false);} 
121 		return new Boolean(true);
122 	}
123 
124 	/**
125 	 * @throws SetupException if there's a problem reading the doc 
126          * note: don't be fooled, we can't report the path of the
127          *       offending document because it is supplied as a Stream.
128 	 * @throws IOException
129 	 * @see net.sf.ginp.setup.SetupManager#testValidConfig(java.io.InputStream)
130 	 */
131 	public Document testValidConfig(InputStream stream) throws SetupException, IOException {
132 		String reader = GinpUtil.readBufferIntoMemory(stream);
133 		try {
134 			return validateConfig(new ByteArrayInputStream(reader.getBytes()));
135 		}
136 		catch (DocumentException d) {
137 			SAXReader read=new SAXReader(false);
138 			try {
139 				InputSource source=new InputSource();
140 				source.setCharacterStream(new StringReader(reader));
141 				String ginpFile=this.getClass().getResource("/net/sf/ginp/config/ginp.dtd").toExternalForm();
142 				ginpFile=ginpFile.substring(0,ginpFile.lastIndexOf("/")+1);
143 				source.setSystemId(ginpFile);
144                 source.setPublicId("ginp.dtd");
145 				Document document = read.read(source);
146 				List list = document.selectNodes("/ginp/collection/users");
147 				list.addAll(document.selectNodes("/ginp/collection/admins"));
148 				Iterator iter=list.iterator();
149 				while (iter.hasNext()) { 
150 					Element e=(Element) iter.next();
151 					String value=e.getText();
152 					String users[]=value.split(",");
153 					for (int x=0;x<users.length;x++) { 
154 						if (users[x].length()==0) { continue; }
155 						Element element = e.addElement("username");
156 						element.setText(users[x].trim());
157 					}
158 					e.setText("");
159 				}
160 				try {
161 					return validateConfig(new ByteArrayInputStream(document.asXML().getBytes()));
162 				}
163 				catch (DocumentException e) {
164 					//Some XML parsers don't support validation
165 					if (e.getMessage().indexOf("not supported")>=0) {
166 						log.warn("Validation Not Supported by your XML parser",e);
167 						return document;
168 					} else {
169                         log.error("Validation Error:", e);
170                     }
171 					throw e;
172 				}
173 			} catch (DocumentException e) {
174                 log.error("throwing new SetupException", e);
175 				throw new SetupException(GinpUtil.message(TROUBLE_PARSING),d);			
176 			}
177 		}
178 		
179 	}
180 
181 	/**
182 	 * @param stream
183 	 * @return
184 	 * @throws IOException
185 	 * @throws DocumentException
186 	 */
187 	private Document validateConfig(InputStream stream) throws IOException, DocumentException {
188 		SAXReader read=new SAXReader(true);
189 		InputSource source=new InputSource();
190 		StringReader stringRead = new StringReader(GinpUtil.readBufferIntoMemory(stream));
191 		String ginpFile=this.getClass().getResource("/net/sf/ginp/config/ginp.dtd").toExternalForm();
192 		ginpFile=ginpFile.substring(0,ginpFile.lastIndexOf("/")+1);
193 		source.setSystemId(ginpFile);
194 		source.setPublicId("ginp.dtd");
195 		source.setCharacterStream(stringRead);
196 		return read.read(source);
197 	}
198 
199 	/**
200 	 * @see net.sf.ginp.setup.SetupManager#validPicturesLoc(java.lang.String)
201 	 */
202 	public Boolean validPicturesLoc(String path) {
203 		File pathDir=new File(path);
204 		if (!pathDir.isDirectory()) { return new Boolean(false); } 
205 		if (!pathDir.canWrite()) { return new Boolean(false);  }
206 		if (!pathDir.canRead()) { return new Boolean(false);} 
207 		return new Boolean(true);	
208 	}
209 
210 	/**
211 	 * @throws SetupException
212 	 * @see net.sf.ginp.setup.SetupManager#writeConfigFromVisit(net.sf.ginp.setup.data.SetupVisit)
213 	 */
214 	public Document writeConfigFromVisit(SetupVisit visit) throws SetupException {
215 		try {
216 			Document outputDocument = getConfigFromSetupVisit(visit);
217 				
218 			if (!writeConfig(visit, outputDocument)){
219 				log.warn("Config not written.");
220 			}
221 		
222 		return outputDocument;
223 		}
224 		catch (Exception e){ 
225 			throw new SetupException(e);			
226 		}
227 	}
228 
229 	/**
230 	 * @param visit
231 	 * @param outputDocument
232 	 * @throws FileNotFoundException
233 	 * @throws UnsupportedEncodingException
234 	 * @throws IOException
235 	 */
236 	public boolean writeConfig(SetupVisit visit, Document outputDocument) throws FileNotFoundException, UnsupportedEncodingException, IOException {
237 		FileOutputStream outputStream = null;
238 		XMLWriter write = null;
239 		boolean success = false;
240 		try {
241                         String confFileLocation = Configuration.getConfigfilelocation();
242 			if (confFileLocation == null) {
243 				log.warn("Configuration.getConfigfilelocation() is NULL!");
244 			}
245 			
246 			// make sure directory exists and be verbose about this to log.
247 			File configDir = new File(Configuration.getConfigfilelocationPath());
248 			if(!configDir.exists()) {
249 				if (configDir.mkdirs()){
250 					log.info("Config directory created at: " 
251 							+ configDir.getAbsolutePath());
252 				} else {	
253 					log.error("Can not create config directory at: " 
254 							+ configDir.getAbsolutePath());
255 				}
256 			} else {
257 				if (log.isDebugEnabled()) {
258 					log.debug("Config dir exists at:" + configDir.getAbsolutePath());
259 				}
260 			}
261 			// warn about overwrite
262 			File configFile = new File(confFileLocation);
263 			if (configFile.exists()) {
264 				log.warn("Overwriting config file at: " + configFile.getAbsolutePath());
265 			}
266 			
267 			outputStream = new FileOutputStream(configFile, false);
268 			OutputFormat format=OutputFormat.createPrettyPrint();
269 			write=new XMLWriter(outputStream,format);			
270 			write.write(outputDocument);
271 			success = configFile.exists();
272 		} catch (Exception ex) {
273 			log.error("Error writing config.", ex);
274 		} finally {
275 			if (write!=null) { 
276 				write.flush();
277 				write.close();
278 			}
279 			if (outputStream!=null) { 
280 				outputStream.flush();
281 				outputStream.close();
282 			}
283 		}
284 		return success;
285 	}
286 
287 	/**
288 	 * @param visit
289 	 * @return
290 	 * @throws DocumentException
291 	 * @throws TransformerException
292 	 */
293 	private Document getConfigFromSetupVisit(SetupVisit visit) throws DocumentException, TransformerException {
294 		XStream stream=new XStream();
295 		String visitXML= stream.toXML(visit);
296 		SAXReader read=new SAXReader();
297 		Document visitDoc = read.read(new StringReader(visitXML));
298 		Document outputDocument = GinpUtil.transform("/net/sf/ginp/setup/visitToConfig.xsl",visitDoc);
299 		// this is necessary because the xsl transform ignores the XSL:output tag
300 		outputDocument.setDocType(new DefaultDocumentType("ginp","-//GINP//DTD ginp XML//EN","ginp.dtd"));
301 		return outputDocument;
302 	}
303 
304 	/**
305 	 * @throws TransformerException
306 	 * @throws DocumentException
307 	 * @see net.sf.ginp.setup.SetupManager#finalPageTransform(net.sf.ginp.setup.data.SetupVisit)
308 	 */
309 	public String finalPageTransform(SetupVisit config) throws TransformerException, DocumentException {
310 		XStream stream=new XStream();
311 		String visitXML= stream.toXML(config);
312 		SAXReader reader=new SAXReader();
313 		Document setupDoc=reader.read(new StringReader(visitXML));
314 		Document i18nDoc = reader.read(this.getClass().getResourceAsStream("/net/sf/ginp/setup/"+GinpUtil.message("finalPageI18n").trim()));
315 		setupDoc.getRootElement().add(i18nDoc.getRootElement());
316 		Document htmlOut = GinpUtil.transform("/net/sf/ginp/setup/finalPage.xsl",setupDoc);
317 		return htmlOut.asXML();
318 	}
319 	
320 
321 	/**
322 	 * @see net.sf.ginp.setup.SetupManager#getCommandLineForSetupVisit(net.sf.ginp.setup.data.SetupVisit)
323 	 */
324 	public String getCommandLineForSetupVisit(SetupVisit visit) {
325 		
326 		String javaCommand="--url "+visit.getSetupURL()+" --config-path "+
327 		Configuration.getConfigfilelocation() +" --photo-path "+visit.getGinpPhotosPath()+" --admin-password "+
328 		visit.getAdminPassword()+" --guest-password "+visit.getGuestPassword();
329 		Iterator iterator = visit.getDirectoryPrefs().iterator();
330 		while (iterator.hasNext()) { 
331 			DirectoryPref dirPref=(DirectoryPref) iterator.next();
332 			if (dirPref.getType().equals(DirectoryPref.ADMIN_ONLY)) {
333 				javaCommand+=" --admin "+dirPref.getRootDir();
334 			}
335 			else if (dirPref.getType().equals(DirectoryPref.PUBLIC)) {
336 				javaCommand+=" --public "+dirPref.getRootDir();
337 			}
338 			else if (dirPref.getType().equals(DirectoryPref.PRIVATE)) {
339 				javaCommand+=" --private "+dirPref.getRootDir();
340 			}
341 		}
342 		return javaCommand;
343 	}
344 
345 	/**
346 	 * @see net.sf.ginp.setup.SetupManager#getSetupVisitForCommandLine(java.lang.String[])
347 	 */
348 	public SetupVisit getSetupVisitForCommandLine(String[] argv) throws DocumentException, TransformerException {
349 		SetupVisit visit=new SetupVisit();
350 		for(int x=0;x<argv.length;x++) { 
351 			String command=argv[x];
352 			if (command.equals("--url")) { 
353 				visit.setSetupURL(argv[x++]);
354 			}
355 			else if (command.equals("--photo-path")) {
356 				visit.setGinpPhotosPath(argv[x++]);
357 			}
358 			else if (command.equals("--admin-password")) { 
359 				visit.setAdminPassword(argv[x++]);
360 			}
361 			else if (command.equals("--guest-password")) {
362 				visit.setGuestPassword(argv[x++]);
363 			}
364 			else if (command.equals("--private")) {
365 				DirectoryPref privDir=new DirectoryPref();
366 				privDir.setRootDir(privDir.getName());
367 				privDir.setType(DirectoryPref.PRIVATE);
368 				privDir.setUserVariables();
369 				visit.addCollection(privDir);
370 			}
371 			else if (command.equals("--admin")) {
372 				DirectoryPref privDir=new DirectoryPref();
373 				privDir.setRootDir(privDir.getName());
374 				privDir.setType(DirectoryPref.ADMIN_ONLY);
375 				privDir.setUserVariables();
376 				visit.addCollection(privDir);
377 			}
378 			else if (command.equals("--public")) {
379 				DirectoryPref privDir=new DirectoryPref();
380 				privDir.setRootDir(privDir.getName());
381 				privDir.setType(DirectoryPref.PUBLIC);
382 				privDir.setUserVariables();
383 				visit.addCollection(privDir);
384 			}
385 		}
386 		//Check for Sanity
387 		this.getConfigFromSetupVisit(visit);
388 		return visit;
389 	}
390 
391 	/**
392 	 * @param sampleConfig
393 	 * @return
394 	 * @see net.sf.ginp.setup.SetupManager#getDirectoriesInPictureDirectory(net.sf.ginp.setup.data.SetupVisit)
395 	 */
396 	public List getDirectoriesInPictureDirectory(SetupVisit sampleConfig) {
397 		File dir=new File(sampleConfig.getGinpPhotosPath());
398 		String[] strings = dir.list();
399 		ArrayList directories = new ArrayList();
400 		for(int x=0;x<strings.length;x++) { 
401 			String path=sampleConfig.getGinpPhotosPath()+System.getProperty("file.separator")+strings[x];
402 			File file = new File(path);
403 			if (!file.exists())  {
404 				 path=sampleConfig.getGinpPhotosPath()+strings[x];				
405 				 file = new File(path);
406 			}
407 			if (file.canRead() && file.canWrite() && file.isDirectory()) {
408 				directories.add(file);
409 			}
410 		}
411 		return directories;
412 	}
413 
414 	/**
415 	 * Sets the configuration in the preferences from the visit's path and url
416 	 * @throws SetupException
417 	 * @throws MalformedURLException
418 	 * @see net.sf.ginp.setup.SetupManager#setConfiguration(net.sf.ginp.setup.data.SetupVisit)
419 	 */
420 	public void setConfiguration(SetupVisit visit) throws MalformedURLException, SetupException {
421 		String uri=visit.getSetupURL();
422 		uri=uri.substring(0,uri.lastIndexOf('/'));
423 		deleteConfiguration(new URL(uri));
424 		createConfiguration(new URL(uri),Configuration.getConfigfilelocation());		
425 	}
426 
427 
428 }