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-07-24 13:52:51 -0700 (Thu, 24 Jul 2008) $'
10
 * '$Revision: 4159 $'
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.SortedMap;
33
import java.util.Vector;
34

    
35
import javax.servlet.ServletContext;
36
import javax.servlet.http.HttpServletRequest;
37
import javax.xml.transform.TransformerException;
38

    
39
import org.apache.log4j.Logger;
40

    
41
import edu.ucsb.nceas.metacat.util.OrganizationUtil;
42
import edu.ucsb.nceas.metacat.util.UtilException;
43
import edu.ucsb.nceas.utilities.FileUtil;
44
import edu.ucsb.nceas.utilities.GeneralPropertyException;
45
import edu.ucsb.nceas.utilities.MetaDataGroup;
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 implements ServiceInterface {
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
	
63
	private static final String MAIN_CONFIG_NAME = "metacat.properties";
64
	private static final String ORG_CONFIG_NAME = "org.properties";
65
	private static final String LDAP_CONFIG_NAME = "ldap.properties";
66
	
67
	private static boolean bypassAlreadyChecked = false;
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 orgBackupPropertiesFilePath = null;
79
	private static SortedProperties orgBackupProperties = null;
80
	
81
	private static String orgMetaDataFilePath = null;
82
	private static PropertiesMetaData orgMetaData = null;
83
	
84
	private static String ldapBackupPropertiesFilePath = null;
85
	private static SortedProperties ldapBackupProperties = null;
86
	
87
	private static String ldapMetaDataFilePath = null;
88
	private static PropertiesMetaData ldapMetaData = null;
89
	
90
	
91
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
92

    
93
	/**
94
	 * private constructor since this is a singleton
95
	 * 
96
	 * @param servletContext the context we will use to get relative paths
97
	 */
98
	private PropertyService(String configDir) throws ServiceException {
99
		try {
100
			initialize(configDir);
101
		} catch (GeneralPropertyException gpe) {
102
			throw new ServiceException("Properties problem while initializing " + 
103
					"PropertyService: " + gpe.getMessage());
104
		} catch (IOException ioe) {
105
			throw new ServiceException("I/O Problem while initializing " + 
106
					"PropertyService: " + ioe.getMessage());
107
		}
108
	}
109
	
110
	/**
111
	 * Get the single instance of AuthAdmin.
112
	 * 
113
	 * @param servletContext the context we will use to get relative paths
114
	 * @return the single instance of AuthAdmin
115
	 */
116
	public static PropertyService getInstance(String configDir) throws ServiceException {
117
		if (propertyService == null) {
118
			propertyService = new PropertyService(configDir);
119
		}
120
		return propertyService;
121
	}
122
	
123
	/**
124
	 * Initialize the singleton.
125
	 * 
126
	 * @param servletContext the context we will use to get relative paths
127
	 */
128
	public void initialize(String configDir)
129
			throws IOException, GeneralPropertyException {
130
		
131
		logMetacat.debug("Initializing PropertyService");
132

    
133
		// mainProperties will hold the primary configuration values for metacat.
134
		mainPropertiesFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME;
135
		if (mainProperties == null) {
136
			mainProperties = new SortedProperties(mainPropertiesFilePath);
137
			mainProperties.load();
138
		}
139

    
140
		try {
141
			// mainMetaData holds configuration information about main properties.
142
			// This is primarily used to display input fields on the configuration
143
			// page. The information is retrieved from an xml metadata file
144
			propertiesMetaDataFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME + ".metadata.xml";
145
			if (mainMetaData == null) {
146
				mainMetaData = new PropertiesMetaData(propertiesMetaDataFilePath);
147
			}
148

    
149
			// orgMetaData holds configuration information about organization level 
150
			// properties.  This is primarily used to display input fields on 
151
			// the organization configuration page. The information is retrieved 
152
			// from an xml metadata file dedicated just to organization properties.
153
			orgMetaDataFilePath = configDir + FileUtil.getFS() + ORG_CONFIG_NAME + ".metadata.xml";
154
			if (orgMetaData == null) {
155
				orgMetaData = new PropertiesMetaData(orgMetaDataFilePath);
156
			}
157
			
158

    
159
			// ldapMetaData holds configuration information about organization level 
160
			// properties.  This is primarily used to display input fields on 
161
			// the ldap configuration page. The information is retrieved 
162
			// from an xml metadata file dedicated just to ldap properties.
163
			ldapMetaDataFilePath = configDir + FileUtil.getFS() + LDAP_CONFIG_NAME + ".metadata.xml";
164
			if (ldapMetaData == null) {
165
				ldapMetaData = new PropertiesMetaData(ldapMetaDataFilePath);
166
			}
167
		} catch (TransformerException te) {
168
			throw new GeneralPropertyException(te.getMessage());
169
		}
170

    
171
		String backupDirPath = getBackupDir();
172
		
173
		// The mainBackupProperties hold properties that were backed up the 
174
		// last time the application was configured.  On disk, the file will
175
		// look like a smaller version of metacat.properties.  It is stored 
176
		// in the data storage directory outside the application directories.
177
		mainBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
178
		if (mainBackupProperties == null) {
179
			mainBackupProperties = 
180
				new SortedProperties(mainBackupPropertiesFilePath);
181
			mainBackupProperties.load();
182
		}
183
		
184
		// The orgBackupProperties hold properties that were backed up the 
185
		// last time the organizations were configured.  On disk, the file will
186
		// look like a smaller version of metacat.properties.  It is stored 
187
		// in the data storage directory outside the application directories.
188
		orgBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() +  ORG_CONFIG_NAME + ".backup";
189
		if (orgBackupProperties == null) {
190
			orgBackupProperties = 
191
				new SortedProperties(orgBackupPropertiesFilePath);
192
			orgBackupProperties.load();
193
		}
194
		
195
		// The ldapBackupProperties hold properties that were backed up the 
196
		// last time the LDAP was configured.  On disk, the file will
197
		// look like a smaller version of metacat.properties.  It is stored 
198
		// in the data storage directory outside the application directories.
199
		ldapBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() +  LDAP_CONFIG_NAME + ".backup";
200
		if (ldapBackupProperties == null) {
201
			ldapBackupProperties = 
202
				new SortedProperties(ldapBackupPropertiesFilePath);
203
			ldapBackupProperties.load();
204
		}
205
	
206
	}
207

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

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

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

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

    
282
	/**
283
	 * Save the properties to a properties file. Note, the 
284
	 * order and comments will be preserved.
285
	 */
286
	public static void persistProperties() throws GeneralPropertyException {
287
		mainProperties.store();
288
	}
289
	
290
	/**
291
	 * Get the main backup properties file. These are configurable properties that
292
	 * are stored outside the metacat install directories so the user does not
293
	 * need to re-enter all the configuration information every time they do an
294
	 * upgrade.
295
	 * 
296
	 * @return a SortedProperties object with the backup properties
297
	 */
298
	public static SortedProperties getMainBackupProperties() {
299
		return mainBackupProperties;
300
	}
301
	
302
	/**
303
	 * Get the organizational backup properties file. These are configurable 
304
	 * properties that are stored outside the metacat install directories so 
305
	 * the user does not need to re-enter all the configuration information 
306
	 * every time they do an upgrade.
307
	 * 
308
	 * @return a SortedProperties object with the backup properties
309
	 */
310
	public static SortedProperties getOrgBackupProperties() {
311
		return orgBackupProperties;
312
	}
313
	
314
	/**
315
	 * Get the LDAP backup properties file. These are configurable 
316
	 * properties that are stored outside the metacat install directories so 
317
	 * the user does not need to re-enter all the configuration information 
318
	 * every time they do an upgrade.
319
	 * 
320
	 * @return a SortedProperties object with the backup properties
321
	 */
322
	public static SortedProperties getLDAPBackupProperties() {
323
		return ldapBackupProperties;
324
	}
325
	
326
	/**
327
	 * Get the main properties metadata. This is retrieved from an xml file that
328
	 * describes the attributes of configurable properties.
329
	 * 
330
	 * @return a PropertiesMetaData object with the main properties metadata
331
	 */
332
	public static PropertiesMetaData getMainMetaData() {
333
		return mainMetaData;
334
	}
335
	
336
	/**
337
	 * Get the organization properties metadata. This is retrieved from an xml
338
	 * file that describes the attributes of configurable properties.
339
	 * 
340
	 * @return a PropertiesMetaData object with the organization properties
341
	 *         metadata
342
	 */
343
	public static PropertiesMetaData getOrgMetaData() {
344
		return orgMetaData;
345
	}
346
	
347
	/**
348
	 * Get the LDAP 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 getLDAPMetaData() {
355
		return ldapMetaData;
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
			Set<String> mainKeySet = mainMetadata.getKeys();
373
			for (String propertyKey : mainKeySet) {
374
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
375
			}
376
			
377
			// store the properties to file
378
			backupProperties.store();
379

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

    
395
		// Use the metadata to extract configurable properties from the 
396
		// overall properties list, and store those properties.
397
		try {
398
			SortedProperties backupProperties = 
399
				new SortedProperties(orgBackupPropertiesFilePath);
400
			
401
			// Populate the backup properties for organization properties using
402
			// the associated metadata file
403
			PropertiesMetaData orgMetadata = new PropertiesMetaData(orgMetaDataFilePath);
404
			
405
			// We do the same thing here for organization specific properies
406
			// with the addition that we need to iterate through all available 
407
			// organizations.  For instance, a metadata section that defines a 
408
			// property as "organization.base" will be entered into the metacat.properties
409
			// file with a key of "organization.base.NCEAS" for the NCEAS organization. 
410
			// This will be repeated for all available organizations.
411
			Set<String> orgKeySet = orgMetadata.getKeys();
412
			for (String orgName : OrganizationUtil.getOrganizations()) {
413
				for (String propertyKey : orgKeySet) {
414
					String orgPropertyKey = propertyKey + "." + orgName;
415
					backupProperties.addProperty(orgPropertyKey, getProperty(orgPropertyKey));
416
				}
417
			}
418
			
419
			// store the properties to file
420
			backupProperties.store();
421

    
422
		} catch (TransformerException te) {
423
			throw new GeneralPropertyException("Could not transform backup properties xml: "
424
					+ te.getMessage());
425
		} catch (IOException ioe) {
426
			throw new GeneralPropertyException("Could not backup configurable properties: "
427
					+ ioe.getMessage());
428
		} catch (UtilException ue) {
429
			throw new GeneralPropertyException("Could not get organizations: " + ue.getMessage());
430
		}
431
	}
432
	
433
	/**
434
	 * Writes out backup configurable properties to a file.
435
	 */
436
	public static void persistLDAPBackupProperties(ServletContext servletContext)
437
			throws GeneralPropertyException {
438

    
439
		// Use the metadata to extract configurable properties from the 
440
		// overall properties list, and store those properties.
441
		try {
442
			SortedProperties backupProperties = 
443
				new SortedProperties(ldapBackupPropertiesFilePath);
444
			
445
			// Populate the backup properties for ldap properties using
446
			// the associated metadata file
447
			PropertiesMetaData ldapMetadata = new PropertiesMetaData(ldapMetaDataFilePath);
448

    
449
			Set<String> ldapKeySet = ldapMetadata.getKeys();
450
			for (String propertyKey : ldapKeySet) {
451
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
452
			}
453
			
454
			// store the properties to file
455
			backupProperties.store();
456

    
457
		} catch (TransformerException te) {
458
			throw new GeneralPropertyException("Could not transform backup properties xml: "
459
					+ te.getMessage());
460
		} catch (IOException ioe) {
461
			throw new GeneralPropertyException("Could not backup configurable properties: "
462
					+ ioe.getMessage());
463
		} 
464
	}
465

    
466
	/**
467
	 * Gets the backup properties directory
468
	 * 
469
	 * TODO MCD figure out how to expand this.  At least for Windows
470
	 * 
471
	 * @return a string which holds the name of the backup directory. returns
472
	 *         null if directory could not be created.
473
	 */
474
	public static String getBackupDir() {
475
		return "/var/metacat/.metacat";
476
	}
477
	
478
	/**
479
	 * Reports whether properties are fully configured.
480
	 * 
481
	 * @return a boolean that is true if properties are not unconfigured and
482
	 *         false otherwise
483
	 */
484
	public static boolean arePropertiesConfigured() throws UtilException {
485
		try {
486
			return !PropertyService.getProperty("configutil.propertiesConfigured").equals(
487
					PropertyService.UNCONFIGURED);
488
		} catch (PropertyNotFoundException pnfe) {
489
			throw new UtilException("Could not determine if properties are configured: "
490
					+ pnfe.getMessage());
491
		}
492
	}
493
	
494
	/**
495
	 * Reports whether the metacat configuration utility should be run.  
496
	 * Returns false if  
497
	 *   -- dev.runConfiguration=false and
498
	 *   -- backup properties file exists
499
	 * Note that dev.runConfiguration should only be set to false when
500
	 * reinstalling the same version of the application in developement.
501
	 * 
502
	 * @return a boolean that is false if dev.runConfiguration is false and 
503
	 * the backup properties file exists.  
504
	 */
505
	public static boolean bypassConfiguration() {
506
		boolean bypass = false;
507
		
508
		// We only want to go through the check once to see if we want to
509
		// bypass the configuration.  We don't want to run through all of
510
		// this every time  we hit metacat. 
511
		if (bypassAlreadyChecked) {
512
			return bypass;
513
		}
514
		
515
		try {
516
			// check how dev.runConfiguration is set in metacat.properties
517
			String strRunConfiguration = PropertyService.getProperty("dev.runConfiguration");
518
			bypass = !(Boolean.parseBoolean(strRunConfiguration));
519
			
520
			// if the deb.runConfiguration is true, return false here.
521
			if (!bypass) {
522
				bypassAlreadyChecked = true;
523
				return false;
524
			}
525

    
526
			// The system is bypassing the configuration utility. We need to
527
			// get the backup properties and replace existing properties with
528
			// backup values.  We do this for main and org properties.		
529
			SortedProperties mainBackupProperties = getMainBackupProperties();
530
			Vector<String> backupPropertyNames = 
531
				mainBackupProperties.getPropertyNames();
532
			for (String backupPropertyName : backupPropertyNames) {
533
				String value = mainBackupProperties.getProperty(backupPropertyName);
534
				setPropertyNoPersist(backupPropertyName, value);
535
			}
536
				
537
			SortedProperties orgBackupProperties = getOrgBackupProperties();
538
			Vector<String> orgBackupPropertyNames = 
539
				orgBackupProperties.getPropertyNames();
540
			for (String orgBackupPropertyName : orgBackupPropertyNames) {
541
				String value = orgBackupProperties.getProperty(orgBackupPropertyName);
542
				setPropertyNoPersist(orgBackupPropertyName, value);
543
			}
544
			
545
			SortedProperties ldapBackupProperties = getLDAPBackupProperties();
546
			Vector<String> ldapBackupPropertyNames = 
547
				ldapBackupProperties.getPropertyNames();
548
			for (String ldapBackupPropertyName : ldapBackupPropertyNames) {
549
				String value = ldapBackupProperties.getProperty(ldapBackupPropertyName);
550
				setPropertyNoPersist(ldapBackupPropertyName, value);
551
			}
552
			
553
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
554
			setPropertyNoPersist("configutil.ldapConfigured", "true");
555
			setPropertyNoPersist("configutil.organizationsConfigured", "true");
556
			setPropertyNoPersist("configutil.skinsConfigured", "true");
557
			setPropertyNoPersist("configutil.databaseConfigured", "true");
558
				
559
			persistProperties();
560

    
561
		} catch (PropertyNotFoundException pnfe) {
562
			logMetacat.error("Could not find property: " + pnfe.getMessage());
563
		} catch (GeneralPropertyException gpe) {
564
			logMetacat.error("General property error: " + gpe.getMessage());
565
		}
566

    
567
		bypassAlreadyChecked = true;
568
		return bypass;
569
	}
570
	
571
	/**
572
	 * Take input from the user in an HTTP request about an property to be changed
573
	 * and update the metacat property file with that new value if it has
574
	 * changed from the value that was originally set.
575
	 * 
576
	 * @param request
577
	 *            that was generated by the user
578
	 * @param response
579
	 *            to send output back to the user
580
	 * @param propertyName
581
	 *            the name of the property to be checked and set
582
	 */
583
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName) 
584
			throws GeneralPropertyException {
585
		String value = PropertyService.getProperty(propertyName);
586
		String newValue = request.getParameter(propertyName);
587
		if (newValue != null && !newValue.equals(value)) {
588
			PropertyService.setPropertyNoPersist(propertyName, newValue);
589
		}
590
	}
591

    
592
}
(1-1/5)