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-11 10:08:07 -0700 (Fri, 11 Jul 2008) $'
10
 * '$Revision: 4107 $'
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.LDAPUtil;
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 CONFIG_DIR = "WEB-INF";
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 Logger logMetacat = Logger.getLogger(PropertyService.class);
83

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

    
122
		// mainProperties will hold the primary configuration values for metacat.
123
		mainPropertiesFilePath = servletContext.getRealPath(CONFIG_DIR) + FileUtil.getFS() + MAIN_CONFIG_NAME;
124
		if (mainProperties == null) {
125
			mainProperties = new SortedProperties(mainPropertiesFilePath);
126
			mainProperties.load();
127
		}
128

    
129
		try {
130
			// mainMetaData holds configuration information about main properties.
131
			// This is primarily used to display input fields on the configuration
132
			// page. The information is retrieved from an xml metadata file
133
			propertiesMetaDataFilePath = servletContext.getRealPath(CONFIG_DIR) + FileUtil.getFS() + MAIN_CONFIG_NAME + ".metadata.xml";
134
			if (mainMetaData == null) {
135
				mainMetaData = new PropertiesMetaData(propertiesMetaDataFilePath);
136
			}
137

    
138
			// orgMetaData holds configuration information about organization level 
139
			// properties.  these are mostly ldap information relative to an 
140
			// organization. This is primarily used to display input fields on 
141
			// the organization configuration page. The information is retrieved 
142
			// from an xml metadata file dedication just to organization properties.
143
			orgMetaDataFilePath = servletContext.getRealPath(CONFIG_DIR) + FileUtil.getFS() + ORG_CONFIG_NAME + ".metadata.xml";
144
			if (orgMetaData == null) {
145
				orgMetaData = new PropertiesMetaData(orgMetaDataFilePath);
146
			}
147
		} catch (TransformerException te) {
148
			throw new GeneralPropertyException(te.getMessage());
149
		}
150

    
151
		String backupDirPath = getBackupDir();
152
		
153
		// The mainBackupProperties hold properties that were backed up the 
154
		// last time the application was configured.  On disk, the file will
155
		// look like a smaller version of metacat.properties.  It is stored 
156
		// in the data storage directory outside the application directories.
157
		mainBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() + MAIN_CONFIG_NAME + ".backup";
158
		if (mainBackupProperties == null) {
159
			mainBackupProperties = 
160
				new SortedProperties(mainBackupPropertiesFilePath);
161
			mainBackupProperties.load();
162
		}
163
		
164
		// The orgBackupProperties hold properties that were backed up the 
165
		// last time the organizations were configured.  On disk, the file will
166
		// look like a smaller version of metacat.properties.  It is stored 
167
		// in the data storage directory outside the application directories.
168
		orgBackupPropertiesFilePath = backupDirPath + FileUtil.getFS() +  ORG_CONFIG_NAME + ".backup";
169
		if (orgBackupProperties == null) {
170
			orgBackupProperties = 
171
				new SortedProperties(orgBackupPropertiesFilePath);
172
			orgBackupProperties.load();
173
		}
174
	
175
	}
176

    
177
	/**
178
	 * Utility method to get a property value from the properties file
179
	 * 
180
	 * @param propertyName
181
	 *            the name of the property requested
182
	 * @return the String value for the property
183
	 */
184
	public static String getProperty(String propertyName)
185
			throws PropertyNotFoundException {
186
		return mainProperties.getProperty(propertyName);
187
	}
188
	
189
	/**
190
     * Get a set of all property names.
191
     * 
192
     * @return Set of property names  
193
     */
194
    public static Vector<String> getPropertyNames() {   	
195
    	return mainProperties.getPropertyNames();
196
    }
197
    
198

    
199
	/**
200
	 * Get a Set of all property names that start with the groupName prefix.
201
	 * 
202
	 * @param groupName
203
	 *            the prefix of the keys to search for.
204
	 * @return enumeration of property names
205
	 */
206
    public static Vector<String> getPropertyNamesByGroup(String groupName) {   	
207
    	return mainProperties.getPropertyNamesByGroup(groupName);
208
    }
209
    
210
	/**
211
	 * Get a Map of all properties that start with the groupName prefix.
212
	 * 
213
	 * @param groupName
214
	 *            the prefix of the keys to search for.
215
	 * @return Map of property names
216
	 */
217
    public static Map<String, String> getPropertiesByGroup(String groupName) throws PropertyNotFoundException {   	
218
    	return mainProperties.getPropertiesByGroup(groupName);
219
    }
220

    
221
	/**
222
	 * Utility method to set a property value both in memory and to the
223
	 * properties file
224
	 * 
225
	 * @param propertyName
226
	 *            the name of the property requested
227
	 * @param newValue
228
	 *            the new value for the property
229
	 */
230
	public static void setProperty(String propertyName, String newValue) throws GeneralPropertyException {
231
			mainProperties.setProperty(propertyName, newValue);
232
			mainProperties.store();
233
	}
234

    
235
	/**
236
	 * Utility method to set a property value in memory. This will NOT cause the
237
	 * property to be written to disk. Use this method to set multiple
238
	 * properties in a row without causing excessive I/O. You must call
239
	 * persistProperties() once you're done setting properties to have them
240
	 * written to disk.
241
	 * 
242
	 * @param propertyName
243
	 *            the name of the property requested
244
	 * @param newValue
245
	 *            the new value for the property
246
	 */
247
	public static void setPropertyNoPersist(String propertyName, String newValue) throws GeneralPropertyException {
248
		mainProperties.setPropertyNoPersist(propertyName, newValue);
249
	}
250

    
251
	/**
252
	 * Save the properties to a properties file. Note, the 
253
	 * order and comments will be preserved.
254
	 */
255
	public static void persistProperties() throws GeneralPropertyException {
256
		mainProperties.store();
257
	}
258

    
259
	/**
260
	 * Get the main backup properties file. These are configurable properties that
261
	 * are stored outside the metacat install directories so the user does not
262
	 * need to re-enter all the configuration information every time they do an
263
	 * upgrade.
264
	 * 
265
	 * @return a SortedProperties object with the backup properties
266
	 */
267
	public static SortedProperties getMainBackupProperties() {
268
		return mainBackupProperties;
269
	}
270
	
271
	/**
272
	 * Get the organizational backup properties file. These are configurable 
273
	 * properties that are stored outside the metacat install directories so 
274
	 * the user does not need to re-enter all the configuration information 
275
	 * every time they do an upgrade.
276
	 * 
277
	 * @return a SortedProperties object with the backup properties
278
	 */
279
	public static SortedProperties getOrgBackupProperties() {
280
		return orgBackupProperties;
281
	}
282
	
283
	/**
284
	 * Get the main properties metadata. This is retrieved from an xml file that
285
	 * describes the attributes of configurable properties.
286
	 * 
287
	 * @return a PropertiesMetaData object with the main properties metadata
288
	 */
289
	public static PropertiesMetaData getMainMetaData() {
290
		return mainMetaData;
291
	}
292
	
293
	/**
294
	 * Get the organization properties metadata. This is retrieved from an xml
295
	 * file that describes the attributes of configurable properties.
296
	 * 
297
	 * @return a PropertiesMetaData object with the organization properties
298
	 *         metadata
299
	 */
300
	public static PropertiesMetaData getOrgMetaData() {
301
		return orgMetaData;
302
	}
303
	
304
	/**
305
	 * Writes out backup configurable properties to a file.
306
	 */
307
	public static void persistMainBackupProperties(ServletContext servletContext)
308
			throws GeneralPropertyException {
309

    
310
		// Use the metadata to extract configurable properties from the 
311
		// overall properties list, and store those properties.
312
		try {
313
			SortedProperties backupProperties = new SortedProperties(mainBackupPropertiesFilePath);
314
			
315
			// Populate the backup properties for main metacat properties using
316
			// the associated metadata file
317
			PropertiesMetaData mainMetadata = new PropertiesMetaData(propertiesMetaDataFilePath);
318
			Set<String> mainKeySet = mainMetadata.getKeys();
319
			for (String propertyKey : mainKeySet) {
320
				backupProperties.addProperty(propertyKey, getProperty(propertyKey));
321
			}
322
			
323
			// store the properties to file
324
			backupProperties.store();
325

    
326
		} catch (TransformerException te) {
327
			throw new GeneralPropertyException("Could not transform backup properties xml: "
328
					+ te.getMessage());
329
		} catch (IOException ioe) {
330
			throw new GeneralPropertyException("Could not backup configurable properties: "
331
					+ ioe.getMessage());
332
		}
333
	}
334
	
335
	/**
336
	 * Writes out backup configurable properties to a file.
337
	 */
338
	public static void persistOrgBackupProperties(ServletContext servletContext)
339
			throws GeneralPropertyException {
340

    
341
		// Use the metadata to extract configurable properties from the 
342
		// overall properties list, and store those properties.
343
		try {
344
			SortedProperties backupProperties = 
345
				new SortedProperties(orgBackupPropertiesFilePath);
346
			
347
			// Populate the backup properties for organization properties using
348
			// the associated metadata file
349
			PropertiesMetaData orgMetadata = new PropertiesMetaData(orgMetaDataFilePath);
350
			
351
			// Here we get the group definition for group 1 from the org
352
			// metadata.  Group 1 should be global values across all 
353
			// organizations.  We then get all properties metadata associated 
354
			// with group 1, iterate through it and set it in the backup
355
			// properties.
356
			MetaDataGroup globalGroup = orgMetadata.getGroup(1);
357
			SortedMap<Integer, MetaDataProperty> globalPropertyMap = 
358
				orgMetadata.getPropertiesInGroup(globalGroup.getIndex());
359
			for (MetaDataProperty property : globalPropertyMap.values()) {
360
				String orgPropertyName = property.getKey();
361
				backupProperties.addProperty(orgPropertyName, getProperty(orgPropertyName));
362
			}
363
			
364
			// We do the same thing here for organization specific properies
365
			// with the addition that we need to iterate through all available 
366
			// organizations.  For instance, a metadata section that defines a 
367
			// property as "ldap.base" will be entered into the metacat.properties
368
			// file with a key of "ldap.base.NCEAS" for the NCEAS organization. 
369
			// This will be repeated for all available organizations.
370
			MetaDataGroup orgGroup = orgMetadata.getGroup(2);
371
			SortedMap<Integer, MetaDataProperty> orgPropertyMap = orgMetadata
372
					.getPropertiesInGroup(orgGroup.getIndex());
373
			for (String orgName : LDAPUtil.getOrganizations()) {
374
				for (MetaDataProperty property : orgPropertyMap.values()) {
375
					String orgPropertyName = property.getKey() + "." + orgName;
376
					backupProperties.addProperty(orgPropertyName, getProperty(orgPropertyName));
377
				}
378
			}
379
			
380
			// store the properties to file
381
			backupProperties.store();
382

    
383
		} catch (TransformerException te) {
384
			throw new GeneralPropertyException("Could not transform backup properties xml: "
385
					+ te.getMessage());
386
		} catch (IOException ioe) {
387
			throw new GeneralPropertyException("Could not backup configurable properties: "
388
					+ ioe.getMessage());
389
		} catch (UtilException ue) {
390
			throw new GeneralPropertyException("Could not get organizations: " + ue.getMessage());
391
		}
392
	}
393

    
394
	/**
395
	 * Gets the backup properties directory
396
	 * 
397
	 * TODO MCD figure out how to expand this.  At least for Windows
398
	 * 
399
	 * @return a string which holds the name of the backup directory. returns
400
	 *         null if directory could not be created.
401
	 */
402
	public static String getBackupDir() {
403
		return "/var/metacat/.metacat";
404
	}
405
	
406
	/**
407
	 * Reports whether properties are fully configured.
408
	 * 
409
	 * @return a boolean that is true if properties are not unconfigured and
410
	 *         false otherwise
411
	 */
412
	public static boolean arePropertiesConfigured() throws UtilException {
413
		try {
414
			return !PropertyService.getProperty("configutil.propertiesConfigured").equals(
415
					PropertyService.UNCONFIGURED);
416
		} catch (PropertyNotFoundException pnfe) {
417
			throw new UtilException("Could not determine if properties are configured: "
418
					+ pnfe.getMessage());
419
		}
420
	}
421
	
422
	/**
423
	 * Take input from the user in an HTTP request about an property to be changed
424
	 * and update the metacat property file with that new value if it has
425
	 * changed from the value that was originally set.
426
	 * 
427
	 * @param request
428
	 *            that was generated by the user
429
	 * @param response
430
	 *            to send output back to the user
431
	 * @param propertyName
432
	 *            the name of the property to be checked and set
433
	 */
434
	public static void checkAndSetProperty(HttpServletRequest request, String propertyName) 
435
			throws GeneralPropertyException {
436
		String value = PropertyService.getProperty(propertyName);
437
		String newValue = request.getParameter(propertyName);
438
		if (newValue != null && !newValue.equals(value)) {
439
			PropertyService.setPropertyNoPersist(propertyName, newValue);
440
		}
441
	}
442

    
443
}
(1-1/5)