View Javadoc

1   package org.astrogrid.applications.manager;
2   
3   import java.io.File;
4   import java.io.FileOutputStream;
5   import java.io.InputStream;
6   import java.io.IOException;
7   import java.io.OutputStream;
8   import java.net.MalformedURLException;
9   import java.net.URL;
10  import junit.framework.Test;
11  import org.astrogrid.applications.contracts.Configuration;
12  import org.astrogrid.component.descriptor.ComponentDescriptor;
13  import org.astrogrid.config.Config;
14  import org.astrogrid.config.SimpleConfig;
15  
16  /***
17   *
18   * @author Guy Rixon
19   */
20  public class BaseConfiguration 
21      implements Configuration, ComponentDescriptor {
22    
23    protected File baseDirectory;
24    protected File configurationDirectory;
25    protected File recordsDirectory;
26    protected File temporaryFilesDirectory;
27    protected URL  cecEndpoint;
28    protected File registrationTemplate;
29    protected URL registrationTemplateUrl;
30    
31    /***
32     * Constructs a BaseConfiguration, establishing the directory
33     * structure on which the configuration is based.
34     *
35     * @throws IOException If one of the directories cannot be created.
36     */
37    public BaseConfiguration() throws IOException {
38      this.chooseBaseDirectory();
39      this.chooseConfigurationDirectory();
40      this.chooseRecordsDirectory();
41      this.chooseTemporaryFilesDirectory();
42      this.initializeRegistrationTemplate();
43      this.initializeCecEndpoint();
44    }
45    
46    /***
47     * Determines the base directory of the configuration structure.
48     * This directory is the only part of the structure that can be
49     * chosen by the deployer.
50     *
51     * @return The directory.
52     */
53    public File getBaseDirectory() {
54      return this.baseDirectory;
55    }
56    
57    /***
58     * Determines the directory in which the CEC can find its 
59     * configuration files.
60     *
61     * @return The directory.
62     */
63    public File getConfigurationDirectory() {
64      return this.configurationDirectory;
65    }  
66    
67    /***
68     * Determines the directory in which the CEC can write its execution histories.
69     *
70     * @return The directory.
71     */
72    public File getRecordsDirectory() {
73      return this.recordsDirectory;
74    }
75    
76    /***
77     * Determines the directory in which the CEC can write the temporary
78     * files for each execution.
79     *
80     * @return The directory.
81     */
82    public File getTemporaryFilesDirectory() {
83      return this.temporaryFilesDirectory;
84    }
85   
86    /***
87     * Establishes the base directory, creating it if necessary.
88     * Normally, the directory is put at the location stated in
89     * the JNDI variable cea.base.dir. If this is not set, or
90     * if a directory cannot be created there, then a directory
91     * with a random name is created in the default temporary-files
92     * directory.
93     *
94     * @throws IOException If the directory cannot be created.
95     */ 
96    private void chooseBaseDirectory() throws IOException {
97      
98      // For preference, base the directory structure at the point
99      // configured in the application properties.
100     try {
101       String property = SimpleConfig.getSingleton().getString("cea.base.dir");
102       this.baseDirectory = new File(property);
103       if (!this.baseDirectory.exists()) {
104         if (!this.baseDirectory.mkdirs()) {
105           throw new Exception("Can't create the base directory " + 
106                               this.baseDirectory.getAbsolutePath());
107         }
108       }
109     }
110     
111     // If the configured location isn't available, make a temporary
112     // directory as the base.
113     catch (Exception e) {
114       this.baseDirectory = this.makeTemporaryDirectory();
115     }
116   }
117   
118   private void chooseConfigurationDirectory() {
119     this.configurationDirectory = new File(this.baseDirectory, "config");
120     this.configurationDirectory.mkdir();
121   }
122   
123   private void chooseRecordsDirectory() {
124     this.recordsDirectory = new File(this.baseDirectory, "records");
125     this.recordsDirectory.mkdir();
126   }
127   
128   private void chooseTemporaryFilesDirectory() {
129     this.temporaryFilesDirectory = new File(this.baseDirectory, "temp");
130     this.temporaryFilesDirectory.mkdir();
131   }
132   
133   /***
134    * Makes sure that there is a registration-template file at the configured
135    * location, creating one if necessary.
136    */
137   private void initializeRegistrationTemplate() 
138       throws IOException, MalformedURLException {
139     
140     // Work out and remember where the template is kept.
141     this.registrationTemplate = new File(this.configurationDirectory, 
142                                          "registration-template.xml");
143     this.registrationTemplateUrl = this.registrationTemplate.toURL();
144     
145     // If there is no template yet, take a copy of a dummy one in the JAR.
146     if (!this.registrationTemplate.exists()) {
147       URL source = this.getClass().getResource("/CEARegistryTemplate.xml");
148       this.copyUrlToFile(source, this.registrationTemplate);
149     }
150   }
151   
152   /***
153    * Records the endpoint URL for the CEC.
154    */
155   private void initializeCecEndpoint() throws MalformedURLException {
156     try {
157       String urlText = SimpleConfig.getSingleton().getString("cea.webapp.url") +
158                        "/services/CommonExecutionConnectorService";
159       this.cecEndpoint = new URL(urlText);
160     }
161     catch (Exception e) {
162       this.cecEndpoint 
163           = new URL("http://localhost:8080/cec/services/CommonExecutionConnectorService");
164     }
165   }
166   
167   /*** 
168    * Sets the registration template, by copying an XML document
169    * from the given URL.
170    *
171    * @param source The source of the template document.
172    */
173   public void setRegistrationTemplate(URL source) throws IOException {
174     this.copyUrlToFile(source, this.registrationTemplate);
175   }
176 
177   /***
178    * Copies the content of a URL to a file. This is useful for transcribing
179    * template files in a jar to writable storage.
180    *
181    * @param source The URL from which to copy.
182    * @param sink The file to which to write.
183    * @throws IOException If anything goes wrong.
184    */
185   public void copyUrlToFile(URL source, File sink) throws IOException {
186     InputStream is = source.openStream();
187     OutputStream os = new FileOutputStream(sink);
188     byte[] b = new byte[32768];
189     while(true) {
190       int nBytes = is.read(b);
191       if (nBytes > 0) {
192         os.write(b, 0, nBytes);
193       }
194       else {
195         break;
196       }
197     }
198   }
199 
200   public void copyUrlToUrl(URL source, URL sink) throws IOException {
201     InputStream is = source.openStream();
202     OutputStream os = sink.openConnection().getOutputStream();
203     byte[] b = new byte[32768];
204     while(true) {
205       int nBytes = is.read(b);
206       if (nBytes > 0) {
207         os.write(b, 0, nBytes);
208       }
209       else {
210         break;
211       }
212     }
213   }
214   /***
215    * Locates the registration template.
216    */
217   public URL getRegistryTemplate() {
218     return this.registrationTemplateUrl;
219   }
220   
221   /***
222    * Locates the registration template.
223    */
224   public URL getServiceEndpoint() {
225     return this.cecEndpoint;
226   }
227   
228   /***
229    * Reveals the name of the component.
230    */
231   public String getName() {
232     return "Configuration for a Generic CEC.";
233   }
234   
235   /***
236    * Describes the component and its current state.
237    */
238   public String getDescription() {
239     StringBuffer sb = new StringBuffer();
240     sb.append("Provides configuration for the CEC in a type-safe form.\n");
241     sb.append("Manages directories where the CEC keeps its files.\n");
242     sb.append("Base directory: ");
243     sb.append(this.getBaseDirectory().getAbsolutePath());
244     sb.append("\n");
245     sb.append("Configuration directory: ");
246     sb.append(this.getConfigurationDirectory().getAbsolutePath());
247     sb.append("\n");
248     sb.append("Temporary-files directory: ");
249     sb.append(this.getTemporaryFilesDirectory().getAbsolutePath());
250     sb.append("\n");
251     sb.append("Execution-records directory: ");
252     sb.append(this.getRecordsDirectory().getAbsolutePath());
253     sb.append("\n");
254     sb.append("Registration-template file: ");
255     sb.append(this.getRegistryTemplate().toString());
256     sb.append("\n");
257     sb.append("CEC endpoint: ");
258     sb.append(this.getServiceEndpoint().toString());
259     sb.append("\n");
260     return sb.toString();
261   }
262   
263   /***
264    * Generates a test-suite (not implemented for this component).
265    */
266   public Test getInstallationTest() {
267     return null;
268   }
269   
270   /***
271    * Creates a temporary directory.
272    * Java has an API to create a temporary file, with safeguards against
273    * duplication, but doesn't have an API for a temporary directory.
274    * This method uses the temporary file (name ending in .tmp) as a lock
275    * and creates a directory based on that name. The simpler technique
276    * of creating the file, deleting it from the file system and replacing
277    * with a directory of the same name doesn't work reliably; it defeats
278    * the locking in the JRE.
279    *
280    * @return The directory.
281    * @throw IOException If the lock file cannot be created (should never happen). 
282    */
283   private File makeTemporaryDirectory() throws IOException {
284     File lockFile = File.createTempFile("CecBaseConfiguration", ".tmp");
285     lockFile.deleteOnExit();
286     String fileName = lockFile.getAbsolutePath();
287     String directoryName 
288         = fileName.substring(0, fileName.lastIndexOf(".tmp")-1).toString();
289     File directory = new File(directoryName);
290     directory.mkdir();
291     directory.deleteOnExit();
292     return directory;
293   }
294 }