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.commons.configuration.ConfigurationException;
39
import org.apache.log4j.Logger;
40
import org.dataone.configuration.Settings;
41

    
42
import edu.ucsb.nceas.metacat.service.ServiceService;
43
import edu.ucsb.nceas.metacat.shared.BaseService;
44
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
45
import edu.ucsb.nceas.metacat.shared.ServiceException;
46
import edu.ucsb.nceas.metacat.util.SystemUtil;
47
import edu.ucsb.nceas.utilities.FileUtil;
48
import edu.ucsb.nceas.utilities.GeneralPropertyException;
49
import edu.ucsb.nceas.utilities.MetaDataProperty;
50
import edu.ucsb.nceas.utilities.PropertiesMetaData;
51
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
52
import edu.ucsb.nceas.utilities.SortedProperties;
53

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

    
83
	/**
84
	 * private constructor since this is a singleton
85
	 * 
86
	 * @param servletContext the context we will use to get relative paths
87
	 */
88
	protected ConfigurableProperties() throws ServiceException {		
89
		_serviceName = "ConfigurableProperties";
90
		
91
		initialize();		
92
	}
93
	
94
	public boolean refreshable() {
95
		return true;
96
	}
97
	
98
	public void doRefresh() throws ServiceException {
99
		initialize();
100
	}
101
	
102
	public void stop() throws ServiceException {
103
		return;
104
	}
105
	
106
	/**
107
	 * Initialize the singleton.
108
	 * 
109
	 * @param servletContext the context we will use to get relative paths
110
	 */
111
	private void initialize() throws ServiceException {
112
		
113
		logMetacat.debug("Initializing ConfigurableProperties");
114
		
115
		try {
116
			mainConfigFilePath = 
117
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_CONFIG_FILE_NAME;
118
			mainMetadataFilePath = 
119
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_METADATA_FILE_NAME;
120
//			mainBackupFilePath = 
121
//				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME;
122
			authMetadataFilePath = 
123
				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_METADATA_FILE_NAME;
124
//			authBackupFilePath = 
125
//				PropertyService.CONFIG_FILE_DIR + FileUtil.getFS() + AUTH_BACKUP_FILE_NAME;
126
			
127
			
128
			// mainProperties will hold the primary configuration values for
129
			// metacat.
130
			mainProperties = new SortedProperties(mainConfigFilePath);
131
			mainProperties.load();
132
			
133
			// include main metacat properties in d1 properties as overrides
134
			try {
135
			    Settings.getConfiguration();
136
				Settings.augmentConfiguration(mainConfigFilePath);
137
			} catch (ConfigurationException e) {
138
				logMetacat.error("Could not augment DataONE properties. " + e.getMessage(), e);
139
			}
140

    
141
			// mainMetaData holds configuration information about main
142
			// properties. This is primarily used to display input fields on 
143
			// the configuration page. The information is retrieved from an 
144
			// xml metadata file
145
			mainMetaData = new PropertiesMetaData(mainMetadataFilePath);
146

    
147
			// authMetaData holds configuration information about organization
148
			// level
149
			// properties. This is primarily used to display input fields on
150
			// the auth configuration page. The information is retrieved
151
			// from an xml metadata file dedicated just to auth properties.
152
			authMetaData = new PropertiesMetaData(authMetadataFilePath);
153

    
154
			String recommendedExternalDir = SystemUtil.discoverExternalDir();
155
			PropertyService.setRecommendedExternalDir(recommendedExternalDir);
156
			
157
			String backupPath = getProperty("application.backupDir");
158
			if (backupPath == null || backupPath.equals("")) {
159
				backupPath = SystemUtil.getStoredBackupDir();
160
			}
161
			if ((backupPath == null || backupPath.equals("")) && recommendedExternalDir != null) {
162
				backupPath = 
163
					recommendedExternalDir + FileUtil.getFS() + "." + ServiceService.getRealApplicationContext();
164
			}
165

    
166
			// if backupPath is still null, no reason to initialize the
167
			// backup properties. The system will need to prompt the user for 
168
			// the backup properties and reinitialize ConfigurableProperties.
169
			if (backupPath != null && !backupPath.equals("")) {		
170
				setProperty("application.backupDir", backupPath);
171
				SystemUtil.writeStoredBackupFile(backupPath);
172

    
173
				// The mainBackupProperties hold properties that were backed up
174
				// the last time the application was configured. On disk, the 
175
				// file will look like a smaller version of metacat.properties. 
176
				// It is stored in the data storage directory outside the 
177
				// application directories.
178
				mainBackupFilePath = backupPath + FileUtil.getFS() + MAIN_BACKUP_FILE_NAME;
179
				mainBackupProperties = new SortedProperties(mainBackupFilePath);
180
				mainBackupProperties.load();
181

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

    
206
	/**
207
	 * Utility method to get a property value from the properties file
208
	 * 
209
	 * @param propertyName
210
	 *            the name of the property requested
211
	 * @return the String value for the property
212
	 */
213
	public String getProperty(String propertyName) throws PropertyNotFoundException {
214
		return mainProperties.getProperty(propertyName);
215
	}
216
	
217
	/**
218
     * Get a set of all property names.
219
     * 
220
     * @return Set of property names  
221
     */
222
    public Vector<String> getPropertyNames() {   	
223
    	return mainProperties.getPropertyNames();
224
    }
225
    
226

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

    
263
	/**
264
	 * Utility method to set a property value both in memory and to the
265
	 * properties file
266
	 * 
267
	 * @param propertyName
268
	 *            the name of the property requested
269
	 * @param newValue
270
	 *            the new value for the property
271
	 */
272
	public void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
273
			mainProperties.setProperty(propertyName, newValue);
274
			mainProperties.store();
275
	}
276

    
277
	/**
278
	 * Utility method to set a property value in memory. This will NOT cause the
279
	 * property to be written to disk. Use this method to set multiple
280
	 * properties in a row without causing excessive I/O. You must call
281
	 * persistProperties() once you're done setting properties to have them
282
	 * written to disk.
283
	 * 
284
	 * @param propertyName
285
	 *            the name of the property requested
286
	 * @param newValue
287
	 *            the new value for the property
288
	 */
289
	public void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
290
		mainProperties.setPropertyNoPersist(propertyName, newValue);
291
	}
292

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

    
352
		// Use the metadata to extract configurable properties from the 
353
		// overall properties list, and store those properties.
354
		try {
355
			SortedProperties backupProperties = new SortedProperties(mainBackupFilePath);
356
			
357
			// Populate the backup properties for main metacat properties using
358
			// the associated metadata file
359
			PropertiesMetaData mainMetadata = new PropertiesMetaData(mainMetadataFilePath);
360

    
361
			Map<String, MetaDataProperty> mainKeyMap = mainMetadata.getProperties();
362
			Set<String> mainKeySet = mainKeyMap.keySet();
363
			for (String propertyKey : mainKeySet) {
364
				// don't backup passwords
365
				MetaDataProperty metaData = mainKeyMap.get(propertyKey);
366
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
367
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
368
				}
369
			}
370
			
371
			// store the properties to file
372
			backupProperties.store();
373
			mainBackupProperties = 
374
				new SortedProperties(mainBackupFilePath);
375
			mainBackupProperties.load();
376

    
377
		} catch (TransformerException te) {
378
			throw new GeneralPropertyException("Could not transform backup properties xml: "
379
					+ te.getMessage());
380
		} catch (IOException ioe) {
381
			throw new GeneralPropertyException("Could not backup configurable properties: "
382
					+ ioe.getMessage());
383
		}
384
	}
385
	
386
	/**
387
	 * Writes out backup configurable properties to a file.
388
	 */
389
	public void persistAuthBackupProperties(ServletContext servletContext)
390
			throws GeneralPropertyException {
391

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

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

    
488
			if (!doBypass) {
489
				throw new GeneralPropertyException(
490
						"Attempting to do bypass when system is not configured for it.");
491
			}			
492

    
493
			// The system is bypassing the configuration utility. We need to
494
			// get the backup properties and replace existing properties with
495
			// backup values.  We do this for main and org properties.		
496
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
497
			SortedProperties mainBackupProperties = getMainBackupProperties();
498
			Vector<String> backupPropertyNames = 
499
				mainBackupProperties.getPropertyNames();
500
			for (String backupPropertyName : backupPropertyNames) {
501
				String value = mainBackupProperties.getProperty(backupPropertyName);
502
				setPropertyNoPersist(backupPropertyName, value);
503
			}
504

    
505
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
506
			SortedProperties authBackupProperties = getAuthBackupProperties();
507
			Vector<String> authBackupPropertyNames = 
508
				authBackupProperties.getPropertyNames();
509
			for (String authBackupPropertyName : authBackupPropertyNames) {
510
				String value = authBackupProperties.getProperty(authBackupPropertyName);
511
				setPropertyNoPersist(authBackupPropertyName, value);
512
			}
513

    
514
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
515
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
516
			setPropertyNoPersist("configutil.authConfigured", "true");
517
			setPropertyNoPersist("configutil.skinsConfigured", "true");
518
			setPropertyNoPersist("configutil.databaseConfigured", "true");
519
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
520
				
521
			persistProperties();
522

    
523
		} catch (PropertyNotFoundException pnfe) {
524
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
525
		} catch (GeneralPropertyException gpe) {
526
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
527
		}
528

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

    
556
}
(1-1/5)