Project

General

Profile

1
/**
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: daigle $'
9
 *     '$Date: 2009-08-14 17:38:05 -0700 (Fri, 14 Aug 2009) $'
10
 * '$Revision: 5028 $'
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.properties;
28

    
29
import java.io.IOException;
30
import java.util.Map;
31
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
import edu.ucsb.nceas.metacat.service.ServiceService;
41
import edu.ucsb.nceas.metacat.shared.BaseService;
42
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
43
import edu.ucsb.nceas.metacat.shared.ServiceException;
44
import edu.ucsb.nceas.metacat.util.SystemUtil;
45
import edu.ucsb.nceas.utilities.FileUtil;
46
import edu.ucsb.nceas.utilities.GeneralPropertyException;
47
import edu.ucsb.nceas.utilities.MetaDataProperty;
48
import edu.ucsb.nceas.utilities.PropertiesMetaData;
49
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
50
import edu.ucsb.nceas.utilities.SortedProperties;
51

    
52
/**
53
 * A suite of utility classes for the metadata configuration utility
54
 */
55
public class ConfigurableProperties extends BaseService implements PropertiesInterface {
56
	
57
	private static final String MAIN_CONFIG_FILE_NAME = "metacat.properties";
58
	private static String mainConfigFilePath  = null;
59
	private static SortedProperties mainProperties = null;
60
	
61
	private static final String MAIN_METADATA_FILE_NAME = "metacat.properties.metadata.xml";
62
	private static String mainMetadataFilePath = null;
63
	private static PropertiesMetaData mainMetaData = null;
64
	
65
	private static final String MAIN_BACKUP_FILE_NAME = "metacat.properties.backup";
66
	private static String mainBackupFilePath  = null;
67
	private static SortedProperties mainBackupProperties = null;
68
	
69
	private static final String AUTH_METADATA_FILE_NAME = "auth.properties.metadata.xml";
70
	private static String authMetadataFilePath = null;
71
	private static PropertiesMetaData authMetaData = null;
72
	
73
	private static final String AUTH_BACKUP_FILE_NAME = "auth.properties.backup";
74
	private static String authBackupFilePath = null;
75
	private static SortedProperties authBackupProperties = null;
76
	
77
	private static boolean bypassAlreadyChecked = false;
78
	
79
	private static Logger logMetacat = Logger.getLogger(ConfigurableProperties.class);
80

    
81
	/**
82
	 * private constructor since this is a singleton
83
	 * 
84
	 * @param servletContext the context we will use to get relative paths
85
	 */
86
	protected ConfigurableProperties() throws ServiceException {		
87
		_serviceName = "ConfigurableProperties";
88
		
89
		initialize();		
90
	}
91
	
92
	public boolean refreshable() {
93
		return true;
94
	}
95
	
96
	public void doRefresh() throws ServiceException {
97
		initialize();
98
	}
99
	
100
	public void stop() throws ServiceException {
101
		return;
102
	}
103
	
104
	/**
105
	 * Initialize the singleton.
106
	 * 
107
	 * @param servletContext the context we will use to get relative paths
108
	 */
109
	private void initialize() throws ServiceException {
110
		
111
		logMetacat.debug("Initializing ConfigurableProperties");
112
		
113
		try {
114
			mainConfigFilePath = 
115
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_CONFIG_FILE_NAME;
116
			mainMetadataFilePath = 
117
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_METADATA_FILE_NAME;
118
//			mainBackupFilePath = 
119
//				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME;
120
			authMetadataFilePath = 
121
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_METADATA_FILE_NAME;
122
//			authBackupFilePath = 
123
//				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_BACKUP_FILE_NAME;
124
			
125
			
126
			// mainProperties will hold the primary configuration values for
127
			// metacat.
128
			mainProperties = new SortedProperties(mainConfigFilePath);
129
			mainProperties.load();
130

    
131
			// mainMetaData holds configuration information about main
132
			// properties. This is primarily used to display input fields on 
133
			// the configuration page. The information is retrieved from an 
134
			// xml metadata file
135
			mainMetaData = new PropertiesMetaData(mainMetadataFilePath);
136

    
137
			// authMetaData holds configuration information about organization
138
			// level
139
			// properties. This is primarily used to display input fields on
140
			// the auth configuration page. The information is retrieved
141
			// from an xml metadata file dedicated just to auth properties.
142
			authMetaData = new PropertiesMetaData(authMetadataFilePath);
143

    
144
			String recommendedExternalDir = SystemUtil.discoverExternalDir();
145
			PropertyService.setRecommendedExternalDir(recommendedExternalDir);
146
			
147
			String backupPath = getProperty("application.backupDir");
148
			if (backupPath == null || backupPath.equals("")) {
149
				backupPath = SystemUtil.getStoredBackupDir();
150
			}
151
			if (backupPath == null || backupPath.equals("") && recommendedExternalDir != null) {
152
				backupPath = 
153
					recommendedExternalDir + FileUtil.getFS() + "." + ServiceService.getRealApplicationContext();
154
			}
155

    
156
			// if backupPath is still null, no reason to initialize the
157
			// backup properties. The system will need to prompt the user for 
158
			// the backup properties and reinitialize ConfigurableProperties.
159
			if (backupPath != null && !backupPath.equals("")) {		
160
				setProperty("application.backupDir", backupPath);
161
				SystemUtil.writeStoredBackupFile(backupPath);
162

    
163
				// The mainBackupProperties hold properties that were backed up
164
				// the last time the application was configured. On disk, the 
165
				// file will look like a smaller version of metacat.properties. 
166
				// It is stored in the data storage directory outside the 
167
				// application directories.
168
				mainBackupFilePath = backupPath + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME;
169
				mainBackupProperties = new SortedProperties(mainBackupFilePath);
170
				mainBackupProperties.load();
171

    
172
				// The authBackupProperties hold properties that were backed up
173
				// the last time the auth was configured. On disk, the file 
174
				// will look like a smaller version of metacat.properties. It 
175
				// is stored in the data storage directory outside the 
176
				// application directories.
177
				authBackupFilePath = backupPath + FileUtil.getFS() + AUTH_BACKUP_FILE_NAME;
178
				authBackupProperties = new SortedProperties(authBackupFilePath);
179
				authBackupProperties.load();
180
			}
181
		} catch (TransformerException te) {
182
			throw new ServiceException("Transform problem while loading properties: "
183
					+ te.getMessage());
184
		} catch (IOException ioe) {
185
			throw new ServiceException("I/O problem while loading properties: "
186
					+ ioe.getMessage());
187
		} catch (GeneralPropertyException gpe) {
188
			throw new ServiceException("General properties problem while loading properties: "
189
					+ gpe.getMessage());
190
		} catch (MetacatUtilException ue) {
191
			throw new ServiceException("Utilities problem while loading properties: "
192
					+ ue.getMessage());
193
		} 
194
	}
195

    
196
	/**
197
	 * Utility method to get a property value from the properties file
198
	 * 
199
	 * @param propertyName
200
	 *            the name of the property requested
201
	 * @return the String value for the property
202
	 */
203
	public String getProperty(String propertyName) throws PropertyNotFoundException {
204
		return mainProperties.getProperty(propertyName);
205
	}
206
	
207
	/**
208
     * Get a set of all property names.
209
     * 
210
     * @return Set of property names  
211
     */
212
    public Vector<String> getPropertyNames() {   	
213
    	return mainProperties.getPropertyNames();
214
    }
215
    
216

    
217
	/**
218
	 * Get a Set of all property names that start with the groupName prefix.
219
	 * 
220
	 * @param groupName
221
	 *            the prefix of the keys to search for.
222
	 * @return enumeration of property names
223
	 */
224
    public Vector<String> getPropertyNamesByGroup(String groupName) {   	
225
    	return mainProperties.getPropertyNamesByGroup(groupName);
226
    }
227
    
228
	/**
229
	 * Get a Map of all properties that start with the groupName prefix.
230
	 * 
231
	 * @param groupName
232
	 *            the prefix of the keys to search for.
233
	 * @return Map of property names
234
	 */
235
    public Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {   	
236
    	return mainProperties.getPropertiesByGroup(groupName);
237
    }
238
    
239
	/**
240
	 * Utility method to add a property value both in memory and to the
241
	 * properties file
242
	 * 
243
	 * @param propertyName
244
	 *            the name of the property to add
245
	 * @param newValue
246
	 *            the value for the property
247
	 */
248
	public void addProperty(String propertyName, String value) throws GeneralPropertyException {
249
			mainProperties.addProperty(propertyName, value);
250
			mainProperties.store();
251
	}
252

    
253
	/**
254
	 * Utility method to set a property value both in memory and to the
255
	 * properties file
256
	 * 
257
	 * @param propertyName
258
	 *            the name of the property requested
259
	 * @param newValue
260
	 *            the new value for the property
261
	 */
262
	public void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
263
			mainProperties.setProperty(propertyName, newValue);
264
			mainProperties.store();
265
	}
266

    
267
	/**
268
	 * Utility method to set a property value in memory. This will NOT cause the
269
	 * property to be written to disk. Use this method to set multiple
270
	 * properties in a row without causing excessive I/O. You must call
271
	 * persistProperties() once you're done setting properties to have them
272
	 * written to disk.
273
	 * 
274
	 * @param propertyName
275
	 *            the name of the property requested
276
	 * @param newValue
277
	 *            the new value for the property
278
	 */
279
	public void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
280
		mainProperties.setPropertyNoPersist(propertyName, newValue);
281
	}
282

    
283
	/**
284
	 * Save the properties to a properties file. Note, the 
285
	 * order and comments will be preserved.
286
	 */
287
	public void persistProperties() throws GeneralPropertyException {
288
		mainProperties.store();
289
	}
290
	
291
	/**
292
	 * Get the main backup properties file. These are configurable properties that
293
	 * are stored outside the metacat install directories so the user does not
294
	 * need to re-enter all the configuration information every time they do an
295
	 * upgrade.
296
	 * 
297
	 * @return a SortedProperties object with the backup properties
298
	 */
299
	public SortedProperties getMainBackupProperties() {
300
		return mainBackupProperties;
301
	}
302
	
303
	/**
304
	 * Get the auth backup properties file. These are configurable 
305
	 * properties that are stored outside the metacat install directories so 
306
	 * the user does not need to re-enter all the configuration information 
307
	 * every time they do an upgrade.
308
	 * 
309
	 * @return a SortedProperties object with the backup properties
310
	 */
311
	public SortedProperties getAuthBackupProperties() {
312
		return authBackupProperties;
313
	}
314
	
315
	/**
316
	 * Get the main properties metadata. This is retrieved from an xml file that
317
	 * describes the attributes of configurable properties.
318
	 * 
319
	 * @return a PropertiesMetaData object with the main properties metadata
320
	 */
321
	public PropertiesMetaData getMainMetaData() {
322
		return mainMetaData;
323
	}
324
	
325
	/**
326
	 * Get the auth properties metadata. This is retrieved from an xml
327
	 * file that describes the attributes of configurable properties.
328
	 * 
329
	 * @return a PropertiesMetaData object with the organization properties
330
	 *         metadata
331
	 */
332
	public PropertiesMetaData getAuthMetaData() {
333
		return authMetaData;
334
	}
335
	
336
	/**
337
	 * Writes out backup configurable properties to a file.
338
	 */
339
	public void persistMainBackupProperties()
340
			throws GeneralPropertyException {
341

    
342
		// Use the metadata to extract configurable properties from the 
343
		// overall properties list, and store those properties.
344
		try {
345
			SortedProperties backupProperties = new SortedProperties(mainBackupFilePath);
346
			
347
			// Populate the backup properties for main metacat properties using
348
			// the associated metadata file
349
			PropertiesMetaData mainMetadata = new PropertiesMetaData(mainMetadataFilePath);
350

    
351
			Map<String, MetaDataProperty> mainKeyMap = mainMetadata.getProperties();
352
			Set<String> mainKeySet = mainKeyMap.keySet();
353
			for (String propertyKey : mainKeySet) {
354
				// don't backup passwords
355
				MetaDataProperty metaData = mainKeyMap.get(propertyKey);
356
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
357
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
358
				}
359
			}
360
			
361
			// store the properties to file
362
			backupProperties.store();
363
			mainBackupProperties = 
364
				new SortedProperties(mainBackupFilePath);
365
			mainBackupProperties.load();
366

    
367
		} catch (TransformerException te) {
368
			throw new GeneralPropertyException("Could not transform backup properties xml: "
369
					+ te.getMessage());
370
		} catch (IOException ioe) {
371
			throw new GeneralPropertyException("Could not backup configurable properties: "
372
					+ ioe.getMessage());
373
		}
374
	}
375
	
376
	/**
377
	 * Writes out backup configurable properties to a file.
378
	 */
379
	public void persistAuthBackupProperties(ServletContext servletContext)
380
			throws GeneralPropertyException {
381

    
382
		// Use the metadata to extract configurable properties from the 
383
		// overall properties list, and store those properties.
384
		try {
385
			SortedProperties backupProperties = 
386
				new SortedProperties(authBackupFilePath);
387
			
388
			// Populate the backup properties for auth properties using
389
			// the associated metadata file
390
			PropertiesMetaData authMetadata = new PropertiesMetaData(authMetadataFilePath);
391
			
392
			Map<String, MetaDataProperty> authKeyMap = authMetadata.getProperties();
393
			Set<String> authKeySet = authKeyMap.keySet();
394
			for (String propertyKey : authKeySet) {
395
				// don't backup passwords
396
				MetaDataProperty metaData = authKeyMap.get(propertyKey);
397
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
398
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
399
				}
400
			}
401
			
402
			// store the properties to file
403
			backupProperties.store();
404
			authBackupProperties = 
405
				new SortedProperties(authBackupFilePath);
406
			authBackupProperties.load();
407

    
408
		} catch (TransformerException te) {
409
			throw new GeneralPropertyException("Could not transform backup properties xml: "
410
					+ te.getMessage());
411
		} catch (IOException ioe) {
412
			throw new GeneralPropertyException("Could not backup configurable properties: "
413
					+ ioe.getMessage());
414
		} 
415
	}
416
	
417
	/**
418
	 * Reports whether properties are fully configured.
419
	 * 
420
	 * @return a boolean that is true if properties are not unconfigured and
421
	 *         false otherwise
422
	 */
423
	public boolean arePropertiesConfigured() throws GeneralPropertyException {		
424
		String propertiesConfigured = getProperty("configutil.propertiesConfigured");
425
		if (propertiesConfigured != null && !propertiesConfigured.equals(UNCONFIGURED)) {
426
			return true;
427
		}			
428
		return false;
429
	}
430
	
431
	/**
432
	 * Determine if the system is configured to bypass configuration. If so, the
433
	 * system will look for backup configuration files at startup time and use
434
	 * those to configure metacat. The bypass options should only be set by
435
	 * developers. Production code should never bypass confguration.
436
	 * 
437
	 * @return true if dev.runConfiguration is set to true in metacat.properties
438
	 *         and we have not already checked for bypass, false otherwise.
439
	 */
440
	public boolean doBypass() throws PropertyNotFoundException {
441
		// We only want to go through the check once to see if we want to
442
		// bypass the configuration.  We don't want to run through all of
443
		// this every time  we hit metacat. 
444
		if (bypassAlreadyChecked) {
445
			logMetacat.debug("bypassConfiguration not performing full bypass check.  Bypass set to false");
446
			return false;
447
		}
448
		
449
		// check how dev.runConfiguration is set in metacat.properties
450
		String strRunConfiguration = getProperty("dev.runConfiguration");
451
		boolean runConfiguration = Boolean.parseBoolean(strRunConfiguration);
452
		logMetacat.debug("bypassConfiguration: dev.runConfiguration property set to: " + strRunConfiguration);
453
		
454
		// if the dev.runConfiguration is true, return false here.
455
		if (runConfiguration) {
456
			bypassAlreadyChecked = true;
457
			return false;
458
		} 
459
		
460
		return true;
461
	}
462
	
463
	/**
464
	 * Reports whether the metacat configuration utility should be run.  
465
	 * Returns false if  
466
	 *   -- dev.runConfiguration=false and
467
	 *   -- backup properties file exists
468
	 * Note that dev.runConfiguration should only be set to false when
469
	 * reinstalling the same version of the application in developement.
470
	 * 
471
	 * @return a boolean that is false if dev.runConfiguration is false and 
472
	 * the backup properties file exists.  
473
	 */
474
	public void bypassConfiguration() throws GeneralPropertyException {
475
		try {
476
			boolean doBypass = doBypass();
477

    
478
			if (!doBypass) {
479
				throw new GeneralPropertyException(
480
						"Attempting to do bypass when system is not configured for it.");
481
			}			
482

    
483
			// The system is bypassing the configuration utility. We need to
484
			// get the backup properties and replace existing properties with
485
			// backup values.  We do this for main and org properties.		
486
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
487
			SortedProperties mainBackupProperties = getMainBackupProperties();
488
			Vector<String> backupPropertyNames = 
489
				mainBackupProperties.getPropertyNames();
490
			for (String backupPropertyName : backupPropertyNames) {
491
				String value = mainBackupProperties.getProperty(backupPropertyName);
492
				setPropertyNoPersist(backupPropertyName, value);
493
			}
494

    
495
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
496
			SortedProperties authBackupProperties = getAuthBackupProperties();
497
			Vector<String> authBackupPropertyNames = 
498
				authBackupProperties.getPropertyNames();
499
			for (String authBackupPropertyName : authBackupPropertyNames) {
500
				String value = authBackupProperties.getProperty(authBackupPropertyName);
501
				setPropertyNoPersist(authBackupPropertyName, value);
502
			}
503

    
504
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
505
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
506
			setPropertyNoPersist("configutil.authConfigured", "true");
507
			setPropertyNoPersist("configutil.skinsConfigured", "true");
508
			setPropertyNoPersist("configutil.databaseConfigured", "true");
509
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
510
				
511
			persistProperties();
512

    
513
		} catch (PropertyNotFoundException pnfe) {
514
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
515
		} catch (GeneralPropertyException gpe) {
516
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
517
		}
518

    
519
		bypassAlreadyChecked = true;
520
	}
521
	
522
	/**
523
	 * Take input from the user in an HTTP request about an property to be changed
524
	 * and update the metacat property file with that new value if it has
525
	 * changed from the value that was originally set.
526
	 * 
527
	 * @param request
528
	 *            that was generated by the user
529
	 * @param response
530
	 *            to send output back to the user
531
	 * @param propertyName
532
	 *            the name of the property to be checked and set
533
	 */
534
	public boolean checkAndSetProperty(HttpServletRequest request, String propertyName) 
535
			throws GeneralPropertyException {
536
		boolean changed = false;
537
		String value = getProperty(propertyName);
538
		String newValue = request.getParameter(propertyName);
539
		if (newValue != null && !newValue.trim().equals(value)) {
540
			setPropertyNoPersist(propertyName, newValue.trim());
541
			changed = true;
542
		}
543
		return changed;
544
	}
545

    
546
}
(1-1/5)