View Javadoc

1   /*$Id: FileJobFactoryImpl.java,v 1.12 2004/12/03 14:47:41 jdt Exp $
2    * Created on 11-Feb-2004
3    *
4    * Copyright (C) AstroGrid. All rights reserved.
5    *
6    * This software is published under the terms of the AstroGrid 
7    * Software License version 1.2, a copy of which has been included 
8    * with this distribution in the LICENSE.txt file.  
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          // possible dir doesn't exist first time round..
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()) { // we've reached the end - maybe can't be helped, if we've had to skip something.
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(); // recursive call, in case of non-existent file.
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 $Log: FileJobFactoryImpl.java,v $
289 Revision 1.12  2004/12/03 14:47:41  jdt
290 Merges from workflow-nww-776
291 
292 Revision 1.11.2.1  2004/12/01 21:48:20  nw
293 adjusted to work with new summary object,
294 and changed package of JobURN
295 
296 Revision 1.11  2004/11/29 20:00:24  clq2
297 jes-nww-714
298 
299 Revision 1.10.30.1  2004/11/24 00:19:38  nw
300 fixed to make reading list of workflows more robust
301  - bz#673
302 
303 Revision 1.10  2004/09/16 21:42:27  nw
304 made sure all streams are closed
305 
306 Revision 1.9  2004/07/09 09:30:28  nw
307 merged in scripting workflow interpreter from branch
308 nww-x-workflow-extensions
309 
310 Revision 1.8  2004/07/01 21:15:00  nw
311 added results-listener interface to jes
312 
313 Revision 1.7  2004/03/15 01:31:12  nw
314 jazzed up javadoc
315 
316 Revision 1.6  2004/03/12 15:31:55  nw
317 added unit tests
318 
319 Revision 1.5  2004/03/07 21:04:38  nw
320 merged in nww-itn05-pico - adds picocontainer
321 
322 Revision 1.4.4.1  2004/03/07 20:41:59  nw
323 altered to look in component manager factory for implementations
324 
325 Revision 1.4  2004/03/04 01:57:35  nw
326 major refactor.
327 upgraded to latest workflow object model.
328 removed internal facade
329 replaced community snippet with objects
330 
331 Revision 1.3  2004/03/03 01:13:41  nw
332 updated jes to work with regenerated workflow object model
333 
334 Revision 1.2  2004/02/27 00:46:03  nw
335 merged branch nww-itn05-bz#91
336 
337 Revision 1.1.2.6  2004/02/19 13:40:09  nw
338 updated to fit new interfaces
339 
340 Revision 1.1.2.5  2004/02/17 14:04:38  nw
341 little fix to match new job urn format
342 
343 Revision 1.1.2.4  2004/02/17 12:25:38  nw
344 improved javadocs for classes
345 
346 Revision 1.1.2.3  2004/02/17 10:58:38  nw
347 altered to implement cut down facade interface, matched with types
348 generated by wsdl2java
349 
350 Revision 1.1.2.2  2004/02/12 12:54:47  nw
351 worked in inversion of control pattern - basically means that
352 components have to be assembled, rather than self-configuring
353 from properties in config files. so easier to test each component in isolation
354 
355 Revision 1.1.2.1  2004/02/12 01:14:01  nw
356 castor implementation of jes object model
357  
358 */