From 15753b2ebdac2ab49cf5682c417851a0653e136e Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 25 Sep 2018 12:49:57 +0300 Subject: notifications server refactoring --- .../java/com/juick/components/MPNSManager.java | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 juick-notifications/src/main/java/com/juick/components/MPNSManager.java (limited to 'juick-notifications/src/main/java/com/juick/components/MPNSManager.java') diff --git a/juick-notifications/src/main/java/com/juick/components/MPNSManager.java b/juick-notifications/src/main/java/com/juick/components/MPNSManager.java new file mode 100644 index 00000000..56c2df8d --- /dev/null +++ b/juick-notifications/src/main/java/com/juick/components/MPNSManager.java @@ -0,0 +1,163 @@ +package com.juick.components; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.juick.ExternalToken; +import com.juick.User; +import com.juick.components.mpns.MPNSError; +import com.juick.components.mpns.MPNSToken; +import com.juick.service.component.*; +import com.juick.util.MessageUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Created by vital on 29.03.2017. + */ +public class MPNSManager implements NotificationListener { + + private static Logger logger = LoggerFactory.getLogger(MPNSManager.class); + + private String accessToken; + + @Inject + private ObjectMapper jsonMapper; + @Value("${wns_application_sip:}") + private String applicationSip; + @Value("${wns_client_secret:}") + private String applicationSecret; + private RestTemplate wnsService; + + @Inject + private NotificationsManager notificationsManager; + + @PostConstruct + public void authenticate() throws IOException { + String url = "https://login.live.com/accesstoken.srf"; + MultiValueMap form = new LinkedMultiValueMap<>(); + form.add("grant_type", "client_credentials"); + form.add("client_id", applicationSip); + form.add("client_secret", applicationSecret); + form.add("scope", "notify.windows.com"); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + wnsService = new RestTemplate(); + wnsService.getMessageConverters().add(new StringHttpMessageConverter(StandardCharsets.UTF_8)); + HttpEntity> entity = new HttpEntity<>(form, httpHeaders); + ResponseEntity response = wnsService.exchange(url, HttpMethod.POST, entity, String.class); + String responseBody = response.getBody(); + HttpStatus statusCode = response.getStatusCode(); + if (statusCode != HttpStatus.OK) { + MPNSError error = jsonMapper.readValue(responseBody, MPNSError.class); + throw new IOException(error.getError() + ": " + error.getErrorDescription()); + } + MPNSToken token = jsonMapper.readValue(responseBody, MPNSToken.class); + if (token.getTokenType().length() >= 1) { + token.setTokenType(Character.toUpperCase(token.getTokenType().charAt(0)) + token.getTokenType().substring(1)); + } + accessToken = token.getTokenType() + " " + token.getAccessToken(); + logger.info("MPNS authenticated"); + } + + void sendNotification(final String url, final String xml) throws IOException { + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setContentType(new MediaType("text", "xml", StandardCharsets.UTF_8)); + httpHeaders.set("Authorization", accessToken); + httpHeaders.set("X-WNS-Type", "wns/toast"); + HttpEntity requestEntity = new HttpEntity<>(xml, httpHeaders); + try { + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); + wnsService.exchange(builder.build(true).toUri(), HttpMethod.POST, requestEntity, Void.class); + } catch (HttpClientErrorException ex) { + HttpStatus statusCode = ex.getStatusCode(); + if (statusCode == HttpStatus.GONE) { + // expired + logger.info("{} is scheduled to remove", url); + notificationsManager.addInvalidMPNSToken(url); + } else { + String headersContent = ex.getResponseHeaders().entrySet().stream() + .filter(x -> x.getKey().startsWith("X-WNS-") || x.getKey().startsWith("WWW-")) + .map(x -> x.getKey() + ": " + String.join(",", x.getValue())) + .collect(Collectors.joining("\n")); + throw new IOException(headersContent); + } + } + } + + @Override + public void processMessageEvent(MessageEvent messageEvent) { + com.juick.Message jmsg = messageEvent.getMessage(); + List users = messageEvent.getUsers(); + + List urls = users.stream().flatMap(u -> u.getTokens().stream()).filter(d -> d.getType().equals("mpns")) + .map(ExternalToken::getToken).collect(Collectors.toList()); + + if (urls.isEmpty()) { + logger.info("WNS: no recipients"); + } else { + try { + String text1 = "@" + jmsg.getUser().getName(); + if (!jmsg.getTags().isEmpty()) { + text1 += ":" + StringEscapeUtils.escapeXml11(MessageUtils.getTagsString(jmsg)); + } + String text2 = StringEscapeUtils.escapeXml11(StringUtils.defaultString(jmsg.getText())); + String xml = "" + + "" + + "" + + "" + + "" + + "" + text1 + "" + + "" + text2 + "" + + "" + + "" + + "" + + "" + + "" + + ""; + logger.trace(xml); + for (String url : urls) { + logger.info("WNS: {}", url); + sendNotification(url, xml); + } + } catch (IOException | IllegalStateException ex) { + logger.error("WNS: ", ex); + } + } + } + + @Override + public void processSubscribeEvent(SubscribeEvent subscribeEvent) { + + } + + @Override + public void processLikeEvent(LikeEvent likeEvent) { + + } + + @Override + public void processPingEvent(PingEvent pingEvent) { + + } + + @Override + public void processMessageReadEvent(MessageReadEvent messageReadEvent) { + + } +} -- cgit v1.2.3