Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements properties methods for metacat
4
 *             skins
5
 *  Copyright: 2008 Regents of the University of California and the
6
 *             National Center for Ecological Analysis and Synthesis
7
 *    Authors: Michael Daigle
8
 *
9
 *   '$Author: daigle $'
10
 *     '$Date: 2008-12-09 14:58:20 -0800 (Tue, 09 Dec 2008) $'
11
 * '$Revision: 4663 $'
12
 *
13
 * This program is free software; you can redistribute it and/or modify
14
 * it under the terms of the GNU General Public License as published by
15
 * the Free Software Foundation; either version 2 of the License, or
16
 * (at your option) any later version.
17
 *
18
 * This program is distributed in the hope that it will be useful,
19
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21
 * GNU General Public License for more details.
22
 *
23
 * You should have received a copy of the GNU General Public License
24
 * along with this program; if not, write to the Free Software
25
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26
 */
27

    
28
package edu.ucsb.nceas.metacat.service;
29

    
30
import java.io.IOException;
31
import java.util.HashMap;
32
import java.util.Set;
33
import java.util.Vector;
34

    
35
import javax.servlet.ServletContext;
36
import javax.servlet.http.HttpServletRequest;
37
import javax.xml.transform.TransformerException;
38

    
39
import org.apache.log4j.Logger;
40

    
41
import edu.ucsb.nceas.metacat.util.SkinUtil;
42
import edu.ucsb.nceas.metacat.util.SystemUtil;
43
import edu.ucsb.nceas.metacat.util.UtilException;
44
import edu.ucsb.nceas.utilities.FileUtil;
45
import edu.ucsb.nceas.utilities.GeneralPropertyException;
46
import edu.ucsb.nceas.utilities.PropertiesMetaData;
47
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
48
import edu.ucsb.nceas.utilities.SortedProperties;
49

    
50
/**
51
 * A suite of utility classes for the skin configuration utility
52
 */
53
public class SkinPropertyService extends BaseService {
54
	
55
	private static SkinPropertyService skinService = null;
56
	
57
	private static boolean bypassAlreadyChecked = false;
58
	
59
	private static String SKIN_DIR = null;
60
	private static String BACKUP_DIR = null;
61
	
62
	private static Vector<String> skinNames = null;
63
	
64
	private static HashMap<String, SortedProperties> skinPropertiesMap = null;	
65
	private static HashMap<String, SortedProperties> skinBackupPropertiesMap = null;
66
	private static HashMap<String, PropertiesMetaData> skinMetaDataMap = null;
67
	
68
	private static Logger logMetacat = Logger.getLogger(SkinPropertyService.class);
69

    
70
	/**
71
	 * private constructor since this is a singleton
72
	 * 
73
	 * @param servletContext the context we will use to get relative paths
74
	 */
75
	private SkinPropertyService(ServletContext servletContext) throws ServiceException {
76
		try {
77
			initialize(servletContext);
78
		} catch (GeneralPropertyException gpe) {
79
			throw new ServiceException(
80
					"Properties problem while initializing PropertyService: "
81
							+ gpe.getMessage());
82
		} catch (IOException ioe) {
83
			throw new ServiceException("I/O Problem while initializing PropertyService: "
84
					+ ioe.getMessage());
85
		}
86
	}
87
	
88
	/**
89
	 * Get the single instance of SkinPropertyService.
90
	 * 
91
	 * @param servletContext the context we will use to get relative paths
92
	 * @return the single instance of SkinPropertyService
93
	 */
94
	public static SkinPropertyService getInstance(ServletContext servletContext) throws ServiceException {
95
		if (skinService == null) {
96
			skinService = new SkinPropertyService(servletContext);
97
		}
98
		return skinService;
99
	}
100
	
101
	public boolean refreshable() {
102
		return false;
103
	}
104
	
105
	protected void doRefresh() {
106
		return;
107
	}
108
	
109
	/**
110
	 * Initialize the singleton.
111
	 * 
112
	 * @param servletContext
113
	 *            the context we will use to get relative paths
114
	 */
115
	private void initialize(ServletContext servletContext) throws IOException,
116
			GeneralPropertyException {
117

    
118
		logMetacat.debug("Initializing SkinService");
119

    
120
		SKIN_DIR = "/style/skins";
121
		BACKUP_DIR = PropertyService.getBackupDir();
122

    
123
		skinNames = SkinUtil.getSkinNames();
124

    
125
		skinPropertiesMap = new HashMap<String, SortedProperties>();
126
		skinBackupPropertiesMap = new HashMap<String, SortedProperties>();
127
		skinMetaDataMap = new HashMap<String, PropertiesMetaData>();
128

    
129
		try {
130
			for (String skinName : skinNames) {
131
				String propertyFilePath = servletContext.getRealPath(SKIN_DIR)
132
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
133
						+ ".properties";
134
				SortedProperties skinProperties = new SortedProperties(propertyFilePath);
135
				skinProperties.load();
136
				skinPropertiesMap.put(skinName, skinProperties);
137

    
138
				String metaDataFilePath = servletContext.getRealPath(SKIN_DIR)
139
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
140
						+ ".properties.metadata.xml";
141
				if (FileUtil.getFileStatus(metaDataFilePath) == FileUtil.DOES_NOT_EXIST) {
142
					throw new GeneralPropertyException("Could not find skin property metadata file: " + metaDataFilePath);
143
				} else {
144
					PropertiesMetaData skinMetaData = 
145
						new PropertiesMetaData(metaDataFilePath);
146
					skinMetaDataMap.put(skinName, skinMetaData);
147
				} 
148

    
149
				String backupPropertyFilePath = 
150
					BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
151
				if (FileUtil.getFileStatus(backupPropertyFilePath) > FileUtil.DOES_NOT_EXIST) {
152
					SortedProperties skinBackupProperties = 
153
						new SortedProperties(backupPropertyFilePath);
154
					skinBackupProperties.load();
155
					skinBackupPropertiesMap.put(skinName, skinBackupProperties);
156
				} else {
157
					logMetacat.info("Could not find backup properties for skin: " + skinName
158
							+ ". Backup file does not exist: " + backupPropertyFilePath);
159
				}
160
			}
161
		} catch (TransformerException te) {
162
			throw new GeneralPropertyException(te.getMessage());
163
		}
164
	}
165

    
166
	/**
167
	 * Utility method to get a property value from the properties file for
168
	 * a specific skin.
169
	 * 
170
	 * @param skinName the skin for which we want to retrieve the property
171
	 * @param propertyName
172
	 *            the name of the property requested
173
	 * @return the String value for the property
174
	 */
175
	public static String getProperty(String skinName, String propertyName)
176
			throws PropertyNotFoundException {
177
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
178
		if (skinProperties == null) {
179
			throw new PropertyNotFoundException("There is not property map for " + skinName);
180
		}
181
		return skinProperties.getProperty(propertyName);
182
	}
183
	
184
	/**
185
     * Get a set of all property names for a given skin.
186
     * 
187
     * @param skinName the skin for which we want to retrieve the property
188
     * names
189
     * @return Set of property names  
190
     */
191
    public static Vector<String> getPropertyNames(String skinName) throws PropertyNotFoundException {   
192
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
193
		if (skinProperties == null) {
194
			throw new PropertyNotFoundException("There is not property map for " + skinName);
195
		}
196
    	return skinProperties.getPropertyNames();
197
    }
198
    
199

    
200
	/**
201
	 * Get a Set of all property names that start with the groupName prefix.
202
	 * 
203
	 * @param groupName
204
	 *            the prefix of the keys to search for.
205
	 * @return Vector of property names
206
	 */
207
    public static Vector<String> getPropertyNamesByGroup(String skinName, String groupName) throws PropertyNotFoundException {  
208
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
209
		if (skinProperties == null) {
210
			throw new PropertyNotFoundException("There is not property map for " + skinName);
211
		}
212
    	return skinProperties.getPropertyNamesByGroup(groupName);
213
    }
214
    
215
	/**
216
	 * Get the main backup properties file. These are configurable properties that
217
	 * are stored outside the metacat install directories so the user does not
218
	 * need to re-enter all the configuration information every time they do an
219
	 * upgrade.
220
	 * 
221
	 * @return a SortedProperties object with the backup properties
222
	 */
223
	public static HashMap<String, SortedProperties> getProperties() {
224
		return skinPropertiesMap;
225
	}
226
    
227
	/**
228
	 * Get the main backup properties file. These are configurable properties that
229
	 * are stored outside the metacat install directories so the user does not
230
	 * need to re-enter all the configuration information every time they do an
231
	 * upgrade.
232
	 * 
233
	 * @return a SortedProperties object with the backup properties
234
	 */
235
	public static SortedProperties getProperties(String skinName) {
236
		return skinPropertiesMap.get(skinName);
237
	}
238

    
239
	/**
240
	 * Get the main backup properties file. These are configurable properties that
241
	 * are stored outside the metacat install directories so the user does not
242
	 * need to re-enter all the configuration information every time they do an
243
	 * upgrade.
244
	 * 
245
	 * @return a SortedProperties object with the backup properties
246
	 */
247
	public static HashMap<String, SortedProperties> getBackupProperties() {
248
		return skinBackupPropertiesMap;
249
	}
250
	
251
	/**
252
	 * Get the main backup properties file. These are configurable properties that
253
	 * are stored outside the metacat install directories so the user does not
254
	 * need to re-enter all the configuration information every time they do an
255
	 * upgrade.
256
	 * 
257
	 * @return a SortedProperties object with the backup properties
258
	 */
259
	public static SortedProperties getBackupProperties(String skinName) {
260
		return skinBackupPropertiesMap.get(skinName);
261
	}
262
	
263
	/**
264
	 * Get the main properties metadata. This is retrieved from an xml file that
265
	 * describes the attributes of configurable properties.
266
	 * 
267
	 * @return a PropertiesMetaData object with the main properties metadata
268
	 */
269
	public static HashMap<String, PropertiesMetaData> getMetaData() {
270
		return skinMetaDataMap;
271
	}
272
	
273
	/**
274
	 * Get the main properties metadata. This is retrieved from an xml file that
275
	 * describes the attributes of configurable properties.
276
	 * 
277
	 * @return a PropertiesMetaData object with the main properties metadata
278
	 */
279
	public static PropertiesMetaData getMetaData(String skinName) {
280
		return skinMetaDataMap.get(skinName);
281
	}
282

    
283
	/**
284
	 * Utility method to set a property value both in memory and to the
285
	 * properties file
286
	 * 
287
	 * @param propertyName
288
	 *            the name of the property requested
289
	 * @param newValue
290
	 *            the new value for the property
291
	 */
292
	public static void setProperty(String skinName, String propertyName, String newValue) throws IOException, GeneralPropertyException {
293
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
294
		if (skinProperties == null) {
295
			throw new GeneralPropertyException("There is not property map for " + skinName);
296
		}
297
		skinProperties.setProperty(propertyName, newValue);
298
		skinProperties.store();
299

    
300
	}
301

    
302
	/**
303
	 * Utility method to set a property value in memory. This will NOT cause the
304
	 * property to be written to disk. Use this method to set multiple
305
	 * properties in a row without causing excessive I/O. You must call
306
	 * persistProperties() once you're done setting properties to have them
307
	 * written to disk.
308
	 * 
309
	 * @param propertyName
310
	 *            the name of the property requested
311
	 * @param newValue
312
	 *            the new value for the property
313
	 */
314
	public static void setPropertyNoPersist(String skinName, String propertyName, String newValue) throws GeneralPropertyException {
315
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
316
		if (skinProperties == null) {
317
			throw new GeneralPropertyException("There is not property map for " + skinName);
318
		}
319
		skinProperties.setPropertyNoPersist(propertyName, newValue);
320
	}
321

    
322
	/**
323
	 * Save the properties to a properties file. Note, the 
324
	 * order and comments will be preserved.
325
	 */
326
	public static void persistProperties(String skinName) throws IOException, GeneralPropertyException {
327
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
328
		if (skinProperties == null) {
329
			throw new GeneralPropertyException("There is not property map for " + skinName);
330
		}
331
		skinProperties.store();
332
	}
333

    
334
	/**
335
	 * Save the properties to a properties file. Note, the order and comments
336
	 * will be preserved.
337
	 */
338
	public static void persistAllProperties() throws IOException,
339
			GeneralPropertyException {
340
		for (String skinName : skinNames) {
341
			persistProperties(skinName);
342
		}
343
	}
344
	
345
	/**
346
	 * Writes out backup configurable properties to a file.
347
	 */
348
	public static void persistBackupProperties(String skinName, ServletContext servletContext)
349
			throws GeneralPropertyException {
350
		
351
		String metaDataFilePath = servletContext.getRealPath(SKIN_DIR) + FileUtil.getFS()
352
				+ skinName + FileUtil.getFS() + skinName + ".properties.metadata.xml";
353
		
354
		String backupPropertyFilePath = 
355
			BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
356

    
357
		// Use the metadata to extract configurable properties from the 
358
		// overall properties list, and store those properties.
359
		try {
360
			SortedProperties backupProperties = 
361
				new SortedProperties(backupPropertyFilePath);
362
			
363
			// Populate the backup properties for main metacat properties using
364
			// the associated metadata file
365
			PropertiesMetaData mainMetadata = new PropertiesMetaData(metaDataFilePath);
366
			Set<String> mainKeySet = mainMetadata.getKeys();
367
			for (String propertyKey : mainKeySet) {
368
				backupProperties.addProperty(propertyKey, getProperty(skinName, propertyKey));
369
			}
370
			
371
			// store the properties to file
372
			backupProperties.store();
373

    
374
		} catch (TransformerException te) {
375
			throw new GeneralPropertyException("Could not transform backup properties xml: "
376
					+ te.getMessage());
377
		} catch (IOException ioe) {
378
			throw new GeneralPropertyException("Could not backup configurable properties: "
379
					+ ioe.getMessage());
380
		}
381
	}
382

    
383
	/**
384
	 * Gets the backup properties directory
385
	 * 
386
	 * TODO MCD figure out how to expand this.  At least for Windows
387
	 * 
388
	 * @return a string which holds the name of the backup directory. returns
389
	 *         null if directory could not be created.
390
	 */
391
	public static String getBackupDir() {
392
		return SystemUtil.discoverExternalDir() + FileUtil.getFS() + ".metacat";
393
	}
394
	
395
	/**
396
	 * Reports whether properties are fully configured.
397
	 * 
398
	 * @return a boolean that is true if properties are not unconfigured and
399
	 *         false otherwise
400
	 */
401
	public static boolean areSkinsConfigured() throws UtilException {
402
		try {
403
			return !PropertyService.getProperty("configutil.skinsConfigured").equals(
404
					PropertyService.UNCONFIGURED);
405
		} catch (PropertyNotFoundException pnfe) {
406
			throw new UtilException("Could not determine if skins are configured: "
407
					+ pnfe.getMessage());
408
		}
409
	}
410
	
411
	/**
412
	 * Take input from the user in an HTTP request about an property to be changed
413
	 * and update the metacat property file with that new value if it has
414
	 * changed from the value that was originally set.
415
	 * 
416
	 * @param request
417
	 *            that was generated by the user
418
	 * @param response
419
	 *            to send output back to the user
420
	 * @param propertyName
421
	 *            the name of the property to be checked and set
422
	 */
423
	public static void checkAndSetProperty(HttpServletRequest request, String skinName, String propertyName) 
424
			throws GeneralPropertyException {
425
		String newValue = request.getParameter(skinName + "." + propertyName);
426
		checkAndSetProperty(newValue, skinName, propertyName); 
427
	}
428
	
429
	/**
430
	 * Check user input against existing value
431
	 * and update the metacat property file with that new value if it has
432
	 * changed from the value that was originally set.
433
	 * 
434
	 * @param newValue
435
	 *            the value that was returned by the form
436
	 * @param skinname
437
	 *            the skin that we are checking
438
	 * @param propertyName
439
	 *            the name of the property to be checked and set
440
	 */
441
	public static void checkAndSetProperty(String newValue, String skinName, String propertyName) 
442
			throws GeneralPropertyException {
443
		String oldValue = SkinPropertyService.getProperty(skinName, propertyName);
444
		if (newValue != null && !newValue.equals(oldValue)) {
445
			SkinPropertyService.setPropertyNoPersist(skinName, propertyName, newValue);
446
		}
447
	}
448
	
449
	/**
450
	 * Reports whether the metacat configuration utility should be run.  
451
	 * Returns false if  
452
	 *   -- dev.runConfiguration=false and
453
	 *   -- backup properties file exists
454
	 * Note that dev.runConfiguration should only be set to false when
455
	 * reinstalling the same version of the application in developement.
456
	 * 
457
	 * @return a boolean that is false if dev.runConfiguration is false and 
458
	 * the backup properties file exists.  
459
	 */
460
	public static boolean bypassConfiguration() {
461
		boolean bypass = false;
462
		
463
		// We only want to go through the check once to see if we want to
464
		// bypass the configuration.  We don't want to run through all of
465
		// this every time  we hit metacat. 
466
		if (bypassAlreadyChecked) {
467
			return bypass;
468
		}
469
		
470
		try {		
471
			// check how dev.runConfiguration is set in metacat.properties
472
			String strRunConfiguration = PropertyService.getProperty("dev.runConfiguration");
473
			bypass = !(Boolean.parseBoolean(strRunConfiguration));
474
			
475
			// if the deb.runConfiguration is true, return false here.
476
			if (!bypass) {
477
				bypassAlreadyChecked = true;
478
				return false;
479
			}
480

    
481
			// the system is bypassing the configuration utility. We need to
482
			// get the backup properties and replace existing properties with
483
			// backup values.  We do this for main and org properties.
484
			for (String skinName : skinNames) {
485
				SortedProperties backupProperties = getBackupProperties(skinName);
486
				Vector<String> backupPropertyNames = 
487
					backupProperties.getPropertyNames();
488
				for (String backupPropertyName : backupPropertyNames) {
489
					String value = backupProperties.getProperty(backupPropertyName);
490
					backupProperties.setPropertyNoPersist(backupPropertyName, value);
491
				}
492
				backupProperties.store();
493
			}
494
		} catch (PropertyNotFoundException pnfe) {
495
			logMetacat.error("Could not find property: " + pnfe.getMessage());
496
		} catch (GeneralPropertyException gpe) {
497
			logMetacat.error("General property error: " + gpe.getMessage());
498
		}
499

    
500
		bypassAlreadyChecked = true;
501
		return bypass;
502
	}
503

    
504
}
(7-7/9)