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-10-10 17:12:49 -0700 (Fri, 10 Oct 2008) $'
11
 * '$Revision: 4442 $'
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
				PropertiesMetaData skinMetaData = new PropertiesMetaData(metaDataFilePath);
141
				skinMetaDataMap.put(skinName, skinMetaData);
142

    
143
				String backupPropertyFilePath = 
144
					BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
145
				SortedProperties skinBackupProperties = 
146
					new SortedProperties(backupPropertyFilePath);
147
				skinBackupProperties.load();
148
				skinBackupPropertiesMap.put(skinName, skinBackupProperties);
149
			}
150
		} catch (TransformerException te) {
151
			throw new GeneralPropertyException(te.getMessage());
152
		}
153
	}
154

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

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

    
228
	/**
229
	 * Get the main backup properties file. These are configurable properties that
230
	 * are stored outside the metacat install directories so the user does not
231
	 * need to re-enter all the configuration information every time they do an
232
	 * upgrade.
233
	 * 
234
	 * @return a SortedProperties object with the backup properties
235
	 */
236
	public static HashMap<String, SortedProperties> getBackupProperties() {
237
		return skinBackupPropertiesMap;
238
	}
239
	
240
	/**
241
	 * Get the main backup properties file. These are configurable properties that
242
	 * are stored outside the metacat install directories so the user does not
243
	 * need to re-enter all the configuration information every time they do an
244
	 * upgrade.
245
	 * 
246
	 * @return a SortedProperties object with the backup properties
247
	 */
248
	public static SortedProperties getBackupProperties(String skinName) {
249
		return skinBackupPropertiesMap.get(skinName);
250
	}
251
	
252
	/**
253
	 * Get the main properties metadata. This is retrieved from an xml file that
254
	 * describes the attributes of configurable properties.
255
	 * 
256
	 * @return a PropertiesMetaData object with the main properties metadata
257
	 */
258
	public static HashMap<String, PropertiesMetaData> getMetaData() {
259
		return skinMetaDataMap;
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 PropertiesMetaData getMetaData(String skinName) {
269
		return skinMetaDataMap.get(skinName);
270
	}
271

    
272
	/**
273
	 * Utility method to set a property value both in memory and to the
274
	 * properties file
275
	 * 
276
	 * @param propertyName
277
	 *            the name of the property requested
278
	 * @param newValue
279
	 *            the new value for the property
280
	 */
281
	public static void setProperty(String skinName, String propertyName, String newValue) throws IOException, GeneralPropertyException {
282
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
283
		if (skinProperties == null) {
284
			throw new GeneralPropertyException("There is not property map for " + skinName);
285
		}
286
		skinProperties.setProperty(propertyName, newValue);
287
		skinProperties.store();
288

    
289
	}
290

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

    
311
	/**
312
	 * Save the properties to a properties file. Note, the 
313
	 * order and comments will be preserved.
314
	 */
315
	public static void persistProperties(String skinName) throws IOException, GeneralPropertyException {
316
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
317
		if (skinProperties == null) {
318
			throw new GeneralPropertyException("There is not property map for " + skinName);
319
		}
320
		skinProperties.store();
321
	}
322

    
323
	/**
324
	 * Save the properties to a properties file. Note, the order and comments
325
	 * will be preserved.
326
	 */
327
	public static void persistAllProperties() throws IOException,
328
			GeneralPropertyException {
329
		for (String skinName : skinNames) {
330
			persistProperties(skinName);
331
		}
332
	}
333
	
334
	/**
335
	 * Writes out backup configurable properties to a file.
336
	 */
337
	public static void persistBackupProperties(String skinName, ServletContext servletContext)
338
			throws GeneralPropertyException {
339
		
340
		String metaDataFilePath = servletContext.getRealPath(SKIN_DIR) + FileUtil.getFS()
341
				+ skinName + FileUtil.getFS() + skinName + ".properties.metadata.xml";
342
		
343
		String backupPropertyFilePath = 
344
			BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
345

    
346
		// Use the metadata to extract configurable properties from the 
347
		// overall properties list, and store those properties.
348
		try {
349
			SortedProperties backupProperties = 
350
				new SortedProperties(backupPropertyFilePath);
351
			
352
			// Populate the backup properties for main metacat properties using
353
			// the associated metadata file
354
			PropertiesMetaData mainMetadata = new PropertiesMetaData(metaDataFilePath);
355
			Set<String> mainKeySet = mainMetadata.getKeys();
356
			for (String propertyKey : mainKeySet) {
357
				backupProperties.addProperty(propertyKey, getProperty(skinName, propertyKey));
358
			}
359
			
360
			// store the properties to file
361
			backupProperties.store();
362

    
363
		} catch (TransformerException te) {
364
			throw new GeneralPropertyException("Could not transform backup properties xml: "
365
					+ te.getMessage());
366
		} catch (IOException ioe) {
367
			throw new GeneralPropertyException("Could not backup configurable properties: "
368
					+ ioe.getMessage());
369
		}
370
	}
371

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

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

    
489
		bypassAlreadyChecked = true;
490
		return bypass;
491
	}
492

    
493
}
(7-7/9)