/* * Copyright 2016 Sam Sun <me@samczsun.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.samczsun.skype4j.internal.threads; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; import com.samczsun.skype4j.exceptions.ConnectionException; import com.samczsun.skype4j.exceptions.handler.ErrorSource; import com.samczsun.skype4j.internal.Endpoints; import com.samczsun.skype4j.internal.EventType; import com.samczsun.skype4j.internal.ExceptionHandler; import com.samczsun.skype4j.internal.SkypeImpl; import com.samczsun.skype4j.internal.SkypeThreadFactory; import com.samczsun.skype4j.internal.Utils; import java.io.IOException; import java.net.HttpURLConnection; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; public class PollThread extends Thread { private final SkypeImpl skype; private final ExecutorService inputFetcher; private final String endpointId; private final Object lock = new Object(); private IOException pendingException; private HttpURLConnection connection; public PollThread(SkypeImpl skype, String endpointId) { super(String.format("Skype4J-Poller-%s", skype.getUsername())); this.skype = skype; this.inputFetcher = Executors.newSingleThreadExecutor(new SkypeThreadFactory(skype, "PollBlocker")); this.endpointId = endpointId; } public void run() { int pollId = 0; while (skype.isAuthenticated()) { final Endpoints.EndpointConnection<HttpURLConnection> epconn = Endpoints.POLL .open(skype, pollId) .header("Content-Type", "application/json") .dontConnect(); final AtomicBoolean complete = new AtomicBoolean(false); while (skype.isAuthenticated()) { try { complete.set(false); connection = epconn.post(); inputFetcher.execute(() -> { try { connection.getResponseCode(); } catch (IOException e) { pendingException = e; } finally { complete.set(true); synchronized (lock) { lock.notify(); } } }); synchronized (lock) { if (!complete.get()) { lock.wait(); } } if (pendingException != null) { skype.handleError(ErrorSource.POLLING_SKYPE, pendingException, false); continue; } if (connection.getHeaderField("Set-RegistrationToken") != null) { skype.setRegistrationToken(connection.getHeaderField("Set-RegistrationToken")); } if (connection.getResponseCode() == 403) { try { HttpURLConnection conn = Endpoints .custom("https://client-s.gateway.messenger.live.com/v1/users/ME/endpoints/" + endpointId, skype) .dontConnect() .header("Authentication", "skypetoken=" + skype.getSkypeToken()) .put(new JsonObject()); if (conn.getResponseCode() != 200) { skype.handleError(ErrorSource.REFRESHING_ENDPOINT, ExceptionHandler.generateException("While refreshing endpoint", conn), true); return; } String regtoken = conn.getHeaderField("Set-RegistrationToken"); if (regtoken != null) { skype.setRegistrationToken(regtoken); } JsonObject object = Utils.parseJsonObject(conn.getInputStream()); if (object.get("subscriptions") != null) { pollId = object.get("subscriptions").asArray().get(0).asObject().get("id").asInt(); } break; } catch (IOException e) { skype.handleError(ErrorSource.REFRESHING_ENDPOINT, e, true); return; } } if (connection.getResponseCode() != 200) { continue; } if (skype.getScheduler().isShutdown()) { if (!skype.isShutdownRequested()) { skype.handleError(ErrorSource.THREAD_POOL_DEAD, null, true); } return; } final JsonObject message = Utils.parseJsonObject(connection.getInputStream()); skype.getScheduler().execute(() -> { if (message.get("eventMessages") != null) { for (JsonValue elem : message.get("eventMessages").asArray()) { JsonObject eventObj = elem.asObject(); EventType type = EventType.getByName(eventObj.get("resourceType").asString()); if (type != null) { try { type.handle(skype, eventObj); } catch (Throwable t) { skype.handleError(ErrorSource.PARSING_MESSAGE, t, false); } } else { skype.handleError(ErrorSource.NO_MESSAGE_TYPE, null, false); } } } }); } catch (InterruptedException e) { return; } catch (IOException | ConnectionException e) { skype.handleError(ErrorSource.POLLING_SKYPE, e, true); return; } finally { connection.disconnect(); } } } } public void shutdown() { this.interrupt(); while (this.getState() != State.TERMINATED) ; if (this.connection != null) { this.connection.disconnect(); } this.inputFetcher.shutdownNow(); while (!this.inputFetcher.isTerminated()) ; } }