Skip to main content

How and why I designed a meeting (iCalendar) library

Greetings!

Have you ever thought about how to send a meeting event programmatically? In one of the projects I worked on, there was a requirement to send the appointment information to the user (in addition to visualizing it in our UIs). This is when I thought to do this as a meeting request.

How can we do that? We learned that these calendar applications follow a standard format, that is "icalendar". Now my job is to create these .ics files. We investigated a few existing libraries as well but I was not happy with them as I wanted to send it as a meeting request but it did not work with existing libraries. At the same time, we have a lengthy process when introducing new libraries. Hence, why don't I build my own library 😉?

What is iCalendar?

The iCalendar is the standard specification for calendaring and scheduling events. It allows users to store and exchange calendaring and scheduling information such as events, to-dos, etc. According to the specification usually have an extension of .ics.

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
BEGIN:VEVENT
UID:uid1@example.com
DTSTAMP:19970714T170000Z
ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com
DTSTART:19970714T170000Z
DTEND:19970715T040000Z
SUMMARY:Bastille Day Party
GEO:48.85299;2.36885
END:VEVENT
END:VCALENDAR

A naive approach

A naive approach would be to create a template of this(as the above sample) or even a simple String and insert value at runtime. That would be faster to develop as well. It will be possible to complete the development in an hour or so. However, I thought that that is not an engineering approach. We can do better.
Also, if we do it in that way, how can we add new features in the future? How can we maintain this? Maybe we never want to do that but still, it is much nicer to create a proper library.

Design considerations

  • Support current requirement
  • Do not over-engineer
  • Reusable as a library
  • The consumer should not worry about the standard

What do I need if I am the consumer?

If I'm the consumer of this library I like to simply call methods in a chain. I do not want to worry about these tags. Instead, I simply build this.
Calendar.newMeeting().event().method().build();

That is why I treated every iCalendar tag as a class that is responsible to generate its tag. I can unit test each tag separately. Finally, the topmost tag will be responsible for generating all the underline tags. As the library consumer, I just call these.

How did I design it?

First I created a common interface for all tags and then create tags around it.

public interface ICalComponent {
  String build();
}

Implementations of this will have the iCalendar tag. For example, the summary tag is like below.

public final class Summary implements ICalComponent {
  private String eventSummary;

  public Summary(String eventSummary) {
    this.eventSummary = eventSummary;
  }

  @Override
  public String build() {
    return "SUMMARY;LANGUAGE=en-US:".concat(eventSummary);
  }
}

As you could see, "BEGIN:VCALENDAR" is the topmost tag. Hence it is responsible for building the final output.

public final class Calendar implements ICalComponent {
  private List<ICalComponent> iCalComponents = new ArrayList();

  private Calendar() {
  }

  public static Calendar newMeeting() {
    return new Calendar();
  }

  public Calendar method(Method method) {
    this.iCalComponents.add(method);
    return this;
  }

  public Calendar version(Version version) {
    this.iCalComponents.add(version);
    return this;
  }

  public Calendar timeZone(TimeZone timeZone) {
    this.iCalComponents.add(timeZone);
    return this;
  }

  public Calendar event(Event event) {
    this.iCalComponents.add(event);
    return this;
  }

  public String build() {
    StringBuilder builder = new StringBuilder();
    builder.append("BEGIN:VCALENDAR\n");
	
    for (ICalComponent iCalComponent : iCalComponents) {
      builder.append(iCalComponent.build()).append("\n");
    }

    builder.append("END:VCALENDAR");
    return builder.toString();
  }
}

How to use it?

The final usage is simply composing tags.
Event event = Event.newEvent()
  .uId(new Uid(calendarRequest.getId()))
  .status(Status.CONFIRMED)
  .organizer(new Organizer("organizer@example.com", Role.CHAIR, "Manjula"))
  .attendees(new Attendees("attendee1@example.com", "attendee2@example.com"))
  .startDateTime(new StartDateTime(start))
  .endDateTime(new EndDateTime(end))
  .summary(new Summary("summary"));	  
String iCalendar = Calendar.newMeeting()
  .version(Version.VERSION_2_0)
  .method(Method.REQUEST)
  .timeZone(timeZone)
  .event(event)
  .build();

Conclusion

Even though I am unable to share the full source code, I have shared my ideas and the approach to solving such a problem. This can be of course an open-source project. However, there are already such projects available that did not match our requirements.

Happy learning guys ☺

References

https://icalendar.org/
https://en.wikipedia.org/wiki/ICalendar

Comments