View Javadoc

1   package org.dfdaemon.il2.core.logfile;
2   
3   import org.apache.commons.beanutils.ConvertUtils;
4   import org.apache.commons.beanutils.Converter;
5   import org.apache.commons.logging.Log;
6   import org.apache.commons.logging.LogFactory;
7   import org.dfdaemon.il2.core.task.CoreTask;
8   import org.dfdaemon.il2.spi.event.EventProcessor;
9   import org.dfdaemon.il2.spi.event.EventProducer;
10  import org.dfdaemon.il2.api.event.Event;
11  import org.dfdaemon.il2.api.event.player.*;
12  import org.dfdaemon.il2.core.logfile.filter.DatedLineFilter;
13  import org.dfdaemon.il2.spi.logfile.LineFilter;
14  import org.dfdaemon.il2.core.logfile.filter.TimedLineFilter;
15  import org.joda.time.DateTime;
16  import org.springframework.beans.factory.annotation.Required;
17  
18  import java.io.File;
19  import java.io.FileReader;
20  import java.io.LineNumberReader;
21  import java.util.concurrent.TimeUnit;
22  import java.util.concurrent.atomic.AtomicBoolean;
23  
24  /**
25   * @author aka50
26   */
27  public class FileLogParser implements EventProducer, CoreTask {
28  
29      private final Log _log = LogFactory.getLog(this.getClass().getName());
30  
31      private boolean _shouldSkip = true;
32      private long _pollDelay = 1000;
33      private long _retryDelay = 5000;
34      private File _file;
35  
36      private EventProcessor _eventProcessor;
37      private DateTime _bias = new DateTime(); // set current date/time
38      private final AtomicBoolean _alive = new AtomicBoolean(false);
39  
40      private static LineFilter[] _logFilters;
41  
42      {
43          // TODO: really ugly hack, need refactoring...
44          ConvertUtils.register(new Converter() {
45              public Object convert(Class type, Object value) {
46                  return MissionStateEvent.State.valueOf((String) value);
47              }
48          }, MissionStateEvent.State.class);
49  
50          // TODO: moved to spring config
51          _logFilters = new LineFilter[]{
52  
53                  DatedLineFilter.createFilter("Mission: (.*) is Playing",
54                          new String[]{"missionFileName"},
55                          MissionPlayingEvent.class),
56  
57                  TimedLineFilter.createFilter("Mission (\\S+)",
58                          new String[]{"state"},
59                          MissionStateEvent.class),
60  
61                  TimedLineFilter.createFilter("(.+) in flight at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
62                          new String[]{"who", "x", "y"},
63                          TakeoffEvent.class),
64  
65                  TimedLineFilter.createFilter("(.+) shot down by landscape at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
66                          new String[]{"who", "x", "y"},
67                          CrashEvent.class),
68  
69                  TimedLineFilter.createFilter("(.+) landed at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
70                          new String[]{"who", "x", "y"},
71                          LandEvent.class),
72  
73                  TimedLineFilter.createFilter("(.+) damaged on the ground at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
74                          new String[]{"who", "x", "y"},
75                          DamageOnGroundEvent.class),
76  
77                  TimedLineFilter.createFilter("(.+) bailed out at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
78                          new String[]{"who", "x", "y"},
79                          BailOutEvent.class),
80  
81                  TimedLineFilter.createFilter("(.+) was captured at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
82                          new String[]{"who", "x", "y"},
83                          CaptureEvent.class),
84  
85                  TimedLineFilter.createFilter("(.+) was wounded at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
86                          new String[]{"who", "x", "y"},
87                          WoundEvent.class),
88  
89                  TimedLineFilter.createFilter("(.+) was heavily wounded at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
90                          new String[]{"who", "x", "y"},
91                          HeavyWoundEvent.class),
92  
93                  TimedLineFilter.createFilter("(.+) was killed at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
94                          new String[]{"who", "x", "y"},
95                          DeathEvent.class),
96  
97                  TimedLineFilter.createFilter("(.+) damaged by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
98                          new String[]{"who", "byWho", "x", "y"},
99                          DamageEvent.class),
100 
101                 TimedLineFilter.createFilter("(.+) was killed by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
102                         new String[]{"who", "byWho", "x", "y"},
103                         DeathEvent.class),
104 
105                 TimedLineFilter.createFilter("(.+) shot down by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
106                         new String[]{"who", "byWho", "x", "y"},
107                         DeathEvent.class),
108 
109                 TimedLineFilter.createFilter("(.+) was killed in his chute by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
110                         new String[]{"who", "byWho", "x", "y"},
111                         DeathEvent.class),
112 
113                 TimedLineFilter.createFilter("(.+) has chute destroyed by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
114                         new String[]{"who", "byWho", "x", "y"},
115                         DeathEvent.class),
116 
117                 TimedLineFilter.createFilter("(.+:\\S+) destroyed by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
118                         new String[]{"who", "byWho", "x", "y"},
119                         DeathEvent.class),
120 
121                 TimedLineFilter.createFilter("(.+) destroyed by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
122                         new String[]{"what", "who", "x", "y"},
123                         DestroyEvent.class),
124 
125                 TimedLineFilter.createFilter("(.+) loaded weapons \\'([^\\']+)\\' fuel (\\d+)%",
126                         new String[]{"who", "weapon", "fuel"},
127                         DestroyEvent.class),
128 
129                 TimedLineFilter.createFilter("(.+) turned wingtip smokes (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
130                         new String[]{"who", "smokeState", "x", "y"},
131                         WingtipSmokesEvent.class),
132 
133                 TimedLineFilter.createFilter("(.+) turned landing lights (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
134                         new String[]{"who", "lightState", "x", "y"},
135                         LandingLightsEvent.class),
136 
137                 TimedLineFilter.createFilter("(.+) entered refly menu",
138                         new String[]{"who"},
139                         ReflyMenuEvent.class),
140 
141                 TimedLineFilter.createFilter("(.+) seat occupied by (\\S+) at ([+-]?\\d+\\.\\d+) ([+-]?\\d+\\.\\d+)",
142                         new String[]{"who", "byWho", "x", "y"},
143                         OccupySeatEvent.class)
144         };
145     }
146 
147 
148     /**
149      * Setter for property 'retryDelay'.
150      *
151      * @param retryDelay Value to set for property 'retryDelay'.
152      */
153     public void setRetryDelay(long retryDelay) {
154         _retryDelay = retryDelay;
155     }
156 
157     /**
158      * Setter for property 'pollDelay'.
159      *
160      * @param pollDelay Value to set for property 'pollDelay'.
161      */
162     public void setPollDelay(long pollDelay) {
163         _pollDelay = pollDelay;
164     }
165 
166     /**
167      * Setter for property 'shouldSkip'.
168      *
169      * @param shouldSkip Value to set for property 'shouldSkip'.
170      */
171     public void setShouldSkip(boolean shouldSkip) {
172         _shouldSkip = shouldSkip;
173     }
174 
175     /**
176      * Setter for property 'file'.
177      *
178      * @param file Value to set for property 'file'.
179      */
180     @Required
181     public void setFile(File file) {
182         _file = file;
183     }
184 
185     /**
186      * Event sink
187      *
188      * @param eventProcessor event sink
189      */
190     @Required
191     public void setEventProcessor(EventProcessor eventProcessor) {
192         _eventProcessor = eventProcessor;
193     }
194 
195     /**
196      * {@inheritDoc}
197      */
198     public void preRun() throws Exception {
199         _alive.set(true);
200         while (!_file.exists()) {
201             if (_log.isErrorEnabled())
202                 _log.error("File not found: " + _file.getAbsolutePath());
203             if (isTerminated()) {
204                 if (_log.isInfoEnabled())
205                     _log.info("Terminated");
206                 throw new InterruptedException();
207             }
208 
209             try {
210                 TimeUnit.MILLISECONDS.sleep(_retryDelay);
211             } catch (InterruptedException e) {
212                 if (_log.isInfoEnabled())
213                     _log.info("Interrupted");
214                 throw e;
215             }
216         }
217         if (_log.isInfoEnabled())
218             _log.debug("Initialized: " + _file);
219     }
220 
221     /**
222      * {@inheritDoc}
223      */
224     public void run() throws Exception {
225 
226         // Remember size, this will help us to detect
227         // file truncation
228         long size = _file.length();
229         if (_log.isDebugEnabled()) {
230             _log.debug("Initialized: " + _file.getAbsolutePath());
231         }
232 
233         LineNumberReader reader = new LineNumberReader(new FileReader(_file));
234 
235         if (_shouldSkip)
236             reader.skip(size);
237 
238         if (_log.isDebugEnabled())
239             _log.debug("Start reading from: " + reader.getLineNumber() + " line.");
240 
241         try {
242             outer:
243             while (!isTerminated()) {
244 
245                 while (!reader.ready()) {
246                     // update file size here
247                     size = _file.length();
248 
249                     if (isTerminated())
250                         break outer;
251 
252                     Thread.sleep(_pollDelay);
253 
254                     /* We don't like file truncation */
255                     if (_file.length() < size)
256                         throw new IllegalStateException("File size changed" + _file.length() + "<" + size);
257                 }
258 
259                 String line = reader.readLine();
260 
261                 // try to handle with configured splitters
262                 Event event = null;
263                 for (LineFilter filter : _logFilters) {
264                     event = filter.handle(_bias, line);
265                     if (event != null) {
266                         // override our bias
267                         _bias = event.getDate();
268                         if (_log.isDebugEnabled()) {
269                             _log.debug("Event:" + event);
270                         }
271                         _eventProcessor.inject(event);
272                         break;
273                     }
274                 }
275                 if (_log.isDebugEnabled() && event == null) {
276                     _log.debug("Unknown line: " + line);
277                 }
278             }
279         } finally {
280             if (_log.isInfoEnabled())
281                 _log.debug("Finished");
282             reader.close();
283         }
284     }
285 
286     /**
287      * Method afterRun ...
288      */
289     public void afterRun() {
290         _alive.set(false);
291     }
292 
293     /**
294      * {@inheritDoc}
295      */
296     public boolean stop() {
297         _alive.set(false);
298         return true;
299     }
300 
301 
302     /**
303      * Getter for property 'terminated'.
304      *
305      * @return Value for property 'terminated'.
306      */
307     public boolean isTerminated() {
308         return !_alive.get() || Thread.currentThread().isInterrupted();
309     }
310 
311 }
312