From 7ed3795da542bab397ee7ed8978bde896abf5cf4 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Tue, 6 Feb 2018 17:54:49 +0300 Subject: server: merge api and ws --- .../main/java/com/juick/ws/WebsocketComponent.java | 161 --------------- .../src/main/java/com/juick/ws/XMPPConnection.java | 218 --------------------- .../ws/configuration/WebsocketConfiguration.java | 75 ------- .../ws/configuration/WebsocketInitializer.java | 57 ------ .../com/juick/ws/controllers/ApiController.java | 42 ---- 5 files changed, 553 deletions(-) delete mode 100644 juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/XMPPConnection.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java (limited to 'juick-ws/src/main/java/com/juick/ws') diff --git a/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java b/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java deleted file mode 100644 index 45814ecb..00000000 --- a/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.ws; - -import com.juick.User; -import com.juick.server.helpers.AnonymousUser; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import com.juick.service.UserService; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpHeaders; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.PingMessage; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.handler.TextWebSocketHandler; -import org.springframework.web.util.UriComponents; -import org.springframework.web.util.UriComponentsBuilder; - -import javax.inject.Inject; -import java.io.IOException; -import java.net.URI; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * Created by vitalyster on 28.06.2016. - */ -public class WebsocketComponent extends TextWebSocketHandler { - private static final Logger logger = LoggerFactory.getLogger(WebsocketComponent.class); - - private final List clients = Collections.synchronizedList(new LinkedList<>()); - - @Inject - private UserService userService; - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - - - @Override - public void afterConnectionEstablished(WebSocketSession session) throws Exception { - URI hLocation; - String hXRealIP; - - hLocation = session.getUri(); - HttpHeaders headers = session.getHandshakeHeaders(); - hXRealIP = headers.getOrDefault("X-Real-IP", - Collections.singletonList(session.getRemoteAddress().toString())).get(0); - - // Auth - User visitor = AnonymousUser.INSTANCE; - UriComponents uriComponents = UriComponentsBuilder.fromUri(hLocation).build(); - List hash = uriComponents.getQueryParams().get("hash"); - if (hash != null && hash.get(0).length() == 16) { - visitor = userService.getUserByHash(hash.get(0)); - } else { - logger.info("wrong hash for {} from {}", visitor.getUid(), hXRealIP); - } - logger.info("user {} connected to {} from {}", visitor.getUid(), hLocation.getPath(), hXRealIP); - - int MID = 0; - SocketSubscribed sockSubscr = null; - if (hLocation.getPath().equals("/")) { - logger.info("user {} connected", visitor.getUid()); - sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, false); - } else if (hLocation.getPath().equals("/_all")) { - logger.info("user {} connected to legacy _all ({})", visitor.getUid(), hLocation.getPath()); - sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); - sockSubscr.allMessages = true; - } else if (hLocation.getPath().equals("/_replies")) { - logger.info("user {} connected to legacy _replies ({})", visitor.getUid(), hLocation.getPath()); - sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); - sockSubscr.allReplies = true; - } else if (hLocation.getPath().matches("/\\d+$")) { - MID = NumberUtils.toInt(hLocation.getPath().substring(1), 0); - if (MID > 0) { - if (messagesService.canViewThread(MID, visitor.getUid())) { - logger.info("user {} connected to legacy thread ({}) from {}", visitor.getUid(), MID, hXRealIP); - sockSubscr = new SocketSubscribed(session, hXRealIP, visitor, true); - sockSubscr.MID = MID; - } else { - try { - session.close(new CloseStatus(403, "Forbidden")); - } catch (IOException e) { - logger.warn("ws error", e); - } - } - } - } - if (sockSubscr != null) { - synchronized (clients) { - clients.add(sockSubscr); - logger.info("{} clients connected", clients.size()); - } - } - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { - synchronized (clients) { - logger.info("session closed with status {}: {}", status.getCode(), status.getReason()); - clients.removeIf(c -> c.session.getId().equals(session.getId())); - logger.info("{} clients connected", clients.size()); - } - - } - - @Scheduled(fixedRate = 30000) - public void ping() { - clients.forEach(c -> { - try { - c.session.sendMessage(new PingMessage()); - } catch (IOException e) { - logger.error("WebSocket PING exception", e); - } - }); - } - public List getClients() { - return clients; - } - - class SocketSubscribed { - WebSocketSession session; - String clientName; - User visitor; - int MID; - boolean allMessages; - boolean allReplies; - long tsConnected; - long tsLastData; - boolean legacy; - - public SocketSubscribed(WebSocketSession session, String clientName, User visitor, boolean legacy) { - this.session = session; - this.clientName = clientName; - this.visitor = visitor; - tsConnected = tsLastData = System.currentTimeMillis(); - this.legacy = legacy; - } - } -} diff --git a/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java b/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java deleted file mode 100644 index e338bf66..00000000 --- a/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.ws; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.juick.User; -import com.juick.service.MessagesService; -import com.juick.service.SubscriptionService; -import org.apache.commons.lang3.math.NumberUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; -import org.springframework.web.socket.TextMessage; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSession; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.session.debug.LogbackDebugger; -import rocks.xmpp.core.stanza.model.Message; -import rocks.xmpp.extensions.component.accept.ExternalComponent; -import rocks.xmpp.util.XmppUtils; - -import javax.annotation.PostConstruct; -import javax.inject.Inject; -import javax.xml.bind.JAXBException; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.IOException; -import java.io.StringWriter; -import java.util.List; -import java.util.stream.Collectors; - -/** - * @author ugnich - */ -@Component -public class XMPPConnection implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(XMPPConnection.class); - - @Inject - private WebsocketComponent wsHandler; - @Value("${xmpp_password:secret}") - private String xmppPassword; - @Inject - private ObjectMapper jsonMapper; - @Value("${xmpp_port:5347}") - private int xmppPort; - @Value("${ws_jid:ws.juick.local}") - private String wsJid; - @Value("${service_user:juick}") - private String serviceUser; - - private XmppSession xmpp; - - @Inject - private MessagesService messagesService; - @Inject - private SubscriptionService subscriptionService; - - - @PostConstruct - public void init() { - Assert.notNull(wsHandler, "WebsocketComponent must be initialized"); - - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(com.juick.Message.class)) - .debugger(LogbackDebugger.class) - .build(); - xmpp = ExternalComponent.create(wsJid, xmppPassword, configuration, "localhost", xmppPort); - xmpp.addInboundMessageListener(e -> { - try { - Message msg = e.getMessage(); - com.juick.Message jmsg = msg.getExtension(com.juick.Message.class); - if (jmsg != null) { - if (logger.isInfoEnabled()) { // prevent writeValueAsString execution if log is disabled - try { - StringWriter stanzaWriter = new StringWriter(); - XMLStreamWriter xmppStreamWriter = XmppUtils.createXmppStreamWriter( - xmpp.getConfiguration().getXmlOutputFactory().createXMLStreamWriter(stanzaWriter)); - xmpp.createMarshaller().marshal(msg, xmppStreamWriter); - xmppStreamWriter.flush(); - xmppStreamWriter.close(); - logger.info("got msg: {}", stanzaWriter.toString()); - } catch (XMLStreamException e1) { - logger.info("jaxb exception", e1); - } - - } - if (jmsg.getMid() == 0) { - int uid_to = NumberUtils.toInt(msg.getTo().getLocal(), 0); - if (uid_to > 0) { - onJuickPM(uid_to, jmsg); - } - } else if (jmsg.getRid() == 0) { - // to get full message with attachment, etc. - onJuickMessagePost(messagesService.getMessage(jmsg.getMid())); - } else { - // to get quote and attachment - com.juick.Message reply = messagesService.getReply(jmsg.getMid(), jmsg.getRid()); - onJuickMessageReply(reply); - } - } - } catch (JsonProcessingException ex) { - logger.error("mapper exception", ex); - } catch (JAXBException exc) { - logger.error("jaxb exception", exc); - } - }); - try { - xmpp.connect(); - } catch (XmppException e) { - logger.warn("xmpp extension", e); - } - } - - @Override - public void close() throws Exception { - if (xmpp != null) - xmpp.close(); - - logger.info("XmppSession on WS destroyed"); - } - - private void onJuickPM(final int uid_to, final com.juick.Message jmsg) throws JsonProcessingException { - String json = jsonMapper.writeValueAsString(jmsg); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.getUid() == uid_to) || c.visitor.getName().equals(serviceUser)) - .forEach(c -> { - try { - logger.info("sending pm to {}", c.visitor.getUid()); - c.session.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } - - private void onJuickMessagePost(final com.juick.Message jmsg) throws JsonProcessingException { - String json = jsonMapper.writeValueAsString(jmsg); - List uids = subscriptionService.getSubscribedUsers(jmsg.getUser().getUid(), jmsg.getMid()) - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.getUid() == 0) // anonymous users - || c.visitor.getName().equals(serviceUser) // services - || (!c.legacy && uids.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.info("sending message to {}", c.visitor.getUid()); - c.session.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - c.legacy && c.allMessages) // legacy all posts - .forEach(c -> { - try { - logger.info("sending message to legacy client {}", c.visitor.getUid()); - c.session.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } - - private void onJuickMessageReply(final com.juick.Message jmsg) throws JsonProcessingException { - String json = jsonMapper.writeValueAsString(jmsg); - List threadUsers = - subscriptionService.getUsersSubscribedToComments(jmsg.getMid(), jmsg.getUser().getUid()) - .stream().map(User::getUid).collect(Collectors.toList()); - synchronized (wsHandler.getClients()) { - wsHandler.getClients().stream().filter(c -> - (!c.legacy && c.visitor.getUid() == 0) // anonymous users - || c.visitor.getName().equals(serviceUser) // services - || (!c.legacy && threadUsers.contains(c.visitor.getUid()))) // subscriptions - .forEach(c -> { - try { - logger.info("sending reply to {}", c.visitor.getUid()); - c.session.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - wsHandler.getClients().stream().filter(c -> - (c.legacy && c.allReplies) || (c.legacy && c.MID == jmsg.getMid())) // legacy replies - .forEach(c -> { - try { - logger.info("sending reply to legacy client {}", c.visitor.getUid()); - c.session.sendMessage(new TextMessage(json)); - } catch (IOException e) { - logger.warn("ws error", e); - } - }); - } - } -} diff --git a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java deleted file mode 100644 index 194a75a5..00000000 --- a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.ws.configuration; - -import com.juick.server.configuration.BaseWebConfiguration; -import com.juick.ws.WebsocketComponent; -import com.juick.ws.XMPPConnection; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.PropertySource; -import org.springframework.core.Ordered; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.socket.config.annotation.EnableWebSocket; -import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry; -import org.springframework.web.socket.config.annotation.WebSocketConfigurer; -import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; -import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean; - -import javax.annotation.Nonnull; - -/** - * Created by aalexeev on 11/24/16. - */ -@Configuration -@ComponentScan(basePackages = {"com.juick.ws.controllers"}) -@EnableScheduling -@EnableWebSocket -@EnableWebMvc -@PropertySource("classpath:juick.conf") -public class WebsocketConfiguration extends BaseWebConfiguration implements WebSocketConfigurer { - @Bean - public XMPPConnection xmpp() { - return new XMPPConnection(); - } - @Bean - public WebsocketComponent wsHandler() { - return new WebsocketComponent(); - } - - @Override - public void registerWebSocketHandlers(@Nonnull WebSocketHandlerRegistry registry) { - ((ServletWebSocketHandlerRegistry) registry).setOrder(Ordered.LOWEST_PRECEDENCE); - registry.addHandler(wsHandler(), "/**").setAllowedOrigins("*"); - } - - @Bean - public ServletServerContainerFactoryBean createWebSocketContainer() { - ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); - container.setMaxTextMessageBufferSize(8192); - container.setMaxBinaryMessageBufferSize(8192); - return container; - } - @Override - public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { - configurer.enable(); - } -} diff --git a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java deleted file mode 100644 index 42f0fac2..00000000 --- a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.ws.configuration; - -import com.juick.configuration.DataConfiguration; -import com.juick.server.configuration.StorageConfiguration; -import org.apache.commons.codec.CharEncoding; -import org.springframework.web.filter.CharacterEncodingFilter; -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -import javax.servlet.Filter; - -/** - * Created by vt on 09/02/16. - */ -public class WebsocketInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { - - @Override - protected Class[] getRootConfigClasses() { - return new Class[]{ WebsocketConfiguration.class, DataConfiguration.class, StorageConfiguration.class}; - } - - @Override - protected Class[] getServletConfigClasses() { - return null; - } - - @Override - protected String[] getServletMappings() { - return new String[]{"/"}; - } - - @Override - protected Filter[] getServletFilters() { - return new Filter[]{new CharacterEncodingFilter(CharEncoding.UTF_8)}; - } - - @Override - protected String getServletName() { - return "Web socket dispatcher servlet"; - } -} diff --git a/juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java b/juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java deleted file mode 100644 index dc59193b..00000000 --- a/juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.ws.controllers; - -import com.juick.Status; -import com.juick.ws.WebsocketComponent; -import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RestController; - -import javax.inject.Inject; - -/** - * Created by vitalyster on 25.07.2016. - */ -@RestController -public class ApiController { - @Inject - private WebsocketComponent wsHandler; - - @RequestMapping(value = "/api/status", method = RequestMethod.GET, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE, headers = "Connection!=Upgrade") - public Status status() { - return Status.getStatus(String.valueOf(wsHandler.getClients().size())); - } -} -- cgit v1.2.3