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: 2008-12-26 16:33:20 -0800 (Fri, 26 Dec 2008) $'
10
 * '$Revision: 4719 $'
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.UtilException;
41
import edu.ucsb.nceas.utilities.FileUtil;
42
import edu.ucsb.nceas.utilities.GeneralPropertyException;
43
import edu.ucsb.nceas.utilities.PropertiesMetaData;
44
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
45
import edu.ucsb.nceas.utilities.SortedProperties;
46

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

    
85
	/**
86
	 * private constructor since this is a singleton
87
	 * 
88
	 * @param servletContext the context we will use to get relative paths
89
	 */
90
	private PropertyService() throws ServiceException {
91
		try {
92
			initialize();
93
		} catch (GeneralPropertyException gpe) {
94
			throw new ServiceException("Properties problem while initializing " + 
95
					"PropertyService: " + gpe.getMessage());
96
		} catch (IOException ioe) {
97
			throw new ServiceException("I/O Problem while initializing " + 
98
					"PropertyService: " + ioe.getMessage());
99
		}
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
	protected void doRefresh() throws ServiceException {
138
		try {
139
			initialize();
140
		} catch (IOException ioe) {
141
			throw new ServiceException("Could not refresh SkinPropertyService due to"
142
					+ " I/O error: " + ioe.getMessage());
143
		} catch (GeneralPropertyException gpe) {
144
			throw new ServiceException("Could not refresh SkinPropertyService due to"
145
					+ " property error: " + gpe.getMessage());
146
		}
147
	}
148
	
149
	/**
150
	 * Initialize the singleton.
151
	 * 
152
	 * @param servletContext the context we will use to get relative paths
153
	 */
154
	private void initialize()
155
			throws IOException, GeneralPropertyException, ServiceException {
156
		
157
		logMetacat.debug("Initializing PropertyService");
158
		
159
		String configDir;
160
		if (testInstance) {
161
			configDir = testConfigDir;
162
		} else {
163
			configDir = ServiceService.getRealConfigDir();
164
		}
165

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

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

    
178
			// authMetaData holds configuration information about organization level 
179
			// properties.  This is primarily used to display input fields on 
180
			// the auth configuration page. The information is retrieved 
181
			// from an xml metadata file dedicated just to auth properties.
182
			authMetaDataFilePath = configDir + FileUtil.getFS() + AUTH_CONFIG_NAME + ".metadata.xml";
183
			authMetaData = new PropertiesMetaData(authMetaDataFilePath);
184
		} catch (TransformerException te) {
185
			throw new GeneralPropertyException(te.getMessage());
186
		}
187

    
188
		String backupDirPath = getProperty("application.backupDir") + FileUtil.getFS() + ".metacat";
189
		
190
		// The mainBackupProperties hold properties that were backed up the 
191
		// last time the application was configured.  On disk, the file will
192
		// look like a smaller version of metacat.properties.  It is stored 
193
		// in the data storage directory outside the application directories.
194
		mainBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
195
		mainBackupProperties = new SortedProperties(mainBackupPropertiesFilePath);
196
		mainBackupProperties.load();
197
		
198
		// The authBackupProperties hold properties that were backed up the 
199
		// last time the auth was configured.  On disk, the file will
200
		// look like a smaller version of metacat.properties.  It is stored 
201
		// in the data storage directory outside the application directories.
202
		authBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() +  AUTH_CONFIG_NAME + ".backup";
203
		authBackupProperties = new SortedProperties(authBackupPropertiesFilePath);
204
		authBackupProperties.load();
205
	}
206

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

    
229
	/**
230
	 * Get a Set of all property names that start with the groupName prefix.
231
	 * 
232
	 * @param groupName
233
	 *            the prefix of the keys to search for.
234
	 * @return enumeration of property names
235
	 */
236
    public static Vector<String> getPropertyNamesByGroup(String groupName) {   	
237
    	return mainProperties.getPropertyNamesByGroup(groupName);
238
    }
239
    
240
	/**
241
	 * Get a Map of all properties that start with the groupName prefix.
242
	 * 
243
	 * @param groupName
244
	 *            the prefix of the keys to search for.
245
	 * @return Map of property names
246
	 */
247
    public static Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {   	
248
    	return mainProperties.getPropertiesByGroup(groupName);
249
    }
250

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

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

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

    
340
		// Use the metadata to extract configurable properties from the 
341
		// overall properties list, and store those properties.
342
		try {
343
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
344
			
345
			// Populate the backup properties for main metacat properties using
346
			// the associated metadata file
347
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
348
			Set<String> mainKeySet = mainMetadata.getKeys();
349
			for (String propertyKey : mainKeySet) {
350
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
351
			}
352
			
353
			// store the properties to file
354
			backupProperties.store();
355
			mainBackupProperties = 
356
				new SortedProperties(mainBackupPropertiesFilePath);
357
			mainBackupProperties.load();
358

    
359
		} catch (TransformerException te) {
360
			throw new GeneralPropertyException("Could not transform backup properties xml: "
361
					+ te.getMessage());
362
		} catch (IOException ioe) {
363
			throw new GeneralPropertyException("Could not backup configurable properties: "
364
					+ ioe.getMessage());
365
		}
366
	}
367
	
368
	/**
369
	 * Writes out backup configurable properties to a file.
370
	 */
371
	public static void persistAuthBackupProperties(ServletContext servletContext)
372
			throws GeneralPropertyException {
373

    
374
		// Use the metadata to extract configurable properties from the 
375
		// overall properties list, and store those properties.
376
		try {
377
			SortedProperties backupProperties = 
378
				new SortedProperties(authBackupPropertiesFilePath);
379
			
380
			// Populate the backup properties for auth properties using
381
			// the associated metadata file
382
			PropertiesMetaData authMetadata = new PropertiesMetaData(authMetaDataFilePath);
383

    
384
			Set<String> authKeySet = authMetadata.getKeys();
385
			for (String propertyKey : authKeySet) {
386
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
387
			}
388
			
389
			// store the properties to file
390
			backupProperties.store();
391
			authBackupProperties = 
392
				new SortedProperties(authBackupPropertiesFilePath);
393
			authBackupProperties.load();
394

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

    
470
			if (!doBypass) {
471
				throw new ServiceException(
472
						"Attempting to do bypass when system is not configured for it.");
473
			}			
474

    
475
			// The system is bypassing the configuration utility. We need to
476
			// get the backup properties and replace existing properties with
477
			// backup values.  We do this for main and org properties.		
478
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
479
			SortedProperties mainBackupProperties = getMainBackupProperties();
480
			Vector<String> backupPropertyNames = 
481
				mainBackupProperties.getPropertyNames();
482
			for (String backupPropertyName : backupPropertyNames) {
483
				String value = mainBackupProperties.getProperty(backupPropertyName);
484
				setPropertyNoPersist(backupPropertyName, value);
485
			}
486

    
487
			logMetacat.debug("bypassConfiguration: setting auth backup properties.");
488
			SortedProperties authBackupProperties = getAuthBackupProperties();
489
			Vector<String> authBackupPropertyNames = 
490
				authBackupProperties.getPropertyNames();
491
			for (String authBackupPropertyName : authBackupPropertyNames) {
492
				String value = authBackupProperties.getProperty(authBackupPropertyName);
493
				setPropertyNoPersist(authBackupPropertyName, value);
494
			}
495

    
496
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
497
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
498
			setPropertyNoPersist("configutil.authConfigured", "true");
499
			setPropertyNoPersist("configutil.skinsConfigured", "true");
500
			setPropertyNoPersist("configutil.databaseConfigured", "true");
501
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
502
				
503
			persistProperties();
504

    
505
		} catch (PropertyNotFoundException pnfe) {
506
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
507
		} catch (GeneralPropertyException gpe) {
508
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
509
		}
510

    
511
		bypassAlreadyChecked = true;
512
	}
513
	
514
	/**
515
	 * Take input from the user in an HTTP request about an property to be changed
516
	 * and update the metacat property file with that new value if it has
517
	 * changed from the value that was originally set.
518
	 * 
519
	 * @param request
520
	 *            that was generated by the user
521
	 * @param response
522
	 *            to send output back to the user
523
	 * @param propertyName
524
	 *            the name of the property to be checked and set
525
	 */
526
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName) 
527
			throws GeneralPropertyException {
528
		String value = PropertyService.getProperty(propertyName);
529
		String newValue = request.getParameter(propertyName);
530
		if (newValue != null && !newValue.trim().equals(value)) {
531
			PropertyService.setPropertyNoPersist(propertyName, newValue.trim());
532
		}
533
	}
534

    
535
}
(3-3/9)