From 194a9a763c6b11d207e682b2f93de94475c473b4 Mon Sep 17 00:00:00 2001 From: Alexander Alexeev Date: Sat, 12 Nov 2016 01:34:07 +0700 Subject: extracted application configuration from Mvc configuration with application specific beans; extracted bean initialization from constructor; force using properties; --- .../com/juick/configuration/DataConfiguration.java | 19 +-- .../src/main/java/com/juick/util/ThreadHelper.java | 36 +++++ .../main/java/com/juick/components/Crosspost.java | 86 +++++++----- .../configuration/CrosspostAppConfiguration.java | 37 +++++ .../configuration/CrosspostConfiguration.java | 122 ----------------- .../configuration/CrosspostInitializer.java | 10 +- .../configuration/CrosspostMvcConfiguration.java | 43 ++++++ .../java/com/juick/components/Notifications.java | 111 ++++++++------- .../NotificationsAppConfiguration.java | 40 ++++++ .../configuration/NotificationsConfiguration.java | 122 ----------------- .../configuration/NotificationsInitializer.java | 10 +- .../NotificationsMvcConfiguration.java | 41 ++++++ .../src/main/java/com/juick/ws/ApiController.java | 24 ---- .../main/java/com/juick/ws/StatusController.java | 30 ---- .../main/java/com/juick/ws/WebsocketComponent.java | 20 ++- .../src/main/java/com/juick/ws/XMPPConnection.java | 152 ++++++++++++--------- .../configuration/WebsocketAppConfiguration.java | 54 ++++++++ .../ws/configuration/WebsocketConfiguration.java | 136 ------------------ .../ws/configuration/WebsocketInitializer.java | 10 +- .../configuration/WebsocketMvcConfiguration.java | 82 +++++++++++ .../com/juick/ws/controllers/ApiController.java | 25 ++++ .../com/juick/ws/controllers/StatusController.java | 32 +++++ .../FileTransferAppConfiguration.java | 38 ++++++ .../configuration/FileTransferConfiguration.java | 107 --------------- .../configuration/FileTransferInitializer.java | 19 ++- .../FileTransferMvcConfiguration.java | 33 +++++ .../main/java/com/juick/components/XMPPServer.java | 60 ++++---- .../configuration/XMPPConfiguration.java | 112 --------------- .../components/configuration/XMPPInitializer.java | 36 ----- .../configuration/XmppAppConfiguration.java | 38 ++++++ .../components/configuration/XmppInitializer.java | 41 ++++++ .../configuration/XmppMvcConfiguration.java | 77 +++++++++++ 32 files changed, 913 insertions(+), 890 deletions(-) create mode 100644 juick-core/src/main/java/com/juick/util/ThreadHelper.java create mode 100644 juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostAppConfiguration.java delete mode 100644 juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostConfiguration.java create mode 100644 juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostMvcConfiguration.java create mode 100644 juick-notifications/src/main/java/com/juick/components/configuration/NotificationsAppConfiguration.java delete mode 100644 juick-notifications/src/main/java/com/juick/components/configuration/NotificationsConfiguration.java create mode 100644 juick-notifications/src/main/java/com/juick/components/configuration/NotificationsMvcConfiguration.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/ApiController.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/StatusController.java create mode 100644 juick-ws/src/main/java/com/juick/ws/configuration/WebsocketAppConfiguration.java delete mode 100644 juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java create mode 100644 juick-ws/src/main/java/com/juick/ws/configuration/WebsocketMvcConfiguration.java create mode 100644 juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java create mode 100644 juick-ws/src/main/java/com/juick/ws/controllers/StatusController.java create mode 100644 juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferAppConfiguration.java delete mode 100644 juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferConfiguration.java create mode 100644 juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferMvcConfiguration.java delete mode 100644 juick-xmpp/src/main/java/com/juick/components/configuration/XMPPConfiguration.java delete mode 100644 juick-xmpp/src/main/java/com/juick/components/configuration/XMPPInitializer.java create mode 100644 juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java create mode 100644 juick-xmpp/src/main/java/com/juick/components/configuration/XmppInitializer.java create mode 100644 juick-xmpp/src/main/java/com/juick/components/configuration/XmppMvcConfiguration.java diff --git a/juick-core/src/main/java/com/juick/configuration/DataConfiguration.java b/juick-core/src/main/java/com/juick/configuration/DataConfiguration.java index 62ca0f88..f31dbcdb 100644 --- a/juick-core/src/main/java/com/juick/configuration/DataConfiguration.java +++ b/juick-core/src/main/java/com/juick/configuration/DataConfiguration.java @@ -1,11 +1,9 @@ package com.juick.configuration; import org.apache.commons.dbcp2.BasicDataSource; -import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; -import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.DataSourceTransactionManager; @@ -21,26 +19,21 @@ import javax.sql.DataSource; */ @Configuration @EnableTransactionManagement -@PropertySource(value = {"classpath:juick.conf", "file:${user.home}/juick.conf"}) +@PropertySource(value = {"classpath:juick.conf"}) public class DataConfiguration implements TransactionManagementConfigurer { @Resource private Environment env; - @Bean - public static PlaceholderConfigurerSupport propertySourcesPlaceholderConfigurer() { - PlaceholderConfigurerSupport configurer = new PropertySourcesPlaceholderConfigurer(); - - configurer.setFileEncoding("utf-8"); - configurer.setOrder(0); - return configurer; - } - @Bean public DataSource dataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriverClassName(env.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); dataSource.setUrl(env.getProperty("datasource_url")); + dataSource.setUsername(env.getProperty("datasource_user", "")); + dataSource.setPassword(env.getProperty("datasource_password", "")); + + dataSource.setValidationQuery("select 1"); return dataSource; } @@ -56,7 +49,7 @@ public class DataConfiguration implements TransactionManagementConfigurer { } @Bean - public JdbcTemplate jdbc() { + public JdbcTemplate jdbcTemplate() { return new JdbcTemplate(dataSource()); } } diff --git a/juick-core/src/main/java/com/juick/util/ThreadHelper.java b/juick-core/src/main/java/com/juick/util/ThreadHelper.java new file mode 100644 index 00000000..7304d158 --- /dev/null +++ b/juick-core/src/main/java/com/juick/util/ThreadHelper.java @@ -0,0 +1,36 @@ +package com.juick.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Created by aalexeev on 11/11/16. + */ +public class ThreadHelper { + private ThreadHelper() { + throw new IllegalStateException(); + } + + private static Logger logger = LoggerFactory.getLogger(ThreadHelper.class); + + public static void shutdownAndAwaitTermination(ExecutorService pool) { + pool.shutdown(); // Disable new tasks from being submitted + try { + // Wait a while for existing tasks to terminate + if (!pool.awaitTermination(5, TimeUnit.SECONDS)) { + pool.shutdownNow(); // Cancel currently executing tasks + // Wait a while for tasks to respond to being cancelled + if (!pool.awaitTermination(5, TimeUnit.SECONDS)) + logger.error("Pool did not terminate"); + } + } catch (InterruptedException ie) { + // (Re-)Cancel if current thread also interrupted + pool.shutdownNow(); + // Preserve interrupt status + Thread.currentThread().interrupt(); + } + } +} diff --git a/juick-crosspost/src/main/java/com/juick/components/Crosspost.java b/juick-crosspost/src/main/java/com/juick/components/Crosspost.java index 35623c40..905816fd 100644 --- a/juick-crosspost/src/main/java/com/juick/components/Crosspost.java +++ b/juick-crosspost/src/main/java/com/juick/components/Crosspost.java @@ -18,6 +18,7 @@ package com.juick.components; import com.juick.server.CrosspostQueries; +import com.juick.util.ThreadHelper; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; @@ -29,8 +30,10 @@ import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.Assert; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; @@ -46,36 +49,40 @@ import java.util.concurrent.ExecutorService; /** * @author Ugnich Anton */ -public class Crosspost implements DisposableBean, Stream.StreamListener, Message.MessageListener { - public final static String TWITTERURL = "https://api.twitter.com/1.1/statuses/update.json"; - public final static String FBURL = "https://graph.facebook.com/me/feed"; - public final static String VKURL = "https://api.vk.com/method/wall.post"; +public class Crosspost implements InitializingBean, DisposableBean, Stream.StreamListener, Message.MessageListener { + final static String TWITTERURL = "https://api.twitter.com/1.1/statuses/update.json"; + final static String FBURL = "https://graph.facebook.com/me/feed"; + final static String VKURL = "https://api.vk.com/method/wall.post"; private static Logger logger = LoggerFactory.getLogger(Crosspost.class); - JdbcTemplate jdbc; - Stream xmpp; - String twitter_consumer_key; - String twitter_consumer_secret; - ExecutorService service; + private final JdbcTemplate jdbc; + private final ExecutorService service; + private Stream xmpp; + + private final String twitter_consumer_key; + private final String twitter_consumer_secret; + private final String crosspostJid; + private final String password; + private final int port; + + public Crosspost(final Environment env, final ExecutorService service, final JdbcTemplate jdbc) { + Assert.notNull(env); + Assert.notNull(service); + Assert.notNull(jdbc); - public Crosspost(Environment env, ExecutorService service, JdbcTemplate jdbc) { this.service = service; this.jdbc = jdbc; - logger.info("component initialized"); - try { - twitter_consumer_key = env.getProperty("twitter_consumer_key", ""); - twitter_consumer_secret = env.getProperty("twitter_consumer_secret", ""); - setupXmppComponent(env.getProperty("crosspost_jid", "crosspost.juick.local"), - env.getProperty("xmpp_password", ""), NumberUtils.toInt(env.getProperty("xmpp_port", ""), 5347)); - service.submit(() -> xmpp.startParsing()); - } catch (Exception e) { - logger.error("crosspost exception", e); - } + twitter_consumer_key = env.getProperty("twitter_consumer_key", ""); + twitter_consumer_secret = env.getProperty("twitter_consumer_secret", ""); + + crosspostJid = env.getProperty("crosspost_jid", "crosspost.juick.local"); + password = env.getProperty("xmpp_password", ""); + port = NumberUtils.toInt(env.getProperty("xmpp_port"), 5347); } - public static String percentEncode(String s) { + public static String percentEncode(final String s) { String ret = ""; try { ret = URLEncoder.encode(s, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); @@ -85,22 +92,29 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message } @Override - public void destroy() { - logger.info("component destroyed"); - } - - public void setupXmppComponent(String jid, String password, int port) { + public void afterPropertiesSet() throws Exception { try { Socket socket = new Socket("localhost", port); - xmpp = new StreamComponent(new JID(jid), socket.getInputStream(), socket.getOutputStream(), password); + xmpp = new StreamComponent(new JID(crosspostJid), socket.getInputStream(), socket.getOutputStream(), password); xmpp.addChildParser(new JuickMessage()); xmpp.addListener((Stream.StreamListener) this); xmpp.addListener((Message.MessageListener) this); - } catch (IOException e) { - logger.error(e.getMessage(), e); + + service.submit(() -> xmpp.startParsing()); + + logger.info("Crosspost initialized"); + } catch (Exception e) { + logger.error("Crosspost initialization exception", e); } } + @Override + public void destroy() throws Exception { + ThreadHelper.shutdownAndAwaitTermination(service); + + logger.info("Crosspost destroyed"); + } + @Override public void onStreamReady() { logger.info("XMPP STREAM READY"); @@ -112,7 +126,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message } @Override - public void onMessage(com.juick.xmpp.Message msg) { + public void onMessage(final com.juick.xmpp.Message msg) { JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (msg.to != null && msg.to.Username != null && jmsg != null && jmsg.getRID() == 0) { if (msg.to.Username.equals("twitter")) { @@ -125,7 +139,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message } } - public boolean facebookPost(com.juick.Message jmsg) { + public boolean facebookPost(final com.juick.Message jmsg) { String token = CrosspostQueries.getFacebookToken(jdbc, jmsg.getUser().getUID()).orElse(""); if (token.isEmpty()) { return false; @@ -167,7 +181,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message return ret; } - public boolean vkontaktePost(com.juick.Message jmsg) { + public boolean vkontaktePost(final com.juick.Message jmsg) { Pair tokens = CrosspostQueries.getVKTokens(jdbc, jmsg.getUser().getUID()).orElse(Pair.of("", "")); if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { return false; @@ -204,7 +218,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message return ret; } - public boolean twitterPost(com.juick.Message jmsg) { + public boolean twitterPost(final com.juick.Message jmsg) { Pair tokens = CrosspostQueries.getTwitterTokens(jdbc, jmsg.getUser().getUID()).orElse(Pair.of("", "")); if (tokens.getLeft().isEmpty() || tokens.getRight().isEmpty()) { return false; @@ -259,7 +273,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message return ret; } - public String twitterSignature(String status, String nonce, String timestamp, String token, String token_secret) { + public String twitterSignature(final String status, final String nonce, final String timestamp, final String token, final String token_secret) { try { // ALPHABET-SORTED String params = "oauth_consumer_key=" + twitter_consumer_key @@ -285,7 +299,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message return null; } - public String streamToString(InputStream is) { + public String streamToString(final InputStream is) { try { BufferedReader buf = new BufferedReader(new InputStreamReader(is)); StringBuilder str = new StringBuilder(); @@ -301,7 +315,7 @@ public class Crosspost implements DisposableBean, Stream.StreamListener, Message return null; } - public String getMessageHashTags(com.juick.Message jmsg) { + public String getMessageHashTags(final com.juick.Message jmsg) { String hashtags = ""; for (int i = 0; i < jmsg.Tags.size(); i++) { hashtags += "#" + jmsg.Tags.get(i) + " "; diff --git a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostAppConfiguration.java b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostAppConfiguration.java new file mode 100644 index 00000000..a7787b26 --- /dev/null +++ b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostAppConfiguration.java @@ -0,0 +1,37 @@ +package com.juick.components.configuration; + +import com.juick.components.Crosspost; +import com.juick.configuration.DataConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by aalexeev on 11/12/16. + */ +@Configuration +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class CrosspostAppConfiguration { + @Inject + private Environment env; + @Inject + private JdbcTemplate jdbcTemplate; + + @Bean + public Crosspost crosspost() { + return new Crosspost(env, service(), jdbcTemplate); + } + + @Bean + public ExecutorService service() { + return Executors.newCachedThreadPool(); + } +} diff --git a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostConfiguration.java b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostConfiguration.java deleted file mode 100644 index 8ae511c4..00000000 --- a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostConfiguration.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.juick.components.configuration; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.juick.components.Crosspost; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.loader.ServletLoader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; -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.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.env.Environment; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import javax.inject.Inject; -import javax.servlet.ServletContext; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -@ComponentScan(basePackages = {"com.juick"}) -@PropertySource(value = {"classpath:juick.conf", "file:${user.home}/juick.conf"}) -public class CrosspostConfiguration extends WebMvcConfigurationSupport { - @Inject - private Environment env; - @Inject - private ServletContext servletContext; - @Inject - private JdbcTemplate jdbc; - - @Bean - public static PlaceholderConfigurerSupport propertySourcesPlaceholderConfigurer() { - PlaceholderConfigurerSupport configurer = new PropertySourcesPlaceholderConfigurer(); - - configurer.setFileEncoding("utf-8"); - configurer.setOrder(1); - return configurer; - } - - @Bean - public Loader templateLoader() { - return new ServletLoader(servletContext); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("/WEB-INF/templates/"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - - return viewResolver; - } - - @Bean - public Crosspost crosspost() { - return new Crosspost(env, service(), jdbc); - } - - @Bean - public ExecutorService service() { - return Executors.newCachedThreadPool(); - } - - @Override - public RequestMappingHandlerMapping requestMappingHandlerMapping() { - RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); - mapping.setUseSuffixPatternMatch(false); - return mapping; - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.setOrder(0); - registry.addResourceHandler("/scripts.js").addResourceLocations("/"); - registry.addResourceHandler("/style.css").addResourceLocations("/"); - } - - @Override - protected void configureMessageConverters(List> converters) { - Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_DEFAULT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .serializationInclusion(JsonInclude.Include.NON_ABSENT) - .serializationInclusion(JsonInclude.Include.NON_EMPTY); - - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); - converter.getObjectMapper().registerModule(new Jdk8Module()); - converters.add(converter); - - super.configureMessageConverters(converters); - } -} diff --git a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostInitializer.java b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostInitializer.java index 0768f834..baedfa29 100644 --- a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostInitializer.java +++ b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostInitializer.java @@ -10,14 +10,15 @@ import javax.servlet.Filter; * Created by vt on 09/02/16. */ public class CrosspostInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + @Override protected Class[] getRootConfigClasses() { - return new Class[]{DataConfiguration.class}; + return new Class[]{CrosspostAppConfiguration.class, DataConfiguration.class}; } @Override protected Class[] getServletConfigClasses() { - return new Class[]{CrosspostConfiguration.class}; + return new Class[]{CrosspostMvcConfiguration.class}; } @Override @@ -32,4 +33,9 @@ public class CrosspostInitializer extends AbstractAnnotationConfigDispatcherServ return new Filter[]{characterEncodingFilter}; } + + @Override + protected String getServletName() { + return "Crosspost dispatcher servlet"; + } } diff --git a/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostMvcConfiguration.java b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostMvcConfiguration.java new file mode 100644 index 00000000..619c3d78 --- /dev/null +++ b/juick-crosspost/src/main/java/com/juick/components/configuration/CrosspostMvcConfiguration.java @@ -0,0 +1,43 @@ +package com.juick.components.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.components.controllers"}) +public class CrosspostMvcConfiguration extends WebMvcConfigurationSupport { + + @Override + public RequestMappingHandlerMapping requestMappingHandlerMapping() { + RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); + mapping.setUseSuffixPatternMatch(false); + return mapping; + } + + @Override + protected void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + + super.configureMessageConverters(converters); + } +} diff --git a/juick-notifications/src/main/java/com/juick/components/Notifications.java b/juick-notifications/src/main/java/com/juick/components/Notifications.java index 12993418..b7e6b2b8 100644 --- a/juick-notifications/src/main/java/com/juick/components/Notifications.java +++ b/juick-notifications/src/main/java/com/juick/components/Notifications.java @@ -19,6 +19,7 @@ package com.juick.components; import com.google.android.gcm.server.*; import com.juick.json.MessageSerializer; +import com.juick.util.ThreadHelper; import com.juick.xmpp.JID; import com.juick.xmpp.Message.MessageListener; import com.juick.xmpp.Stream; @@ -44,13 +45,12 @@ import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.env.Environment; import org.springframework.http.HttpMethod; -import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; -import javax.inject.Inject; import java.io.IOException; import java.net.Socket; import java.util.ArrayList; @@ -59,63 +59,75 @@ import java.util.List; import java.util.concurrent.ExecutorService; /** - * * @author Ugnich Anton */ -public class Notifications implements DisposableBean, Stream.StreamListener, MessageListener { - +public class Notifications implements InitializingBean, DisposableBean, Stream.StreamListener, MessageListener { private static Logger logger = LoggerFactory.getLogger(Notifications.class); - String wns_application_sip; - String wns_client_secret; - @Inject - RestTemplate rest; - Socket socket; - Stream xmpp; - Sender GCMSender; - ExecutorService service; + private final RestTemplate rest; + private final ExecutorService service; + private Socket socket; + private Stream xmpp; + private final Sender GCMSender; + + private final String wns_application_sip; + private final String wns_client_secret; + private final String pushJid; + private final String xmppHost; + private final int xmppPort; + private final String xmppPushPassword; - @Inject - public Notifications(Environment env, ExecutorService service) { + + public Notifications(final Environment env, final ExecutorService service, final RestTemplate rest) { this.service = service; - logger.info("component initialized"); + this.rest = rest; + wns_application_sip = env.getProperty("wns_application_sip", ""); wns_client_secret = env.getProperty("wns_client_secret", ""); GCMSender = new Sender(env.getProperty("gcm_key", ""), Endpoint.GCM); - - setupXmppComponent(new JID("", env.getProperty("push_jid"), ""), env.getProperty("xmpp_host", "localhost"), - NumberUtils.toInt(env.getProperty("xmpp_port", ""), 5347), env.getProperty("push_xmpp_password", "")); - service.submit(() -> xmpp.startParsing()); + pushJid = env.getProperty("push_jid"); + xmppHost = env.getProperty("xmpp_host", "localhost"); + xmppPort = NumberUtils.toInt(env.getProperty("xmpp_port"), 5347); + xmppPushPassword = env.getProperty("push_xmpp_password", ""); } @Override - public void destroy() { - logger.info("component destroyed"); - } - - public void setupXmppComponent(JID jid, String host, int port, String password) { + public void afterPropertiesSet() throws Exception { try { - socket = new Socket(host, port); - xmpp = new StreamComponent(jid, socket.getInputStream(), socket.getOutputStream(), password); + socket = new Socket(xmppHost, xmppPort); + xmpp = new StreamComponent(new JID("", pushJid, ""), socket.getInputStream(), socket.getOutputStream(), xmppPushPassword); xmpp.addChildParser(new JuickMessage()); xmpp.addListener((Stream.StreamListener) this); xmpp.addListener((MessageListener) this); - } catch (IOException e) { - logger.error(e.getMessage(), e); + + service.submit(() -> xmpp.startParsing()); + + logger.info("Notifications initialized"); + } catch (Exception e) { + logger.error("Notifications initialization error", e); } } + @Override + public void destroy() { + ThreadHelper.shutdownAndAwaitTermination(service); + + logger.info("component destroyed"); + } + @Override public void onStreamReady() { logger.info("XMPP STREAM READY"); } @Override - public void onStreamFail(Exception e) {logger.error("XMPP STREAM FAIL", e);} + public void onStreamFail(final Exception e) { + logger.error("XMPP STREAM FAIL", e); + } @Override - public void onMessage(com.juick.xmpp.Message msg) { - JuickMessage jmsg = (JuickMessage)msg.getChild(JuickMessage.XMLNS); + public void onMessage(final com.juick.xmpp.Message msg) { + JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); boolean isPM = jmsg.getMID() == 0; boolean isReply = jmsg.getRID() > 0; int pmTo = 0; @@ -125,11 +137,13 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes if (isPM) { regids.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=gcm&uid=%s", jmsg.getUser().getUID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } else { regids.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=gcm&uid=%s&mid=%s", jmsg.getUser().getUID(), jmsg.getMID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } if (!regids.isEmpty()) { @@ -157,14 +171,15 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes if (isPM) { urls.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=mpns&uid=%s", jmsg.getUser().getUID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } else { urls.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=mpns&uid=%s&mid=%s", jmsg.getUser().getUID(), jmsg.getMID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } - if (urls.isEmpty()) { logger.info("WNS: no recipients"); } else { @@ -203,11 +218,13 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes if (isPM) { tokens.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=apns&uid=%s", jmsg.getUser().getUID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } else { tokens.addAll(rest.exchange(String.format("http://api.juick.com/notifications?type=apns&uid=%s&mid=%s", jmsg.getUser().getUID(), jmsg.getMID()), - HttpMethod.GET, null, new ParameterizedTypeReference>() {}).getBody()); + HttpMethod.GET, null, new ParameterizedTypeReference>() { + }).getBody()); } if (!tokens.isEmpty()) { ApnsService service = APNS.newService().withCert("/etc/juick/ios.p12", "juick") @@ -223,10 +240,10 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes } String getWnsAccessToken() throws IOException, IllegalStateException { - if(TextUtils.isEmpty(wns_application_sip)) { + if (TextUtils.isEmpty(wns_application_sip)) { throw new IllegalStateException("'wns_application_sip' is not initialized"); } - if(TextUtils.isEmpty(wns_client_secret)) { + if (TextUtils.isEmpty(wns_client_secret)) { throw new IllegalStateException("'wns_client_secret' is not initialized"); } HttpClient client = HttpClientBuilder.create().build(); @@ -243,17 +260,17 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes int statusCode = response.getStatusLine().getStatusCode(); String responseContent = EntityUtils.toString(response.getEntity(), Consts.UTF_8); JSONObject json = new JSONObject(responseContent); - if(statusCode != 200) { + if (statusCode != 200) { throw new IOException(json.opt("error") + ": " + json.opt("error_description")); } - String tokenType = (String)json.get("token_type"); - if(tokenType.length() >= 1) { + String tokenType = (String) json.get("token_type"); + if (tokenType.length() >= 1) { tokenType = Character.toUpperCase(tokenType.charAt(0)) + tokenType.substring(1); } return tokenType + " " + json.get("access_token"); } - void sendWNS(String wnsToken, String url, String xml) throws IOException { + void sendWNS(final String wnsToken, final String url, final String xml) throws IOException { HttpClient client = HttpClientBuilder.create().build(); StringEntity entity = new StringEntity(xml, Consts.UTF_8); HttpPost httpPost = new HttpPost(url); @@ -263,13 +280,13 @@ public class Notifications implements DisposableBean, Stream.StreamListener, Mes httpPost.setEntity(entity); HttpResponse response = client.execute(httpPost); int statusCode = response.getStatusLine().getStatusCode(); - if(statusCode != 200) { + if (statusCode != 200) { String headersContent = stringifyWnsHttpHeaders(response.getAllHeaders()); throw new IOException(headersContent); } } - static String stringifyWnsHttpHeaders(Header[] allHeaders) { + static String stringifyWnsHttpHeaders(final Header[] allHeaders) { String[] wnsHeaders = Arrays.stream(allHeaders) .filter(x -> x.getName().startsWith("X-WNS-") || x.getName().startsWith("WWW-")) .map(x -> x.getName() + ": " + x.getValue()) diff --git a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsAppConfiguration.java b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsAppConfiguration.java new file mode 100644 index 00000000..1974830a --- /dev/null +++ b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsAppConfiguration.java @@ -0,0 +1,40 @@ +package com.juick.components.configuration; + +import com.juick.components.Notifications; +import com.juick.configuration.DataConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.web.client.RestTemplate; + +import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by aalexeev on 11/12/16. + */ +@Configuration +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class NotificationsAppConfiguration { + @Inject + private Environment env; + + @Bean + public RestTemplate rest() { + return new RestTemplate(); + } + + @Bean + public Notifications push() { + return new Notifications(env, service(), rest()); + } + + @Bean + public ExecutorService service() { + return Executors.newCachedThreadPool(); + } +} diff --git a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsConfiguration.java b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsConfiguration.java deleted file mode 100644 index 7410648a..00000000 --- a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsConfiguration.java +++ /dev/null @@ -1,122 +0,0 @@ -package com.juick.components.configuration; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.juick.components.Notifications; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.loader.ServletLoader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; -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.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.env.Environment; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; - -import javax.inject.Inject; -import javax.servlet.ServletContext; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -@ComponentScan(basePackages = {"com.juick"}) -@PropertySource(value = {"classpath:juick.conf", "file:${user.home}/juick.conf"}) -public class NotificationsConfiguration extends WebMvcConfigurationSupport { - @Inject - Environment env; - @Inject - private ServletContext servletContext; - - @Bean - public static PlaceholderConfigurerSupport propertySourcesPlaceholderConfigurer() { - PlaceholderConfigurerSupport configurer = new PropertySourcesPlaceholderConfigurer(); - - configurer.setFileEncoding("utf-8"); - configurer.setOrder(1); - return configurer; - } - - @Bean - RestTemplate rest() { - return new RestTemplate(); - } - - @Bean - public Loader templateLoader() { - return new ServletLoader(servletContext); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("/WEB-INF/templates/"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - return viewResolver; - } - - @Bean - public Notifications push() { - return new Notifications(env, service()); - } - - @Bean - public ExecutorService service() { - return Executors.newCachedThreadPool(); - } - - @Override - public RequestMappingHandlerMapping requestMappingHandlerMapping() { - RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); - mapping.setUseSuffixPatternMatch(false); - return mapping; - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.setOrder(0); - registry.addResourceHandler("/scripts.js").addResourceLocations("/"); - registry.addResourceHandler("/style.css").addResourceLocations("/"); - } - - @Override - protected void configureMessageConverters(List> converters) { - Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_DEFAULT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .serializationInclusion(JsonInclude.Include.NON_ABSENT) - .serializationInclusion(JsonInclude.Include.NON_EMPTY); - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); - converter.getObjectMapper().registerModule(new Jdk8Module()); - converters.add(converter); - super.configureMessageConverters(converters); - } -} diff --git a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsInitializer.java b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsInitializer.java index 417cc7fa..48ff52c2 100644 --- a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsInitializer.java +++ b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsInitializer.java @@ -10,14 +10,15 @@ import javax.servlet.Filter; * Created by vt on 09/02/16. */ public class NotificationsInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + @Override protected Class[] getRootConfigClasses() { - return new Class[]{DataConfiguration.class}; + return new Class[]{NotificationsAppConfiguration.class, DataConfiguration.class}; } @Override protected Class[] getServletConfigClasses() { - return new Class[]{NotificationsConfiguration.class}; + return new Class[]{NotificationsMvcConfiguration.class}; } @Override @@ -31,4 +32,9 @@ public class NotificationsInitializer extends AbstractAnnotationConfigDispatcher characterEncodingFilter.setEncoding("UTF-8"); return new Filter[]{characterEncodingFilter}; } + + @Override + protected String getServletName() { + return "Notifications dispatcher servlet"; + } } diff --git a/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsMvcConfiguration.java b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsMvcConfiguration.java new file mode 100644 index 00000000..a0f0ad06 --- /dev/null +++ b/juick-notifications/src/main/java/com/juick/components/configuration/NotificationsMvcConfiguration.java @@ -0,0 +1,41 @@ +package com.juick.components.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.components.controllers"}) +public class NotificationsMvcConfiguration extends WebMvcConfigurationSupport { + + @Override + public RequestMappingHandlerMapping requestMappingHandlerMapping() { + RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); + mapping.setUseSuffixPatternMatch(false); + return mapping; + } + + @Override + protected void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + super.configureMessageConverters(converters); + } +} diff --git a/juick-ws/src/main/java/com/juick/ws/ApiController.java b/juick-ws/src/main/java/com/juick/ws/ApiController.java deleted file mode 100644 index 5203de57..00000000 --- a/juick-ws/src/main/java/com/juick/ws/ApiController.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.juick.ws; - -import com.juick.server.helpers.Status; -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 - WebsocketComponent wsHandler; - - @RequestMapping(value = "/api/status", method = RequestMethod.GET, - produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public Status status() { - return new Status(String.valueOf(wsHandler.clients.size())); - } -} diff --git a/juick-ws/src/main/java/com/juick/ws/StatusController.java b/juick-ws/src/main/java/com/juick/ws/StatusController.java deleted file mode 100644 index f7e80106..00000000 --- a/juick-ws/src/main/java/com/juick/ws/StatusController.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.juick.ws; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.servlet.ModelAndView; - -import javax.inject.Inject; - -/** - * Created by vitalyster on 19.07.2016. - */ -@Controller -@ResponseBody -public class StatusController { - @Inject - WebsocketComponent wsHandler; - @Inject - XMPPConnection ws; - - @RequestMapping(method = RequestMethod.GET, headers = "Connection!=upgrade", value = "/") - public ModelAndView status() { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.addObject("clients", wsHandler.clients.size()); - modelAndView.setViewName("index"); - return modelAndView; - } - -} diff --git a/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java b/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java index 2ab3a94c..4ce230a4 100644 --- a/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java +++ b/juick-ws/src/main/java/com/juick/ws/WebsocketComponent.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.util.Assert; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; @@ -24,13 +25,17 @@ 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); - @Inject - JdbcTemplate jdbc; + private JdbcTemplate jdbc; - private static final Logger logger = LoggerFactory.getLogger(WebsocketComponent.class); + private final List clients = Collections.synchronizedList(new ArrayList()); - final List clients = Collections.synchronizedList(new ArrayList()); + + public WebsocketComponent(JdbcTemplate jdbc) { + Assert.notNull(jdbc); + this.jdbc = jdbc; + } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { @@ -102,6 +107,7 @@ public class WebsocketComponent extends TextWebSocketHandler { } } } + @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { synchronized (clients) { @@ -116,8 +122,12 @@ public class WebsocketComponent extends TextWebSocketHandler { } } - class SocketSubscribed { + public List getClients() { + return clients; + } + + class SocketSubscribed { WebSocketSession session; String clientName; User visitor; diff --git a/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java b/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java index 9e793a44..04ea5378 100644 --- a/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java +++ b/juick-ws/src/main/java/com/juick/ws/XMPPConnection.java @@ -4,6 +4,7 @@ import com.juick.User; import com.juick.json.MessageSerializer; import com.juick.server.MessagesQueries; import com.juick.server.SubscriptionsQueries; +import com.juick.util.ThreadHelper; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Stream; @@ -12,12 +13,13 @@ import com.juick.xmpp.extensions.JuickMessage; import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Component; +import org.springframework.util.Assert; import org.springframework.web.socket.TextMessage; -import javax.inject.Inject; import java.io.IOException; import java.net.Socket; import java.util.List; @@ -25,52 +27,76 @@ import java.util.concurrent.ExecutorService; import java.util.stream.Collectors; /** - * * @author ugnich */ -@Component -public class XMPPConnection implements Stream.StreamListener, Message.MessageListener { +public class XMPPConnection implements InitializingBean, DisposableBean, Stream.StreamListener, Message.MessageListener { private static final Logger logger = LoggerFactory.getLogger(XMPPConnection.class); - @Inject - JdbcTemplate jdbc; - ExecutorService service; - Stream xmpp; - String xmppPassword; - MessageSerializer ms; - @Inject - WebsocketComponent wsHandler; - - @Inject - public XMPPConnection(Environment env, ExecutorService service) { + private final JdbcTemplate jdbc; + private final ExecutorService service; + private final WebsocketComponent wsHandler; + private final String xmppPassword; + private final MessageSerializer ms; + private final int xmppPort; + private final String wsJid; + + private Stream xmpp; + + + public XMPPConnection( + final Environment env, final ExecutorService service, final WebsocketComponent wsHandler, final JdbcTemplate jdbc) { + Assert.notNull(env); + Assert.notNull(service); + Assert.notNull(wsHandler); + Assert.notNull(jdbc); + this.service = service; + this.wsHandler = wsHandler; + this.jdbc = jdbc; + xmppPassword = env.getProperty("xmpp_password"); + xmppPort = NumberUtils.toInt(env.getProperty("xmpp_port"), 5347); + wsJid = env.getProperty("ws_jid", "ws.juick.local"); + ms = new MessageSerializer(); + } + + @Override + public void afterPropertiesSet() throws Exception { try { - Socket socket = new Socket("localhost", NumberUtils.toInt(env.getProperty("xmpp_port", ""), 5347)); - xmpp = new StreamComponent(new JID(env.getProperty("ws_jid", "ws.juick.local")), socket.getInputStream(), socket.getOutputStream(), xmppPassword); + Socket socket = new Socket("localhost", xmppPort); + xmpp = new StreamComponent(new JID(wsJid), socket.getInputStream(), socket.getOutputStream(), xmppPassword); xmpp.addChildParser(new JuickMessage()); xmpp.addListener((Stream.StreamListener) this); xmpp.addListener((Message.MessageListener) this); + service.submit(() -> xmpp.startParsing()); + + logger.info("XMPPConnection initialized"); } catch (IOException e) { - logger.error("XMPPConnection error", e); + logger.error("XMPPConnection initialization error", e); } } + @Override + public void destroy() throws Exception { + ThreadHelper.shutdownAndAwaitTermination(service); + + logger.info("XMPPConnection destroyed"); + } @Override public void onStreamReady() { - logger.info("XMPP stream ready"); + logger.info("XMPP stream ready"); } @Override - public void onStreamFail(Exception ex) { + public void onStreamFail(final Exception ex) { logger.error("XMPP stream failed", ex); } @Override - public void onMessage(com.juick.xmpp.Message msg) { + public void onMessage(final com.juick.xmpp.Message msg) { JuickMessage jmsg = (JuickMessage) msg.getChild(JuickMessage.XMLNS); if (jmsg != null) { logger.info("got jmsg: " + ms.serialize(jmsg).toString()); @@ -95,10 +121,10 @@ public class XMPPConnection implements Stream.StreamListener, Message.MessageLis MessageSerializer messageSerializer = new MessageSerializer(); - private void onJuickPM(int uid_to, com.juick.Message jmsg) { + private void onJuickPM(final int uid_to, final com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); - synchronized (wsHandler.clients) { - wsHandler.clients.stream().filter(c -> !c.legacy && c.visitor.getUID() == uid_to).forEach(c -> { + synchronized (wsHandler.getClients()) { + wsHandler.getClients().stream().filter(c -> !c.legacy && c.visitor.getUID() == uid_to).forEach(c -> { try { logger.info("sending pm to " + c.visitor.getUID()); c.session.sendMessage(new TextMessage(json)); @@ -109,62 +135,62 @@ public class XMPPConnection implements Stream.StreamListener, Message.MessageLis } } - private void onJuickMessagePost(com.juick.Message jmsg) { + private void onJuickMessagePost(final com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); List uids = SubscriptionsQueries.getSubscribedUsers(jdbc, jmsg.getUser().getUID(), jmsg.getMID()) .stream().map(User::getUID).collect(Collectors.toList()); - synchronized (wsHandler.clients) { - wsHandler.clients.stream().filter(c -> + synchronized (wsHandler.getClients()) { + wsHandler.getClients().stream().filter(c -> (!c.legacy && c.visitor.getUID() == 0) // anonymous users - || (!c.legacy && uids.contains(c.visitor.getUID()))) // subscriptions + || (!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.clients.stream().filter(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); - } - }); + 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(com.juick.Message jmsg) { + private void onJuickMessageReply(final com.juick.Message jmsg) { String json = messageSerializer.serialize(jmsg).toString(); List threadUsers = SubscriptionsQueries.getUsersSubscribedToComments(jdbc, jmsg.getMID(), jmsg.getUser().getUID()) .stream().map(User::getUID).collect(Collectors.toList()); - synchronized (wsHandler.clients) { - wsHandler.clients.stream().filter(c -> + synchronized (wsHandler.getClients()) { + wsHandler.getClients().stream().filter(c -> (!c.legacy && c.visitor.getUID() == 0) // anonymous users - || (!c.legacy && threadUsers.contains(c.visitor.getUID()))) // subscriptions + || (!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.clients.stream().filter(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); - } - }); + 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/WebsocketAppConfiguration.java b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketAppConfiguration.java new file mode 100644 index 00000000..9e2e4f6f --- /dev/null +++ b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketAppConfiguration.java @@ -0,0 +1,54 @@ +package com.juick.ws.configuration; + +import com.juick.configuration.DataConfiguration; +import com.juick.ws.WebsocketComponent; +import com.juick.ws.XMPPConnection; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; +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 javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by aalexeev on 11/12/16. + */ +@Configuration +@EnableWebSocket +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class WebsocketAppConfiguration implements WebSocketConfigurer { + @Inject + private Environment env; + @Inject + private JdbcTemplate jdbc; + + @Bean + public WebsocketComponent wsHandler() { + return new WebsocketComponent(jdbc); + } + + @Bean + public XMPPConnection ws() { + return new XMPPConnection(env, service(), wsHandler(), jdbc); + } + + @Bean + public ExecutorService service() { + return Executors.newCachedThreadPool(); + } + + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + ((ServletWebSocketHandlerRegistry) registry).setOrder(2); + registry.addHandler(wsHandler(), "/**").setAllowedOrigins("*"); + } +} 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 0a2a91fa..00000000 --- a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketConfiguration.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.juick.ws.configuration; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.juick.ws.WebsocketComponent; -import com.juick.ws.XMPPConnection; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.loader.ServletLoader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; -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.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.env.Environment; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; -import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; -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 javax.inject.Inject; -import javax.servlet.ServletContext; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -@EnableWebSocket -@ComponentScan(basePackages = {"com.juick"}) -@PropertySource(value = {"classpath:juick.conf", "file:${user.home}/juick.conf"}) -public class WebsocketConfiguration extends WebMvcConfigurationSupport implements WebSocketConfigurer { - @Inject - private Environment env; - @Inject - private JdbcTemplate jdbc; - @Inject - private ServletContext servletContext; - - @Bean - public static PlaceholderConfigurerSupport propertySourcesPlaceholderConfigurer() { - PlaceholderConfigurerSupport configurer = new PropertySourcesPlaceholderConfigurer(); - - configurer.setFileEncoding("utf-8"); - configurer.setOrder(1); - return configurer; - } - - @Bean - public WebsocketComponent wsHandler() { - return new WebsocketComponent(); - } - - @Bean - public Loader templateLoader() { - return new ServletLoader(servletContext); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("/WEB-INF/templates/"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - return viewResolver; - } - - @Bean - public XMPPConnection ws() { - return new XMPPConnection(env, service()); - } - - @Bean - public ExecutorService service() { - return Executors.newCachedThreadPool(); - } - - @Override - public RequestMappingHandlerMapping requestMappingHandlerMapping() { - RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); - mapping.setUseSuffixPatternMatch(false); - return mapping; - } - - @Override - public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { - ((ServletWebSocketHandlerRegistry) registry).setOrder(2); - registry.addHandler(wsHandler(), "/**").setAllowedOrigins("*"); - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.setOrder(0); - registry.addResourceHandler("/scripts.js").addResourceLocations("/"); - registry.addResourceHandler("/style.css").addResourceLocations("/"); - } - - @Override - protected void configureMessageConverters(List> converters) { - Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_DEFAULT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .serializationInclusion(JsonInclude.Include.NON_ABSENT) - .serializationInclusion(JsonInclude.Include.NON_EMPTY); - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); - converter.getObjectMapper().registerModule(new Jdk8Module()); - converters.add(converter); - super.configureMessageConverters(converters); - } -} 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 index 4829710f..d4e797ad 100644 --- a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java +++ b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketInitializer.java @@ -10,14 +10,15 @@ import javax.servlet.Filter; * Created by vt on 09/02/16. */ public class WebsocketInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + @Override protected Class[] getRootConfigClasses() { - return new Class[]{DataConfiguration.class}; + return new Class[]{WebsocketAppConfiguration.class, DataConfiguration.class}; } @Override protected Class[] getServletConfigClasses() { - return new Class[]{WebsocketConfiguration.class}; + return new Class[]{WebsocketMvcConfiguration.class}; } @Override @@ -31,4 +32,9 @@ public class WebsocketInitializer extends AbstractAnnotationConfigDispatcherServ characterEncodingFilter.setEncoding("UTF-8"); return new Filter[]{characterEncodingFilter}; } + + @Override + protected String getServletName() { + return "Web socket dispather servlet"; + } } diff --git a/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketMvcConfiguration.java b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketMvcConfiguration.java new file mode 100644 index 00000000..a8a88dcc --- /dev/null +++ b/juick-ws/src/main/java/com/juick/ws/configuration/WebsocketMvcConfiguration.java @@ -0,0 +1,82 @@ +package com.juick.ws.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.mitchellbosecke.pebble.PebbleEngine; +import com.mitchellbosecke.pebble.loader.Loader; +import com.mitchellbosecke.pebble.loader.ServletLoader; +import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; +import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.ws.controllers"}) +public class WebsocketMvcConfiguration extends WebMvcConfigurationSupport { + @Bean + public Loader templateLoader() { + return new ServletLoader(getServletContext()); + } + + @Bean + public SpringExtension springExtension() { + return new SpringExtension(); + } + + @Bean + public PebbleEngine pebbleEngine() { + return new PebbleEngine.Builder() + .loader(this.templateLoader()) + .extension(springExtension()) + .build(); + } + + @Bean + public ViewResolver viewResolver() { + PebbleViewResolver viewResolver = new PebbleViewResolver(); + viewResolver.setPrefix("/WEB-INF/templates/"); + viewResolver.setSuffix(".html"); + viewResolver.setPebbleEngine(pebbleEngine()); + return viewResolver; + } + + @Override + public RequestMappingHandlerMapping requestMappingHandlerMapping() { + RequestMappingHandlerMapping mapping = super.requestMappingHandlerMapping(); + mapping.setUseSuffixPatternMatch(false); + return mapping; + } + + @Override + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.setOrder(0); + registry.addResourceHandler("/scripts.js").addResourceLocations("/"); + registry.addResourceHandler("/style.css").addResourceLocations("/"); + } + + @Override + protected void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + super.configureMessageConverters(converters); + } +} 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 new file mode 100644 index 00000000..2f57b0b6 --- /dev/null +++ b/juick-ws/src/main/java/com/juick/ws/controllers/ApiController.java @@ -0,0 +1,25 @@ +package com.juick.ws.controllers; + +import com.juick.server.helpers.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 + WebsocketComponent wsHandler; + + @RequestMapping(value = "/api/status", method = RequestMethod.GET, + produces = MediaType.APPLICATION_JSON_UTF8_VALUE) + public Status status() { + return new Status(String.valueOf(wsHandler.getClients().size())); + } +} diff --git a/juick-ws/src/main/java/com/juick/ws/controllers/StatusController.java b/juick-ws/src/main/java/com/juick/ws/controllers/StatusController.java new file mode 100644 index 00000000..2fe55eeb --- /dev/null +++ b/juick-ws/src/main/java/com/juick/ws/controllers/StatusController.java @@ -0,0 +1,32 @@ +package com.juick.ws.controllers; + +import com.juick.ws.WebsocketComponent; +import com.juick.ws.XMPPConnection; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.servlet.ModelAndView; + +import javax.inject.Inject; + +/** + * Created by vitalyster on 19.07.2016. + */ +@Controller +@ResponseBody +public class StatusController { + @Inject + WebsocketComponent wsHandler; + @Inject + XMPPConnection ws; + + @RequestMapping(method = RequestMethod.GET, headers = "Connection!=upgrade", value = "/") + public ModelAndView status() { + ModelAndView modelAndView = new ModelAndView(); + modelAndView.addObject("clients", wsHandler.getClients().size()); + modelAndView.setViewName("index"); + return modelAndView; + } + +} diff --git a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferAppConfiguration.java b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferAppConfiguration.java new file mode 100644 index 00000000..88579328 --- /dev/null +++ b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferAppConfiguration.java @@ -0,0 +1,38 @@ +package com.juick.components.configuration; + +import com.juick.components.XMPPFTServer; +import com.juick.configuration.DataConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Created by aalexeev on 11/12/16. + */ +@Configuration +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class FileTransferAppConfiguration { + @Inject + private Environment env; + @Inject + private JdbcTemplate jdbc; + + @Bean + public XMPPFTServer xmpp() { + return new XMPPFTServer(env); + } + + @Bean + public ExecutorService service() { + return Executors.newCachedThreadPool(); + } + +} diff --git a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferConfiguration.java b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferConfiguration.java deleted file mode 100644 index de47ddc9..00000000 --- a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferConfiguration.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.juick.components.configuration; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.juick.components.XMPPFTServer; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.loader.ServletLoader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.apache.commons.dbcp2.BasicDataSource; -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.env.Environment; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; - -import javax.inject.Inject; -import javax.servlet.ServletContext; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -@ComponentScan(basePackages = {"com.juick"}) -@PropertySource(value = "classpath:juick.conf", ignoreResourceNotFound = true) -public class FileTransferConfiguration extends WebMvcConfigurationSupport { - @Inject - Environment env; - @Inject - ExecutorService service; - - @Bean - JdbcTemplate jdbc() { - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setDriverClassName(env.getProperty("datasource_driver", "com.mysql.jdbc.Driver")); - dataSource.setUrl(env.getProperty("datasource_url")); - return new JdbcTemplate(dataSource); - } - @Inject - private ServletContext servletContext; - - @Bean - public Loader templateLoader(){ - return new ServletLoader(servletContext); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("/WEB-INF/templates/"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - return viewResolver; - } - @Bean - public XMPPFTServer xmpp() { - return new XMPPFTServer(env); - } - @Bean - public ExecutorService service() { - return Executors.newCachedThreadPool(); - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.setOrder(0); - registry.addResourceHandler("/scripts.js").addResourceLocations("/"); - registry.addResourceHandler("/style.css").addResourceLocations("/"); - } - - @Override - protected void configureMessageConverters(List> converters) { - Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_DEFAULT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .serializationInclusion(JsonInclude.Include.NON_ABSENT) - .serializationInclusion(JsonInclude.Include.NON_EMPTY); - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); - converter.getObjectMapper().registerModule(new Jdk8Module()); - converters.add(converter); - super.configureMessageConverters(converters); - } -} diff --git a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferInitializer.java b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferInitializer.java index db514e21..bf0e7257 100644 --- a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferInitializer.java +++ b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferInitializer.java @@ -1,4 +1,6 @@ package com.juick.components.configuration; + +import com.juick.configuration.DataConfiguration; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; @@ -8,26 +10,31 @@ import javax.servlet.Filter; * Created by vt on 09/02/16. */ public class FileTransferInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + @Override protected Class[] getRootConfigClasses() { - return new Class[] {FileTransferConfiguration.class}; + return new Class[]{FileTransferAppConfiguration.class, DataConfiguration.class}; } @Override protected Class[] getServletConfigClasses() { - return null; + return new Class[]{FileTransferMvcConfiguration.class}; } @Override protected String[] getServletMappings() { - return new String[] { - "/" - }; + return new String[]{"/"}; } + @Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); - return new Filter[] { characterEncodingFilter}; + return new Filter[]{characterEncodingFilter}; + } + + @Override + protected String getServletName() { + return "File transfer dispantcher servlet"; } } diff --git a/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferMvcConfiguration.java b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferMvcConfiguration.java new file mode 100644 index 00000000..9500f4e6 --- /dev/null +++ b/juick-xmpp-ft/src/main/java/com/juick/components/configuration/FileTransferMvcConfiguration.java @@ -0,0 +1,33 @@ +package com.juick.components.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.components.controllers"}) +public class FileTransferMvcConfiguration extends WebMvcConfigurationSupport { + + @Override + protected void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + super.configureMessageConverters(converters); + } +} diff --git a/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java b/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java index 463d8c2a..e26e0a1e 100644 --- a/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java +++ b/juick-xmpp/src/main/java/com/juick/components/XMPPServer.java @@ -1,7 +1,10 @@ package com.juick.components; import com.juick.components.s2s.*; -import com.juick.xmpp.*; +import com.juick.util.ThreadHelper; +import com.juick.xmpp.JID; +import com.juick.xmpp.Stanza; +import com.juick.xmpp.StanzaChild; import com.juick.xmpp.extensions.JuickMessage; import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; @@ -23,11 +26,9 @@ import java.util.*; import java.util.concurrent.ExecutorService; /** - * * @author ugnich */ public class XMPPServer implements DisposableBean { - private static final Logger logger = LoggerFactory.getLogger(XMPPServer.class); public ExecutorService service; @@ -39,15 +40,19 @@ public class XMPPServer implements DisposableBean { public String keystorePassword; public List brokenSSLhosts; public List bannedHosts; + private final List inConnections = Collections.synchronizedList(new ArrayList<>()); private final List outConnections = Collections.synchronizedList(new ArrayList<>()); private final List outCache = Collections.synchronizedList(new ArrayList<>()); + public JdbcTemplate jdbc; final public HashMap childParsers = new HashMap<>(); + public XMPPServer(Environment env, ExecutorService service, JdbcTemplate jdbc) { this.service = service; this.jdbc = jdbc; + logger.info("component initialized"); try { HOSTNAME = env.getProperty("hostname"); @@ -64,7 +69,9 @@ public class XMPPServer implements DisposableBean { childParsers.put(JuickMessage.XMLNS, new JuickMessage()); router = new ConnectionRouter(this, componentName, componentPort, env.getProperty("xmpp_password")); + service.submit(router); + service.submit(() -> { final ServerSocket listener = new ServerSocket(s2sPort); logger.info("s2s listener ready"); @@ -79,6 +86,7 @@ public class XMPPServer implements DisposableBean { } } }); + service.submit(new CleaningUp(this)); } catch (Exception e) { @@ -86,6 +94,29 @@ public class XMPPServer implements DisposableBean { } } + @Override + public void destroy() throws Exception { + synchronized (getOutConnections()) { + for (Iterator i = getOutConnections().iterator(); i.hasNext(); ) { + ConnectionOut c = i.next(); + c.closeConnection(); + i.remove(); + } + } + + synchronized (getInConnections()) { + for (Iterator i = getInConnections().iterator(); i.hasNext(); ) { + ConnectionIn c = i.next(); + c.closeConnection(); + i.remove(); + } + } + + ThreadHelper.shutdownAndAwaitTermination(service); + + logger.info("Xmpp server destroyed"); + } + public void addConnectionIn(ConnectionIn c) { synchronized (getInConnections()) { getInConnections().add(c); @@ -113,7 +144,7 @@ public class XMPPServer implements DisposableBean { public String getFromCache(String hostname) { CacheEntry ret = null; synchronized (getOutCache()) { - for (Iterator i = getOutCache().iterator(); i.hasNext();) { + for (Iterator i = getOutCache().iterator(); i.hasNext(); ) { CacheEntry c = i.next(); if (c.hostname != null && c.hostname.equals(hostname)) { ret = c; @@ -202,27 +233,6 @@ public class XMPPServer implements DisposableBean { } } - @Override - public void destroy() { - synchronized (getOutConnections()) { - for (Iterator i = getOutConnections().iterator(); i.hasNext();) { - ConnectionOut c = i.next(); - c.closeConnection(); - i.remove(); - } - } - - synchronized (getInConnections()) { - for (Iterator i = getInConnections().iterator(); i.hasNext();) { - ConnectionIn c = i.next(); - c.closeConnection(); - i.remove(); - } - } - service.shutdown(); - logger.info("component destroyed"); - } - public ConnectionRouter getRouter() { return router; } diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPConfiguration.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPConfiguration.java deleted file mode 100644 index 13ac2a9d..00000000 --- a/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPConfiguration.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.juick.components.configuration; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.juick.components.XMPPServer; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.loader.ServletLoader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.springframework.beans.factory.config.PlaceholderConfigurerSupport; -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.context.support.PropertySourcesPlaceholderConfigurer; -import org.springframework.core.env.Environment; -import org.springframework.http.converter.HttpMessageConverter; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; - -import javax.inject.Inject; -import javax.servlet.ServletContext; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Created by vitalyster on 28.06.2016. - */ -@Configuration -@ComponentScan(basePackages = {"com.juick"}) -@PropertySource(value = {"classpath:juick.conf", "file:${user.home}/juick.conf"}) -public class XMPPConfiguration extends WebMvcConfigurationSupport { - @Inject - private Environment env; - @Inject - private JdbcTemplate jdbc; - - @Inject - private ServletContext servletContext; - - @Bean - public static PlaceholderConfigurerSupport propertySourcesPlaceholderConfigurer() { - PlaceholderConfigurerSupport configurer = new PropertySourcesPlaceholderConfigurer(); - - configurer.setFileEncoding("utf-8"); - configurer.setOrder(1); - return configurer; - } - - @Bean - public Loader templateLoader() { - return new ServletLoader(servletContext); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("/WEB-INF/templates/"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - return viewResolver; - } - - @Bean - public XMPPServer xmpp() { - return new XMPPServer(env, service(), jdbc); - } - - @Bean - public ExecutorService service() { - return Executors.newCachedThreadPool(); - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.setOrder(0); - registry.addResourceHandler("/scripts.js").addResourceLocations("/"); - registry.addResourceHandler("/style.css").addResourceLocations("/"); - } - - @Override - protected void configureMessageConverters(List> converters) { - Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() - .serializationInclusion(JsonInclude.Include.NON_DEFAULT) - .serializationInclusion(JsonInclude.Include.NON_NULL) - .serializationInclusion(JsonInclude.Include.NON_ABSENT) - .serializationInclusion(JsonInclude.Include.NON_EMPTY); - MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); - converter.getObjectMapper().registerModule(new Jdk8Module()); - converters.add(converter); - super.configureMessageConverters(converters); - } -} diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPInitializer.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPInitializer.java deleted file mode 100644 index 3942f889..00000000 --- a/juick-xmpp/src/main/java/com/juick/components/configuration/XMPPInitializer.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.juick.components.configuration; - -import com.juick.configuration.DataConfiguration; -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 XMPPInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { - @Override - protected Class[] getRootConfigClasses() { - return new Class[]{DataConfiguration.class}; - } - - @Override - protected Class[] getServletConfigClasses() { - return new Class[]{XMPPConfiguration.class}; - } - - @Override - protected String[] getServletMappings() { - return new String[]{ - "/" - }; - } - - @Override - protected Filter[] getServletFilters() { - CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); - characterEncodingFilter.setEncoding("UTF-8"); - return new Filter[]{characterEncodingFilter}; - } -} diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java new file mode 100644 index 00000000..eb3dd726 --- /dev/null +++ b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppAppConfiguration.java @@ -0,0 +1,38 @@ +package com.juick.components.configuration; + +/** + * Created by aalexeev on 11/12/16. + */ + +import com.juick.components.XMPPServer; +import com.juick.configuration.DataConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.PropertySource; +import org.springframework.core.env.Environment; +import org.springframework.jdbc.core.JdbcTemplate; + +import javax.inject.Inject; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Configuration +@PropertySource("classpath:juick.conf") +@Import(DataConfiguration.class) +public class XmppAppConfiguration { + @Inject + private Environment env; + @Inject + private JdbcTemplate jdbc; + + @Bean + public XMPPServer xmpp() { + return new XMPPServer(env, service(), jdbc); + } + + @Bean + public ExecutorService service() { + return Executors.newCachedThreadPool(); + } +} diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XmppInitializer.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppInitializer.java new file mode 100644 index 00000000..89dd69e1 --- /dev/null +++ b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppInitializer.java @@ -0,0 +1,41 @@ +package com.juick.components.configuration; + +import com.juick.configuration.DataConfiguration; +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 XmppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { + + @Override + protected Class[] getRootConfigClasses() { + return new Class[]{XmppAppConfiguration.class, DataConfiguration.class}; + } + + @Override + protected Class[] getServletConfigClasses() { + return new Class[]{XmppMvcConfiguration.class}; + } + + @Override + protected String[] getServletMappings() { + return new String[]{"/"}; + } + + @Override + protected Filter[] getServletFilters() { + CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); + characterEncodingFilter.setEncoding("UTF-8"); + + return new Filter[]{characterEncodingFilter}; + } + + @Override + protected String getServletName() { + return "Xmpp dispatcher servlet"; + } +} diff --git a/juick-xmpp/src/main/java/com/juick/components/configuration/XmppMvcConfiguration.java b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppMvcConfiguration.java new file mode 100644 index 00000000..4a0e8ced --- /dev/null +++ b/juick-xmpp/src/main/java/com/juick/components/configuration/XmppMvcConfiguration.java @@ -0,0 +1,77 @@ +package com.juick.components.configuration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.mitchellbosecke.pebble.PebbleEngine; +import com.mitchellbosecke.pebble.loader.Loader; +import com.mitchellbosecke.pebble.loader.ServletLoader; +import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; +import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; +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.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; + +import java.util.List; + +/** + * Created by vitalyster on 28.06.2016. + */ +@Configuration +@ComponentScan(basePackages = {"com.juick.components.controllers"}) +@PropertySource("classpath:juick.conf") +public class XmppMvcConfiguration extends WebMvcConfigurationSupport { + + @Bean + public Loader templateLoader() { + return new ServletLoader(getServletContext()); + } + + @Bean + public SpringExtension springExtension() { + return new SpringExtension(); + } + + @Bean + public PebbleEngine pebbleEngine() { + return new PebbleEngine.Builder() + .loader(this.templateLoader()) + .extension(springExtension()) + .build(); + } + + @Bean + public ViewResolver viewResolver() { + PebbleViewResolver viewResolver = new PebbleViewResolver(); + viewResolver.setPrefix("/WEB-INF/templates/"); + viewResolver.setSuffix(".html"); + viewResolver.setPebbleEngine(pebbleEngine()); + return viewResolver; + } + + @Override + protected void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.setOrder(0); + registry.addResourceHandler("/scripts.js").addResourceLocations("/"); + registry.addResourceHandler("/style.css").addResourceLocations("/"); + } + + @Override + protected void configureMessageConverters(List> converters) { + Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder() + .serializationInclusion(JsonInclude.Include.NON_DEFAULT) + .serializationInclusion(JsonInclude.Include.NON_NULL) + .serializationInclusion(JsonInclude.Include.NON_ABSENT) + .serializationInclusion(JsonInclude.Include.NON_EMPTY); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(builder.build()); + converter.getObjectMapper().registerModule(new Jdk8Module()); + converters.add(converter); + super.configureMessageConverters(converters); + } +} -- cgit v1.2.3