Reputation: 3
I have an app which gets our users availability and customers can schedule meeting. We just noticed that for a month now randomly we were getting issues with microsoft graph (our app was getting throttled).
We used 2.5.0 version untill now and i upgraded it to 3.0.0 and implemented batch.
In our app we have a data cache refresher that refreshes data every hour. But when it calls getSchedule i get "socket timeout" errors. But not everytime. It works 2 times then 2 times it fails then it works 2 times etc...
I have refactored my code 10 times already. This version i have now is the most "stable" version. In other versions of my method it was getting application throttled errors or batch exceeded limits error.
Can anyone please help me / give me any guidence on why does this randomly fail and how can i fix this "timeout" issue...
This is the error that i get:
Error:
com.microsoft.graph.core.ClientException: Error executing the request
at com.microsoft.graph.http.CoreHttpProvider.sendRequestInternal(CoreHttpProvider.java:388)
at com.microsoft.graph.http.CoreHttpProvider.send(CoreHttpProvider.java:214)
at com.microsoft.graph.http.CoreHttpProvider.send(CoreHttpProvider.java:191)
at com.microsoft.graph.content.BatchRequest.post(BatchRequest.java:60)
at com.company.myapp.microsoftgraph.MicrosoftGraphClient.getUserCalendarFreeSlots(MicrosoftGraphClient.java:205)
at com.company.myapp.service.DataCacheService.getAvailability(DataCacheService.java:192)
at com.company.myapp.service.DataCacheService.refreshAvailability(DataCacheService.java:178)
at com.company.myapp.service.DataCacheService.refreshAvailabilityWithLocks(DataCacheService.java:90)
at com.company.myapp.service.DataCacheRefresher.refreshAvailabilityCache(DataCacheRefresher.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:503)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:84)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:95)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:826)
Caused by: java.net.SocketTimeoutException: timeout
at okio.SocketAsyncTimeout.newTimeoutException(JvmOkio.kt:143)
at okio.AsyncTimeout.access$newTimeoutException(AsyncTimeout.kt:162)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:335)
at okio.RealBufferedSource.indexOf(RealBufferedSource.kt:427)
at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.kt:320)
at okhttp3.internal.http1.HeadersReader.readLine(HeadersReader.kt:29)
at okhttp3.internal.http1.Http1ExchangeCodec.readResponseHeaders(Http1ExchangeCodec.kt:178)
at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.microsoft.graph.httpcore.RedirectHandler.intercept(RedirectHandler.java:137)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.microsoft.graph.httpcore.RetryHandler.intercept(RetryHandler.java:176)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.microsoft.graph.httpcore.AuthenticationHandler.intercept(AuthenticationHandler.java:59)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at com.microsoft.graph.httpcore.TelemetryHandler.intercept(TelemetryHandler.java:69)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
at com.microsoft.graph.http.CoreHttpProvider.sendRequestInternal(CoreHttpProvider.java:385)
... 22 more
Caused by: javax.net.ssl.SSLException: Socket closed
at sun.security.ssl.Alert.createSSLException(Alert.java:127)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:331)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:274)
at sun.security.ssl.TransportContext.fatal(TransportContext.java:269)
at sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1572)
at sun.security.ssl.SSLSocketImpl.access$400(SSLSocketImpl.java:73)
at sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:982)
at okio.InputStreamSource.read(JvmOkio.kt:90)
at okio.AsyncTimeout$source$1.read(AsyncTimeout.kt:129)
... 48 more
Caused by: java.net.SocketException: Socket closed
at java.net.SocketInputStream.read(SocketInputStream.java:204)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:464)
at sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68)
at sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1350)
at sun.security.ssl.SSLSocketImpl.access$300(SSLSocketImpl.java:73)
at sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:966)
... 50 more
This is my method:
MicrosoftGraphClient:
@Component
public class MicrosoftGraphClient<IHttpRequest> {
private static Logger logger = LoggerFactory.getLogger(MicrosoftGraphClient.class);
private static final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
public static final String timeZone="Central Europe Standard Time";
@Autowired GraphServiceClient graphClient;
@Value("${microsoft.graph.defaultUserId}") private String defaultUserId;
public List<CalendarAvailabilityView> getUserCalendarFreeSlots(String userid, List<String> calendarIds, LocalDate startDate, LocalDate endDate){
logger.info("getUserCalendarFreeSlots()");
if (userid == null)
userid=defaultUserId;
StopWatch sw = new StopWatch("getUserCalendarFreeSlots");
LinkedList<Option> requestOptions = new LinkedList<Option>();
requestOptions.add(new HeaderOption("Prefer", "outlook.timezone=\""+timeZone+"\""));
requestOptions.add(new HeaderOption("Content-Type", "application/json"));
LocalDateTime startDateTime = LocalDateTime.of(startDate, LocalTime.of(0, 0));
LocalDateTime endDateTime = LocalDateTime.of(endDate, LocalTime.of(23, 59));
// calculate dates between
List<LocalDate> dates = new ArrayList<> ();
for (LocalDate d = startDate; !d.isAfter(endDate); d = d.plusDays(1)) {
dates.add(d);
}
DateTimeTimeZone startTime = new DateTimeTimeZone();
startTime.dateTime = dtf.format(startDateTime);
startTime.timeZone = timeZone;
DateTimeTimeZone endTime = new DateTimeTimeZone();
endTime.dateTime = dtf.format(endDateTime);
endTime.timeZone = timeZone;
int availabilityViewInterval = 30;
List<List<String>> partitions = Lists.partition(calendarIds, 5);
List<CalendarAvailabilityView> availability = new ArrayList<CalendarAvailabilityView>();
CalendarGetScheduleCollectionRequest request = null;
CalendarGetScheduleParameterSet req = null;
for (List<String> schedulesList: partitions) {
BatchRequestContent batchRequestContent = new BatchRequestContent();
logger.info("reading partition: "+StringUtils.join(schedulesList, ", "));
sw.start("get data");
req = CalendarGetScheduleParameterSet.newBuilder()
.withSchedules(schedulesList)
.withEndTime(endTime)
.withStartTime(startTime)
.withAvailabilityViewInterval(availabilityViewInterval).build();
request = graphClient.users()
.byId(userid).calendar().getSchedule(req)
.buildRequest(requestOptions);
String requestId = batchRequestContent.addBatchRequestStep(request, HttpMethod.POST, req);
BatchResponseContent batchResponseContent = graphClient.batch()
.buildRequest()
.post(batchRequestContent);
CalendarGetScheduleCollectionResponse response = new CalendarGetScheduleCollectionResponse();
ISerializer serializer = graphClient.getSerializer();
JsonObject obj = batchResponseContent.getResponseById(requestId).body.getAsJsonObject();
// Populate CalendarGetScheduleCollectionResponse
if (obj.has("value")) {
JsonArray scheduleItems = obj.getAsJsonArray("value");
List<ScheduleInformation> scheduleInformationList = new ArrayList<>();
for (JsonElement item : scheduleItems) {
ScheduleInformation info = serializer.deserializeObject(item.toString(), ScheduleInformation.class);
scheduleInformationList.add(info);
}
response.value = scheduleInformationList;
}
CalendarGetScheduleCollectionPage page = request.buildFromResponse(response);
List<ScheduleInformation> objects = new ArrayList<>();
objects.addAll(page.getCurrentPage());
CalendarGetScheduleCollectionRequestBuilder builder = page.getNextPage();
while (builder != null) {
request = builder.buildRequest();
page = request.post();
objects.addAll(page.getCurrentPage());
builder = page.getNextPage();
}
sw.stop();
if (objects != null && objects.size() > 0) {
for (ScheduleInformation si: objects) {
String availabilityView = si.availabilityView;
WorkingHours workingHours =si.workingHours;
if (availabilityView != null) {
CalendarAvailabilityView calav=new CalendarAvailabilityView();
calav.setAvailabilityView(availabilityView);
calav.setIdCalendar(si.scheduleId);
calav.setWorkingHours(convert(workingHours));
calav.setDates(dates);
availability.add(calav);
}
}
}
}
logger.info(sw.prettyPrint());
return availability;
}
private Map<String, WorkingHour> convert(WorkingHours wh) {
Map<String, WorkingHour> workingHoursMap = new LinkedHashMap<String, WorkingHour>();
if (wh==null)
return null;
//List<com.microsoft.graph.models.generated.DayOfWeek> daysOfWeek = wh.daysOfWeek;
List<com.microsoft.graph.models.DayOfWeek> daysOfWeek = wh.daysOfWeek;
for (com.microsoft.graph.models.DayOfWeek dow: daysOfWeek) {
WorkingHour workingHour = new WorkingHour();
TimeOfDay endTime = wh.endTime;
workingHour.setEndTime(LocalTime.of(endTime.getHour(), endTime.getMinute()));
TimeOfDay startTime = wh.startTime;
workingHour.setStartTime(LocalTime.of(startTime.getHour(), startTime.getMinute()));
workingHoursMap.put(dow.name().toUpperCase(), workingHour);
}
return workingHoursMap;
}
}
GraphClientConfig
@Configuration
public class GraphClientConfig {
@Value("${microsoft.graph.tenantId}") private String tenantId;
@Value("${microsoft.graph.clientId}") private String clientId;
@Value("${microsoft.graph.clientSecret}") private String clientSecret;
@Value("${microsoft.graph.authority}") private String authority;
@Value("${microsoft.graph.scopes}") private List<String> scopes;
@Bean
public ClientSecretCredential graphClientAuthProvider() {
ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder()
.clientId(clientId)
.clientSecret(clientSecret)
.tenantId(tenantId)
.build();
return clientSecretCredential;
}
@Bean
public GraphServiceClient graphClient() {
TokenCredentialAuthProvider authProvider = new TokenCredentialAuthProvider(scopes, graphClientAuthProvider());
GraphServiceClient<?> graphClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
return graphClient;
}
}
Upvotes: 0
Views: 205