1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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
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
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;
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
424
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
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
531 int statusCode = client.executeMethod(method);
532 if (statusCode != HttpStatus.SC_OK) {
533 System.err.println("Method failed: " + method.getStatusLine());
534 }
535
536 byte[] responseBody = method.getResponseBody();
537
538
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
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