View Javadoc

1   /*
2    * FileConfigurationStore.java
3    *
4    * Created on October 20, 2004, 12:48 PM
5    */
6   
7   package gov.bnl.gums.configuration;
8   
9   import gov.bnl.gums.GUMS;
10  import gov.bnl.gums.account.AccountMapper;
11  import gov.bnl.gums.groupToAccount.GroupToAccountMapping;
12  import gov.bnl.gums.hostToGroup.HostToGroupMapping;
13  import gov.bnl.gums.persistence.PersistenceFactory;
14  import gov.bnl.gums.userGroup.UserGroup;
15  import gov.bnl.gums.userGroup.VomsServer;
16  
17  import java.io.BufferedWriter;
18  import java.io.File;
19  import java.io.FileInputStream;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.net.URL;
25  import java.text.DateFormat;
26  import java.text.SimpleDateFormat;
27  import java.util.ArrayList;
28  import java.util.Collection;
29  import java.util.Collections;
30  import java.util.Date;
31  import java.util.Iterator;
32  
33  import org.apache.commons.logging.*;
34  
35  /** Implements the logic to retrieve the configuration from the gums.config file
36   * taken from the classpath. The file will be reloaded as soon as if it changes,
37   * on demand (no polling).
38   *
39   * @author Gabriele Carcassi, Jay Packard
40   */
41  public class FileConfigurationStore implements ConfigurationStore {
42      /**
43       * Copy source to target
44       * 
45       * @param source
46       * @param target
47       */
48      static public void copyFile(String source, String target) {
49          try {
50          	if (System.getProperty("os.name").indexOf("Linux")!=-1) {
51  				// This will preserve soft links
52  				String[] cpArgs = new String[4];
53  				cpArgs[0] = "/bin/cp";
54  				cpArgs[1] = "-f";
55  				cpArgs[2] = source;
56  				cpArgs[3] = target;
57  				if (Runtime.getRuntime().exec(cpArgs).waitFor() != 0)
58  					throw new RuntimeException("Error copying file");
59          	}
60          	else {
61  				FileInputStream fis  = new FileInputStream(source);
62  				FileOutputStream fos = new FileOutputStream(target);
63  				byte[] buf = new byte[1024];
64  				int i = 0;
65  				while((i=fis.read(buf))!=-1)
66  				  fos.write(buf, 0, i);
67  				fis.close();
68  				fos.close();
69          	}
70  		} catch (FileNotFoundException e) {
71  			e.printStackTrace();
72  		} catch (IOException e) {
73  			e.printStackTrace();
74  		} catch (InterruptedException e) {
75  			e.printStackTrace();
76  		}
77  	}
78      
79      /**
80       * Move source to target
81       * 
82       * @param source
83       * @param target
84       */
85      static public void moveFile(String source, String target) {
86      	copyFile(source, target);
87          new File(source).delete();
88      }
89  	
90      private Log log = LogFactory.getLog(FileConfigurationStore.class);
91      private Log gumsSiteAdminLog = LogFactory.getLog(GUMS.siteAdminLog);
92      private Log gumsResourceAdminLog = LogFactory.getLog(GUMS.resourceAdminLog);
93      private Configuration conf;
94      private Date lastRetrival;
95      private String configBackupDir = null;
96      private String configPath = null;
97      private String schemaPath = null;
98      private String transformPath = null;
99      private DateFormat format = new SimpleDateFormat("yyyy_MM_dd_HHmm");
100     private String version;
101 
102     /**
103      * Creates a new FileConfigurationStore object.
104      * Used to instantiate class when run as a unit test.
105      */
106     public FileConfigurationStore() {
107     	URL resource = getClass().getClassLoader().getResource("gums.config");
108     	if (resource!=null) {
109 	        String configDir = resource.getPath().replace("/gums.config", "");
110 	        this.configPath = configDir+"/gums.config";
111 	        this.schemaPath = configDir+"/gums.config.schema";
112 	        this.transformPath = configDir+"/gums.config.transform";   
113 	        this.configBackupDir = configDir+"/backup";
114     	}
115     }
116     
117     /**
118      * Creates a new FileConfigurationStore object.
119      * Allows for specifying the absolute name of the configuration file.
120      * Used to instantiate class when GUMS is run within servlet.
121      * 
122      * @param filename
123      * @param create if true, a new barbones configuration file will be created
124      * at given filename if no file currently exists there
125      */
126     public FileConfigurationStore(String configDir, String resourceDir, String version, boolean create) {
127     	this.version = version;
128         this.configPath = configDir+"/gums.config";
129         this.schemaPath = resourceDir+"/gums.config.schema";
130         this.transformPath = resourceDir+"/gums.config.transform";
131         this.configBackupDir = configDir+"/backup";
132         
133         if (create && !(new File(configPath).exists())) {
134     		try {
135     			BufferedWriter out = new BufferedWriter(new FileWriter(configPath));
136     			out.write("<?xml version='1.0' encoding='UTF-8'?>\n\n"+
137 	    			"<gums version='"+version+"'>\n\n"+
138 	    				"\t<persistenceFactories>\n\n"+
139 	    					"\t\t<hibernatePersistenceFactory\n"+
140 				    			"\t\t\tname='mysql'\n"+
141 				    			"\t\t\tdescription=''\n"+
142 				    			"\t\t\thibernate.connection.driver_class='com.mysql.jdbc.Driver'\n"+
143 				    			"\t\t\thibernate.dialect='net.sf.hibernate.dialect.MySQLDialect'\n"+
144 				    			"\t\t\thibernate.connection.url='jdbc:mysql://localhost.localdomain:3306/GUMS_1_1'\n"+
145 				    			"\t\t\thibernate.connection.username='gums'\n"+
146 				    			"\t\t\thibernate.connection.password=''\n"+
147 				    			"\t\t\thibernate.connection.autoReconnect='true'\n"+
148 				    			"\t\t\thibernate.c3p0.min_size='3'\n"+
149 				    			"\t\t\thibernate.c3p0.max_size='20'\n"+
150 				    			"\t\t\thibernate.c3p0.timeout='180' />\n\n"+
151 				    	"\t</persistenceFactories>\n\n"+
152 	    				"\t<userGroups>\n\n"+
153 				    		"\t\t<manualUserGroup\n"+
154 				    			"\t\t\tname='admins'\n"+
155 				    			"\t\t\tdescription=''\n"+
156 				    			"\t\t\tpersistenceFactory='mysql'\n"+
157 				    			"\t\t\taccess='write'/>\n\n"+
158 	    				"\t</userGroups>\n\n"+
159 	    			"</gums>");
160     			out.close();
161     		} catch (IOException e1) {
162     			gumsResourceAdminLog.error("Could not create gums.config: " + e1.getMessage());
163     		}
164         }
165     }
166     
167     public void deleteBackupConfiguration(String dateStr) {
168     	new File(configBackupDir+"/gums.config."+dateStr).delete();
169     }
170     
171     public Collection getBackupConfigDates() {
172     	ArrayList backupConfigDates = new ArrayList();
173     	File dir = new File(configBackupDir);
174     	String[] children = dir.list();
175     	if (children!=null) {
176 	        for (int i=0; i<children.length; i++) {
177 	        	backupConfigDates.add( children[i].substring(children[i].lastIndexOf(".")+1) );
178 	        }    	
179     	}
180     	Collections.sort(backupConfigDates);
181     	return backupConfigDates;
182     }
183     
184     public boolean isActive() {
185         log.debug("Checking whether gums.config is present");
186         return new File(configPath).exists();
187     }
188     
189     public boolean isReadOnly() {
190         return false;
191     }
192     
193     public synchronized Configuration retrieveConfiguration() {
194         try {
195 			if ((lastRetrival == null) || (lastRetrival.before(lastModification())))
196 			    reloadConfiguration();
197 		} catch (Exception e) {
198 	        throw new RuntimeException(e.getMessage());
199 		}
200         return conf;
201     }
202     
203     public synchronized Configuration restoreConfiguration(String dateStr) {
204        	moveFile(configPath, configBackupDir + "/gums.config~");
205        	copyFile(configBackupDir + "/gums.config." + dateStr, configPath);
206        	moveFile(configBackupDir + "/gums.config~", configBackupDir + "/gums.config.prev");
207         return retrieveConfiguration();
208     }
209     
210     public synchronized void setConfiguration(Configuration conf, boolean backupCopy) throws Exception {
211         log.trace("Configuration set programically");
212         gumsResourceAdminLog.info("Configuration set programically");
213         gumsSiteAdminLog.info("Configuration set programically");
214         if (conf == null)
215             throw new RuntimeException("Configuration has not been loaded");
216         
217         log.debug("Attempting to store configuration");
218         String tempGumsConfigPath = configPath+"~";
219 
220         BufferedWriter out;
221         out = new BufferedWriter(new FileWriter(tempGumsConfigPath));
222        	
223         out.write("<?xml version='1.0' encoding='UTF-8'?>\n\n");
224         
225         out.write("<gums version='"+version+"'>\n\n");
226         
227         // Write persistence factories
228         if( conf.getPersistenceFactories().size()>0 ) {
229             out.write("\t<persistenceFactories>\n\n");
230         	Iterator it = conf.getPersistenceFactories().values().iterator();
231         	while( it.hasNext() ) {
232         		PersistenceFactory persistenceFactory = (PersistenceFactory)it.next();
233         		out.write( persistenceFactory.toXML() );
234         	}
235             out.write("\t</persistenceFactories>\n\n");
236         }
237         
238         // Write Voms Servers
239         if( conf.getVomsServers().size()>0 ) {
240             out.write("\t<vomsServers>\n\n");
241         	Iterator it = conf.getVomsServers().values().iterator();
242         	while( it.hasNext() ) {
243         		VomsServer vo = (VomsServer)it.next();
244         		out.write( vo.toXML() );
245         	}
246             out.write("\t</vomsServers>\n\n");
247         }           
248 
249         // Write User Groups
250         if( conf.getUserGroups().size()>0 ) {
251             out.write("\t<userGroups>\n\n");
252         	Iterator it = conf.getUserGroups().values().iterator();
253         	while( it.hasNext() ) {
254         		UserGroup userGroup = (UserGroup)it.next();
255         		out.write( userGroup.toXML() );
256         	}
257             out.write("\t</userGroups>\n\n");
258         }                
259 
260         // Write Account Mappers
261         if( conf.getAccountMappers().size()>0 ) {
262             out.write("\t<accountMappers>\n\n");
263         	Iterator it = conf.getAccountMappers().values().iterator();
264         	while( it.hasNext() ) {
265         		AccountMapper accountMapper = (AccountMapper)it.next();
266         		out.write( accountMapper.toXML() );
267         	}
268             out.write("\t</accountMappers>\n\n");
269         }             
270 
271         // Write Group To Account Mappings
272         if( conf.getAccountMappers().size()>0 ) {
273             out.write("\t<groupToAccountMappings>\n\n");
274         	Iterator it = conf.getGroupToAccountMappings().values().iterator();
275         	while( it.hasNext() ) {
276         		GroupToAccountMapping groupToAccountMapping = (GroupToAccountMapping)it.next();
277         		out.write( groupToAccountMapping.toXML() );
278         	}
279             out.write("\t</groupToAccountMappings>\n\n");
280         }                
281 
282         // Write Host To Group Mappings
283         if( conf.getAccountMappers().size()>0 ) {
284             out.write("\t<hostToGroupMappings>\n\n");
285         	Iterator it = conf.getHostToGroupMappings().iterator();
286         	while( it.hasNext() ) {
287         		HostToGroupMapping hostToGroupMapping = (HostToGroupMapping)it.next();
288         		out.write( hostToGroupMapping.toXML() );
289         	}
290             out.write("\t</hostToGroupMappings>\n\n");
291         }                
292         
293         out.write("</gums>");
294         
295         out.close();
296 
297 		// Make sure configuration is valid
298 	    this.conf = ConfigurationToolkit.loadConfiguration(tempGumsConfigPath, schemaPath);
299 	
300         // copy gums.config to gums.config.prev
301 		new File(configBackupDir).mkdir();
302 		if (!backupCopy && new File(configPath).exists())
303         	copyFile(configPath, configBackupDir+"/gums.config.prev");
304 
305         // move temp file to gums.config or gums.config.date
306     	moveFile(tempGumsConfigPath, (backupCopy?configBackupDir+"/gums.config."+format.format(new Date()):configPath));
307     }
308     
309     private Date lastModification() {
310         try {
311             File file = new File(configPath);
312             return new Date(file.lastModified());
313         } catch (Exception e) {
314             gumsResourceAdminLog.fatal("The configuration wasn't read properly. GUMS is not operational.", e);
315             return null;
316         }
317     }
318     
319     private void reloadConfiguration() {
320 		conf = null;
321         try {
322             if (ConfigurationToolkit.getVersion(configPath).equals("1.1")) {
323             	copyFile(configPath,configPath+".1.1");
324             	Configuration configuration = ConfigurationTransform.doTransform(configPath, transformPath);
325                 setConfiguration(configuration, false);
326             }
327 
328             log.debug("Attempting to load configuration from gums.config");
329     		conf = ConfigurationToolkit.loadConfiguration(configPath, schemaPath);
330             log.trace("Configuration reloaded from '" + configPath + "'");
331             gumsResourceAdminLog.info("Configuration reloaded from '" + configPath + "'");
332             gumsSiteAdminLog.info("Configuration reloaded from '" + configPath + "'");
333             lastRetrival = new Date();
334         } catch (Exception e) {
335             gumsResourceAdminLog.error("The configuration wasn't read correctly: " + e.getMessage());
336             log.error("The configuration wasn't read correctly.", e);
337             throw new RuntimeException("The configuration wasn't read correctly: " + e.getMessage());
338         }
339     }
340 }