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-11-25 12:40:45 -0800 (Tue, 25 Nov 2008) $'
11
 * '$Revision: 4632 $'
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.UtilException;
43
import edu.ucsb.nceas.utilities.FileUtil;
44
import edu.ucsb.nceas.utilities.GeneralPropertyException;
45
import edu.ucsb.nceas.utilities.PropertiesMetaData;
46
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
47
import edu.ucsb.nceas.utilities.SortedProperties;
48

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

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

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

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

    
122
		skinNames = SkinUtil.getSkinNames();
123

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

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

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

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

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

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

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

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

    
299
	}
300

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

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

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

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

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

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

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

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

    
503
}
(7-7/9)