Skip to content

Commit e367c3c

Browse files
Merge pull request #3 from choonchernlim/feature/dec16
exchange.sleep.on.connection.error
2 parents 0902b49 + 33e77a1 commit e367c3c

File tree

10 files changed

+127
-23
lines changed

10 files changed

+127
-23
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Change Log
22

3+
## 0.4.0 - 2016-12-16
4+
5+
* `exchange.sleep.on.connection.error` = Whether to suppress thrown Exchange connection error or not.
6+
37
## 0.3.0 - 2016-12-15
48

59
* Handled all-day event.

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ exchange.password.env=CALSYNC_EXCHANGE_PASSWORD
6161
# Accepted value: string.
6262
exchange.url=https://[EXCHANGE_SERVER]/ews/exchange.asmx
6363

64+
# Sleep on Exchange connection error.
65+
#
66+
# When set to `false`, an exception is thrown when failing to connect against Exchange server.
67+
#
68+
# When set to `true`, CalSync will swallow the thrown connection exception and re-attempt
69+
# on next sync if `next.sync.in.minutes` is greater than 0. This is useful if you are only
70+
# able to connect against Exchange server within work firewall.
71+
#
72+
# Ensure `exchange.url` is set correctly first before enabling this feature so that you
73+
# are able to connect against Exchange server.
74+
#
75+
# Accepted value: true, false.
76+
exchange.sleep.on.connection.error=false
77+
6478
# File path to Google client_secret.json.
6579
#
6680
# Accepted value: string.

src/main/groovy/com/github/choonchernlim/calsync/core/ExchangeToGoogleService.groovy

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,17 @@ package com.github.choonchernlim.calsync.core
33
import com.github.choonchernlim.calsync.exchange.ExchangeService
44
import com.github.choonchernlim.calsync.google.GoogleService
55
import com.google.inject.Inject
6+
import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException
7+
import org.apache.commons.lang3.exception.ExceptionUtils
68
import org.joda.time.DateTime
9+
import org.slf4j.Logger
10+
import org.slf4j.LoggerFactory
711

812
/**
913
* Class to sync events Exchange to Google Calendar.
1014
*/
1115
class ExchangeToGoogleService {
16+
private static final Logger LOGGER = LoggerFactory.getLogger(ExchangeToGoogleService)
1217

1318
ExchangeService exchangeService
1419
GoogleService googleService
@@ -34,12 +39,26 @@ class ExchangeToGoogleService {
3439
exchangeService.init(userConfig)
3540
googleService.init(userConfig)
3641

37-
// retrieve exchange events
38-
List<CalSyncEvent> exchangeEvents = exchangeService.getEvents(
39-
startDateTime,
40-
endDateTime,
41-
userConfig.includeCanceledEvents,
42-
userConfig.includeEventBody)
42+
List<CalSyncEvent> exchangeEvents = []
43+
try {
44+
// retrieve exchange events
45+
exchangeEvents = exchangeService.getEvents(
46+
startDateTime,
47+
endDateTime,
48+
userConfig.includeCanceledEvents,
49+
userConfig.includeEventBody)
50+
}
51+
catch (ServiceRequestException e) {
52+
// on connection exception, suppress exception if user says so
53+
if (ExceptionUtils.getStackTrace(e).contains('java.net.ConnectException') &&
54+
userConfig.exchangeSleepOnConnectionError) {
55+
LOGGER.error(e.getMessage())
56+
return
57+
}
58+
59+
// otherwise, throw exception
60+
throw e
61+
}
4362

4463
// retrieve google calendar
4564
String calendarId = googleService.getCalendarId(userConfig.googleCalendarName)

src/main/groovy/com/github/choonchernlim/calsync/core/UserConfig.groovy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class UserConfig {
1010
String exchangeUserName
1111
String exchangePassword
1212
String exchangeUrl
13+
Boolean exchangeSleepOnConnectionError
1314
String googleClientSecretJsonFilePath
1415
String googleCalendarName
1516
Integer totalSyncDays

src/main/groovy/com/github/choonchernlim/calsync/core/UserConfigReader.groovy

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class UserConfigReader {
1212
static final String EXCHANGE_USERNAME_ENV_KEY = 'exchange.username.env'
1313
static final String EXCHANGE_PASSWORD_ENV_KEY = 'exchange.password.env'
1414
static final String EXCHANGE_URL_KEY = 'exchange.url'
15+
static final String EXCHANGE_SLEEP_ON_CONNECTION_ERROR = 'exchange.sleep.on.connection.error'
1516
static final String GOOGLE_CLIENT_SECRET_JSON_KEY = 'google.client.secret.json.file.path'
1617
static final String GOOGLE_CALENDAR_NAME_KEY = 'google.calendar.name'
1718
static final String TOTAL_SYNC_IN_DAYS_KEY = 'total.sync.in.days'
@@ -65,8 +66,9 @@ class UserConfigReader {
6566

6667
String exchangeUserName = validatePropEnv(props, errors, EXCHANGE_USERNAME_ENV_KEY)
6768
String exchangePassword = validatePropEnv(props, errors, EXCHANGE_PASSWORD_ENV_KEY)
68-
6969
String exchangeUrl = validatePropString(props, errors, EXCHANGE_URL_KEY)
70+
Boolean exchangeSleepOnConnectionError = validatePropBoolean(props, errors, EXCHANGE_SLEEP_ON_CONNECTION_ERROR)
71+
7072
String googleClientSecretJsonFilePath = validatePropString(props, errors, GOOGLE_CLIENT_SECRET_JSON_KEY)
7173
String googleCalendarName = validatePropString(props, errors, GOOGLE_CALENDAR_NAME_KEY)
7274

@@ -91,6 +93,7 @@ class UserConfigReader {
9193
exchangeUserName: exchangeUserName,
9294
exchangePassword: exchangePassword,
9395
exchangeUrl: exchangeUrl,
96+
exchangeSleepOnConnectionError: exchangeSleepOnConnectionError,
9497
googleClientSecretJsonFilePath: googleClientSecretJsonFilePath,
9598
googleCalendarName: googleCalendarName,
9699
totalSyncDays: totalSyncDays,

src/main/resources/calsync-sample.conf

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,20 @@ exchange.password.env=CALSYNC_EXCHANGE_PASSWORD
1313
# Accepted value: string.
1414
exchange.url=https://[EXCHANGE_SERVER]/ews/exchange.asmx
1515

16+
# Sleep on Exchange connection error.
17+
#
18+
# When set to `false`, an exception is thrown when failing to connect against Exchange server.
19+
#
20+
# When set to `true`, CalSync will swallow the thrown connection exception and re-attempt
21+
# on next sync if `next.sync.in.minutes` is greater than 0. This is useful if you are only
22+
# able to connect against Exchange server within work firewall.
23+
#
24+
# Ensure `exchange.url` is set correctly first before enabling this feature so that you
25+
# are able to connect against Exchange server.
26+
#
27+
# Accepted value: true, false.
28+
exchange.sleep.on.connection.error=false
29+
1630
# File path to Google client_secret.json.
1731
#
1832
# Accepted value: string.

src/main/resources/log4j.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
66
<param name="Target" value="System.out"/>
77
<layout class="org.apache.log4j.PatternLayout">
8-
<param name="ConversionPattern" value="%d{MMM dd, yyyy hh:mm a} %-5p %-20c{1} - %m%n"/>
8+
<param name="ConversionPattern" value="%d{MMM dd '@' hh:mm a} %-5p %-25c{1} - %m%n"/>
99
</layout>
1010
</appender>
1111

src/test/groovy/com/github/choonchernlim/calsync/core/ExchangeToGoogleServiceSpec.groovy

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.github.choonchernlim.calsync.core
22

33
import com.github.choonchernlim.calsync.exchange.ExchangeService
44
import com.github.choonchernlim.calsync.google.GoogleService
5+
import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException
56
import org.joda.time.DateTime
67
import spock.lang.Specification
78

@@ -31,6 +32,7 @@ class ExchangeToGoogleServiceSpec extends Specification {
3132
exchangeUserName: 'exchangeUserName',
3233
exchangePassword: 'exchangePassword',
3334
exchangeUrl: 'exchangeUrl',
35+
exchangeSleepOnConnectionError: false,
3436
googleClientSecretJsonFilePath: 'googleClientSecretJsonFilePath',
3537
googleCalendarName: googleCalendarName,
3638
totalSyncDays: 1,
@@ -126,4 +128,42 @@ class ExchangeToGoogleServiceSpec extends Specification {
126128
1 * googleService.executeBatch(googleCalendarId)
127129
0 * _
128130
}
131+
132+
def 'run - given exchange connection error and exchangeSleepOnConnectionError == false, should throw exception'() {
133+
given:
134+
userConfig.exchangeSleepOnConnectionError = false
135+
136+
when:
137+
service.run(userConfig)
138+
139+
then:
140+
1 * dateTimeNowSupplier.get() >> dateTime
141+
1 * exchangeService.init(userConfig)
142+
1 * googleService.init(userConfig)
143+
1 * exchangeService.getEvents(startDateTime, endDateTime, true, true) >> {
144+
throw new ServiceRequestException('test', new ConnectException('connection error'))
145+
}
146+
0 * _
147+
148+
thrown ServiceRequestException
149+
}
150+
151+
def 'run - given exchange connection error and exchangeSleepOnConnectionError == true, should not throw exception'() {
152+
given:
153+
userConfig.exchangeSleepOnConnectionError = true
154+
155+
when:
156+
service.run(userConfig)
157+
158+
then:
159+
1 * dateTimeNowSupplier.get() >> dateTime
160+
1 * exchangeService.init(userConfig)
161+
1 * googleService.init(userConfig)
162+
1 * exchangeService.getEvents(startDateTime, endDateTime, true, true) >> {
163+
throw new ServiceRequestException('test', new ConnectException('connection error'))
164+
}
165+
0 * _
166+
167+
notThrown Exception
168+
}
129169
}

src/test/groovy/com/github/choonchernlim/calsync/core/UserConfigReaderSpec.groovy

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class UserConfigReaderSpec extends Specification {
1313
properties.setProperty(UserConfigReader.EXCHANGE_USERNAME_ENV_KEY, 'EXCHANGE_USERNAME_ENV')
1414
properties.setProperty(UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY, 'EXCHANGE_PASSWORD_ENV')
1515
properties.setProperty(UserConfigReader.EXCHANGE_URL_KEY, 'URL')
16+
properties.setProperty(UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR, 'true')
1617
properties.setProperty(UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY, 'CLIENT_JSON')
1718
properties.setProperty(UserConfigReader.GOOGLE_CALENDAR_NAME_KEY, 'CALENDAR_NAME')
1819
properties.setProperty(UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY, '1')
@@ -59,6 +60,7 @@ class UserConfigReaderSpec extends Specification {
5960
exchange.username.env=EXCHANGE_USERNAME_ENV
6061
exchange.password.env=EXCHANGE_PASSWORD_ENV
6162
exchange.url=URL
63+
exchange.sleep.on.connection.error=true
6264
google.client.secret.json.file.path=CLIENT_JSON
6365
google.calendar.name=CALENDAR_NAME
6466
total.sync.in.days=1
@@ -74,6 +76,7 @@ include.event.body=false
7476
userConfig.exchangeUserName == 'EXCHANGE_USERNAME'
7577
userConfig.exchangePassword == 'EXCHANGE_PASSWORD'
7678
userConfig.exchangeUrl == 'URL'
79+
userConfig.exchangeSleepOnConnectionError
7780
userConfig.googleClientSecretJsonFilePath == 'CLIENT_JSON'
7881
userConfig.googleCalendarName == 'CALENDAR_NAME'
7982
userConfig.totalSyncDays == 1
@@ -113,21 +116,23 @@ include.event.body=false
113116
thrown CalSyncException
114117

115118
where:
116-
label | key | value
117-
'blank' | UserConfigReader.EXCHANGE_USERNAME_ENV_KEY | ' '
118-
'blank' | UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY | ' '
119-
'blank' | UserConfigReader.EXCHANGE_URL_KEY | ' '
120-
'blank' | UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY | ' '
121-
'blank' | UserConfigReader.GOOGLE_CALENDAR_NAME_KEY | ' '
122-
'blank' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | ' '
123-
'not integer' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | 'val'
124-
'zero' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | '0'
125-
'blank' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | ' '
126-
'not integer' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | 'val'
127-
'blank' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | ' '
128-
'not boolean' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | 'val'
129-
'blank' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | ' '
130-
'not boolean' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | 'val'
119+
label | key | value
120+
'blank' | UserConfigReader.EXCHANGE_USERNAME_ENV_KEY | ' '
121+
'blank' | UserConfigReader.EXCHANGE_PASSWORD_ENV_KEY | ' '
122+
'blank' | UserConfigReader.EXCHANGE_URL_KEY | ' '
123+
'blank' | UserConfigReader.GOOGLE_CLIENT_SECRET_JSON_KEY | ' '
124+
'blank' | UserConfigReader.GOOGLE_CALENDAR_NAME_KEY | ' '
125+
'blank' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | ' '
126+
'not integer' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | 'val'
127+
'zero' | UserConfigReader.TOTAL_SYNC_IN_DAYS_KEY | '0'
128+
'blank' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | ' '
129+
'not integer' | UserConfigReader.NEXT_SYNC_IN_MINUTES_KEY | 'val'
130+
'blank' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | ' '
131+
'not boolean' | UserConfigReader.INCLUDE_CANCELED_EVENTS_KEY | 'val'
132+
'blank' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | ' '
133+
'not boolean' | UserConfigReader.INCLUDE_EVENT_BODY_KEY | 'val'
134+
'blank' | UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR | ' '
135+
'not boolean' | UserConfigReader.EXCHANGE_SLEEP_ON_CONNECTION_ERROR | 'val'
131136
}
132137

133138
@Unroll

src/test/groovy/com/github/choonchernlim/calsync/core/UserConfigSpec.groovy

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class UserConfigSpec extends Specification {
1010
exchangeUserName: null,
1111
exchangePassword: null,
1212
exchangeUrl: null,
13+
exchangeSleepOnConnectionError: null,
1314
googleClientSecretJsonFilePath: null,
1415
googleCalendarName: null,
1516
totalSyncDays: null,
@@ -21,6 +22,7 @@ class UserConfigSpec extends Specification {
2122
userConfig.exchangeUserName == null
2223
userConfig.exchangePassword == null
2324
userConfig.exchangeUrl == null
25+
userConfig.exchangeSleepOnConnectionError == null
2426
userConfig.googleClientSecretJsonFilePath == null
2527
userConfig.googleCalendarName == null
2628
userConfig.totalSyncDays == null
@@ -34,6 +36,7 @@ class UserConfigSpec extends Specification {
3436
exchangeUserName: 'exchangeUserName',
3537
exchangePassword: 'exchangePassword',
3638
exchangeUrl: 'exchangeUrl',
39+
exchangeSleepOnConnectionError: true,
3740
googleClientSecretJsonFilePath: 'googleClientSecretJsonFilePath',
3841
googleCalendarName: 'googleCalendarName',
3942
totalSyncDays: 1,
@@ -45,6 +48,7 @@ class UserConfigSpec extends Specification {
4548
userConfig.exchangeUserName == 'exchangeUserName'
4649
userConfig.exchangePassword == 'exchangePassword'
4750
userConfig.exchangeUrl == 'exchangeUrl'
51+
userConfig.exchangeSleepOnConnectionError
4852
userConfig.googleClientSecretJsonFilePath == 'googleClientSecretJsonFilePath'
4953
userConfig.googleCalendarName == 'googleCalendarName'
5054
userConfig.totalSyncDays == 1

0 commit comments

Comments
 (0)