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-06-12 16:39:43 -0700 (Fri, 12 Jun 2009) $'
10
 * '$Revision: 4950 $'
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.util.SystemUtil;
41
import edu.ucsb.nceas.metacat.util.MetacatUtilException;
42
import edu.ucsb.nceas.utilities.FileUtil;
43
import edu.ucsb.nceas.utilities.GeneralPropertyException;
44
import edu.ucsb.nceas.utilities.MetaDataProperty;
45
import edu.ucsb.nceas.utilities.PropertiesMetaData;
46
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
47
import edu.ucsb.nceas.utilities.SortedProperties;
48

    
49
/**
50
 * A suite of utility classes for the metadata configuration utility
51
 */
52
public class PropertyService extends BaseService {
53
	
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
	public static final String BYPASSED = "bypassed";
61
	
62
	private static final String MAIN_CONFIG_NAME = "metacat.properties";
63
	private static final String AUTH_CONFIG_NAME = "auth.properties";
64
	
65
	private static boolean bypassAlreadyChecked = false;
66
	
67
	private static String recommendedExternalDir = null;
68
	
69
	private static String mainPropertiesFilePath = null;
70
	private static SortedProperties mainProperties = null;
71
	
72
	private static String mainBackupPropertiesFilePath = null;
73
	private static SortedProperties mainBackupProperties = null;
74
	
75
	private static String propertiesMetaDataFilePath = null;
76
	private static PropertiesMetaData mainMetaData = null;
77
	
78
	private static String authBackupPropertiesFilePath = null;
79
	private static SortedProperties authBackupProperties = null;
80
	
81
	private static String authMetaDataFilePath = null;
82
	private static PropertiesMetaData authMetaData = null;
83
	
84
	private static boolean testInstance = false;
85
	private static String testConfigDir = null;
86
	
87
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
88

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

    
160
			// mainMetaData holds configuration information about main
161
			// properties. This is primarily used to display input fields on 
162
			// the configuration page. The information is retrieved from an 
163
			// xml metadata file
164
			propertiesMetaDataFilePath = 
165
				configDir + FileUtil.getFS() + MAIN_CONFIG_NAME + ".metadata.xml";
166
			mainMetaData = new PropertiesMetaData(propertiesMetaDataFilePath);
167

    
168
			// authMetaData holds configuration information about organization
169
			// level
170
			// properties. This is primarily used to display input fields on
171
			// the auth configuration page. The information is retrieved
172
			// from an xml metadata file dedicated just to auth properties.
173
			authMetaDataFilePath = 
174
				configDir + FileUtil.getFS() + AUTH_CONFIG_NAME + ".metadata.xml";
175
			authMetaData = new PropertiesMetaData(authMetaDataFilePath);
176

    
177
			String recommendedExternalDir = SystemUtil.discoverExternalDir();
178
			setRecommendedExternalDir(recommendedExternalDir);
179
			
180
			String backupPath = getProperty("application.backupDir");
181
			if (backupPath == null || backupPath.equals("")) {
182
				backupPath = SystemUtil.getStoredBackupDir();
183
			}
184
			if (backupPath == null || backupPath.equals("") && recommendedExternalDir != null) {
185
				backupPath = 
186
					recommendedExternalDir + FileUtil.getFS() + "." + ServiceService.getRealApplicationContext();
187
			}
188

    
189
			// if backupPath is still null, no reason to initialize the
190
			// backup properties. The system will need to prompt the user for 
191
			// the backup properties and reinitialize PropertyService.
192
			if (backupPath != null && !backupPath.equals("")) {		
193
				setProperty("application.backupDir", backupPath);
194
				SystemUtil.writeStoredBackupFile(backupPath);
195

    
196
				// The mainBackupProperties hold properties that were backed up
197
				// the last time the application was configured. On disk, the 
198
				// file will look like a smaller version of metacat.properties. 
199
				// It is stored in the data storage directory outside the 
200
				// application directories.
201
				mainBackupPropertiesFilePath =
202
					backupPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
203
				mainBackupProperties = new SortedProperties(mainBackupPropertiesFilePath);
204
				mainBackupProperties.load();
205

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

    
231
	/**
232
	 * Utility method to get a property value from the properties file
233
	 * 
234
	 * @param propertyName
235
	 *            the name of the property requested
236
	 * @return the String value for the property
237
	 */
238
	public static String getProperty(String propertyName)
239
			throws PropertyNotFoundException {
240
		return mainProperties.getProperty(propertyName);
241
	}
242
	
243
	/**
244
     * Get a set of all property names.
245
     * 
246
     * @return Set of property names  
247
     */
248
    public static Vector<String> getPropertyNames() {   	
249
    	return mainProperties.getPropertyNames();
250
    }
251
    
252

    
253
	/**
254
	 * Get a Set of all property names that start with the groupName prefix.
255
	 * 
256
	 * @param groupName
257
	 *            the prefix of the keys to search for.
258
	 * @return enumeration of property names
259
	 */
260
    public static Vector<String> getPropertyNamesByGroup(String groupName) {   	
261
    	return mainProperties.getPropertyNamesByGroup(groupName);
262
    }
263
    
264
	/**
265
	 * Get a Map of all properties that start with the groupName prefix.
266
	 * 
267
	 * @param groupName
268
	 *            the prefix of the keys to search for.
269
	 * @return Map of property names
270
	 */
271
    public static Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {   	
272
    	return mainProperties.getPropertiesByGroup(groupName);
273
    }
274

    
275
	/**
276
	 * Utility method to set a property value both in memory and to the
277
	 * properties file
278
	 * 
279
	 * @param propertyName
280
	 *            the name of the property requested
281
	 * @param newValue
282
	 *            the new value for the property
283
	 */
284
	public static void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
285
			mainProperties.setProperty(propertyName, newValue);
286
			mainProperties.store();
287
	}
288

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

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

    
364
		// Use the metadata to extract configurable properties from the 
365
		// overall properties list, and store those properties.
366
		try {
367
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
368
			
369
			// Populate the backup properties for main metacat properties using
370
			// the associated metadata file
371
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
372

    
373
			Map<String, MetaDataProperty> mainKeyMap = mainMetadata.getProperties();
374
			Set<String> mainKeySet = mainKeyMap.keySet();
375
			for (String propertyKey : mainKeySet) {
376
				// don't backup passwords
377
				MetaDataProperty metaData = mainKeyMap.get(propertyKey);
378
				if (!metaData.getFieldType().equals(MetaDataProperty.PASSWORD_TYPE)) {
379
					backupProperties.addProperty(propertyKey, getProperty(propertyKey));
380
				}
381
			}
382
			
383
			// store the properties to file
384
			backupProperties.store();
385
			mainBackupProperties = 
386
				new SortedProperties(mainBackupPropertiesFilePath);
387
			mainBackupProperties.load();
388

    
389
		} catch (TransformerException te) {
390
			throw new GeneralPropertyException("Could not transform backup properties xml: "
391
					+ te.getMessage());
392
		} catch (IOException ioe) {
393
			throw new GeneralPropertyException("Could not backup configurable properties: "
394
					+ ioe.getMessage());
395
		}
396
	}
397
	
398
	/**
399
	 * Writes out backup configurable properties to a file.
400
	 */
401
	public static void persistAuthBackupProperties(ServletContext servletContext)
402
			throws GeneralPropertyException {
403

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

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

    
505
			if (!doBypass) {
506
				throw new ServiceException(
507
						"Attempting to do bypass when system is not configured for it.");
508
			}			
509

    
510
			// The system is bypassing the configuration utility. We need to
511
			// get the backup properties and replace existing properties with
512
			// backup values.  We do this for main and org properties.		
513
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
514
			SortedProperties mainBackupProperties = getMainBackupProperties();
515
			Vector<String> backupPropertyNames = 
516
				mainBackupProperties.getPropertyNames();
517
			for (String backupPropertyName : backupPropertyNames) {
518
				String value = mainBackupProperties.getProperty(backupPropertyName);
519
				setPropertyNoPersist(backupPropertyName, value);
520
			}
521

    
522
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
523
			SortedProperties authBackupProperties = getAuthBackupProperties();
524
			Vector<String> authBackupPropertyNames = 
525
				authBackupProperties.getPropertyNames();
526
			for (String authBackupPropertyName : authBackupPropertyNames) {
527
				String value = authBackupProperties.getProperty(authBackupPropertyName);
528
				setPropertyNoPersist(authBackupPropertyName, value);
529
			}
530

    
531
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
532
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
533
			setPropertyNoPersist("configutil.authConfigured", "true");
534
			setPropertyNoPersist("configutil.skinsConfigured", "true");
535
			setPropertyNoPersist("configutil.databaseConfigured", "true");
536
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
537
				
538
			persistProperties();
539

    
540
		} catch (PropertyNotFoundException pnfe) {
541
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
542
		} catch (GeneralPropertyException gpe) {
543
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
544
		}
545

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

    
591
}
(3-3/9)