View Javadoc

1   package se.citerus.dddsample.interfaces.handling.file;
2   
3   import org.apache.commons.io.FileUtils;
4   import org.apache.commons.logging.Log;
5   import org.apache.commons.logging.LogFactory;
6   import org.springframework.beans.factory.InitializingBean;
7   import se.citerus.dddsample.application.ApplicationEvents;
8   import se.citerus.dddsample.domain.model.cargo.TrackingId;
9   import se.citerus.dddsample.domain.model.handling.HandlingEvent;
10  import se.citerus.dddsample.domain.model.location.UnLocode;
11  import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
12  import se.citerus.dddsample.interfaces.handling.HandlingEventRegistrationAttempt;
13  import static se.citerus.dddsample.interfaces.handling.HandlingReportParser.*;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Date;
19  import java.util.List;
20  import java.util.TimerTask;
21  
22  /**
23   * Periodically scans a certain directory for files and attempts
24   * to parse handling event registrations from the contents.
25   * <p/>
26   * Files that fail to parse are moved into a separate directory,
27   * succesful files are deleted.
28   */
29  public class UploadDirectoryScanner extends TimerTask implements InitializingBean {
30  
31    private File uploadDirectory;
32    private File parseFailureDirectory;
33  
34    private final static Log logger = LogFactory.getLog(UploadDirectoryScanner.class);
35    private ApplicationEvents applicationEvents;
36  
37    @Override
38    public void run() {
39      for (File file : uploadDirectory.listFiles()) {
40        try {
41          parse(file);
42          delete(file);
43          logger.info("Import of " + file.getName() + " complete");
44        } catch (Exception e) {
45          logger.error(e, e);
46          move(file);
47        }
48      }
49    }
50  
51    private void parse(final File file) throws IOException {
52      final List<String> lines = FileUtils.readLines(file);
53      final List<String> rejectedLines = new ArrayList<String>();
54      for (String line : lines) {
55        try {
56          parseLine(line);
57        } catch (Exception e) {
58          logger.error("Rejected line \n" + line + "\nReason is: " + e);
59          rejectedLines.add(line);
60        }
61      }
62      if (!rejectedLines.isEmpty()) {
63        writeRejectedLinesToFile(toRejectedFilename(file), rejectedLines);
64      }
65    }
66  
67    private String toRejectedFilename(final File file) {
68      return file.getName() + ".reject";
69    }
70  
71    private void writeRejectedLinesToFile(final String filename, final List<String> rejectedLines) throws IOException {
72      FileUtils.writeLines(
73        new File(parseFailureDirectory, filename), rejectedLines
74      );
75    }
76  
77    private void parseLine(final String line) throws Exception {
78      final String[] columns = line.split("\t");
79      if (columns.length == 5) {
80        queueAttempt(columns[0], columns[1], columns[2], columns[3], columns[4]);
81      } else if (columns.length == 4) {
82        queueAttempt(columns[0], columns[1], "", columns[2], columns[3]);
83      } else {
84        throw new IllegalArgumentException("Wrong number of columns on line: " + line + ", must be 4 or 5");
85      }
86    }
87  
88    private void queueAttempt(String completionTimeStr, String trackingIdStr, String voyageNumberStr, String unLocodeStr, String eventTypeStr) throws Exception {
89      final List<String> errors = new ArrayList<String>();
90  
91      final Date date = parseDate(completionTimeStr, errors);
92      final TrackingId trackingId = parseTrackingId(trackingIdStr, errors);
93      final VoyageNumber voyageNumber = parseVoyageNumber(voyageNumberStr, errors);
94      final HandlingEvent.Type eventType = parseEventType(eventTypeStr, errors);
95      final UnLocode unLocode = parseUnLocode(unLocodeStr, errors);
96  
97      if (errors.isEmpty()) {
98        final HandlingEventRegistrationAttempt attempt = new HandlingEventRegistrationAttempt(new Date(), date, trackingId, voyageNumber, eventType, unLocode);
99        applicationEvents.receivedHandlingEventRegistrationAttempt(attempt);
100     } else {
101       throw new Exception(errors.toString());
102     }
103   }
104 
105   private void delete(final File file) {
106     if (!file.delete()) {
107       logger.error("Could not delete " + file.getName());
108     }
109   }
110 
111   private void move(final File file) {
112     final File destination = new File(parseFailureDirectory, file.getName());
113     final boolean result = file.renameTo(destination);
114     if (!result) {
115       logger.error("Could not move " + file.getName() + " to " + destination.getAbsolutePath());
116     }
117   }
118 
119   @Override
120   public void afterPropertiesSet() throws Exception {
121     if (uploadDirectory.equals(parseFailureDirectory)) {
122       throw new Exception("Upload and parse failed directories must not be the same directory: " + uploadDirectory);
123     }
124     if (!uploadDirectory.exists()) {
125       uploadDirectory.mkdirs();
126     }
127     if (!parseFailureDirectory.exists()) {
128       parseFailureDirectory.mkdirs();
129     }
130   }
131 
132   public void setUploadDirectory(File uploadDirectory) {
133     this.uploadDirectory = uploadDirectory;
134   }
135 
136   public void setParseFailureDirectory(File parseFailureDirectory) {
137     this.parseFailureDirectory = parseFailureDirectory;
138   }
139 
140   public void setApplicationEvents(ApplicationEvents applicationEvents) {
141     this.applicationEvents = applicationEvents;
142   }
143 }