Project

General

Profile

1 4080 daigle
/**
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$'
10
 *     '$Date$'
11
 * '$Revision$'
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 4442 daigle
public class SkinPropertyService extends BaseService {
53 4080 daigle
54
	private static SkinPropertyService skinService = null;
55
56 4154 daigle
	private static boolean bypassAlreadyChecked = false;
57
58 4080 daigle
	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 4429 daigle
	 * Get the single instance of SkinPropertyService.
89 4080 daigle
	 *
90
	 * @param servletContext the context we will use to get relative paths
91 4429 daigle
	 * @return the single instance of SkinPropertyService
92 4080 daigle
	 */
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 4442 daigle
	public boolean refreshable() {
101
		return false;
102
	}
103
104
	protected void doRefresh() {
105
		return;
106
	}
107
108 4080 daigle
	/**
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 4108 daigle
				String backupPropertyFilePath =
144 4321 daigle
					BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
145 4108 daigle
				SortedProperties skinBackupProperties =
146
					new SortedProperties(backupPropertyFilePath);
147 4080 daigle
				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 4108 daigle
	 * @return Vector of property names
195 4080 daigle
	 */
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 4108 daigle
		String backupPropertyFilePath =
344 4321 daigle
			BACKUP_DIR + FileUtil.getFS() + skinName + ".properties.backup";
345 4080 daigle
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 4108 daigle
				backupProperties.addProperty(propertyKey, getProperty(skinName, propertyKey));
358 4080 daigle
			}
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 4108 daigle
	public static void checkAndSetProperty(HttpServletRequest request, String skinName, String propertyName)
413 4080 daigle
			throws GeneralPropertyException {
414 4108 daigle
		String newValue = request.getParameter(skinName + "." + propertyName);
415 4321 daigle
		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 4080 daigle
			SkinPropertyService.setPropertyNoPersist(skinName, propertyName, newValue);
435
		}
436
	}
437 4154 daigle
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 4080 daigle
470 4154 daigle
			// 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 4080 daigle
}