1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.jes.impl.workflow;
12
13 import org.astrogrid.community.beans.v1.Account;
14 import org.astrogrid.component.descriptor.ComponentDescriptor;
15 import org.astrogrid.jes.job.JobException;
16 import org.astrogrid.jes.job.NotFoundException;
17 import org.astrogrid.workflow.beans.v1.Workflow;
18 import org.astrogrid.workflow.beans.v1.execution.JobURN;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.exolab.castor.xml.CastorException;
23
24 import java.io.BufferedReader;
25 import java.io.File;
26 import java.io.FileReader;
27 import java.io.FileWriter;
28 import java.io.FilenameFilter;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.net.URLEncoder;
32 import java.util.Arrays;
33 import java.util.Iterator;
34 import java.util.List;
35
36 import junit.framework.Test;
37 import junit.framework.TestCase;
38 import junit.framework.TestSuite;
39
40 /*** Implementation of JobFactory that stores xml documents on a filesystem.
41 * @author Noel Winstanley nw@jb.man.ac.uk 11-Feb-2004
42 *
43 */
44 public class FileJobFactoryImpl extends AbstractJobFactoryImpl implements ComponentDescriptor{
45 /***
46 * Commons Logger for this class
47 */
48 private static final Log logger = LogFactory
49 .getLog(FileJobFactoryImpl.class);
50
51 /*** Configuration component for FileJobFactory Impl
52 * @author Noel Winstanley nw@jb.man.ac.uk 07-Mar-2004
53 *
54 */
55 public static interface BaseDirectory {
56
57
58 File getDir();
59 }
60 private static final String WORKFLOW_SUFFIX = "-workflow.xml";
61 /*** Construct a new FileJobFactoryImpl
62 * Construct a new FileJobFactoryImpl
63 * @param baseDir directory to store workflow documents in
64 * @throws IOException if basedir inaccessible.
65 */
66 public FileJobFactoryImpl(BaseDirectory bd) throws IOException{
67 super();
68 log.info("File Store Job Factory");
69 this.baseDir = bd.getDir();
70 assert baseDir != null;
71 log.info("Base directory of file store:" + baseDir.getAbsolutePath());
72 initStore();
73 }
74
75
76 protected final File baseDir;
77 /*** initialize the store directory */
78 protected void initStore() throws IOException {
79
80 if (! baseDir.exists()) {
81 log.info("Initializing file store");
82 baseDir.mkdirs();
83 }
84 assert baseDir.isDirectory();
85 assert baseDir.canRead();
86 assert baseDir.canWrite();
87 }
88
89 protected File mkOutputFile(Workflow j) {
90 return mkOutputFile(j.getJobExecutionRecord().getJobId());
91 }
92
93 protected File mkOutputFile(JobURN jobURN) {
94 return new File(baseDir,URLEncoder.encode(jobURN.getContent() + WORKFLOW_SUFFIX));
95 }
96
97 /***
98 * @see org.astrogrid.jes.job.JobFactory#createJob(org.astrogrid.jes.job.SubmitJobRequest)
99 */
100 public Workflow initializeJob(Workflow req) throws JobException {
101 Workflow j = buildJob(req);
102 FileWriter fw = null;
103 try {
104 File outFile = mkOutputFile(j);
105 fw = new FileWriter(outFile);
106 j.marshal(fw);
107 } catch (Exception e) {
108 throw new JobException("Problem with store",e);
109 } finally {
110 if (fw != null) {
111 try {
112 fw.close();
113 } catch (IOException ioe) {
114 log.error("failed to close filestore");
115 }
116 }
117 }
118 return j;
119 }
120
121 /***
122 * @see org.astrogrid.jes.job.JobFactory#findJob(java.lang.String)
123 */
124 public Workflow findJob(JobURN jobURN) throws JobException {
125 FileReader fr = null;
126 try {
127 File f = mkOutputFile(jobURN);
128 if (! f.exists()) {
129 throw new NotFoundException("Couldn't find job " + jobURN);
130 }
131 fr = new FileReader(f);
132 return Workflow.unmarshalWorkflow(fr);
133 } catch (CastorException e) {
134 throw new JobException("Problem with creating object model",e);
135 } catch (IOException e) {
136 throw new JobException("Problem with reading xml from store",e);
137 } finally {
138 if (fr != null) {
139 try {
140 fr.close();
141 } catch (IOException ioe) {
142 log.error("failed to close file");
143 }
144 }
145 }
146 }
147 /***
148 * @see org.astrogrid.jes.job.JobFactory#findUserJobs(java.lang.String, java.lang.String, java.lang.String)
149 */
150 public Iterator findUserJobs(final Account acc) throws JobException {
151 File[] jobFiles = baseDir.listFiles(new FilenameFilter() {
152 final String searchString = URLEncoder.encode("jes:" + hostname + "/" + acc.getName() +"@" + acc.getCommunity() + "/");
153 public boolean accept(File dir, String name) {
154 return name.startsWith(searchString);
155 }
156 });
157 List l = Arrays.asList(jobFiles);
158 final Iterator i = l.iterator();
159 return new Iterator() {
160
161 public void remove() {
162 throw new UnsupportedOperationException("don't remove");
163 }
164
165 public boolean hasNext() {
166 return i.hasNext();
167 }
168 /*** need to make this more resiliant to changes on disk - it's possible that queued changes will be processed, and so the files
169 * won't be there when they're come to being read. If this happens now, it skips onto the next item.
170 * 'Course this means that we need to handle a null at the end of the list. which is a pity.
171 * @see java.util.Iterator#next()
172 */
173 public Object next() {
174 if (!i.hasNext()) {
175 logger.warn("Reached unexpected end of iterator");
176 return null;
177 }
178 File f = (File)i.next();
179 try {
180 if (! f.exists()) {
181 logger.info("Skipping non-existent file " + f);
182 return this.next();
183 }
184 return Workflow.unmarshalWorkflow(new FileReader(f));
185 } catch (Exception e) {
186 logger.warn("Failed to unmarshal this file " + f + " skipping");
187 return this.next();
188 }
189 }
190 };
191
192 }
193 /***
194 * @see org.astrogrid.jes.job.JobFactory#deleteJob(org.astrogrid.jes.job.Job)
195 */
196 public void deleteJob(Workflow job) throws JobException {
197 File f = mkOutputFile(job);
198 if (f.exists()) {
199 f.delete();
200 } else {
201 throw new NotFoundException("Job URN " + id(job) + " not found");
202 }
203 }
204 /***
205 * @see org.astrogrid.jes.job.JobFactory#updateJob(org.astrogrid.jes.job.Job)
206 */
207 public void updateJob(Workflow j) throws JobException {
208 FileWriter fw = null;
209 try {
210 File outFile = mkOutputFile(j);
211 if (! outFile.exists()) {
212 throw new NotFoundException("Job URN " + id(j) + " not found");
213 }
214 fw = new FileWriter(outFile);
215 j.marshal(fw);
216 } catch (Exception e) {
217 throw new JobException("Problem with store",e);
218 } finally {
219 if (fw != null) {
220 try {
221 fw.close();
222 } catch (IOException e) {
223 log.error("Could not close file writer");
224 }
225 }
226 }
227 }
228
229 /***
230 * @see org.astrogrid.jes.component.ComponentDescriptor#getName()
231 */
232 public String getName() {
233 return "Filestore-backed job factory";
234 }
235
236 /***
237 * @see org.astrogrid.jes.component.ComponentDescriptor#getDescription()
238 */
239 public String getDescription() {
240 return "Stores jobs on disk as workflow xml document files\n"
241 + "store directory: " + baseDir.getAbsolutePath();
242 }
243
244
245 /***
246 * @see org.astrogrid.jes.component.ComponentDescriptor#getInstallationTest()
247 */
248 public Test getInstallationTest() {
249 TestSuite suite = new TestSuite("Tests for File Job Factory Imp");
250 suite.addTest(new InstallationTest("testBaseDirExists"));
251 suite.addTest(new InstallationTest("testWriteFile"));
252 suite.addTest(new InstallationTest("testReadFile"));
253 return suite;
254 }
255
256
257 protected class InstallationTest extends TestCase {
258
259
260 public InstallationTest(String s) {
261 super(s);
262 }
263
264 public void testBaseDirExists() {
265 assertTrue("base dir does not exist",baseDir.exists());
266 assertTrue("base dir is not a directory",baseDir.isDirectory());
267 assertTrue("base dir is not readable and writable",baseDir.canRead() && baseDir.canWrite());
268 }
269 final File testFile = new File(baseDir,"test-file");
270 private final static String CONTENTS = "test file contents";
271 public void testWriteFile() throws IOException {
272 PrintWriter pw = new PrintWriter( new FileWriter(testFile));
273 pw.println(CONTENTS);
274 pw.close();
275 assertTrue(testFile.exists());
276 }
277 public void testReadFile() throws IOException {
278 BufferedReader reader = new BufferedReader( new FileReader(testFile));
279 String line = reader.readLine();
280 assertEquals("does not match expected",CONTENTS,line);
281 }
282 }
283
284 }
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358