Rework push server so it holds the subscriptions in a DB
This commit is contained in:
@@ -16,10 +16,6 @@
|
||||
<name>push-service-client-lib</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
@@ -28,6 +24,16 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Misc dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
2. How to use the client-lib in a client application
|
||||
====================================================
|
||||
- Include property 'push-service-client.serverUrl=' and set it to the path to the server part
|
||||
- (optional) Enable loggin via property 'logging.level.de.pushservice=DEBUG'
|
||||
- (optional) Enable logging via property 'logging.level.de.pushservice=DEBUG'
|
||||
- Enable component scan in app configuration via '@ComponentScan("de.pushservice.client")'
|
||||
- Obtain an instance of SubscriptionService via '@Autowired'
|
||||
- Add a JavaScript file with content 'registerServiceWorker();' to register the service worker and subscribing at the
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.http.ResponseEntity;
|
||||
|
||||
public enum ResponseReason {
|
||||
OK(HttpStatus.OK),
|
||||
EMPTY_SCOPE(HttpStatus.BAD_REQUEST),
|
||||
UNKNOWN_ERROR(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
private final HttpStatus httpStatus;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package de.pushservice.client.controller;
|
||||
|
||||
import de.pushservice.client.ResponseReason;
|
||||
import de.pushservice.client.dto.SubscriptionDto;
|
||||
import de.pushservice.client.service.SubscriptionService;
|
||||
import org.slf4j.Logger;
|
||||
@@ -22,8 +21,6 @@ public class SubscriptionController {
|
||||
public ResponseEntity subscribe(@RequestBody SubscriptionDto subscriptionDto) {
|
||||
LOGGER.debug(String.format("Received subscription for endpoint %s", subscriptionDto.getEndpoint()));
|
||||
|
||||
this.subscriptionService.subscribe(subscriptionDto);
|
||||
|
||||
return ResponseReason.OK.toResponseEntity();
|
||||
return this.subscriptionService.subscribe(subscriptionDto).toResponseEntity();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
package de.pushservice.client.dto;
|
||||
|
||||
public class NotificationRequestDto {
|
||||
private SubscriptionDto subscriptionDto;
|
||||
private String scope;
|
||||
private MessageDto messageDto;
|
||||
|
||||
public NotificationRequestDto(SubscriptionDto subscriptionDto, MessageDto messageDto) {
|
||||
this.subscriptionDto = subscriptionDto;
|
||||
public NotificationRequestDto(String scope, MessageDto messageDto) {
|
||||
this.scope = scope;
|
||||
this.messageDto = messageDto;
|
||||
}
|
||||
|
||||
public SubscriptionDto getSubscriptionDto() {
|
||||
return subscriptionDto;
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setSubscriptionDto(SubscriptionDto subscriptionDto) {
|
||||
this.subscriptionDto = subscriptionDto;
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
|
||||
public MessageDto getMessageDto() {
|
||||
|
||||
@@ -4,6 +4,7 @@ public class SubscriptionDto {
|
||||
private String auth;
|
||||
private String endpoint;
|
||||
private String key;
|
||||
private String scope;
|
||||
|
||||
public void setAuth(String auth) {
|
||||
this.auth = auth;
|
||||
@@ -28,4 +29,12 @@ public class SubscriptionDto {
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public String getScope() {
|
||||
return scope;
|
||||
}
|
||||
|
||||
public void setScope(String scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import de.pushservice.client.dto.NotificationRequestDto;
|
||||
import de.pushservice.client.dto.PayloadDto;
|
||||
import de.pushservice.client.dto.SubscriptionDto;
|
||||
import de.pushservice.client.model.Urgency;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -30,37 +31,41 @@ public class SubscriptionService {
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
private Set<SubscriptionDto> subscriptions = new HashSet<>();
|
||||
private String scope;
|
||||
|
||||
public void subscribe(SubscriptionDto subscriptionDto) {
|
||||
this.subscriptions.add(subscriptionDto);
|
||||
public boolean isInitialized() {
|
||||
return StringUtils.isNotEmpty(this.scope);
|
||||
}
|
||||
|
||||
public int notifyAll(PayloadDto payload, String topic, Urgency urgency) throws JsonProcessingException {
|
||||
int notificationsSend = 0;
|
||||
final Urgency tmpUrgency = Optional.ofNullable(urgency).orElse(Urgency.NORMAL);
|
||||
|
||||
final MessageDto messageDto = new MessageDto(objectMapper.writeValueAsString(payload), topic, tmpUrgency);
|
||||
|
||||
for (SubscriptionDto subscription : this.subscriptions) {
|
||||
final NotificationRequestDto requestDto = new NotificationRequestDto(subscription, messageDto);
|
||||
|
||||
LOGGER.debug(String.format("Sending notification for endpoint %s", subscription.getEndpoint()));
|
||||
|
||||
final ResponseEntity<String> responseEntity = new RestTemplate()
|
||||
.exchange(this.config.getServerUrl(), HttpMethod.POST, new HttpEntity<>(requestDto), String.class);
|
||||
|
||||
final ResponseReason responseReason = ResponseReason.fromResponseEntity(responseEntity);
|
||||
|
||||
if (ResponseReason.OK == responseReason) {
|
||||
notificationsSend++;
|
||||
}
|
||||
else {
|
||||
// Well, nothing we can do about it
|
||||
LOGGER.error("Sending notification to endpoint failed! %s", responseReason);
|
||||
public ResponseReason subscribe(SubscriptionDto subscriptionDto) {
|
||||
if (StringUtils.isEmpty(this.scope)) {
|
||||
this.scope = subscriptionDto.getScope();
|
||||
}
|
||||
else {
|
||||
if(!this.scope.equals(subscriptionDto.getScope())) {
|
||||
// Should not happen since the scope is the host + context path of the hosting app
|
||||
LOGGER.warn(String.format("Scope changed! Old: %s, new %s", this.scope, subscriptionDto.getScope()));
|
||||
}
|
||||
}
|
||||
|
||||
return notificationsSend;
|
||||
final ResponseEntity<String> responseEntity = new RestTemplate()
|
||||
.exchange(this.config.getServerUrl() + "subscribe", HttpMethod.POST, new HttpEntity<>(subscriptionDto), String.class);
|
||||
|
||||
return ResponseReason.fromResponseEntity(responseEntity);
|
||||
}
|
||||
|
||||
public void notify(PayloadDto payload, String topic, Urgency urgency) throws JsonProcessingException {
|
||||
final Urgency tmpUrgency = Optional.ofNullable(urgency).orElse(Urgency.NORMAL);
|
||||
final MessageDto messageDto = new MessageDto(objectMapper.writeValueAsString(payload), topic, tmpUrgency);
|
||||
final NotificationRequestDto requestDto = new NotificationRequestDto(this.scope, messageDto);
|
||||
|
||||
final ResponseEntity<String> responseEntity = new RestTemplate()
|
||||
.exchange(this.config.getServerUrl() + "notify", HttpMethod.POST, new HttpEntity<>(requestDto), String.class);
|
||||
|
||||
final ResponseReason responseReason = ResponseReason.fromResponseEntity(responseEntity);
|
||||
|
||||
if (ResponseReason.OK != responseReason) {
|
||||
LOGGER.error(String.format("Could not send notification! %s", responseReason));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ function registerServiceWorker() {
|
||||
// Registration was successful
|
||||
console.log('ServiceWorker registration successful with scope: ', registration.scope);
|
||||
|
||||
initializeState();
|
||||
initializeState(registration.scope);
|
||||
}, function(err) {
|
||||
// registration failed :(
|
||||
console.log('ServiceWorker registration failed: ', err);
|
||||
@@ -14,7 +14,7 @@ function registerServiceWorker() {
|
||||
}
|
||||
}
|
||||
|
||||
function initializeState() {
|
||||
function initializeState(registrationScope) {
|
||||
if (!('showNotification' in ServiceWorkerRegistration.prototype)) {
|
||||
console.warn('Notifications aren\'t supported.');
|
||||
return;
|
||||
@@ -33,11 +33,11 @@ function initializeState() {
|
||||
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
|
||||
serviceWorkerRegistration.pushManager.getSubscription().then(function (subscription) {
|
||||
if (!subscription) {
|
||||
subscribe();
|
||||
subscribe(registrationScope);
|
||||
return;
|
||||
}
|
||||
|
||||
sendSubscriptionToServer(subscription);
|
||||
sendSubscriptionToServer(subscription, registrationScope);
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('Error during getSubscription()', err);
|
||||
@@ -45,10 +45,10 @@ function initializeState() {
|
||||
});
|
||||
}
|
||||
|
||||
function subscribe() {
|
||||
function subscribe(registrationScope) {
|
||||
navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) {
|
||||
serviceWorkerRegistration.pushManager.subscribe({userVisibleOnly: true}).then(function (subscription) {
|
||||
return sendSubscriptionToServer(subscription);
|
||||
return sendSubscriptionToServer(subscription, registrationScope);
|
||||
})
|
||||
.catch(function (e) {
|
||||
if (Notification.permission === 'denied') {
|
||||
@@ -60,7 +60,7 @@ function subscribe() {
|
||||
});
|
||||
}
|
||||
|
||||
function sendSubscriptionToServer(subscription) {
|
||||
function sendSubscriptionToServer(subscription, registrationScope) {
|
||||
var key = subscription.getKey ? subscription.getKey('p256dh') : '';
|
||||
var auth = subscription.getKey ? subscription.getKey('auth') : '';
|
||||
|
||||
@@ -76,7 +76,8 @@ function sendSubscriptionToServer(subscription) {
|
||||
// Take byte[] and turn it into a base64 encoded string suitable for
|
||||
// POSTing to a server over HTTP
|
||||
key: key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : '',
|
||||
auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : ''
|
||||
auth: auth ? btoa(String.fromCharCode.apply(null, new Uint8Array(auth))) : '',
|
||||
scope: registrationScope
|
||||
})
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user