dexter
dexter

Reputation: 3

Microsoft Graph - Error executing the request

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

Answers (0)

Related Questions