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-04 14:32:58 -0700 (Tue, 04 Aug 2009) $'
10
 * '$Revision: 5015 $'
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
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.shared.BaseService;
41
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
42
import edu.ucsb.nceas.metacat.shared.ServiceException;
43
import edu.ucsb.nceas.metacat.util.SystemUtil;
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 metadata configuration utility
53
 */
54
public class PropertyService extends BaseService {
55
	
56
	private static PropertyService propertyService = null;
57
	
58
	// system is configured
59
	public static final String CONFIGURED = "true"; 
60
	// system has never been configured
61
	public static final String UNCONFIGURED = "false"; 
62
	public static final String BYPASSED = "bypassed";
63
	
64
	private static final String MAIN_CONFIG_NAME = "metacat.properties";
65
	private static final String AUTH_CONFIG_NAME = "auth.properties";
66
	
67
	private static boolean bypassAlreadyChecked = false;
68
	
69
	private static String recommendedExternalDir = null;
70
	
71
	private static String mainPropertiesFilePath = null;
72
	private static SortedProperties mainProperties = null;
73
	
74
	private static String mainBackupPropertiesFilePath = null;
75
	private static SortedProperties mainBackupProperties = null;
76
	
77
	private static String propertiesMetaDataFilePath = null;
78
	private static PropertiesMetaData mainMetaData = null;
79
	
80
	private static String authBackupPropertiesFilePath = null;
81
	private static SortedProperties authBackupProperties = null;
82
	
83
	private static String authMetaDataFilePath = null;
84
	private static PropertiesMetaData authMetaData = null;
85
	
86
	private static boolean testInstance = false;
87
	private static String testConfigDir = null;
88
	
89
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
90

    
91
	/**
92
	 * private constructor since this is a singleton
93
	 * 
94
	 * @param servletContext the context we will use to get relative paths
95
	 */
96
	private PropertyService() throws ServiceException {		
97
		_serviceName = "PropertyService";
98
		
99
		initialize();		
100
	}
101
	
102
	/**
103
	 * Get the single instance of PropertyService.
104
	 * 
105
	 * @return the single instance of PropertyService
106
	 */
107
	public static PropertyService getInstance() throws ServiceException {
108
		if (propertyService == null) {
109
			
110
			propertyService = new PropertyService();
111
		}
112
		return propertyService;
113
	}
114
	
115
	/**
116
	 * Get the single instance of PropertyService for test purposes.  In this
117
	 * case, we allow the configuration directory to be passed in.
118
	 * 
119
	 * @param configDir the configuration directory we need to look in
120
	 * @return the single instance of PropertyService
121
	 */
122
	public static PropertyService getTestInstance(String configDir) throws ServiceException {
123
		if (propertyService == null) {
124
			
125
			testInstance = true;
126
			testConfigDir = configDir;
127
			
128
			propertyService = new PropertyService();
129
		}
130
		return propertyService;
131
	}
132
	
133
	public boolean refreshable() {
134
		return true;
135
	}
136
	
137
	public void doRefresh() throws ServiceException {
138
		initialize();
139
	}
140
	
141
	public void stop() throws ServiceException {
142
		return;
143
	}
144
	
145
	/**
146
	 * Initialize the singleton.
147
	 * 
148
	 * @param servletContext the context we will use to get relative paths
149
	 */
150
	private void initialize() throws ServiceException {
151
		
152
		logMetacat.debug("Initializing PropertyService");
153
		
154
		String configDir;
155
		if (testInstance) {
156
			configDir = testConfigDir;
157
		} else {
158
			configDir = ServiceService.getRealConfigDir();
159
		}
160
		
161
		try {
162
			// mainProperties will hold the primary configuration values for
163
			// metacat.
164
			mainPropertiesFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME;
165
			mainProperties = new SortedProperties(mainPropertiesFilePath);
166
			mainProperties.load();
167

    
168
			// mainMetaData holds configuration information about main
169
			// properties. This is primarily used to display input fields on 
170
			// the configuration page. The information is retrieved from an 
171
			// xml metadata file
172
			propertiesMetaDataFilePath = 
173
				configDir + FileUtil.getFS() + MAIN_CONFIG_NAME + ".metadata.xml";
174
			mainMetaData = new PropertiesMetaData(propertiesMetaDataFilePath);
175

    
176
			// authMetaData holds configuration information about organization
177
			// level
178
			// properties. This is primarily used to display input fields on
179
			// the auth configuration page. The information is retrieved
180
			// from an xml metadata file dedicated just to auth properties.
181
			authMetaDataFilePath = 
182
				configDir + FileUtil.getFS() + AUTH_CONFIG_NAME + ".metadata.xml";
183
			authMetaData = new PropertiesMetaData(authMetaDataFilePath);
184

    
185
			String recommendedExternalDir = SystemUtil.discoverExternalDir();
186
			setRecommendedExternalDir(recommendedExternalDir);
187
			
188
			String backupPath = getProperty("application.backupDir");
189
			if (backupPath == null || backupPath.equals("")) {
190
				backupPath = SystemUtil.getStoredBackupDir();
191
			}
192
			if (backupPath == null || backupPath.equals("") && recommendedExternalDir != null) {
193
				backupPath = 
194
					recommendedExternalDir + FileUtil.getFS() + "." + ServiceService.getRealApplicationContext();
195
			}
196

    
197
			// if backupPath is still null, no reason to initialize the
198
			// backup properties. The system will need to prompt the user for 
199
			// the backup properties and reinitialize PropertyService.
200
			if (backupPath != null && !backupPath.equals("")) {		
201
				setProperty("application.backupDir", backupPath);
202
				SystemUtil.writeStoredBackupFile(backupPath);
203

    
204
				// The mainBackupProperties hold properties that were backed up
205
				// the last time the application was configured. On disk, the 
206
				// file will look like a smaller version of metacat.properties. 
207
				// It is stored in the data storage directory outside the 
208
				// application directories.
209
				mainBackupPropertiesFilePath =
210
					backupPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
211
				mainBackupProperties = new SortedProperties(mainBackupPropertiesFilePath);
212
				mainBackupProperties.load();
213

    
214
				// The authBackupProperties hold properties that were backed up
215
				// the last time the auth was configured. On disk, the file 
216
				// will look like a smaller version of metacat.properties. It 
217
				// is stored in the data storage directory outside the 
218
				// application directories.
219
				authBackupPropertiesFilePath = 
220
					backupPath + FileUtil.getFS() + AUTH_CONFIG_NAME + ".backup";
221
				authBackupProperties = new SortedProperties(authBackupPropertiesFilePath);
222
				authBackupProperties.load();
223
			}
224
		} catch (TransformerException te) {
225
			throw new ServiceException("Transform problem while loading properties: "
226
					+ te.getMessage());
227
		} catch (IOException ioe) {
228
			throw new ServiceException("I/O problem while loading properties: "
229
					+ ioe.getMessage());
230
		} catch (GeneralPropertyException gpe) {
231
			throw new ServiceException("General properties problem while loading properties: "
232
					+ gpe.getMessage());
233
		} catch (MetacatUtilException ue) {
234
			throw new ServiceException("Utilities problem while loading properties: "
235
					+ ue.getMessage());
236
		} 
237
	}
238

    
239
	/**
240
	 * Utility method to get a property value from the properties file
241
	 * 
242
	 * @param propertyName
243
	 *            the name of the property requested
244
	 * @return the String value for the property
245
	 */
246
	public static String getProperty(String propertyName)
247
			throws PropertyNotFoundException {
248
		return mainProperties.getProperty(propertyName);
249
	}
250
	
251
	/**
252
     * Get a set of all property names.
253
     * 
254
     * @return Set of property names  
255
     */
256
    public static Vector<String> getPropertyNames() {   	
257
    	return mainProperties.getPropertyNames();
258
    }
259
    
260

    
261
	/**
262
	 * Get a Set of all property names that start with the groupName prefix.
263
	 * 
264
	 * @param groupName
265
	 *            the prefix of the keys to search for.
266
	 * @return enumeration of property names
267
	 */
268
    public static Vector<String> getPropertyNamesByGroup(String groupName) {   	
269
    	return mainProperties.getPropertyNamesByGroup(groupName);
270
    }
271
    
272
	/**
273
	 * Get a Map of all properties that start with the groupName prefix.
274
	 * 
275
	 * @param groupName
276
	 *            the prefix of the keys to search for.
277
	 * @return Map of property names
278
	 */
279
    public static Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {   	
280
    	return mainProperties.getPropertiesByGroup(groupName);
281
    }
282

    
283
	/**
284
	 * Utility method to set a property value both in memory and to the
285
	 * properties file
286
	 * 
287
	 * @param propertyName
288
	 *            the name of the property requested
289
	 * @param newValue
290
	 *            the new value for the property
291
	 */
292
	public static void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
293
			mainProperties.setProperty(propertyName, newValue);
294
			mainProperties.store();
295
	}
296

    
297
	/**
298
	 * Utility method to set a property value in memory. This will NOT cause the
299
	 * property to be written to disk. Use this method to set multiple
300
	 * properties in a row without causing excessive I/O. You must call
301
	 * persistProperties() once you're done setting properties to have them
302
	 * written to disk.
303
	 * 
304
	 * @param propertyName
305
	 *            the name of the property requested
306
	 * @param newValue
307
	 *            the new value for the property
308
	 */
309
	public static void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
310
		mainProperties.setPropertyNoPersist(propertyName, newValue);
311
	}
312

    
313
	/**
314
	 * Save the properties to a properties file. Note, the 
315
	 * order and comments will be preserved.
316
	 */
317
	public static void persistProperties() throws GeneralPropertyException {
318
		mainProperties.store();
319
	}
320
	
321
	/**
322
	 * Get the main backup properties file. These are configurable properties that
323
	 * are stored outside the metacat install directories so the user does not
324
	 * need to re-enter all the configuration information every time they do an
325
	 * upgrade.
326
	 * 
327
	 * @return a SortedProperties object with the backup properties
328
	 */
329
	public static SortedProperties getMainBackupProperties() {
330
		return mainBackupProperties;
331
	}
332
	
333
	/**
334
	 * Get the auth backup properties file. These are configurable 
335
	 * properties that are stored outside the metacat install directories so 
336
	 * the user does not need to re-enter all the configuration information 
337
	 * every time they do an upgrade.
338
	 * 
339
	 * @return a SortedProperties object with the backup properties
340
	 */
341
	public static SortedProperties getAuthBackupProperties() {
342
		return authBackupProperties;
343
	}
344
	
345
	/**
346
	 * Get the main properties metadata. This is retrieved from an xml file that
347
	 * describes the attributes of configurable properties.
348
	 * 
349
	 * @return a PropertiesMetaData object with the main properties metadata
350
	 */
351
	public static PropertiesMetaData getMainMetaData() {
352
		return mainMetaData;
353
	}
354
	
355
	/**
356
	 * Get the auth properties metadata. This is retrieved from an xml
357
	 * file that describes the attributes of configurable properties.
358
	 * 
359
	 * @return a PropertiesMetaData object with the organization properties
360
	 *         metadata
361
	 */
362
	public static PropertiesMetaData getAuthMetaData() {
363
		return authMetaData;
364
	}
365
	
366
	/**
367
	 * Writes out backup configurable properties to a file.
368
	 */
369
	public static void persistMainBackupProperties(ServletContext servletContext)
370
			throws GeneralPropertyException {
371

    
372
		// Use the metadata to extract configurable properties from the 
373
		// overall properties list, and store those properties.
374
		try {
375
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
376
			
377
			// Populate the backup properties for main metacat properties using
378
			// the associated metadata file
379
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
380

    
381
			Map<String, MetaDataProperty> mainKeyMap = mainMetadata.getProperties();
382
			Set<String> mainKeySet = mainKeyMap.keySet();
383
			for (String propertyKey : mainKeySet) {
384
				// don't backup passwords
385
				MetaDataProperty metaData = mainKeyMap.get(propertyKey);
386
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
387
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
388
				}
389
			}
390
			
391
			// store the properties to file
392
			backupProperties.store();
393
			mainBackupProperties = 
394
				new SortedProperties(mainBackupPropertiesFilePath);
395
			mainBackupProperties.load();
396

    
397
		} catch (TransformerException te) {
398
			throw new GeneralPropertyException("Could not transform backup properties xml: "
399
					+ te.getMessage());
400
		} catch (IOException ioe) {
401
			throw new GeneralPropertyException("Could not backup configurable properties: "
402
					+ ioe.getMessage());
403
		}
404
	}
405
	
406
	/**
407
	 * Writes out backup configurable properties to a file.
408
	 */
409
	public static void persistAuthBackupProperties(ServletContext servletContext)
410
			throws GeneralPropertyException {
411

    
412
		// Use the metadata to extract configurable properties from the 
413
		// overall properties list, and store those properties.
414
		try {
415
			SortedProperties backupProperties = 
416
				new SortedProperties(authBackupPropertiesFilePath);
417
			
418
			// Populate the backup properties for auth properties using
419
			// the associated metadata file
420
			PropertiesMetaData authMetadata = new PropertiesMetaData(authMetaDataFilePath);
421
			
422
			Map<String, MetaDataProperty> authKeyMap = authMetadata.getProperties();
423
			Set<String> authKeySet = authKeyMap.keySet();
424
			for (String propertyKey : authKeySet) {
425
				// don't backup passwords
426
				MetaDataProperty metaData = authKeyMap.get(propertyKey);
427
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
428
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
429
				}
430
			}
431
			
432
			// store the properties to file
433
			backupProperties.store();
434
			authBackupProperties = 
435
				new SortedProperties(authBackupPropertiesFilePath);
436
			authBackupProperties.load();
437

    
438
		} catch (TransformerException te) {
439
			throw new GeneralPropertyException("Could not transform backup properties xml: "
440
					+ te.getMessage());
441
		} catch (IOException ioe) {
442
			throw new GeneralPropertyException("Could not backup configurable properties: "
443
					+ ioe.getMessage());
444
		} 
445
	}
446
	
447
	/**
448
	 * Reports whether properties are fully configured.
449
	 * 
450
	 * @return a boolean that is true if properties are not unconfigured and
451
	 *         false otherwise
452
	 */
453
	public static boolean arePropertiesConfigured() throws MetacatUtilException {		
454
		try {
455
			String propertiesConfigured = PropertyService.getProperty("configutil.propertiesConfigured");
456
			if (propertiesConfigured != null && !propertiesConfigured.equals(UNCONFIGURED)) {
457
				return true;
458
			}			
459
			return false;
460
		} catch (PropertyNotFoundException pnfe) {
461
			throw new MetacatUtilException("Could not determine if properties are configured: "
462
					+ pnfe.getMessage());
463
		}
464
	}
465
	
466
	/**
467
	 * Determine if the system is configured to bypass configuration. If so, the
468
	 * system will look for backup configuration files at startup time and use
469
	 * those to configure metacat. The bypass options should only be set by
470
	 * developers. Production code should never bypass confguration.
471
	 * 
472
	 * @return true if dev.runConfiguration is set to true in metacat.properties
473
	 *         and we have not already checked for bypass, false otherwise.
474
	 */
475
	public static boolean doBypass() throws PropertyNotFoundException {
476
		// We only want to go through the check once to see if we want to
477
		// bypass the configuration.  We don't want to run through all of
478
		// this every time  we hit metacat. 
479
		if (bypassAlreadyChecked) {
480
			logMetacat.debug("bypassConfiguration not performing full bypass check.  Bypass set to false");
481
			return false;
482
		}
483
		
484
		// check how dev.runConfiguration is set in metacat.properties
485
		String strRunConfiguration = PropertyService.getProperty("dev.runConfiguration");
486
		boolean runConfiguration = Boolean.parseBoolean(strRunConfiguration);
487
		logMetacat.debug("bypassConfiguration: dev.runConfiguration property set to: " + strRunConfiguration);
488
		
489
		// if the dev.runConfiguration is true, return false here.
490
		if (runConfiguration) {
491
			bypassAlreadyChecked = true;
492
			return false;
493
		} 
494
		
495
		return true;
496
	}
497
	
498
	/**
499
	 * Reports whether the metacat configuration utility should be run.  
500
	 * Returns false if  
501
	 *   -- dev.runConfiguration=false and
502
	 *   -- backup properties file exists
503
	 * Note that dev.runConfiguration should only be set to false when
504
	 * reinstalling the same version of the application in developement.
505
	 * 
506
	 * @return a boolean that is false if dev.runConfiguration is false and 
507
	 * the backup properties file exists.  
508
	 */
509
	public static void bypassConfiguration() throws ServiceException {
510
		try {
511
			boolean doBypass = doBypass();
512

    
513
			if (!doBypass) {
514
				throw new ServiceException(
515
						"Attempting to do bypass when system is not configured for it.");
516
			}			
517

    
518
			// The system is bypassing the configuration utility. We need to
519
			// get the backup properties and replace existing properties with
520
			// backup values.  We do this for main and org properties.		
521
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
522
			SortedProperties mainBackupProperties = getMainBackupProperties();
523
			Vector<String> backupPropertyNames = 
524
				mainBackupProperties.getPropertyNames();
525
			for (String backupPropertyName : backupPropertyNames) {
526
				String value = mainBackupProperties.getProperty(backupPropertyName);
527
				setPropertyNoPersist(backupPropertyName, value);
528
			}
529

    
530
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
531
			SortedProperties authBackupProperties = getAuthBackupProperties();
532
			Vector<String> authBackupPropertyNames = 
533
				authBackupProperties.getPropertyNames();
534
			for (String authBackupPropertyName : authBackupPropertyNames) {
535
				String value = authBackupProperties.getProperty(authBackupPropertyName);
536
				setPropertyNoPersist(authBackupPropertyName, value);
537
			}
538

    
539
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
540
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
541
			setPropertyNoPersist("configutil.authConfigured", "true");
542
			setPropertyNoPersist("configutil.skinsConfigured", "true");
543
			setPropertyNoPersist("configutil.databaseConfigured", "true");
544
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
545
				
546
			persistProperties();
547

    
548
		} catch (PropertyNotFoundException pnfe) {
549
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
550
		} catch (GeneralPropertyException gpe) {
551
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
552
		}
553

    
554
		bypassAlreadyChecked = true;
555
	}
556
	
557
	/**
558
	 * Take input from the user in an HTTP request about an property to be changed
559
	 * and update the metacat property file with that new value if it has
560
	 * changed from the value that was originally set.
561
	 * 
562
	 * @param request
563
	 *            that was generated by the user
564
	 * @param response
565
	 *            to send output back to the user
566
	 * @param propertyName
567
	 *            the name of the property to be checked and set
568
	 */
569
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName) 
570
			throws GeneralPropertyException {
571
		String value = PropertyService.getProperty(propertyName);
572
		String newValue = request.getParameter(propertyName);
573
		if (newValue != null && !newValue.trim().equals(value)) {
574
			PropertyService.setPropertyNoPersist(propertyName, newValue.trim());
575
		}
576
	}
577
	
578
	/**
579
	 * Sets the recommended external directory.  This is populated during initialization 
580
	 * time using the SystemUtil.discoverExternalDir() method.  This directory will be used to
581
	 * suggest external user directories when the user configures metacat for the first time.
582
	 * 
583
	 */
584
	public static void setRecommendedExternalDir(String extBaseDir) {
585
		recommendedExternalDir = extBaseDir;
586
	}
587
	
588
	/**
589
	 * Returns the recommended external base directory.  This is populated during initialization 
590
	 * time using the SystemUtil.discoverExternalBaseDir() method.  This directory will be used to
591
	 * suggest external user directories when the user configures metacat for the first time.
592
	 * 
593
	 * @return a String holding the recommended external directory
594
	 */
595
	public static String getRecommendedExternalDir() {
596
		return recommendedExternalDir;
597
	}
598

    
599
}
(1-1/6)