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: 2009-03-23 14:56:56 -0700 (Mon, 23 Mar 2009) $'
11
 * '$Revision: 4854 $'
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.Map;
33
import java.util.Set;
34
import java.util.Vector;
35

    
36

    
37
import javax.servlet.http.HttpServletRequest;
38
import javax.xml.transform.TransformerException;
39

    
40
import org.apache.log4j.Logger;
41

    
42
import edu.ucsb.nceas.metacat.util.SkinUtil;
43
import edu.ucsb.nceas.metacat.util.MetacatUtilException;
44
import edu.ucsb.nceas.utilities.FileUtil;
45
import edu.ucsb.nceas.utilities.GeneralPropertyException;
46
import edu.ucsb.nceas.utilities.MetaDataProperty;
47
import edu.ucsb.nceas.utilities.PropertiesMetaData;
48
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
49
import edu.ucsb.nceas.utilities.SortedProperties;
50

    
51
/**
52
 * A suite of utility classes for the skin configuration utility
53
 */
54
public class SkinPropertyService extends BaseService {
55

    
56
	private static SkinPropertyService skinService = null;
57

    
58
	private static boolean bypassAlreadyChecked = false;
59

    
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
74
	 *            the context we will use to get relative paths
75
	 */
76
	private SkinPropertyService() throws ServiceException {
77
		try {
78
			initialize();
79
		} catch (GeneralPropertyException gpe) {
80
			throw new ServiceException(
81
					"Properties problem while initializing SkinPropertyService: "
82
							+ gpe.getMessage());
83
		} catch (IOException ioe) {
84
			throw new ServiceException("I/O Problem while initializing SkinPropertyService: "
85
					+ ioe.getMessage());
86
		}
87
	}
88

    
89
	/**
90
	 * Get the single instance of SkinPropertyService.
91
	 * 
92
	 * @param servletContext
93
	 *            the context we will use to get relative paths
94
	 * @return the single instance of SkinPropertyService
95
	 */
96
	public static SkinPropertyService getInstance() throws ServiceException {
97
		if (skinService == null) {
98
			skinService = new SkinPropertyService();
99
		}
100
		return skinService;
101
	}
102

    
103
	public boolean refreshable() {
104
		return true;
105
	}
106

    
107
	protected void doRefresh() throws ServiceException {
108
		try {
109
			initialize();
110
		} catch (IOException ioe) {
111
			throw new ServiceException("Could not refresh SkinPropertyService due to"
112
					+ " I/O error: " + ioe.getMessage());
113
		} catch (GeneralPropertyException gpe) {
114
			throw new ServiceException("Could not refresh SkinPropertyService due to"
115
					+ " property error: " + gpe.getMessage());
116
		}
117
	}
118

    
119
	/**
120
	 * Initialize the singleton.
121
	 * 
122
	 * @param servletContext
123
	 *            the context we will use to get relative paths
124
	 */
125
	private void initialize() throws IOException, GeneralPropertyException,
126
			ServiceException {
127

    
128
		logMetacat.debug("Initializing SkinService");
129

    
130
		BACKUP_DIR = PropertyService.getProperty("application.backupDir");
131

    
132
		skinNames = SkinUtil.getSkinNames();
133

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

    
138
		try {
139
			for (String skinName : skinNames) {
140
				String propertyFilePath = ServiceService.getRealSkinDir()
141
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
142
						+ ".properties";
143

    
144
				if (FileUtil.getFileStatus(propertyFilePath) < FileUtil.EXISTS_READ_WRITABLE) {
145
					logMetacat.error("Skin property file: " + propertyFilePath
146
							+ " does not exist read/writable. This skin will not be available.");
147
					continue;
148
				}
149

    
150
				SortedProperties skinProperties = new SortedProperties(propertyFilePath);
151
				skinProperties.load();
152
				skinPropertiesMap.put(skinName, skinProperties);
153

    
154
				String metaDataFilePath = ServiceService.getRealSkinDir()
155
						+ FileUtil.getFS() + skinName + FileUtil.getFS() + skinName
156
						+ ".properties.metadata.xml";
157
				if (FileUtil.getFileStatus(metaDataFilePath) > FileUtil.DOES_NOT_EXIST) {
158
					PropertiesMetaData skinMetaData = new PropertiesMetaData(metaDataFilePath);
159
					skinMetaDataMap.put(skinName, skinMetaData);
160
				} else {
161
					skinPropertiesMap.remove(skinName);
162
					logMetacat.error("Could not find skin property metadata file for skin: " 
163
							+ skinName + " at: " + metaDataFilePath  
164
							+ ". This skin will not be available.");
165
					continue;
166
				}
167

    
168
				String backupPropertyFilePath = BACKUP_DIR + FileUtil.getFS() + skinName
169
						+ ".properties.backup";
170
				if (FileUtil.getFileStatus(backupPropertyFilePath) > FileUtil.DOES_NOT_EXIST) {
171
					SortedProperties skinBackupProperties = new SortedProperties(
172
							backupPropertyFilePath);
173
					skinBackupProperties.load();
174
					skinBackupPropertiesMap.put(skinName, skinBackupProperties);
175
				} else {
176
					logMetacat.warn("Could not find backup properties for skin: "
177
							+ skinName + " at: " + backupPropertyFilePath);
178
				}
179
			}
180
		} catch (TransformerException te) {
181
			throw new GeneralPropertyException(te.getMessage());
182
		}
183
	}
184

    
185
	/**
186
	 * Utility method to get a property value from the properties file for a
187
	 * specific skin.
188
	 * 
189
	 * @param skinName
190
	 *            the skin for which we want to retrieve the property
191
	 * @param propertyName
192
	 *            the name of the property requested
193
	 * @return the String value for the property
194
	 */
195
	public static String getProperty(String skinName, String propertyName)
196
			throws PropertyNotFoundException {
197
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
198
		if (skinProperties == null) {
199
			throw new PropertyNotFoundException("There is not property map for "
200
					+ skinName);
201
		}
202
		return skinProperties.getProperty(propertyName);
203
	}
204

    
205
	/**
206
	 * Get a set of all property names for a given skin.
207
	 * 
208
	 * @param skinName
209
	 *            the skin for which we want to retrieve the property names
210
	 * @return Set of property names
211
	 */
212
	public static Vector<String> getPropertyNames(String skinName)
213
			throws PropertyNotFoundException {
214
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
215
		if (skinProperties == null) {
216
			throw new PropertyNotFoundException("There is not property map for "
217
					+ skinName);
218
		}
219
		return skinProperties.getPropertyNames();
220
	}
221

    
222
	/**
223
	 * Get a Set of all property names that start with the groupName prefix.
224
	 * 
225
	 * @param groupName
226
	 *            the prefix of the keys to search for.
227
	 * @return Vector of property names
228
	 */
229
	public static Vector<String> getPropertyNamesByGroup(String skinName, String groupName)
230
			throws PropertyNotFoundException {
231
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
232
		if (skinProperties == null) {
233
			throw new PropertyNotFoundException("There is not property map for "
234
					+ skinName);
235
		}
236
		return skinProperties.getPropertyNamesByGroup(groupName);
237
	}
238

    
239
	/**
240
	 * Get the main backup properties file. These are configurable properties
241
	 * that are stored outside the metacat install directories so the user does
242
	 * not need to re-enter all the configuration information every time they do
243
	 * an upgrade.
244
	 * 
245
	 * @return a SortedProperties object with the backup properties
246
	 */
247
	public static HashMap<String, SortedProperties> getProperties() {
248
		return skinPropertiesMap;
249
	}
250

    
251
	/**
252
	 * Get the main backup properties file. These are configurable properties
253
	 * that are stored outside the metacat install directories so the user does
254
	 * not need to re-enter all the configuration information every time they do
255
	 * an upgrade.
256
	 * 
257
	 * @return a SortedProperties object with the backup properties
258
	 */
259
	public static SortedProperties getProperties(String skinName) {
260
		return skinPropertiesMap.get(skinName);
261
	}
262

    
263
	/**
264
	 * Get the main backup properties file. These are configurable properties
265
	 * that are stored outside the metacat install directories so the user does
266
	 * not need to re-enter all the configuration information every time they do
267
	 * an upgrade.
268
	 * 
269
	 * @return a SortedProperties object with the backup properties
270
	 */
271
	public static HashMap<String, SortedProperties> getBackupProperties() {
272
		return skinBackupPropertiesMap;
273
	}
274

    
275
	/**
276
	 * Get the main backup properties file. These are configurable properties
277
	 * that are stored outside the metacat install directories so the user does
278
	 * not need to re-enter all the configuration information every time they do
279
	 * an upgrade.
280
	 * 
281
	 * @return a SortedProperties object with the backup properties
282
	 */
283
	public static SortedProperties getBackupProperties(String skinName) {
284
		return skinBackupPropertiesMap.get(skinName);
285
	}
286

    
287
	/**
288
	 * Get the main properties metadata. This is retrieved from an xml file that
289
	 * describes the attributes of configurable properties.
290
	 * 
291
	 * @return a PropertiesMetaData object with the main properties metadata
292
	 */
293
	public static HashMap<String, PropertiesMetaData> getMetaData() {
294
		return skinMetaDataMap;
295
	}
296

    
297
	/**
298
	 * Get the main properties metadata. This is retrieved from an xml file that
299
	 * describes the attributes of configurable properties.
300
	 * 
301
	 * @return a PropertiesMetaData object with the main properties metadata
302
	 */
303
	public static PropertiesMetaData getMetaData(String skinName) {
304
		return skinMetaDataMap.get(skinName);
305
	}
306

    
307
	/**
308
	 * Utility method to set a property value both in memory and to the
309
	 * properties file
310
	 * 
311
	 * @param propertyName
312
	 *            the name of the property requested
313
	 * @param newValue
314
	 *            the new value for the property
315
	 */
316
	public static void setProperty(String skinName, String propertyName, String newValue)
317
			throws IOException, GeneralPropertyException {
318
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
319
		if (skinProperties == null) {
320
			throw new GeneralPropertyException("There is not property map for "
321
					+ skinName);
322
		}
323
		skinProperties.setProperty(propertyName, newValue);
324
		skinProperties.store();
325

    
326
	}
327

    
328
	/**
329
	 * Utility method to set a property value in memory. This will NOT cause the
330
	 * property to be written to disk. Use this method to set multiple
331
	 * properties in a row without causing excessive I/O. You must call
332
	 * persistProperties() once you're done setting properties to have them
333
	 * written to disk.
334
	 * 
335
	 * @param propertyName
336
	 *            the name of the property requested
337
	 * @param newValue
338
	 *            the new value for the property
339
	 */
340
	public static void setPropertyNoPersist(String skinName, String propertyName,
341
			String newValue) throws GeneralPropertyException {
342
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
343
		if (skinProperties == null) {
344
			throw new GeneralPropertyException("There is not property map for "
345
					+ skinName);
346
		}
347
		skinProperties.setPropertyNoPersist(propertyName, newValue);
348
	}
349

    
350
	/**
351
	 * Save the properties to a properties file. Note, the order and comments
352
	 * will be preserved.
353
	 */
354
	public static void persistProperties(String skinName) throws IOException,
355
			GeneralPropertyException {
356
		SortedProperties skinProperties = skinPropertiesMap.get(skinName);
357
		if (skinProperties == null) {
358
			throw new GeneralPropertyException("There is not property map for "
359
					+ skinName);
360
		}
361
		skinProperties.store();
362
	}
363

    
364
	/**
365
	 * Save the properties to a properties file. Note, the order and comments
366
	 * will be preserved.
367
	 */
368
	public static void persistAllProperties() throws IOException,
369
			GeneralPropertyException {
370
		for (String skinName : skinNames) {
371
			persistProperties(skinName);
372
		}
373
	}
374

    
375
	/**
376
	 * Writes out backup configurable properties to a file.
377
	 */
378
	public static void persistBackupProperties(String skinName)
379
			throws GeneralPropertyException {
380
		try {
381
			String metaDataFilePath = ServiceService.getRealSkinDir() + FileUtil.getFS()
382
					+ skinName + FileUtil.getFS() + skinName + ".properties.metadata.xml";
383

    
384
			String backupPropertyFilePath = BACKUP_DIR + FileUtil.getFS() + skinName
385
					+ ".properties.backup";
386

    
387
			// Use the metadata to extract configurable properties from the
388
			// overall properties list, and store those properties.
389
			SortedProperties backupProperties = new SortedProperties(
390
					backupPropertyFilePath);
391

    
392
			// Populate the backup properties for main metacat properties using
393
			// the associated metadata file
394
			PropertiesMetaData skinMetadata = new PropertiesMetaData(metaDataFilePath);
395
			
396
			Map<String, MetaDataProperty> skinKeyMap = skinMetadata.getProperties();
397
			Set<String> skinKeySet = skinKeyMap.keySet();
398
			for (String propertyKey : skinKeySet) {
399
				// don't backup passwords
400
				MetaDataProperty metaData = skinKeyMap.get(propertyKey);
401
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
402
					backupProperties.addProperty(propertyKey, getProperty(skinName, propertyKey));
403
				}
404
			}			
405

    
406
			// store the properties to file
407
			backupProperties.store();
408

    
409
		} catch (TransformerException te) {
410
			throw new GeneralPropertyException(
411
					"Could not transform backup properties xml: " + te.getMessage());
412
		} catch (IOException ioe) {
413
			throw new GeneralPropertyException(
414
					"Could not backup configurable properties: " + ioe.getMessage());
415
		} catch (ServiceException se) {
416
			throw new GeneralPropertyException("Could not get skins property file: "
417
					+ se.getMessage());
418
		}
419
	}
420

    
421
	/**
422
	 * Reports whether properties are fully configured.
423
	 * 
424
	 * @return a boolean that is true if properties are not unconfigured and
425
	 *         false otherwise
426
	 */
427
	public static boolean areSkinsConfigured() throws MetacatUtilException {
428
		try {
429
			return !PropertyService.getProperty("configutil.skinsConfigured").equals(
430
					PropertyService.UNCONFIGURED);
431
		} catch (PropertyNotFoundException pnfe) {
432
			throw new MetacatUtilException("Could not determine if skins are configured: "
433
					+ pnfe.getMessage());
434
		}
435
	}
436

    
437
	/**
438
	 * Take input from the user in an HTTP request about an property to be
439
	 * changed and update the metacat property file with that new value if it
440
	 * has changed from the value that was originally set.
441
	 * 
442
	 * @param request
443
	 *            that was generated by the user
444
	 * @param response
445
	 *            to send output back to the user
446
	 * @param propertyName
447
	 *            the name of the property to be checked and set
448
	 */
449
	public static void checkAndSetProperty(HttpServletRequest request, String skinName,
450
			String propertyName) throws GeneralPropertyException {
451
		String newValue = request.getParameter(skinName + "." + propertyName);
452
		checkAndSetProperty(newValue, skinName, propertyName);
453
	}
454

    
455
	/**
456
	 * Check user input against existing value and update the metacat property
457
	 * file with that new value if it has changed from the value that was
458
	 * originally set.
459
	 * 
460
	 * @param newValue
461
	 *            the value that was returned by the form
462
	 * @param skinname
463
	 *            the skin that we are checking
464
	 * @param propertyName
465
	 *            the name of the property to be checked and set
466
	 */
467
	public static void checkAndSetProperty(String newValue, String skinName,
468
			String propertyName) throws GeneralPropertyException {
469
		String oldValue = SkinPropertyService.getProperty(skinName, propertyName);
470
		if (newValue != null && !newValue.equals(oldValue)) {
471
			SkinPropertyService.setPropertyNoPersist(skinName, propertyName, newValue);
472
		}
473
	}
474

    
475
	/**
476
	 * Reports whether the metacat configuration utility should be run. Returns
477
	 * false if -- dev.runConfiguration=false and -- backup properties file
478
	 * exists Note that dev.runConfiguration should only be set to false when
479
	 * reinstalling the same version of the application in developement.
480
	 * 
481
	 * @return a boolean that is false if dev.runConfiguration is false and the
482
	 *         backup properties file exists.
483
	 */
484
	public static boolean bypassConfiguration() {
485
		boolean bypass = false;
486

    
487
		// We only want to go through the check once to see if we want to
488
		// bypass the configuration. We don't want to run through all of
489
		// this every time we hit metacat.
490
		if (bypassAlreadyChecked) {
491
			return bypass;
492
		}
493

    
494
		try {
495
			// check how dev.runConfiguration is set in metacat.properties
496
			String strRunConfiguration = PropertyService
497
					.getProperty("dev.runConfiguration");
498
			bypass = !(Boolean.parseBoolean(strRunConfiguration));
499

    
500
			// if the deb.runConfiguration is true, return false here.
501
			if (!bypass) {
502
				bypassAlreadyChecked = true;
503
				return false;
504
			}
505

    
506
			// the system is bypassing the configuration utility. We need to
507
			// get the backup properties and replace existing properties with
508
			// backup values. We do this for main and org properties.
509
			for (String skinName : skinNames) {
510
				SortedProperties backupProperties = getBackupProperties(skinName);
511
				Vector<String> backupPropertyNames = backupProperties.getPropertyNames();
512
				for (String backupPropertyName : backupPropertyNames) {
513
					String value = backupProperties.getProperty(backupPropertyName);
514
					backupProperties.setPropertyNoPersist(backupPropertyName, value);
515
				}
516
				backupProperties.store();
517
			}
518
		} catch (PropertyNotFoundException pnfe) {
519
			logMetacat.error("Could not find property: " + pnfe.getMessage());
520
		} catch (GeneralPropertyException gpe) {
521
			logMetacat.error("General property error: " + gpe.getMessage());
522
		}
523

    
524
		bypassAlreadyChecked = true;
525
		return bypass;
526
	}
527

    
528
}
(7-7/9)