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-10-10 17:12:49 -0700 (Fri, 10 Oct 2008) $'
10
 * '$Revision: 4442 $'
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.OrganizationUtil;
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 ORG_CONFIG_NAME = "org.properties";
63
	private static final String LDAP_CONFIG_NAME = "ldap.properties";
64
	
65
	private static boolean bypassAlreadyChecked = false;
66
	
67
	private static String mainPropertiesFilePath = null;
68
	private static SortedProperties mainProperties = null;
69
	
70
	private static String mainBackupPropertiesFilePath = null;
71
	private static SortedProperties mainBackupProperties = null;
72
	
73
	private static String propertiesMetaDataFilePath = null;
74
	private static PropertiesMetaData mainMetaData = null;
75
	
76
	private static String orgBackupPropertiesFilePath = null;
77
	private static SortedProperties orgBackupProperties = null;
78
	
79
	private static String orgMetaDataFilePath = null;
80
	private static PropertiesMetaData orgMetaData = null;
81
	
82
	private static String ldapBackupPropertiesFilePath = null;
83
	private static SortedProperties ldapBackupProperties = null;
84
	
85
	private static String ldapMetaDataFilePath = null;
86
	private static PropertiesMetaData ldapMetaData = null;
87
	
88
	
89
	private static Logger logMetacat = Logger.getLogger(PropertyService.class);
90

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

    
139
		// mainProperties will hold the primary configuration values for metacat.
140
		mainPropertiesFilePath = configDir + FileUtil.getFS() + MAIN_CONFIG_NAME;
141
		if (mainProperties == null) {
142
			mainProperties = new SortedProperties(mainPropertiesFilePath);
143
			mainProperties.load();
144
		}
145

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

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

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

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

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

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

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

    
272
	/**
273
	 * Utility method to set a property value in memory. This will NOT cause the
274
	 * property to be written to disk. Use this method to set multiple
275
	 * properties in a row without causing excessive I/O. You must call
276
	 * persistProperties() once you're done setting properties to have them
277
	 * written to disk.
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 setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
285
		mainProperties.setPropertyNoPersist(propertyName, newValue);
286
	}
287

    
288
	/**
289
	 * Save the properties to a properties file. Note, the 
290
	 * order and comments will be preserved.
291
	 */
292
	public static void persistProperties() throws GeneralPropertyException {
293
		mainProperties.store();
294
	}
295
	
296
	/**
297
	 * Get the main backup properties file. These are configurable properties that
298
	 * are stored outside the metacat install directories so the user does not
299
	 * need to re-enter all the configuration information every time they do an
300
	 * upgrade.
301
	 * 
302
	 * @return a SortedProperties object with the backup properties
303
	 */
304
	public static SortedProperties getMainBackupProperties() {
305
		return mainBackupProperties;
306
	}
307
	
308
	/**
309
	 * Get the organizational backup properties file. These are configurable 
310
	 * properties that are stored outside the metacat install directories so 
311
	 * the user does not need to re-enter all the configuration information 
312
	 * every time they do an upgrade.
313
	 * 
314
	 * @return a SortedProperties object with the backup properties
315
	 */
316
	public static SortedProperties getOrgBackupProperties() {
317
		return orgBackupProperties;
318
	}
319
	
320
	/**
321
	 * Get the LDAP 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 getLDAPBackupProperties() {
329
		return ldapBackupProperties;
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 organization 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 getOrgMetaData() {
350
		return orgMetaData;
351
	}
352
	
353
	/**
354
	 * Get the LDAP properties metadata. This is retrieved from an xml
355
	 * file that describes the attributes of configurable properties.
356
	 * 
357
	 * @return a PropertiesMetaData object with the organization properties
358
	 *         metadata
359
	 */
360
	public static PropertiesMetaData getLDAPMetaData() {
361
		return ldapMetaData;
362
	}
363
	
364
	/**
365
	 * Writes out backup configurable properties to a file.
366
	 */
367
	public static void persistMainBackupProperties(ServletContext servletContext)
368
			throws GeneralPropertyException {
369

    
370
		// Use the metadata to extract configurable properties from the 
371
		// overall properties list, and store those properties.
372
		try {
373
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
374
			
375
			// Populate the backup properties for main metacat properties using
376
			// the associated metadata file
377
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
378
			Set<String> mainKeySet = mainMetadata.getKeys();
379
			for (String propertyKey : mainKeySet) {
380
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
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 persistOrgBackupProperties(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(orgBackupPropertiesFilePath);
409
			
410
			// Populate the backup properties for organization properties using
411
			// the associated metadata file
412
			PropertiesMetaData orgMetadata = new PropertiesMetaData(orgMetaDataFilePath);
413
			
414
			// We do the same thing here for organization specific properies
415
			// with the addition that we need to iterate through all available 
416
			// organizations.  For instance, a metadata section that defines a 
417
			// property as "organization.base" will be entered into the metacat.properties
418
			// file with a key of "organization.base.NCEAS" for the NCEAS organization. 
419
			// This will be repeated for all available organizations.
420
			Set<String> orgKeySet = orgMetadata.getKeys();
421
			for (String orgName : OrganizationUtil.getOrganizations()) {
422
				for (String propertyKey : orgKeySet) {
423
					String orgPropertyKey = propertyKey + "." + orgName;
424
					backupProperties.addProperty(orgPropertyKey, getProperty(orgPropertyKey));
425
				}
426
			}
427
			
428
			// store the properties to file
429
			backupProperties.store();
430
			orgBackupProperties = 
431
				new SortedProperties(orgBackupPropertiesFilePath);
432
			orgBackupProperties.load();
433

    
434
		} catch (TransformerException te) {
435
			throw new GeneralPropertyException("Could not transform backup properties xml: "
436
					+ te.getMessage());
437
		} catch (IOException ioe) {
438
			throw new GeneralPropertyException("Could not backup configurable properties: "
439
					+ ioe.getMessage());
440
		} catch (UtilException ue) {
441
			throw new GeneralPropertyException("Could not get organizations: " + ue.getMessage());
442
		}
443
	}
444
	
445
	/**
446
	 * Writes out backup configurable properties to a file.
447
	 */
448
	public static void persistLDAPBackupProperties(ServletContext servletContext)
449
			throws GeneralPropertyException {
450

    
451
		// Use the metadata to extract configurable properties from the 
452
		// overall properties list, and store those properties.
453
		try {
454
			SortedProperties backupProperties = 
455
				new SortedProperties(ldapBackupPropertiesFilePath);
456
			
457
			// Populate the backup properties for ldap properties using
458
			// the associated metadata file
459
			PropertiesMetaData ldapMetadata = new PropertiesMetaData(ldapMetaDataFilePath);
460

    
461
			Set<String> ldapKeySet = ldapMetadata.getKeys();
462
			for (String propertyKey : ldapKeySet) {
463
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
464
			}
465
			
466
			// store the properties to file
467
			backupProperties.store();
468
			ldapBackupProperties = 
469
				new SortedProperties(ldapBackupPropertiesFilePath);
470
			ldapBackupProperties.load();
471

    
472
		} catch (TransformerException te) {
473
			throw new GeneralPropertyException("Could not transform backup properties xml: "
474
					+ te.getMessage());
475
		} catch (IOException ioe) {
476
			throw new GeneralPropertyException("Could not backup configurable properties: "
477
					+ ioe.getMessage());
478
		} 
479
	}
480

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

    
543
			// The system is bypassing the configuration utility. We need to
544
			// get the backup properties and replace existing properties with
545
			// backup values.  We do this for main and org properties.		
546
			logMetacat.debug("bypassConfiguration: setting main backup properties.");
547
			SortedProperties mainBackupProperties = getMainBackupProperties();
548
			Vector<String> backupPropertyNames = 
549
				mainBackupProperties.getPropertyNames();
550
			for (String backupPropertyName : backupPropertyNames) {
551
				String value = mainBackupProperties.getProperty(backupPropertyName);
552
				setPropertyNoPersist(backupPropertyName, value);
553
			}
554
				
555
//			SortedProperties orgBackupProperties = getOrgBackupProperties();
556
//			Vector<String> orgBackupPropertyNames = 
557
//				orgBackupProperties.getPropertyNames();
558
//			for (String orgBackupPropertyName : orgBackupPropertyNames) {
559
//				String value = orgBackupProperties.getProperty(orgBackupPropertyName);
560
//				setPropertyNoPersist(orgBackupPropertyName, value);
561
//			}
562

    
563
			logMetacat.debug("bypassConfiguration: setting ldap backup properties.");
564
			SortedProperties ldapBackupProperties = getLDAPBackupProperties();
565
			Vector<String> ldapBackupPropertyNames = 
566
				ldapBackupProperties.getPropertyNames();
567
			for (String ldapBackupPropertyName : ldapBackupPropertyNames) {
568
				String value = ldapBackupProperties.getProperty(ldapBackupPropertyName);
569
				setPropertyNoPersist(ldapBackupPropertyName, value);
570
			}
571

    
572
			logMetacat.debug("bypassConfiguration: setting configutil sections to true.");
573
			setPropertyNoPersist("configutil.propertiesConfigured", "true");
574
			setPropertyNoPersist("configutil.ldapConfigured", "true");
575
//			setPropertyNoPersist("configutil.organizationsConfigured", "true");
576
			setPropertyNoPersist("configutil.skinsConfigured", "true");
577
			setPropertyNoPersist("configutil.databaseConfigured", "true");
578
			setPropertyNoPersist("configutil.geoserverConfigured", "bypassed");
579
				
580
			persistProperties();
581

    
582
		} catch (PropertyNotFoundException pnfe) {
583
			logMetacat.error("bypassConfiguration: Could not find property: " + pnfe.getMessage());
584
		} catch (GeneralPropertyException gpe) {
585
			logMetacat.error("bypassConfiguration: General property error: " + gpe.getMessage());
586
		}
587

    
588
		bypassAlreadyChecked = true;
589
		return bypass;
590
	}
591
	
592
	/**
593
	 * Take input from the user in an HTTP request about an property to be changed
594
	 * and update the metacat property file with that new value if it has
595
	 * changed from the value that was originally set.
596
	 * 
597
	 * @param request
598
	 *            that was generated by the user
599
	 * @param response
600
	 *            to send output back to the user
601
	 * @param propertyName
602
	 *            the name of the property to be checked and set
603
	 */
604
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName) 
605
			throws GeneralPropertyException {
606
		String value = PropertyService.getProperty(propertyName);
607
		String newValue = request.getParameter(propertyName);
608
		if (newValue != null && !newValue.equals(value)) {
609
			PropertyService.setPropertyNoPersist(propertyName, newValue);
610
		}
611
	}
612

    
613
}
(3-3/9)