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-01-19 11:52:17 -0800 (Mon, 19 Jan 2009) $'
10
 * '$Revision: 4761 $'
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.UtilException;
42
import edu.ucsb.nceas.utilities.FileUtil;
43
import edu.ucsb.nceas.utilities.GeneralPropertyException;
44
import edu.ucsb.nceas.utilities.PropertiesMetaData;
45
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
46
import edu.ucsb.nceas.utilities.SortedProperties;
47

    
48
/**
49
 * A suite of utility classes for the metadata configuration utility
50
 */
51
public class PropertyService extends BaseService {
52
	
53
	private static PropertyService propertyService = null;
54
	
55
	// system is configured
56
	public static final String CONFIGURED = "true"; 
57
	// system has never been configured
58
	public static final String UNCONFIGURED = "false"; 
59
	public static final String BYPASSED = "bypassed";
60
	
61
	private static final String MAIN_CONFIG_NAME = "metacat.properties";
62
	private static final String AUTH_CONFIG_NAME = "auth.properties";
63
	
64
	private static boolean bypassAlreadyChecked = false;
65
	
66
	private static String mainPropertiesFilePath = null;
67
	private static SortedProperties mainProperties = null;
68
	
69
	private static String mainBackupPropertiesFilePath = null;
70
	private static SortedProperties mainBackupProperties = null;
71
	
72
	private static String propertiesMetaDataFilePath = null;
73
	private static PropertiesMetaData mainMetaData = null;
74
	
75
	private static String authBackupPropertiesFilePath = null;
76
	private static SortedProperties authBackupProperties = null;
77
	
78
	private static String authMetaDataFilePath = null;
79
	private static PropertiesMetaData authMetaData = null;
80
	
81
	private static boolean testInstance = false;
82
	private static String testConfigDir = null;
83
	
84
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
85

    
86
	/**
87
	 * private constructor since this is a singleton
88
	 * 
89
	 * @param servletContext the context we will use to get relative paths
90
	 */
91
	private PropertyService() throws ServiceException {
92
		try {
93
			initialize();
94
		} catch (GeneralPropertyException gpe) {
95
			throw new ServiceException("Properties problem while initializing " + 
96
					"PropertyService: " + gpe.getMessage());
97
		} catch (IOException ioe) {
98
			throw new ServiceException("I/O Problem while initializing " + 
99
					"PropertyService: " + ioe.getMessage());
100
		}
101
	}
102
	
103
	/**
104
	 * Get the single instance of PropertyService.
105
	 * 
106
	 * @return the single instance of PropertyService
107
	 */
108
	public static PropertyService getInstance() throws ServiceException {
109
		if (propertyService == null) {
110
			
111
			propertyService = new PropertyService();
112
		}
113
		return propertyService;
114
	}
115
	
116
	/**
117
	 * Get the single instance of PropertyService for test purposes.  In this
118
	 * case, we allow the configuration directory to be passed in.
119
	 * 
120
	 * @param configDir the configuration directory we need to look in
121
	 * @return the single instance of PropertyService
122
	 */
123
	public static PropertyService getTestInstance(String configDir) throws ServiceException {
124
		if (propertyService == null) {
125
			
126
			testInstance = true;
127
			testConfigDir = configDir;
128
			
129
			propertyService = new PropertyService();
130
		}
131
		return propertyService;
132
	}
133
	
134
	public boolean refreshable() {
135
		return true;
136
	}
137
	
138
	protected void doRefresh() throws ServiceException {
139
		try {
140
			initialize();
141
		} catch (IOException ioe) {
142
			throw new ServiceException("Could not refresh SkinPropertyService due to"
143
					+ " I/O error: " + ioe.getMessage());
144
		} catch (GeneralPropertyException gpe) {
145
			throw new ServiceException("Could not refresh SkinPropertyService due to"
146
					+ " property error: " + gpe.getMessage());
147
		}
148
	}
149
	
150
	/**
151
	 * Initialize the singleton.
152
	 * 
153
	 * @param servletContext the context we will use to get relative paths
154
	 */
155
	private void initialize()
156
			throws IOException, GeneralPropertyException, ServiceException {
157
		
158
		logMetacat.debug("Initializing PropertyService");
159
		
160
		String configDir;
161
		if (testInstance) {
162
			configDir = testConfigDir;
163
		} else {
164
			configDir = ServiceService.getRealConfigDir();
165
		}
166

    
167
		// mainProperties will hold the primary configuration values for metacat.
168
		mainPropertiesFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME;
169
		mainProperties = new SortedProperties(mainPropertiesFilePath);
170
		mainProperties.load();
171

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

    
179
			// authMetaData holds configuration information about organization level 
180
			// properties.  This is primarily used to display input fields on 
181
			// the auth configuration page. The information is retrieved 
182
			// from an xml metadata file dedicated just to auth properties.
183
			authMetaDataFilePath = configDir + FileUtil.getFS() + AUTH_CONFIG_NAME + ".metadata.xml";
184
			authMetaData = new PropertiesMetaData(authMetaDataFilePath);
185
		} catch (TransformerException te) {
186
			throw new GeneralPropertyException(te.getMessage());
187
		}
188
		
189
		String backupBasePath = getProperty("application.backupDir");
190
		
191
		// If the backupBasePath does not exist in metacat.properties, see if we can discover it.
192
		if (backupBasePath == null || backupBasePath.equals("")) {
193
			backupBasePath = SystemUtil.discoverExternalBaseDir();
194
		}
195

    
196
		// if backupBasePath is still null, no reason to initialize the backup properties. The
197
		// system will need to prompt the user for the backup properties and reinitialize 
198
		// PropertyService.
199
		if (backupBasePath != null && !backupBasePath.equals("")) {
200
			setProperty("application.backupDir", backupBasePath);
201
			
202
			String backupDirPath = backupBasePath + FileUtil.getFS() + ".metacat";
203

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

    
214
			// The authBackupProperties hold properties that were backed up the
215
			// last time the auth was configured. On disk, the file will
216
			// look like a smaller version of metacat.properties. It is stored
217
			// in the data storage directory outside the application
218
			// directories.
219
			authBackupPropertiesFilePath = 
220
				backupDirPath + FileUtil.getFS() + AUTH_CONFIG_NAME + ".backup";
221
			authBackupProperties = new SortedProperties(authBackupPropertiesFilePath);
222
			authBackupProperties.load();
223
		}
224
	}
225

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

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

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

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

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

    
359
		// Use the metadata to extract configurable properties from the 
360
		// overall properties list, and store those properties.
361
		try {
362
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
363
			
364
			// Populate the backup properties for main metacat properties using
365
			// the associated metadata file
366
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
367
			Set<String> mainKeySet = mainMetadata.getKeys();
368
			for (String propertyKey : mainKeySet) {
369
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
370
			}
371
			
372
			// store the properties to file
373
			backupProperties.store();
374
			mainBackupProperties = 
375
				new SortedProperties(mainBackupPropertiesFilePath);
376
			mainBackupProperties.load();
377

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

    
393
		// Use the metadata to extract configurable properties from the 
394
		// overall properties list, and store those properties.
395
		try {
396
			SortedProperties backupProperties = 
397
				new SortedProperties(authBackupPropertiesFilePath);
398
			
399
			// Populate the backup properties for auth properties using
400
			// the associated metadata file
401
			PropertiesMetaData authMetadata = new PropertiesMetaData(authMetaDataFilePath);
402

    
403
			Set<String> authKeySet = authMetadata.getKeys();
404
			for (String propertyKey : authKeySet) {
405
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
406
			}
407
			
408
			// store the properties to file
409
			backupProperties.store();
410
			authBackupProperties = 
411
				new SortedProperties(authBackupPropertiesFilePath);
412
			authBackupProperties.load();
413

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

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

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

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

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

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

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

    
554
}
(3-3/9)