Project

General

Profile

1 4080 daigle
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements properties methods for metacat
4
 *  Copyright: 2008 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Michael Daigle
7
 *
8
 *   '$Author$'
9
 *     '$Date$'
10
 * '$Revision$'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26
27
package edu.ucsb.nceas.metacat.service;
28
29
import java.io.IOException;
30 4107 daigle
import java.util.Map;
31 4080 daigle
import java.util.Set;
32
import java.util.Vector;
33
34
import javax.servlet.ServletContext;
35
import javax.servlet.http.HttpServletRequest;
36
import javax.xml.transform.TransformerException;
37
38
import org.apache.log4j.Logger;
39
40 4761 daigle
import edu.ucsb.nceas.metacat.util.SystemUtil;
41 4080 daigle
import edu.ucsb.nceas.metacat.util.UtilException;
42
import edu.ucsb.nceas.utilities.FileUtil;
43
import edu.ucsb.nceas.utilities.GeneralPropertyException;
44 4766 daigle
import edu.ucsb.nceas.utilities.MetaDataProperty;
45 4080 daigle
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 metadata configuration utility
51
 */
52 4442 daigle
public class PropertyService extends BaseService {
53 4080 daigle
54
	private static PropertyService propertyService = null;
55
56
	// system is configured
57
	public static final String CONFIGURED = "true";
58
	// system has never been configured
59
	public static final String UNCONFIGURED = "false";
60 4206 daigle
	public static final String BYPASSED = "bypassed";
61 4080 daigle
62
	private static final String MAIN_CONFIG_NAME = "metacat.properties";
63 4589 daigle
	private static final String AUTH_CONFIG_NAME = "auth.properties";
64 4080 daigle
65 4154 daigle
	private static boolean bypassAlreadyChecked = false;
66
67 4080 daigle
	private static String mainPropertiesFilePath = null;
68
	private static SortedProperties mainProperties = null;
69
70
	private static String mainBackupPropertiesFilePath = null;
71
	private static SortedProperties mainBackupProperties = null;
72
73
	private static String propertiesMetaDataFilePath = null;
74
	private static PropertiesMetaData mainMetaData = null;
75
76 4589 daigle
	private static String authBackupPropertiesFilePath = null;
77
	private static SortedProperties authBackupProperties = null;
78 4159 daigle
79 4589 daigle
	private static String authMetaDataFilePath = null;
80
	private static PropertiesMetaData authMetaData = null;
81 4159 daigle
82 4719 daigle
	private static boolean testInstance = false;
83
	private static String testConfigDir = null;
84 4159 daigle
85 4080 daigle
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
86
87
	/**
88
	 * private constructor since this is a singleton
89
	 *
90
	 * @param servletContext the context we will use to get relative paths
91
	 */
92 4711 daigle
	private PropertyService() throws ServiceException {
93 4080 daigle
		try {
94 4711 daigle
			initialize();
95 4080 daigle
		} catch (GeneralPropertyException gpe) {
96 4124 daigle
			throw new ServiceException("Properties problem while initializing " +
97
					"PropertyService: " + gpe.getMessage());
98 4080 daigle
		} catch (IOException ioe) {
99 4124 daigle
			throw new ServiceException("I/O Problem while initializing " +
100
					"PropertyService: " + ioe.getMessage());
101 4080 daigle
		}
102
	}
103
104
	/**
105 4429 daigle
	 * Get the single instance of PropertyService.
106 4080 daigle
	 *
107 4429 daigle
	 * @return the single instance of PropertyService
108 4080 daigle
	 */
109 4711 daigle
	public static PropertyService getInstance() throws ServiceException {
110 4080 daigle
		if (propertyService == null) {
111 4711 daigle
112
			propertyService = new PropertyService();
113 4080 daigle
		}
114
		return propertyService;
115
	}
116
117 4719 daigle
	/**
118
	 * Get the single instance of PropertyService for test purposes.  In this
119
	 * case, we allow the configuration directory to be passed in.
120
	 *
121
	 * @param configDir the configuration directory we need to look in
122
	 * @return the single instance of PropertyService
123
	 */
124
	public static PropertyService getTestInstance(String configDir) throws ServiceException {
125
		if (propertyService == null) {
126
127
			testInstance = true;
128
			testConfigDir = configDir;
129
130
			propertyService = new PropertyService();
131
		}
132
		return propertyService;
133
	}
134
135 4442 daigle
	public boolean refreshable() {
136 4711 daigle
		return true;
137 4442 daigle
	}
138
139 4711 daigle
	protected void doRefresh() throws ServiceException {
140
		try {
141
			initialize();
142
		} catch (IOException ioe) {
143
			throw new ServiceException("Could not refresh SkinPropertyService due to"
144
					+ " I/O error: " + ioe.getMessage());
145
		} catch (GeneralPropertyException gpe) {
146
			throw new ServiceException("Could not refresh SkinPropertyService due to"
147
					+ " property error: " + gpe.getMessage());
148
		}
149 4442 daigle
	}
150
151 4080 daigle
	/**
152
	 * Initialize the singleton.
153
	 *
154
	 * @param servletContext the context we will use to get relative paths
155
	 */
156 4711 daigle
	private void initialize()
157
			throws IOException, GeneralPropertyException, ServiceException {
158 4080 daigle
159
		logMetacat.debug("Initializing PropertyService");
160 4711 daigle
161 4719 daigle
		String configDir;
162
		if (testInstance) {
163
			configDir = testConfigDir;
164
		} else {
165
			configDir = ServiceService.getRealConfigDir();
166
		}
167 4080 daigle
168
		// mainProperties will hold the primary configuration values for metacat.
169 4124 daigle
		mainPropertiesFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME;
170 4711 daigle
		mainProperties = new SortedProperties(mainPropertiesFilePath);
171
		mainProperties.load();
172 4080 daigle
173
		try {
174
			// mainMetaData holds configuration information about main properties.
175
			// This is primarily used to display input fields on the configuration
176
			// page. The information is retrieved from an xml metadata file
177 4124 daigle
			propertiesMetaDataFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME + ".metadata.xml";
178 4711 daigle
			mainMetaData = new PropertiesMetaData(propertiesMetaDataFilePath);
179 4080 daigle
180 4589 daigle
			// authMetaData holds configuration information about organization level
181 4159 daigle
			// properties.  This is primarily used to display input fields on
182 4589 daigle
			// the auth configuration page. The information is retrieved
183
			// from an xml metadata file dedicated just to auth properties.
184
			authMetaDataFilePath = configDir + FileUtil.getFS() + AUTH_CONFIG_NAME + ".metadata.xml";
185 4711 daigle
			authMetaData = new PropertiesMetaData(authMetaDataFilePath);
186 4080 daigle
		} catch (TransformerException te) {
187
			throw new GeneralPropertyException(te.getMessage());
188
		}
189
190 4761 daigle
		String backupBasePath = getProperty("application.backupDir");
191 4080 daigle
192 4761 daigle
		// If the backupBasePath does not exist in metacat.properties, see if we can discover it.
193
		if (backupBasePath == null || backupBasePath.equals("")) {
194
			backupBasePath = SystemUtil.discoverExternalBaseDir();
195
		}
196
197
		// if backupBasePath is still null, no reason to initialize the backup properties. The
198
		// system will need to prompt the user for the backup properties and reinitialize
199
		// PropertyService.
200
		if (backupBasePath != null && !backupBasePath.equals("")) {
201
			setProperty("application.backupDir", backupBasePath);
202
203
			String backupDirPath = backupBasePath + FileUtil.getFS() + ".metacat";
204
205
			// The mainBackupProperties hold properties that were backed up the
206
			// last time the application was configured. On disk, the file will
207
			// look like a smaller version of metacat.properties. It is stored
208
			// in the data storage directory outside the application
209
			// directories.
210
			mainBackupPropertiesFilePath =
211
				backupDirPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
212
			mainBackupProperties = new SortedProperties(mainBackupPropertiesFilePath);
213
			mainBackupProperties.load();
214
215
			// The authBackupProperties hold properties that were backed up the
216
			// last time the auth was configured. On disk, the file will
217
			// look like a smaller version of metacat.properties. It is stored
218
			// in the data storage directory outside the application
219
			// directories.
220
			authBackupPropertiesFilePath =
221
				backupDirPath + FileUtil.getFS() + AUTH_CONFIG_NAME + ".backup";
222
			authBackupProperties = new SortedProperties(authBackupPropertiesFilePath);
223
			authBackupProperties.load();
224
		}
225 4080 daigle
	}
226
227
	/**
228
	 * Utility method to get a property value from the properties file
229
	 *
230
	 * @param propertyName
231
	 *            the name of the property requested
232
	 * @return the String value for the property
233
	 */
234
	public static String getProperty(String propertyName)
235
			throws PropertyNotFoundException {
236
		return mainProperties.getProperty(propertyName);
237
	}
238
239
	/**
240
     * Get a set of all property names.
241
     *
242
     * @return Set of property names
243
     */
244
    public static Vector<String> getPropertyNames() {
245
    	return mainProperties.getPropertyNames();
246
    }
247
248
249
	/**
250
	 * Get a Set of all property names that start with the groupName prefix.
251
	 *
252
	 * @param groupName
253
	 *            the prefix of the keys to search for.
254
	 * @return enumeration of property names
255
	 */
256
    public static Vector<String> getPropertyNamesByGroup(String groupName) {
257
    	return mainProperties.getPropertyNamesByGroup(groupName);
258
    }
259 4107 daigle
260
	/**
261
	 * Get a Map of all properties that start with the groupName prefix.
262
	 *
263
	 * @param groupName
264
	 *            the prefix of the keys to search for.
265
	 * @return Map of property names
266
	 */
267
    public static Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {
268
    	return mainProperties.getPropertiesByGroup(groupName);
269
    }
270 4080 daigle
271
	/**
272
	 * Utility method to set a property value both in memory and to the
273
	 * properties file
274
	 *
275
	 * @param propertyName
276
	 *            the name of the property requested
277
	 * @param newValue
278
	 *            the new value for the property
279
	 */
280 4085 daigle
	public static void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
281
			mainProperties.setProperty(propertyName, newValue);
282
			mainProperties.store();
283 4080 daigle
	}
284
285
	/**
286
	 * Utility method to set a property value in memory. This will NOT cause the
287
	 * property to be written to disk. Use this method to set multiple
288
	 * properties in a row without causing excessive I/O. You must call
289
	 * persistProperties() once you're done setting properties to have them
290
	 * written to disk.
291
	 *
292
	 * @param propertyName
293
	 *            the name of the property requested
294
	 * @param newValue
295
	 *            the new value for the property
296
	 */
297 4085 daigle
	public static void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
298 4080 daigle
		mainProperties.setPropertyNoPersist(propertyName, newValue);
299
	}
300
301
	/**
302
	 * Save the properties to a properties file. Note, the
303
	 * order and comments will be preserved.
304
	 */
305 4085 daigle
	public static void persistProperties() throws GeneralPropertyException {
306 4080 daigle
		mainProperties.store();
307
	}
308 4154 daigle
309 4080 daigle
	/**
310
	 * Get the main backup properties file. These are configurable properties that
311
	 * are stored outside the metacat install directories so the user does not
312
	 * need to re-enter all the configuration information every time they do an
313
	 * upgrade.
314
	 *
315
	 * @return a SortedProperties object with the backup properties
316
	 */
317
	public static SortedProperties getMainBackupProperties() {
318
		return mainBackupProperties;
319
	}
320
321
	/**
322 4589 daigle
	 * Get the auth backup properties file. These are configurable
323 4159 daigle
	 * properties that are stored outside the metacat install directories so
324
	 * the user does not need to re-enter all the configuration information
325
	 * every time they do an upgrade.
326
	 *
327
	 * @return a SortedProperties object with the backup properties
328
	 */
329 4589 daigle
	public static SortedProperties getAuthBackupProperties() {
330
		return authBackupProperties;
331 4159 daigle
	}
332
333
	/**
334 4080 daigle
	 * Get the main properties metadata. This is retrieved from an xml file that
335
	 * describes the attributes of configurable properties.
336
	 *
337
	 * @return a PropertiesMetaData object with the main properties metadata
338
	 */
339
	public static PropertiesMetaData getMainMetaData() {
340
		return mainMetaData;
341
	}
342
343
	/**
344 4589 daigle
	 * Get the auth properties metadata. This is retrieved from an xml
345 4159 daigle
	 * file that describes the attributes of configurable properties.
346
	 *
347
	 * @return a PropertiesMetaData object with the organization properties
348
	 *         metadata
349
	 */
350 4589 daigle
	public static PropertiesMetaData getAuthMetaData() {
351
		return authMetaData;
352 4159 daigle
	}
353
354
	/**
355 4080 daigle
	 * Writes out backup configurable properties to a file.
356
	 */
357
	public static void persistMainBackupProperties(ServletContext servletContext)
358
			throws GeneralPropertyException {
359
360
		// Use the metadata to extract configurable properties from the
361
		// overall properties list, and store those properties.
362
		try {
363
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
364
365
			// Populate the backup properties for main metacat properties using
366
			// the associated metadata file
367
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
368 4766 daigle
369
			Map<String, MetaDataProperty> mainKeyMap = mainMetadata.getProperties();
370
			Set<String> mainKeySet = mainKeyMap.keySet();
371 4080 daigle
			for (String propertyKey : mainKeySet) {
372 4766 daigle
				// don't backup passwords
373
				MetaDataProperty metaData = mainKeyMap.get(propertyKey);
374
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
375
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
376
				}
377 4080 daigle
			}
378
379
			// store the properties to file
380
			backupProperties.store();
381 4403 daigle
			mainBackupProperties =
382
				new SortedProperties(mainBackupPropertiesFilePath);
383
			mainBackupProperties.load();
384 4080 daigle
385
		} catch (TransformerException te) {
386
			throw new GeneralPropertyException("Could not transform backup properties xml: "
387
					+ te.getMessage());
388
		} catch (IOException ioe) {
389
			throw new GeneralPropertyException("Could not backup configurable properties: "
390
					+ ioe.getMessage());
391
		}
392
	}
393
394
	/**
395
	 * Writes out backup configurable properties to a file.
396
	 */
397 4589 daigle
	public static void persistAuthBackupProperties(ServletContext servletContext)
398 4159 daigle
			throws GeneralPropertyException {
399 4080 daigle
400 4159 daigle
		// Use the metadata to extract configurable properties from the
401
		// overall properties list, and store those properties.
402
		try {
403
			SortedProperties backupProperties =
404 4589 daigle
				new SortedProperties(authBackupPropertiesFilePath);
405 4159 daigle
406 4589 daigle
			// Populate the backup properties for auth properties using
407 4159 daigle
			// the associated metadata file
408 4589 daigle
			PropertiesMetaData authMetadata = new PropertiesMetaData(authMetaDataFilePath);
409 4766 daigle
410
			Map<String, MetaDataProperty> authKeyMap = authMetadata.getProperties();
411
			Set<String> authKeySet = authKeyMap.keySet();
412 4589 daigle
			for (String propertyKey : authKeySet) {
413 4766 daigle
				// don't backup passwords
414
				MetaDataProperty metaData = authKeyMap.get(propertyKey);
415
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
416
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
417
				}
418 4159 daigle
			}
419
420
			// store the properties to file
421
			backupProperties.store();
422 4589 daigle
			authBackupProperties =
423
				new SortedProperties(authBackupPropertiesFilePath);
424
			authBackupProperties.load();
425 4159 daigle
426
		} catch (TransformerException te) {
427
			throw new GeneralPropertyException("Could not transform backup properties xml: "
428
					+ te.getMessage());
429
		} catch (IOException ioe) {
430
			throw new GeneralPropertyException("Could not backup configurable properties: "
431
					+ ioe.getMessage());
432
		}
433
	}
434 4080 daigle
435
	/**
436
	 * Reports whether properties are fully configured.
437
	 *
438
	 * @return a boolean that is true if properties are not unconfigured and
439
	 *         false otherwise
440
	 */
441 4711 daigle
	public static boolean arePropertiesConfigured() throws UtilException {
442 4080 daigle
		try {
443 4711 daigle
			String propertiesConfigured = PropertyService.getProperty("configutil.propertiesConfigured");
444
			if (propertiesConfigured != null && !propertiesConfigured.equals(UNCONFIGURED)) {
445
				return true;
446
			}
447
			return false;
448 4080 daigle
		} catch (PropertyNotFoundException pnfe) {
449
			throw new UtilException("Could not determine if properties are configured: "
450
					+ pnfe.getMessage());
451
		}
452
	}
453
454
	/**
455 4711 daigle
	 * Determine if the system is configured to bypass configuration. If so, the
456
	 * system will look for backup configuration files at startup time and use
457
	 * those to configure metacat. The bypass options should only be set by
458
	 * developers. Production code should never bypass confguration.
459
	 *
460
	 * @return true if dev.runConfiguration is set to true in metacat.properties
461
	 *         and we have not already checked for bypass, false otherwise.
462
	 */
463
	public static boolean doBypass() throws PropertyNotFoundException {
464
		// We only want to go through the check once to see if we want to
465
		// bypass the configuration.  We don't want to run through all of
466
		// this every time  we hit metacat.
467
		if (bypassAlreadyChecked) {
468
			logMetacat.debug("bypassConfiguration not performing full bypass check.  Bypass set to false");
469
			return false;
470
		}
471
472
		// check how dev.runConfiguration is set in metacat.properties
473
		String strRunConfiguration = PropertyService.getProperty("dev.runConfiguration");
474
		boolean runConfiguration = Boolean.parseBoolean(strRunConfiguration);
475
		logMetacat.debug("bypassConfiguration: dev.runConfiguration property set to: " + strRunConfiguration);
476
477
		// if the dev.runConfiguration is true, return false here.
478
		if (runConfiguration) {
479
			bypassAlreadyChecked = true;
480
			return false;
481
		}
482
483
		return true;
484
	}
485
486
	/**
487 4154 daigle
	 * Reports whether the metacat configuration utility should be run.
488
	 * Returns false if
489
	 *   -- dev.runConfiguration=false and
490
	 *   -- backup properties file exists
491
	 * Note that dev.runConfiguration should only be set to false when
492
	 * reinstalling the same version of the application in developement.
493
	 *
494
	 * @return a boolean that is false if dev.runConfiguration is false and
495
	 * the backup properties file exists.
496
	 */
497 4711 daigle
	public static void bypassConfiguration() throws ServiceException {
498 4154 daigle
		try {
499 4711 daigle
			boolean doBypass = doBypass();
500 4154 daigle
501 4711 daigle
			if (!doBypass) {
502
				throw new ServiceException(
503
						"Attempting to do bypass when system is not configured for it.");
504
			}
505
506 4154 daigle
			// 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 4408 daigle
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
510 4154 daigle
			SortedProperties mainBackupProperties = getMainBackupProperties();
511
			Vector<String> backupPropertyNames =
512
				mainBackupProperties.getPropertyNames();
513
			for (String backupPropertyName : backupPropertyNames) {
514
				String value = mainBackupProperties.getProperty(backupPropertyName);
515
				setPropertyNoPersist(backupPropertyName, value);
516
			}
517 4408 daigle
518 4589 daigle
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
519
			SortedProperties authBackupProperties = getAuthBackupProperties();
520
			Vector<String> authBackupPropertyNames =
521
				authBackupProperties.getPropertyNames();
522
			for (String authBackupPropertyName : authBackupPropertyNames) {
523
				String value = authBackupProperties.getProperty(authBackupPropertyName);
524
				setPropertyNoPersist(authBackupPropertyName, value);
525 4159 daigle
			}
526 4408 daigle
527
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
528 4154 daigle
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
529 4589 daigle
			setPropertyNoPersist("configutil.authConfigured", "true");
530 4154 daigle
			setPropertyNoPersist("configutil.skinsConfigured", "true");
531
			setPropertyNoPersist("configutil.databaseConfigured", "true");
532 4340 daigle
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
533 4154 daigle
534
			persistProperties();
535
536
		} catch (PropertyNotFoundException pnfe) {
537 4408 daigle
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
538 4154 daigle
		} catch (GeneralPropertyException gpe) {
539 4408 daigle
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
540 4154 daigle
		}
541
542
		bypassAlreadyChecked = true;
543
	}
544
545
	/**
546 4080 daigle
	 * Take input from the user in an HTTP request about an property to be changed
547
	 * and update the metacat property file with that new value if it has
548
	 * changed from the value that was originally set.
549
	 *
550
	 * @param request
551
	 *            that was generated by the user
552
	 * @param response
553
	 *            to send output back to the user
554
	 * @param propertyName
555
	 *            the name of the property to be checked and set
556
	 */
557 4107 daigle
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName)
558 4080 daigle
			throws GeneralPropertyException {
559
		String value = PropertyService.getProperty(propertyName);
560
		String newValue = request.getParameter(propertyName);
561 4548 daigle
		if (newValue != null && !newValue.trim().equals(value)) {
562
			PropertyService.setPropertyNoPersist(propertyName, newValue.trim());
563 4080 daigle
		}
564
	}
565
566
}