Skip to content

Commit

Permalink
Merge pull request #444 from RADAR-base/fix-fcm-token-deadlock
Browse files Browse the repository at this point in the history
Fix deadlock in fetching protocols
  • Loading branch information
blootsvoets authored Oct 20, 2023
2 parents 9247004 + cffb75a commit ee10868
Show file tree
Hide file tree
Showing 13 changed files with 240 additions and 200 deletions.
12 changes: 6 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apply plugin: 'io.spring.dependency-management'
apply plugin: 'scala'

group = 'org.radarbase'
version = '2.4.1-SNAPSHOT'
version = '2.4.0'

java {
toolchain {
Expand Down Expand Up @@ -111,7 +111,7 @@ javafx {
}

checkstyle {
configDirectory = file("config/checkstyle")
configDirectory.set(file("config/checkstyle"))
toolVersion = "10.8.0"
showViolations = false
ignoreFailures = true
Expand All @@ -133,14 +133,14 @@ test {
}
}

task unpack(type: Copy) {
duplicatesStrategy = 'include'
tasks.register('unpack', Copy) {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
dependsOn bootJar
from(zipTree(tasks.bootJar.outputs.files.singleFile))
into("build/dependency")
}

task loadTest(type: JavaExec) {
tasks.register('loadTest', JavaExec) {
dependsOn testClasses
description = "Load Test With Gatling"
group = "Load Test"
Expand All @@ -157,7 +157,7 @@ task loadTest(type: JavaExec) {
]
}

task integrationTest(type: Test) {
tasks.register('integrationTest', Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
useJUnitPlatform() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.radarbase.appserver.entity.DataMessageStateEvent;
import org.radarbase.appserver.entity.NotificationStateEvent;
import org.radarbase.appserver.event.state.dto.DataMessageStateEventDto;
import org.radarbase.appserver.event.state.dto.NotificationStateEventDto;
import org.radarbase.appserver.service.DataMessageStateEventService;
Expand Down Expand Up @@ -65,9 +67,8 @@ public MessageStateEventListener(ObjectMapper objectMapper,
public void onNotificationStateChange(NotificationStateEventDto event) {
String info = convertMapToString(event.getAdditionalInfo());
log.debug("ID: {}, STATE: {}", event.getNotification().getId(), event.getState());
org.radarbase.appserver.entity.NotificationStateEvent eventEntity =
new org.radarbase.appserver.entity.NotificationStateEvent(
event.getNotification(), event.getState(), event.getTime(), info);
NotificationStateEvent eventEntity = new NotificationStateEvent(
event.getNotification(), event.getState(), event.getTime(), info);
notificationStateEventService.addNotificationStateEvent(eventEntity);
}

Expand All @@ -77,22 +78,21 @@ public void onNotificationStateChange(NotificationStateEventDto event) {
public void onDataMessageStateChange(DataMessageStateEventDto event) {
String info = convertMapToString(event.getAdditionalInfo());
log.debug("ID: {}, STATE: {}", event.getDataMessage().getId(), event.getState());
org.radarbase.appserver.entity.DataMessageStateEvent eventEntity =
new org.radarbase.appserver.entity.DataMessageStateEvent(
event.getDataMessage(), event.getState(), event.getTime(), info);
DataMessageStateEvent eventEntity = new DataMessageStateEvent(
event.getDataMessage(), event.getState(), event.getTime(), info);
dataMessageStateEventService.addDataMessageStateEvent(eventEntity);
}

public String convertMapToString(Map<String, String> additionalInfoMap) {
String info = null;
if (additionalInfoMap != null) {
try {
info = objectMapper.writeValueAsString(additionalInfoMap);
} catch (JsonProcessingException exc) {
log.warn("error processing event's additional info: {}", additionalInfoMap);
}
if (additionalInfoMap == null) {
return null;
}
try {
return objectMapper.writeValueAsString(additionalInfoMap);
} catch (JsonProcessingException exc) {
log.warn("error processing event's additional info: {}", additionalInfoMap);
return null;
}
return info;
}
// we can add more event listeners by annotating with @EventListener
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,9 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.radarbase.appserver.event.state.dto.NotificationStateEventDto;
import org.radarbase.appserver.entity.TaskStateEvent;
import org.radarbase.appserver.event.state.dto.TaskStateEventDto;
import org.radarbase.appserver.service.TaskStateEventService;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
Expand All @@ -50,7 +49,6 @@ public TaskStateEventListener(ObjectMapper objectMapper,
this.taskStateEventService = taskStateEventService;
}


/**
* Handle an application event.
*
Expand All @@ -62,22 +60,21 @@ public TaskStateEventListener(ObjectMapper objectMapper,
public void onTaskStateChange(TaskStateEventDto event) {
String info = convertMapToString(event.getAdditionalInfo());
log.debug("ID: {}, STATE: {}", event.getTask().getId(), event.getState());
org.radarbase.appserver.entity.TaskStateEvent eventEntity =
new org.radarbase.appserver.entity.TaskStateEvent(
TaskStateEvent eventEntity = new TaskStateEvent(
event.getTask(), event.getState(), event.getTime(), info);
taskStateEventService.addTaskStateEvent(eventEntity);
}

public String convertMapToString(Map<String, String> additionalInfoMap) {
String info = null;
if (additionalInfoMap != null) {
try {
info = objectMapper.writeValueAsString(additionalInfoMap);
} catch (JsonProcessingException exc) {
log.warn("error processing event's additional info: {}", additionalInfoMap);
}
if (additionalInfoMap == null) {
return null;
}
try {
return objectMapper.writeValueAsString(additionalInfoMap);
} catch (JsonProcessingException exc) {
log.warn("error processing event's additional info: {}", additionalInfoMap);
return null;
}
return info;
}
// we can add more event listeners by annotating with @EventListener
}
40 changes: 20 additions & 20 deletions src/main/java/org/radarbase/appserver/service/GithubClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.io.InputStream;
Expand All @@ -56,7 +57,7 @@ public class GithubClient {
private final transient String authorizationHeader;

private transient final Duration httpTimeout;
private transient final Executor executor;
private transient final HttpClient client;

@Value("${security.github.client.maxContentLength:1000000}")
private transient int maxContentLength;
Expand All @@ -68,18 +69,16 @@ public GithubClient(
@Value("${security.github.client.token:}") String githubToken) {
this.authorizationHeader = githubToken != null ? "Bearer " + githubToken.trim() : "";
this.httpTimeout = Duration.ofSeconds(httpTimeout);
this.executor = new ThreadPoolExecutor(0,
8,
30,
TimeUnit.SECONDS,
new SynchronousQueue<>());
this.client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(this.httpTimeout)
.build();
}

public String getGithubContent(String url) throws IOException, InterruptedException {
log.debug("Fetching Github URL {}", url);
URI uri = URI.create(url);
if (!this.isValidGithubUri(uri)) {
throw new MalformedURLException("Invalid Github url.");
}
HttpResponse<InputStream> response = makeRequest(uri);

if (response.statusCode() >= 200 && response.statusCode() < 300) {
Expand All @@ -98,12 +97,6 @@ public String getGithubContent(String url) throws IOException, InterruptedExcept
}

private HttpResponse<InputStream> makeRequest(URI uri) throws InterruptedException {
HttpClient client = HttpClient.newBuilder()
.executor(executor)
.version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(this.httpTimeout)
.build();
try {
return client.send(getRequest(uri), HttpResponse.BodyHandlers.ofInputStream());
} catch (IOException ex) {
Expand Down Expand Up @@ -131,14 +124,21 @@ private void checkContentLength(int contentLength) {
}
}

public boolean isValidGithubUri(URI uri) {
return uri.getHost().equalsIgnoreCase(GITHUB_API_URI)
public URI getValidGithubUri(URI uri) throws IOException {
if (uri.getHost().equalsIgnoreCase(GITHUB_API_URI)
&& uri.getScheme().equalsIgnoreCase("https")
&& (uri.getPort() == -1 || uri.getPort() == 443);
&& (uri.getPort() == -1 || uri.getPort() == 443)) {
return UriComponentsBuilder.newInstance()
.scheme("https").host(uri.getHost()).path(uri.getPath()).query(uri.getQuery()).build().toUri();
}
else {
throw new MalformedURLException("Invalid Github url.");
}
}

private HttpRequest getRequest(URI uri) {
HttpRequest.Builder request = HttpRequest.newBuilder(uri)
private HttpRequest getRequest(URI uri) throws IOException {
URI validUri = this.getValidGithubUri(uri);
HttpRequest.Builder request = HttpRequest.newBuilder(validUri)
.header("Accept", GITHUB_API_ACCEPT_HEADER)
.GET()
.timeout(httpTimeout);
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/org/radarbase/appserver/service/GithubService.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.Duration;

@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON)
public class GithubService {

private final transient CachedFunction<String, String> cachedGetContent;
private final transient GithubClient githubClient;

@Autowired
public GithubService(
Expand All @@ -24,13 +26,24 @@ public GithubService(
int retryTime,
@Value("${security.github.cache.size:10000}")
int maxSize) {
this.githubClient = githubClient;
this.cachedGetContent = new CachedFunction<>(githubClient::getGithubContent,
Duration.ofSeconds(cacheTime),
Duration.ofSeconds(retryTime),
maxSize);
}

public String getGithubContent(String url) throws Exception {
return this.cachedGetContent.applyWithException(url);
public String getGithubContent(String url) throws IOException, InterruptedException {
try {
return this.cachedGetContent.applyWithException(url);
} catch (IOException | InterruptedException ex) {
throw ex;
} catch (Exception ex) {
throw new IllegalStateException("Unknown exception " + ex, ex);
}
}

public String getGithubContentWithoutCache(String url) throws IOException, InterruptedException {
return githubClient.getGithubContent(url);
}
}
Loading

0 comments on commit ee10868

Please sign in to comment.