View Javadoc

1   /*
2     ICalGrabber - Copyright (C) 2005 Che Inc., Rosario Argentina
3    
4     This program is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8    
9     This library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13   
14    You should have received a copy of the GNU Library General Public
15    License along with this library; if not, write to the Free
16    Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18  package inc.che.icalgrabber;
19  
20  import java.io.File;
21  import java.io.InputStream;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.io.OutputStreamWriter;
25  import java.io.Writer;
26  import java.net.URI;
27  import java.net.URISyntaxException;
28  import java.net.URL;
29  import java.text.ParseException;
30  import java.text.SimpleDateFormat;
31  import java.util.ArrayList;
32  import java.util.Collection;
33  import java.util.Date;
34  import java.util.GregorianCalendar;
35  import java.util.List;
36  import java.util.Properties;
37  import org.apache.commons.configuration.Configuration;
38  import org.apache.commons.configuration.ConfigurationException;
39  import org.apache.commons.configuration.ConfigurationFactory;
40  import org.apache.commons.configuration.ConfigurationUtils;
41  import org.apache.commons.httpclient.HttpClient;
42  import org.apache.commons.httpclient.HttpException;
43  import org.apache.commons.httpclient.HttpStatus;
44  import org.apache.commons.httpclient.methods.GetMethod;
45  import org.apache.commons.lang.StringEscapeUtils;
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  import org.apache.regexp.RE;
49  import org.apache.velocity.Template;
50  import org.apache.velocity.VelocityContext;
51  import org.apache.velocity.app.Velocity;
52  import org.apache.velocity.exception.ParseErrorException;
53  import org.apache.velocity.exception.ResourceNotFoundException;
54  
55  
56  
57  
58  
59  /***
60   * Main class for ICalGrabber.
61   *
62   * @author <a href="steve.mcmee@shadoplway-online.de">Steve McMee</a>
63   *
64   */
65  public class Main {
66      /***
67       * the logger.
68       */
69      private static Log log = LogFactory.getLog(Main.class);
70      /***
71       * ical time format pattern.
72       */
73      static final SimpleDateFormat
74              ICAL_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
75      /***
76       * ical date format pattern.
77       */
78      static final SimpleDateFormat
79              ICAL_FORMATDATE = new SimpleDateFormat("yyyyMMdd");
80      /*
81       * the configuration.
82       */
83      private Configuration config;
84  
85      /***
86       * locate a specific file.
87       * @param fileName the filename
88       * @return InputStream or null if the file could not be found
89       * @throws URISyntaxException if an error occurs
90       * @see org.apache.commons.configuration.ConfigurationUtils#locate
91       */
92      private InputStream locateFile(String fileName) throws IOException {
93          if (log.isTraceEnabled()) {
94              log.trace("private File locateFile(String " + fileName + ")");
95          }
96          InputStream iStream = null;
97          URL fileURL = ConfigurationUtils.locate(fileName);
98          if (log.isDebugEnabled()) {
99              log.debug("located file " + fileName + " at " + fileURL);
100         }
101         if (fileURL != null) {
102             iStream =  fileURL.openStream();
103             /*
104             String path = fileURL.getPath();
105             URI uri = null;
106             try {
107                 uri = new URI(fileURL.toString());
108             if (log.isDebugEnabled()) {
109                 log.debug("URI:" + uri);
110             }
111                 path = uri.getPath();
112             } catch (URISyntaxException ex) {
113                 // Do Nothing
114             }
115             if (log.isDebugEnabled()) {
116                 log.debug("path:" + path);
117             }
118             if (path!=null) {
119                 file = new File(path);
120             }
121             else {
122               file = new File(uri);
123             }
124             if (log.isDebugEnabled()) {
125                 log.debug("Using file " + file.getAbsolutePath());
126             }
127              **/
128         }
129         return iStream;
130     }
131     /***
132      * initialize the velocity context.
133      * @throws Exception if an error occurs
134      */
135     private void initVelocity() throws Exception {
136         if (log.isTraceEnabled()) {
137             log.trace("private void initVelocity() throws Exception");
138         }
139         Properties velocityProps = new Properties();
140         velocityProps.load(ConfigurationUtils.locate("velocity.properties").openStream());
141         URL templateUrl = ConfigurationUtils.locate("events.vm");
142         if (log.isDebugEnabled()) {
143                 log.debug("templateUrl:" + templateUrl);
144                 log.debug("templateUrl.getProtocol():" + templateUrl.getProtocol());
145             }
146         if (templateUrl.getProtocol().startsWith("jar")) {
147             int index = templateUrl.toString().indexOf("!");
148             if (index == -1) {
149                 velocityProps.setProperty("jar.resource.loader.path", templateUrl.toString());
150             } else {
151                 velocityProps.setProperty("jar.resource.loader.path",
152                         templateUrl.toString().substring(0, index));
153             }
154         } else {
155             File file = null;
156             String path = templateUrl.getPath();
157             try {
158                 URI uri = new URI(templateUrl.toString());
159             if (log.isDebugEnabled()) {
160                 log.debug("URI:" + uri);
161             }
162                 path = uri.getPath();
163             } catch (URISyntaxException ex) {
164                 // Do Nothing
165             }
166             if (log.isDebugEnabled()) {
167                 log.debug("path:" + path);
168             }
169             if (path != null) {
170                 file = new File(path);
171             }
172             velocityProps.setProperty("file.resource.loader.path", file.getParent());
173         }
174         Velocity.init(velocityProps);
175         if (log.isDebugEnabled()) {
176             log.debug("Velocity initialized with " + velocityProps);
177         }
178     }
179     /***
180      * create an .ics event file based on velocity template.
181      * @param eventList list of events
182      * @param outFile output file
183      * @throws Exception if an error occurs
184      */
185     private void mergeTemplate(List eventList, File outFile) throws Exception {
186         if (log.isTraceEnabled()) {
187             log.trace(" private void mergeTemplate(List "
188                     + eventList + " File "
189                     + outFile + ") throws Exception ");
190         }
191         VelocityContext context = new VelocityContext();
192         context.put("events", eventList);
193         context.put("icaldate", ICAL_FORMAT.format(new Date()));
194         try {
195             Writer writer =
196                     new OutputStreamWriter(
197                     new FileOutputStream(outFile), "UTF-8");
198             Template template = Velocity.getTemplate("events.vm");
199             template.merge(context, writer);
200             //flush and cleanup
201             writer.flush();
202             writer.close();
203             if (log.isDebugEnabled()) {
204                 log.debug("Template merged into file "
205                         + outFile.getAbsolutePath());
206             }
207         } catch (ResourceNotFoundException rnfe) {
208             System.err.println("cannot find template events.vm");
209             throw new Exception("cannot find template events.vm", rnfe);
210         } catch (ParseErrorException pee) {
211             System.err.println("Syntax error in template events.vm :" + pee);
212             throw new Exception("Syntax error in template events.vm", pee);
213         }
214     }
215     /***
216      * porcess the building.
217      */
218     private void process() {
219         if (log.isTraceEnabled()) {
220             log.trace("private void process()");
221         }
222         ConfigurationFactory factory = new ConfigurationFactory("config.xml");
223         try {
224             this.config = factory.getConfiguration();
225         } catch (ConfigurationException ex) {
226             ex.printStackTrace();
227             log.error("Cannot configure the system", ex);
228             return;
229         }
230         try {
231             this.initVelocity();
232         } catch (Exception ex) {
233             ex.printStackTrace();
234             log.error("Cannot initialize velocity ", ex);
235             return;
236         }
237         Collection calendars = (Collection) config.getList("calendar[@id]");
238         for (int i = 0; i < calendars.size(); i++) {
239             try {
240                 String calendar = "calendar(" + i + ")";
241                 List eventList = getEventList(calendar);
242                 this.mergeTemplate(eventList,
243                         new File(config.getString("out.dir",
244                         System.getProperty("user.home")),
245                         config.getString(calendar + "[@id]")
246                         + ".ics"));
247             } catch (ParseException ex) {
248                 ex.printStackTrace();
249                 log.error("Cannot parse calendar ", ex);
250             } catch (Exception ex) {
251                 ex.printStackTrace();
252                 log.error("Cannot merge template ", ex);
253             }
254         }
255     }
256     /***
257      * creates list of events from specific calendar.
258      * @param calendar the specific calendar
259      * @return list of events
260      * @throws ParseException if an error occurs
261      */
262     private List getEventList(String calendar) throws ParseException {
263         if (log.isTraceEnabled()) {
264             log.trace("private List getEventList(String "
265                     + calendar + ") throws ParseException");
266         }
267         String id = config.getString(calendar + "[@id]");
268         log.info("Analize calendar " + id);
269         List eventList = new ArrayList();
270         String[] urls = config.getStringArray(calendar + ").url");
271         String eventListStartPattern
272                 = config.getString(calendar + ".pattern.eventlistStart");
273         String eventListEndPattern
274                 = config.getString(calendar + ".pattern.eventlistEnd");
275         String eventPattern
276                 = config.getString(calendar + ".pattern.eventSeparator");
277         String titlePattern
278                 = config.getString(calendar + ".pattern.title");
279         String startDatePattern
280                 = config.getString(calendar + ".pattern.dateStart");
281         String endDatePattern = config.getString(calendar + ".pattern.dateEnd");
282         String startTimePattern
283                 = config.getString(calendar + ".pattern.timeStart");
284         String endTimePattern
285                 = config.getString(calendar + ".pattern.timeEnd");
286         String locationPattern
287                 = config.getString(calendar + ".pattern.location");
288         String descriptionPattern
289                 = config.getString(calendar + ".pattern.description");
290         String yearPattern
291                 = config.getString(calendar + ".pattern.year");
292         String[] startDateFormats
293                 = config.getStringArray(calendar
294                 + ".pattern.dateStart[@formats]");
295         String[] endDateFormats
296                 = config.getStringArray(calendar
297                 + ".pattern.dateEnd[@formats]");
298         String[] startTimeFormats
299                 = config.getStringArray(calendar
300                 + ".pattern.timeStart[@formats]");
301         String[] endTimeFormats
302                 = config.getStringArray(calendar
303                 + ".pattern.timeEnd[@formats]");
304         if (log.isDebugEnabled()) {
305             log.debug("urls:" + urls);
306             log.debug("eventListStartPattern:" + eventListStartPattern);
307             log.debug("eventListEndPattern:" + eventListEndPattern);
308             log.debug("eventPattern:" + eventPattern);
309             log.debug("titlePattern:" + titlePattern);
310             log.debug("startDatePattern:" + startDatePattern);
311             log.debug("endDatePattern:" + endDatePattern);
312             log.debug("startTimePattern:" + startTimePattern);
313             log.debug("endTimePattern:" + endTimePattern);
314             log.debug("descriptionPattern:" + descriptionPattern);
315             log.debug("startDateFormats:" + startDateFormats);
316             log.debug("endDateFormats:" + endDateFormats);
317             log.debug("startTimeFormats:" + startTimeFormats);
318             log.debug("endTimeFormats:" + endTimeFormats);
319             log.debug("yearPattern:" + yearPattern);
320         }
321         RE eventListStartRE = new RE(eventListStartPattern);
322         RE eventListEndRE = new RE(eventListEndPattern);
323         RE eventRE = new RE(eventPattern);
324         RE titleRE = new RE(titlePattern);
325         RE descriptionRE = descriptionPattern != null
326                 ? new RE(descriptionPattern) : null;
327         RE locationRE = locationPattern != null
328                 ? new RE(locationPattern) : null;
329         RE startDateRE = startDatePattern != null
330                 ? new RE(startDatePattern) : null;
331         RE endDateRE = endDatePattern != null
332                 ? new RE(endDatePattern) : null;
333         RE startTimeRE = startTimePattern != null
334                 ? new RE(startTimePattern) : null;
335         RE endTimeRE = endTimePattern != null
336                 ? new RE(endTimePattern) : null;
337         RE yearRE = yearPattern != null
338                 ? new RE(yearPattern) : null;
339         for (int i = 0; i < urls.length; i++) {
340             String url = urls[i];
341             String fixedYear = null;
342             String page = getWebPageContent(url);
343             page = StringEscapeUtils.unescapeHtml(page);
344             if (page == null) {
345                 return eventList; // nothing to do!
346             }
347             String[] lists = eventListStartRE.split(page);
348             if (lists == null || lists.length < 2) {
349                 throw new ParseException("Cannot extract begin of event list! Pattern:'"
350                                          + eventListStartPattern + "' in page \n" + page, 0);
351             }
352             String list = lists[1];
353             lists = eventListEndRE.split(list);
354             if (lists == null || lists.length == 0) {
355                 throw new ParseException("Cannot extract end of event list! Pattern:'"
356                                          + eventListEndPattern + "'in page \n" + page, 0);
357             }
358             list = lists[0];
359             if (log.isDebugEnabled()) {
360                 log.debug("EventList:" + list);
361             }
362             String[] events = eventRE.split(list);
363             if (log.isInfoEnabled()) {
364                 log.info("Found " + events.length + " possible events");
365             }
366             if (yearRE != null && yearRE.match(page)) {
367                 fixedYear = yearRE.getParen(1).trim();
368             }
369             if (log.isInfoEnabled()) {
370                 log.info("Found fixed year " + fixedYear);
371             }
372             for (int j = 0; j < events.length; j++) {
373                 if (log.isDebugEnabled()) {
374                     log.debug("Get raw event :" + events[j]);
375                 }
376                 if (titleRE.match(events[j])) {
377                     Event event = new Event();
378                     event.setId(id + "-" + i + "-" + j);
379                     event.setSummary(titleRE.getParen(1).trim());
380                     if (fixedYear != null) {
381                         event.setYear(fixedYear);
382                     }
383                     if (event.getSummary().trim().length() == 0) {
384                         continue;
385                     }
386                     if (startDateRE != null && startDateRE.match(events[j])) {
387                         for (int k = 0; k < startDateFormats.length; k++) {
388                             String format = startDateFormats[k];
389                             if (log.isDebugEnabled()) {
390                                 log.debug("Parse with format " + format);
391                             }
392                             SimpleDateFormat sdf = new SimpleDateFormat(format);
393                             try {
394                                 String dateStr = startDateRE.getParen(1).trim();
395                                 Date date = sdf.parse(dateStr);
396                                 event.setStartDate(date);
397                                 event.setEndDate(date);
398                                 if (log.isDebugEnabled()) {
399                                     log.debug("Parse " + dateStr + " succeed with format "
400                                                + format + " and date " + date);
401                                 }
402                                 break;
403                             } catch (ParseException ex) {
404                                 if (log.isInfoEnabled()) {
405                                     log.info("Parse failed with format " + format);
406                                 }
407                             }
408                         }
409                     }
410                     if (endDateRE != null && endDateRE.match(events[j])) {
411                         for (int k = 0; k < endDateFormats.length; k++) {
412                             String format = endDateFormats[k];
413                             if (log.isDebugEnabled()) {
414                                 log.debug("Parse with format " + format);
415                             }
416                             SimpleDateFormat sdf = new SimpleDateFormat(format);
417                             try {
418                                 String dateStr = endDateRE.getParen(1).trim();
419                                 Date endDate = sdf.parse(dateStr);
420                                 GregorianCalendar cal = new GregorianCalendar();
421                                 cal.setTime(endDate);
422                                 cal.add(GregorianCalendar.DAY_OF_WEEK, 1);
423                                 //cal.add(GregorianCalendar.MINUTE, -1);
424                                 // add one day due to 00:00:00
425                                 event.setEndDate(cal.getTime());
426                                 if (log.isDebugEnabled()) {
427                                     log.debug("Parse " + dateStr + " succeed with format "
428                                               + format + " and date " + endDate);
429                                 }
430                                 break;
431                             } catch (ParseException ex) {
432                                 if (log.isInfoEnabled()) {
433                                     log.info("Parse failed with format " + format);
434                                 }
435                             }
436                         }
437                     }
438                     if (startTimeRE != null && startTimeRE.match(events[j])) {
439                         for (int k = 0; k < startTimeFormats.length; k++) {
440                             String format = startTimeFormats[k];
441                             if (log.isDebugEnabled()) {
442                                 log.debug("Parse with format " + format);
443                             }
444                             SimpleDateFormat sdf = new SimpleDateFormat(format);
445                             try {
446                                 String dateStr = startTimeRE.getParen(1).trim();
447                                 Date date = sdf.parse(dateStr);
448                                 event.setStartTime(date);
449                                 event.setEndTime(date);
450                                 if (log.isDebugEnabled()) {
451                                     log.debug("Parse " + dateStr + " succeed with format "
452                                                + format + " and date " + date);
453                                 }
454                                 break;
455                             } catch (ParseException ex) {
456                                 if (log.isInfoEnabled()) {
457                                     log.info("Parse failed with format " + format);
458                                 }
459                             }
460                         }
461                     }
462                     if (endTimeRE != null && endTimeRE.match(events[j])) {
463                         for (int k = 0; k < endTimeFormats.length; k++) {
464                             String format = endTimeFormats[k];
465                             if (log.isDebugEnabled()) {
466                                 log.debug("Parse with format " + format);
467                             }
468                             SimpleDateFormat sdf = new SimpleDateFormat(format);
469                             try {
470                                 String dateStr = endTimeRE.getParen(1).trim();
471                                 Date date = sdf.parse(dateStr);
472                                 event.setEndTime(date);
473                                 if (log.isDebugEnabled()) {
474                                     log.debug("Parse " + dateStr + " succeed with format "
475                                               + format + " and date " + date);
476                                 }
477                                 break;
478                             } catch (ParseException ex) {
479                                 if (log.isInfoEnabled()) {
480                                     log.info("Parse failed with format " + format);
481                                 }
482                             }
483                         }
484                     }
485                     if (descriptionRE != null
486                             && descriptionRE.match(events[j])) {
487                         event.setDescription(descriptionRE.getParen(1).trim());
488                     }
489                     if (locationRE != null && locationRE.match(events[j])) {
490                         event.setLocation(locationRE.getParen(1).trim());
491                     }
492                     event.setConverter(new ICalConverter());
493                     if (log.isInfoEnabled()) {
494                         log.info("Event:\n" + event);
495                     }
496                     try {
497                         event.check();
498                         eventList.add(event);
499                     } catch (Exception ex) {
500                         ex.printStackTrace();
501                         log.error("Check FAILED " + ex + " for event " + event);
502                     }
503                 }
504             }
505         }
506         return eventList;
507     }
508     /***
509      * gets the content from a web page.
510      *@param url the url of the web page
511      *@return the content of the web page
512      */
513     private String getWebPageContent(String url) {
514         if (log.isTraceEnabled()) {
515             log.trace("private String getWebPageContent(String " + url + ")");
516         }
517         // Create an instance of HttpClient.
518         HttpClient client = new HttpClient();
519         String proxyHost = config.getString("proxyHost", null);
520         int proxyPort = 0;
521         if (proxyHost != null && config.containsKey("proxyPort")) {
522             proxyPort = config.getInt("proxyPort");
523         }
524         if (log.isDebugEnabled()) {
525             log.debug("Using proxyHost:"
526                     + proxyHost + " proxyPort:" + proxyPort);
527         }
528         GetMethod method = new GetMethod(url);
529         try {
530             // Execute the method.
531             int statusCode = client.executeMethod(method);
532             if (statusCode != HttpStatus.SC_OK) {
533                 System.err.println("Method failed: " + method.getStatusLine());
534             }
535             // Read the response body.
536             byte[] responseBody = method.getResponseBody();
537             // Deal with the response.
538             // Use caution: ensure correct character encoding and is not binary data
539             return new String(responseBody);
540         } catch (HttpException e) {
541             System.err.println("Fatal protocol violation: " + e.getMessage());
542             e.printStackTrace();
543         } catch (IOException e) {
544             System.err.println("Fatal transport error: " + e.getMessage());
545             e.printStackTrace();
546         } finally {
547             // Release the connection.
548             method.releaseConnection();
549         }
550         return null;
551     }
552     /***
553      * the main method.
554      * @param args arguments
555      * @throws Exception if an error occurs
556      */
557     public static void main(String[] args) throws Exception {
558         if (log.isDebugEnabled()) {
559             log.debug("Application started");
560         }
561         Main process = new Main();
562         process.process();
563         if (log.isDebugEnabled()) {
564             log.debug("Application finished");
565         }
566     }
567 }
568