View Javadoc

1   /*
2    * ConfigurationToolkit.java
3    *
4    * Created on June 4, 2004, 10:51 AM
5    */
6   
7   package gov.bnl.gums.configuration;
8   
9   import gov.bnl.gums.account.AccountPoolMapper;
10  import gov.bnl.gums.account.GecosAccountMapper;
11  import gov.bnl.gums.account.GecosLdapAccountMapper;
12  import gov.bnl.gums.account.LdapAccountMapper;
13  import gov.bnl.gums.account.GecosNisAccountMapper;
14  import gov.bnl.gums.account.GroupAccountMapper;
15  import gov.bnl.gums.account.ManualAccountMapper;
16  import gov.bnl.gums.account.GecosNisAccountMapper;
17  import gov.bnl.gums.groupToAccount.GroupToAccountMapping;
18  import gov.bnl.gums.hostToGroup.CertificateHostToGroupMapping;
19  import gov.bnl.gums.persistence.HibernatePersistenceFactory;
20  import gov.bnl.gums.persistence.LDAPPersistenceFactory;
21  import gov.bnl.gums.persistence.LocalPersistenceFactory;
22  import gov.bnl.gums.userGroup.LDAPUserGroup;
23  import gov.bnl.gums.userGroup.ManualUserGroup;
24  import gov.bnl.gums.userGroup.VOMSUserGroup;
25  import gov.bnl.gums.userGroup.VomsServer;
26  
27  import java.io.IOException;
28  import java.util.*;
29  
30  import javax.xml.parsers.DocumentBuilder;
31  import javax.xml.parsers.DocumentBuilderFactory;
32  import javax.xml.parsers.ParserConfigurationException;
33  
34  import org.xml.sax.ErrorHandler;
35  import org.xml.sax.SAXParseException;
36  import org.xml.sax.SAXException;
37  import org.apache.commons.beanutils.MethodUtils;
38  import org.apache.commons.digester.*;
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  /** 
43   * Contains the logic on how to parse an XML configuration file to create a
44   * correctly built Configuration object.
45   *
46   * @author Gabriele Carcassi, Jay Packard
47   */
48  class ConfigurationToolkit {
49  	static private Log log = LogFactory.getLog(ConfigurationToolkit.class); 
50      
51      /**
52       * Simple error handler that logs errors
53       * 
54       * @author jpackard
55       */
56      public class SimpleErrorHandler implements ErrorHandler {
57          public boolean error = false;
58      	
59          public void error(SAXParseException exception) {
60          	log.error(exception.getMessage());
61          	error = true;
62          }
63               
64          public void fatalError(SAXParseException exception) {
65          	log.fatal(exception.getMessage());
66          	error = true;
67          }
68               
69          public void warning(SAXParseException exception) {
70          	log.warn(exception.getMessage());
71          }
72      }
73      
74      // Rule for handling the list of accountMappers within a groupToAccountMapping
75      private static class AccountMapperListRule extends Rule {
76          
77          public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
78              if (attributes.getValue("accountMappers") != null) {
79                  Configuration conf = (Configuration) getDigester().getRoot();
80                  GroupToAccountMapping gTAMapping = (GroupToAccountMapping)getDigester().peek();
81                  StringTokenizer tokens = new StringTokenizer(attributes.getValue("accountMappers"), ",");
82                  while (tokens.hasMoreTokens()) {
83                      String accountMapperName = tokens.nextToken().trim();
84                      Object accountMapper = conf.getAccountMappers().get(accountMapperName);
85                      if (accountMapper == null) {
86                          throw new IllegalArgumentException("The accountMapper '" + accountMapperName + "' is used within a groupToAccountMapping, but it was not defined.");
87                      }
88                      MethodUtils.invokeMethod(gTAMapping, "addAccountMapper", accountMapperName);
89                  }
90              }
91          }
92          
93      }
94  
95      // Rule for handling the list of groupToAccountMappings within a hostToGroupMapping
96      private static class GroupListRule extends Rule {
97          
98          public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
99              if (attributes.getValue("groupToAccountMappings") != null) {
100                 Configuration conf = (Configuration) getDigester().getRoot();
101                 Object obj = getDigester().peek();
102                 StringTokenizer tokens = new StringTokenizer(attributes.getValue("groupToAccountMappings"), ",");
103                 while (tokens.hasMoreTokens()) {
104                     String groupToAccountMappingName = tokens.nextToken().trim();
105                     Object groupToAccountMapping = conf.getGroupToAccountMappings().get(groupToAccountMappingName);
106                     if (groupToAccountMapping == null) {
107                         throw new IllegalArgumentException("The groupToAccountMapping '" + groupToAccountMappingName + "' is used within a hostToGroupMapping, but it was not defined.");
108                     }
109                     MethodUtils.invokeMethod(obj, "addGroupToAccountMapping", groupToAccountMappingName);
110                 }
111             }
112         }
113         
114     }    
115     
116     // Simple override rule that ignores missing properties
117     // and excludes given properties
118     private static class PassRule extends SetPropertiesRule {
119     	
120     	public PassRule(String [] excludes) {
121     		super(excludes, new String[]{});
122     		setIgnoreMissingProperty(false);
123     	}
124     	
125     }
126 
127     // Rule for handling a reference to a persistent Factory
128     private static class PersistenceFactoryRule extends Rule {
129         
130         public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
131             if (attributes.getValue("persistenceFactory") != null) {
132                 Configuration conf = (Configuration) getDigester().getRoot();
133                 Object mapper = getDigester().peek();
134                 String persistenceFactoryName = attributes.getValue("persistenceFactory").trim();
135                 Object persistenceFactory = conf.getPersistenceFactories().get(persistenceFactoryName);
136                 if (persistenceFactory == null) {
137                     throw new IllegalArgumentException("The persistence factory '" + persistenceFactoryName + "' is used, but it was not defined.");
138                 }
139                 MethodUtils.invokeMethod(mapper, "setPersistenceFactory", new Object[] {persistenceFactoryName});
140             }
141         }
142         
143     }
144     
145     // Rule for distinguishing between 3rd party properties and GUMS properties
146     private static class PersistencePropertiesRule extends Rule {
147         
148         public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
149             Object digestor = getDigester().peek();
150             Properties properties = new Properties();
151             for (int nAtt = 0; nAtt < attributes.getLength(); nAtt++) {
152                 String name = attributes.getQName(nAtt);
153                 String value = attributes.getValue(nAtt);
154                 log.trace("Adding " + name + " " + value + " property");
155                 if (name.equals("name"))
156                     MethodUtils.invokeMethod(digestor, "setName", new Object[] {value});
157                 else if (name.equals("description"))
158                     MethodUtils.invokeMethod(digestor, "setDescription", new Object[] {value});
159                 else if (name.equals("synchGroups"))
160                     MethodUtils.invokeMethod(digestor, "setSynchGroups", new Object[] {new Boolean(value.equals("true"))});
161                 else if (name.equals("caCertFile"))
162                     MethodUtils.invokeMethod(digestor, "setCaCertFile", new Object[] {value});
163                 else if (name.equals("groupIdField"))
164                     MethodUtils.invokeMethod(digestor, "setGroupIdField", new Object[] {value});
165                 else if (name.equals("accountField"))
166                     MethodUtils.invokeMethod(digestor, "setAccountField", new Object[] {value});
167                 else if (name.equals("memberAccountField"))
168                     MethodUtils.invokeMethod(digestor, "setMemberAccountField", new Object[] {value});
169                 else if (name.equals("trustStorePassword"))
170                     MethodUtils.invokeMethod(digestor, "setTrustStorePassword", new Object[] {value});
171                 else if (!name.equals("className"))
172                 	properties.setProperty(name, value);
173             }
174             MethodUtils.invokeMethod(digestor, "setProperties", properties);
175         }
176         
177     };
178     
179     // Rule for handling the list of userGroups within a groupToAccountMapping
180     private static class UserGroupListRule extends Rule {
181         
182         public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
183             if (attributes.getValue("userGroups") != null) {
184                 Configuration conf = (Configuration) getDigester().getRoot();
185                 Object mapping = getDigester().peek();
186                 StringTokenizer tokens = new StringTokenizer(attributes.getValue("userGroups"), ",");
187                 while (tokens.hasMoreTokens()) {
188                     String userGroupName = tokens.nextToken().trim();
189                     Object userGroup = conf.getUserGroups().get(userGroupName);
190                     if (userGroup == null) {
191                         throw new IllegalArgumentException("The userGroup '" + userGroupName + "' is used within a groupToAccountMapping, but it was not defined.");
192                     }
193                     MethodUtils.invokeMethod(mapping, "addUserGroup", userGroupName);
194                 }
195             }
196         }
197         
198     }
199     
200     //  Rule for handling a reference to a VO
201     private static class VomsServerRule extends Rule {
202         
203         public void begin(String str, String str1, org.xml.sax.Attributes attributes) throws java.lang.Exception {
204             if (attributes.getValue("vomsServer") != null) {
205                 Configuration conf = (Configuration) getDigester().getRoot();
206                 Object obj = getDigester().peek();
207                 String vomsServerName = attributes.getValue("vomsServer").trim();
208                 Object vomsServer = conf.getVomsServers().get(vomsServerName);
209                 if (vomsServer == null) {
210                     throw new IllegalArgumentException("The VOMS server '" + vomsServerName + "' is used, but it was not defined.");
211                 }
212                 MethodUtils.invokeMethod(obj, "setVomsServer", new Object[] {vomsServerName});
213             }
214         }
215         
216     }          
217     
218     /**
219      * Get the gums.config version for the given file
220      * 
221      * @param filename
222      * @return
223      * @throws IOException
224      * @throws SAXException
225      */
226     public static String getVersion(String filename) throws IOException, SAXException {
227         Digester digester = new Digester();
228         digester.setValidating(false);
229         digester.addObjectCreate("gums", Version.class);
230         digester.addSetProperties("gums");
231         log.trace("Loading the version from configuration file '" + filename + "'");
232         digester.parse("file://"+filename);
233         String version = ((Version)digester.getRoot()).getVersion();
234         log.trace("Loaded gums.config is version " + version );
235         if (version == null)
236         	return "1.1";
237         return version;
238     }
239     
240     /**
241      * @return a Digestor object for parsing gums.config
242      */
243     public static Digester retrieveDigester() {
244         Digester digester = new Digester();
245         digester.setValidating(false);
246        
247         digester.addSetProperties("gums");
248         
249         digester.addObjectCreate("gums/persistenceFactories/hibernatePersistenceFactory", HibernatePersistenceFactory.class);
250         digester.addSetProperties("gums/persistenceFactories/hibernatePersistenceFactory");
251         digester.addRule("gums/persistenceFactories/hibernatePersistenceFactory", new PersistencePropertiesRule());
252         digester.addSetNext("gums/persistenceFactories/hibernatePersistenceFactory", "addPersistenceFactory", "gov.bnl.gums.persistence.PersistenceFactory");
253 
254         digester.addObjectCreate("gums/persistenceFactories/ldapPersistenceFactory", LDAPPersistenceFactory.class);
255         digester.addSetProperties("gums/persistenceFactories/ldapPersistenceFactory");
256         digester.addRule("gums/persistenceFactories/ldapPersistenceFactory", new PersistencePropertiesRule());
257         digester.addSetNext("gums/persistenceFactories/ldapPersistenceFactory", "addPersistenceFactory", "gov.bnl.gums.persistence.PersistenceFactory");
258         
259         digester.addObjectCreate("gums/persistenceFactories/localPersistenceFactory", LocalPersistenceFactory.class);
260         digester.addSetProperties("gums/persistenceFactories/localPersistenceFactory");
261         digester.addRule("gums/persistenceFactories/localPersistenceFactory", new PersistencePropertiesRule());
262         digester.addSetNext("gums/persistenceFactories/localPersistenceFactory", "addPersistenceFactory", "gov.bnl.gums.persistence.PersistenceFactory");
263 
264         digester.addObjectCreate("gums/vomsServers/vomsServer", VomsServer.class);
265         digester.addSetProperties("gums/vomsServers/vomsServer");
266         digester.addRule("gums/vomsServers/vomsServer", new PassRule(new String[] {"persistenceFactory"}));
267         digester.addRule("gums/vomsServers/vomsServer", new PersistenceFactoryRule());
268         digester.addSetNext("gums/vomsServers/vomsServer", "addVomsServer", "gov.bnl.gums.userGroup.VomsServer");
269         
270         digester.addObjectCreate("gums/userGroups/ldapUserGroup", LDAPUserGroup.class);
271         digester.addRule("gums/userGroups/ldapUserGroup", new PassRule(new String[] {"className", "persistenceFactory", "vomsServer"}));
272         digester.addRule("gums/userGroups/ldapUserGroup", new PersistenceFactoryRule());
273         digester.addRule("gums/userGroups/ldapUserGroup", new VomsServerRule());
274         digester.addSetNext("gums/userGroups/ldapUserGroup", "addUserGroup", "gov.bnl.gums.userGroup.UserGroup");
275 
276         digester.addObjectCreate("gums/userGroups/manualUserGroup", ManualUserGroup.class);
277         digester.addRule("gums/userGroups/manualUserGroup", new PassRule(new String[] {"className", "persistenceFactory", "vomsServer"}));
278         digester.addRule("gums/userGroups/manualUserGroup", new PersistenceFactoryRule());
279         digester.addRule("gums/userGroups/manualUserGroup", new VomsServerRule());
280         digester.addSetNext("gums/userGroups/manualUserGroup", "addUserGroup", "gov.bnl.gums.userGroup.UserGroup");
281         
282         digester.addObjectCreate("gums/userGroups/vomsUserGroup", VOMSUserGroup.class);
283         digester.addRule("gums/userGroups/vomsUserGroup", new PassRule(new String[] {"className", "persistenceFactory", "vomsServer"}));
284         digester.addRule("gums/userGroups/vomsUserGroup", new PersistenceFactoryRule());
285         digester.addRule("gums/userGroups/vomsUserGroup", new VomsServerRule());
286         digester.addSetNext("gums/userGroups/vomsUserGroup", "addUserGroup", "gov.bnl.gums.userGroup.UserGroup");
287         
288         digester.addObjectCreate("gums/accountMappers/accountPoolMapper", AccountPoolMapper.class);
289         digester.addSetProperties("gums/accountMappers/accountPoolMapper");
290         digester.addRule("gums/accountMappers/accountPoolMapper", new PersistenceFactoryRule());
291         digester.addSetNext("gums/accountMappers/accountPoolMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
292         
293         digester.addObjectCreate("gums/accountMappers/gecosAccountMapper", GecosAccountMapper.class);
294         digester.addSetProperties("gums/accountMappers/gecosAccountMapper");
295         digester.addRule("gums/accountMappers/gecosAccountMapper", new PersistenceFactoryRule());
296         digester.addSetNext("gums/accountMappers/gecosAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
297         
298         digester.addObjectCreate("gums/accountMappers/gecosLdapAccountMapper", GecosLdapAccountMapper.class);
299         digester.addSetProperties("gums/accountMappers/gecosLdapAccountMapper");
300         digester.addSetNext("gums/accountMappers/gecosLdapAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
301 
302         digester.addObjectCreate("gums/accountMappers/ldapAccountMapper", LdapAccountMapper.class);
303         digester.addSetProperties("gums/accountMappers/ldapAccountMapper");
304         digester.addSetNext("gums/accountMappers/ldapAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
305         
306         digester.addObjectCreate("gums/accountMappers/gecosNisAccountMapper", GecosNisAccountMapper.class);
307         digester.addSetProperties("gums/accountMappers/gecosNisAccountMapper");
308         digester.addSetNext("gums/accountMappers/gecosNisAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
309 
310         digester.addObjectCreate("gums/accountMappers/groupAccountMapper", GroupAccountMapper.class);
311         digester.addSetProperties("gums/accountMappers/groupAccountMapper");
312         digester.addSetNext("gums/accountMappers/groupAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
313         
314         digester.addObjectCreate("gums/accountMappers/manualAccountMapper", ManualAccountMapper.class);
315         digester.addSetProperties("gums/accountMappers/manualAccountMapper");
316         digester.addRule("gums/accountMappers/manualAccountMapper", new PersistenceFactoryRule());
317         digester.addSetNext("gums/accountMappers/manualAccountMapper", "addAccountMapper", "gov.bnl.gums.account.AccountMapper");
318         
319         digester.addObjectCreate("gums/groupToAccountMappings/groupToAccountMapping", GroupToAccountMapping.class);
320         digester.addSetProperties("gums/groupToAccountMappings/groupToAccountMapping");
321         digester.addRule("gums/groupToAccountMappings/groupToAccountMapping", new PassRule(new String[] {"userGroups", "accountMappers"}));
322         digester.addRule("gums/groupToAccountMappings/groupToAccountMapping", new UserGroupListRule());
323         digester.addRule("gums/groupToAccountMappings/groupToAccountMapping", new AccountMapperListRule());
324         digester.addSetNext("gums/groupToAccountMappings/groupToAccountMapping", "addGroupToAccountMapping", "gov.bnl.gums.groupToAccount.GroupToAccountMapping");
325 
326         digester.addObjectCreate("gums/hostToGroupMappings/hostToGroupMapping", CertificateHostToGroupMapping.class);
327         digester.addSetProperties("gums/hostToGroupMappings/hostToGroupMapping");
328         digester.addRule("gums/hostToGroupMappings/hostToGroupMapping", new PassRule(new String[] {"groupToAccountMappings"}));
329         digester.addRule("gums/hostToGroupMappings/hostToGroupMapping", new GroupListRule());
330         digester.addSetNext("gums/hostToGroupMappings/hostToGroupMapping", "addHostToGroupMapping", "gov.bnl.gums.hostToGroup.HostToGroupMapping");
331        
332         return digester;
333     }
334     
335     /**
336      * Validate gums.config given a config file and a schema file
337      * 
338      * @param configFile
339      * @param schemaFile
340      * @throws ParserConfigurationException
341      * @throws SAXException
342      * @throws IOException
343      */
344     public static void validate(String configFile, String schemaFile) throws ParserConfigurationException, SAXException, IOException {
345     	System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
346     	DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
347         log.trace("DocumentBuilderFactory: "+ factory.getClass().getName());
348         
349         factory.setNamespaceAware(true);
350         factory.setValidating(true);
351         factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema");
352         factory.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaSource", "file:"+schemaFile);
353         
354         DocumentBuilder builder = factory.newDocumentBuilder();
355         SimpleErrorHandler errorHandler = new ConfigurationToolkit().new SimpleErrorHandler();
356         builder.setErrorHandler( errorHandler );
357 
358         builder.parse(configFile); 
359         
360         if (errorHandler.error)
361         	throw new ParserConfigurationException();
362     }
363 
364     /**
365      * Load gums.config
366      * 
367      * @param configFile
368      * @return
369      * @throws ParserConfigurationException
370      * @throws IOException
371      * @throws SAXException
372      */
373     public static synchronized Configuration loadConfiguration(String configPath, String schemaPath) throws ParserConfigurationException, IOException, SAXException {
374        	validate(configPath, schemaPath);
375     	Digester digester = retrieveDigester();
376     	Configuration configuration = new Configuration();
377         digester.push(configuration);
378         log.trace("Loading the configuration from file '" + configPath + "' using schema '" + schemaPath);
379         digester.parse("file://"+configPath);
380         return configuration;
381     }
382     
383 }