Skip to content

Commit

Permalink
Merge pull request #3 from choonchernlim/feature/dec16
Browse files Browse the repository at this point in the history
exchange.sleep.on.connection.error
  • Loading branch information
choonchernlim authored Dec 17, 2016
2 parents 0902b49 + 33e77a1 commit e367c3c
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 23 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 0.4.0 - 2016-12-16

* `exchange.sleep.on.connection.error` = Whether to suppress thrown Exchange connection error or not.

## 0.3.0 - 2016-12-15

* Handled all-day event.
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ exchange.password.env=CALSYNC_EXCHANGE_PASSWORD
# Accepted value: string.
exchange.url=https://[EXCHANGE_SERVER]/ews/exchange.asmx

# Sleep on Exchange connection error.
#
# When set to `false`, an exception is thrown when failing to connect against Exchange server.
#
# When set to `true`, CalSync will swallow the thrown connection exception and re-attempt
# on next sync if `next.sync.in.minutes` is greater than 0. This is useful if you are only
# able to connect against Exchange server within work firewall.
#
# Ensure `exchange.url` is set correctly first before enabling this feature so that you
# are able to connect against Exchange server.
#
# Accepted value: true, false.
exchange.sleep.on.connection.error=false

# File path to Google client_secret.json.
#
# Accepted value: string.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package com.github.choonchernlim.calsync.core
import com.github.choonchernlim.calsync.exchange.ExchangeService
import com.github.choonchernlim.calsync.google.GoogleService
import com.google.inject.Inject
import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException
import org.apache.commons.lang3.exception.ExceptionUtils
import org.joda.time.DateTime
import org.slf4j.Logger
import org.slf4j.LoggerFactory

/**
* Class to sync events Exchange to Google Calendar.
*/
class ExchangeToGoogleService {
private static final Logger LOGGER = LoggerFactory.getLogger(ExchangeToGoogleService)

ExchangeService exchangeService
GoogleService googleService
Expand All @@ -34,12 +39,26 @@ class ExchangeToGoogleService {
exchangeService.init(userConfig)
googleService.init(userConfig)

// retrieve exchange events
List<CalSyncEvent> exchangeEvents = exchangeService.getEvents(
startDateTime,
endDateTime,
userConfig.includeCanceledEvents,
userConfig.includeEventBody)
List<CalSyncEvent> exchangeEvents = []
try {
// retrieve exchange events
exchangeEvents = exchangeService.getEvents(
startDateTime,
endDateTime,
userConfig.includeCanceledEvents,
userConfig.includeEventBody)
}
catch (ServiceRequestException e) {
// on connection exception, suppress exception if user says so
if (ExceptionUtils.getStackTrace(e).contains('java.net.ConnectException') &&
userConfig.exchangeSleepOnConnectionError) {
LOGGER.error(e.getMessage())
return
}

// otherwise, throw exception
throw e
}

// retrieve google calendar
String calendarId = googleService.getCalendarId(userConfig.googleCalendarName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class UserConfig {
String exchangeUserName
String exchangePassword
String exchangeUrl
Boolean exchangeSleepOnConnectionError
String googleClientSecretJsonFilePath
String googleCalendarName
Integer totalSyncDays
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class UserConfigReader {
static final String EXCHANGE_USERNAME_ENV_KEY = 'exchange.username.env'
static final String EXCHANGE_PASSWORD_ENV_KEY = 'exchange.password.env'
static final String EXCHANGE_URL_KEY = 'exchange.url'
static final String EXCHANGE_SLEEP_ON_CONNECTION_ERROR = 'exchange.sleep.on.connection.error'
static final String GOOGLE_CLIENT_SECRET_JSON_KEY = 'google.client.secret.json.file.path'
static final String GOOGLE_CALENDAR_NAME_KEY = 'google.calendar.name'
static final String TOTAL_SYNC_IN_DAYS_KEY = 'total.sync.in.days'
Expand Down Expand Up @@ -65,8 +66,9 @@ class UserConfigReader {

String exchangeUserName = validatePropEnv(props, errors, EXCHANGE_USERNAME_ENV_KEY)
String exchangePassword = validatePropEnv(props, errors, EXCHANGE_PASSWORD_ENV_KEY)

String exchangeUrl = validatePropString(props, errors, EXCHANGE_URL_KEY)
Boolean exchangeSleepOnConnectionError = validatePropBoolean(props, errors, EXCHANGE_SLEEP_ON_CONNECTION_ERROR)

String googleClientSecretJsonFilePath = validatePropString(props, errors, GOOGLE_CLIENT_SECRET_JSON_KEY)
String googleCalendarName = validatePropString(props, errors, GOOGLE_CALENDAR_NAME_KEY)

Expand All @@ -91,6 +93,7 @@ class UserConfigReader {
exchangeUserName: exchangeUserName,
exchangePassword: exchangePassword,
exchangeUrl: exchangeUrl,
exchangeSleepOnConnectionError: exchangeSleepOnConnectionError,
googleClientSecretJsonFilePath: googleClientSecretJsonFilePath,
googleCalendarName: googleCalendarName,
totalSyncDays: totalSyncDays,
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/calsync-sample.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ exchange.password.env=CALSYNC_EXCHANGE_PASSWORD
# Accepted value: string.
exchange.url=https://[EXCHANGE_SERVER]/ews/exchange.asmx

# Sleep on Exchange connection error.
#
# When set to `false`, an exception is thrown when failing to connect against Exchange server.
#
# When set to `true`, CalSync will swallow the thrown connection exception and re-attempt
# on next sync if `next.sync.in.minutes` is greater than 0. This is useful if you are only
# able to connect against Exchange server within work firewall.
#
# Ensure `exchange.url` is set correctly first before enabling this feature so that you
# are able to connect against Exchange server.
#
# Accepted value: true, false.
exchange.sleep.on.connection.error=false

# File path to Google client_secret.json.
#
# Accepted value: string.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/log4j.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out"/>
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d{MMM dd, yyyy hh:mm a} %-5p %-20c{1} - %m%n"/>
<param name="ConversionPattern" value="%d{MMM dd '@' hh:mm a} %-5p %-25c{1} - %m%n"/>
</layout>
</appender>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.choonchernlim.calsync.core

import com.github.choonchernlim.calsync.exchange.ExchangeService
import com.github.choonchernlim.calsync.google.GoogleService
import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException
import org.joda.time.DateTime
import spock.lang.Specification

Expand Down Expand Up @@ -31,6 +32,7 @@ class ExchangeToGoogleServiceSpec extends Specification {
exchangeUserName: 'exchangeUserName',
exchangePassword: 'exchangePassword',
exchangeUrl: 'exchangeUrl',
exchangeSleepOnConnectionError: false,
googleClientSecretJsonFilePath: 'googleClientSecretJsonFilePath',
googleCalendarName: googleCalendarName,
totalSyncDays: 1,
Expand Down Expand Up @@ -126,4 +128,42 @@ class ExchangeToGoogleServiceSpec extends Specification {
1 * googleService.executeBatch(googleCalendarId)
0 * _
}

def 'run - given exchange connection error and exchangeSleepOnConnectionError == false, should throw exception'() {
given:
userConfig.exchangeSleepOnConnectionError = false

when:
service.run(userConfig)

then:
1 * dateTimeNowSupplier.get() >> dateTime
1 * exchangeService.init(userConfig)
1 * googleService.init(userConfig)
1 * exchangeService.getEvents(startDateTime, endDateTime, true, true) >> {
throw new ServiceRequestException('test', new ConnectException('connection error'))
}
0 * _

thrown ServiceRequestException
}

def 'run - given exchange connection error and exchangeSleepOnConnectionError == true, should not throw exception'() {
given:
userConfig.exchangeSleepOnConnectionError = true

when:
service.run(userConfig)

then:
1 * dateTimeNowSupplier.get() >> dateTime
1 * exchangeService.init(userConfig)
1 * googleService.init(userConfig)
1 * exchangeService.getEvents(startDateTime, endDateTime, true, true) >> {
throw new ServiceRequestException('test', new ConnectException('connection error'))
}
0 * _

notThrown Exception
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class UserConfigReaderSpec extends Specification {
properties.setProperty(UserConfigReader.EXCHANGE_USERNAME_ENV_KEY, 'EXCHANGE_USERNAME_ENV')
properties.setProperty(UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY, 'EXCHANGE_PASSWORD_ENV')
properties.setProperty(UserConfigReader.EXCHANGE_URL_KEY, 'URL')
properties.setProperty(UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR, 'true')
properties.setProperty(UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY, 'CLIENT_JSON')
properties.setProperty(UserConfigReader.GOOGLE_CALENDAR_NAME_KEY, 'CALENDAR_NAME')
properties.setProperty(UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY, '1')
Expand Down Expand Up @@ -59,6 +60,7 @@ class UserConfigReaderSpec extends Specification {
exchange.username.env=EXCHANGE_USERNAME_ENV
exchange.password.env=EXCHANGE_PASSWORD_ENV
exchange.url=URL
exchange.sleep.on.connection.error=true
google.client.secret.json.file.path=CLIENT_JSON
google.calendar.name=CALENDAR_NAME
total.sync.in.days=1
Expand All @@ -74,6 +76,7 @@ include.event.body=false
userConfig.exchangeUserName == 'EXCHANGE_USERNAME'
userConfig.exchangePassword == 'EXCHANGE_PASSWORD'
userConfig.exchangeUrl == 'URL'
userConfig.exchangeSleepOnConnectionError
userConfig.googleClientSecretJsonFilePath == 'CLIENT_JSON'
userConfig.googleCalendarName == 'CALENDAR_NAME'
userConfig.totalSyncDays == 1
Expand Down Expand Up @@ -113,21 +116,23 @@ include.event.body=false
thrown CalSyncException

where:
label | key | value
'blank' | UserConfigReader.EXCHANGE_USERNAME_ENV_KEY | ' '
'blank' | UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY | ' '
'blank' | UserConfigReader.EXCHANGE_URL_KEY | ' '
'blank' | UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY | ' '
'blank' | UserConfigReader.GOOGLE_CALENDAR_NAME_KEY | ' '
'blank' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | ' '
'not integer' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | 'val'
'zero' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | '0'
'blank' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | ' '
'not integer' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | 'val'
'blank' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | ' '
'not boolean' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | 'val'
'blank' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | ' '
'not boolean' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | 'val'
label | key | value
'blank' | UserConfigReader.EXCHANGE_USERNAME_ENV_KEY | ' '
'blank' | UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY | ' '
'blank' | UserConfigReader.EXCHANGE_URL_KEY | ' '
'blank' | UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY | ' '
'blank' | UserConfigReader.GOOGLE_CALENDAR_NAME_KEY | ' '
'blank' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | ' '
'not integer' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | 'val'
'zero' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | '0'
'blank' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | ' '
'not integer' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | 'val'
'blank' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | ' '
'not boolean' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | 'val'
'blank' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | ' '
'not boolean' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | 'val'
'blank' | UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR | ' '
'not boolean' | UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR | 'val'
}

@Unroll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class UserConfigSpec extends Specification {
exchangeUserName: null,
exchangePassword: null,
exchangeUrl: null,
exchangeSleepOnConnectionError: null,
googleClientSecretJsonFilePath: null,
googleCalendarName: null,
totalSyncDays: null,
Expand All @@ -21,6 +22,7 @@ class UserConfigSpec extends Specification {
userConfig.exchangeUserName == null
userConfig.exchangePassword == null
userConfig.exchangeUrl == null
userConfig.exchangeSleepOnConnectionError == null
userConfig.googleClientSecretJsonFilePath == null
userConfig.googleCalendarName == null
userConfig.totalSyncDays == null
Expand All @@ -34,6 +36,7 @@ class UserConfigSpec extends Specification {
exchangeUserName: 'exchangeUserName',
exchangePassword: 'exchangePassword',
exchangeUrl: 'exchangeUrl',
exchangeSleepOnConnectionError: true,
googleClientSecretJsonFilePath: 'googleClientSecretJsonFilePath',
googleCalendarName: 'googleCalendarName',
totalSyncDays: 1,
Expand All @@ -45,6 +48,7 @@ class UserConfigSpec extends Specification {
userConfig.exchangeUserName == 'exchangeUserName'
userConfig.exchangePassword == 'exchangePassword'
userConfig.exchangeUrl == 'exchangeUrl'
userConfig.exchangeSleepOnConnectionError
userConfig.googleClientSecretJsonFilePath == 'googleClientSecretJsonFilePath'
userConfig.googleCalendarName == 'googleCalendarName'
userConfig.totalSyncDays == 1
Expand Down

0 comments on commit e367c3c

Please sign in to comment.