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
99
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
112
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
141 this.registrationTemplate = new File(this.configurationDirectory,
142 "registration-template.xml");
143 this.registrationTemplateUrl = this.registrationTemplate.toURL();
144
145
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 }