/*
* 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()) ;
}
}