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-26 13:18:53 -0800 (Fri, 26 Dec 2008) $'
11
 * '$Revision: 4709 $'
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() throws ServiceException {
76
		try {
77
			initialize();
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() throws ServiceException {
95
		if (skinService == null) {
96
			skinService = new SkinPropertyService();
97
		}
98
		return skinService;
99
	}
100
	
101
	public boolean refreshable() {
102
		return true;
103
	}
104
	
105
	protected void doRefresh() throws ServiceException {
106
		try {
107
			initialize();
108
		} catch (IOException ioe) {
109
			throw new ServiceException("Could not refresh SkinPropertyService due to"
110
					+ " I/O error: " + ioe.getMessage());
111
		} catch (GeneralPropertyException gpe) {
112
			throw new ServiceException("Could not refresh SkinPropertyService due to"
113
					+ " property error: " + gpe.getMessage());
114
		}
115
	}
116
	
117
	/**
118
	 * Initialize the singleton.
119
	 * 
120
	 * @param servletContext
121
	 *            the context we will use to get relative paths
122
	 */
123
	private void initialize() throws IOException,
124
			GeneralPropertyException, ServiceException {
125

    
126
		logMetacat.debug("Initializing SkinService");
127

    
128
		BACKUP_DIR = PropertyService.getProperty("application.backupDir") + FileUtil.getFS() + ".metacat";
129

    
130
		skinNames = SkinUtil.getSkinNames();
131

    
132
		skinPropertiesMap = new HashMap<String, SortedProperties>();
133
		skinBackupPropertiesMap = new HashMap<String, SortedProperties>();
134
		skinMetaDataMap = new HashMap<String, PropertiesMetaData>();
135

    
136
		try {
137
			for (String skinName : skinNames) {
138
				String propertyFilePath = ServiceService.getRealSkinDir()
139
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
140
						+ ".properties";
141
				SortedProperties skinProperties = new SortedProperties(propertyFilePath);
142
				skinProperties.load();
143
				skinPropertiesMap.put(skinName, skinProperties);
144

    
145
				String metaDataFilePath = ServiceService.getRealSkinDir()
146
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
147
						+ ".properties.metadata.xml";
148
				if (FileUtil.getFileStatus(metaDataFilePath) == FileUtil.DOES_NOT_EXIST) {
149
					throw new GeneralPropertyException("Could not find skin property metadata file: " + metaDataFilePath);
150
				} else {
151
					PropertiesMetaData skinMetaData = new PropertiesMetaData(metaDataFilePath);
152
					skinMetaDataMap.put(skinName, skinMetaData);
153
				} 
154

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

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

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

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

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

    
306
	}
307

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

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

    
340
	/**
341
	 * Save the properties to a properties file. Note, the order and comments
342
	 * will be preserved.
343
	 */
344
	public static void persistAllProperties() throws IOException,
345
			GeneralPropertyException {
346
		for (String skinName : skinNames) {
347
			persistProperties(skinName);
348
		}
349
	}
350
	
351
	/**
352
	 * Writes out backup configurable properties to a file.
353
	 */
354
	public static void persistBackupProperties(String skinName) throws GeneralPropertyException {
355
		try {
356
			String metaDataFilePath = ServiceService.getRealSkinDir() + FileUtil.getFS()
357
					+ skinName + FileUtil.getFS() + skinName + ".properties.metadata.xml";
358

    
359
			String backupPropertyFilePath = BACKUP_DIR + FileUtil.getFS() + skinName
360
					+ ".properties.backup";
361

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

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

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

    
495
		bypassAlreadyChecked = true;
496
		return bypass;
497
	}
498

    
499
}
(7-7/9)