aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/juick/xmpp/s2s/XMPPComponent.java')
-rw-r--r--src/main/java/com/juick/xmpp/s2s/XMPPComponent.java377
1 files changed, 284 insertions, 93 deletions
diff --git a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java
index 13df5fd0..d0b231e2 100644
--- a/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java
+++ b/src/main/java/com/juick/xmpp/s2s/XMPPComponent.java
@@ -1,21 +1,27 @@
package com.juick.xmpp.s2s;
-import com.juick.xmpp.Stanza;
-import com.juick.xmpp.StanzaChild;
+import com.juick.User;
+import com.juick.server.MessagesQueries;
+import com.juick.server.SubscriptionsQueries;
+import com.juick.server.UserQueries;
+import com.juick.xmpp.*;
import com.juick.xmpp.extensions.JuickMessage;
+import com.juick.xmpp.extensions.Nickname;
+import com.juick.xmpp.extensions.XOOB;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.xmlpull.v1.XmlPullParserException;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
-import java.sql.Driver;
-import java.sql.DriverManager;
-import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -26,50 +32,51 @@ import java.util.logging.Logger;
*
* @author ugnich
*/
-public class XMPPComponent implements ServletContextListener {
-
- private static final Logger LOGGER = Logger.getLogger(XMPPComponent.class.getName());
-
- public static final ExecutorService executorService = Executors.newCachedThreadPool();
-
- public static String HOSTNAME = null;
- public static String STATSFILE = null;
- public static String keystore;
- public static String keystorePassword;
- public static List<String> brokenSSLhosts;
- public static ConnectionRouter connRouter;
- static final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>());
- static final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>());
- static final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>());
- static final Integer sqlSync = 0;
- static java.sql.Connection sql;
- final public static HashMap<String, StanzaChild> childParsers = new HashMap<>();
-
- public static void addConnectionIn(ConnectionIn c) {
+public class XMPPComponent implements ServletContextListener, Stream.StreamListener,
+ Message.MessageListener, Iq.IqListener, Presence.PresenceListener {
+
+ private static final Logger logger = Logger.getLogger(XMPPComponent.class.getName());
+
+ public final ExecutorService executorService = Executors.newCachedThreadPool();
+ StreamComponent router;
+ JuickBot bot;
+
+ public String HOSTNAME, componentName;
+ public String STATSFILE = null;
+ public String keystore;
+ public String keystorePassword;
+ public List<String> brokenSSLhosts;
+ final List<ConnectionIn> inConnections = Collections.synchronizedList(new ArrayList<>());
+ final List<ConnectionOut> outConnections = Collections.synchronizedList(new ArrayList<>());
+ final List<CacheEntry> outCache = Collections.synchronizedList(new ArrayList<>());
+ JdbcTemplate sql;
+ final public HashMap<String, StanzaChild> childParsers = new HashMap<>();
+
+ public void addConnectionIn(ConnectionIn c) {
synchronized (inConnections) {
inConnections.add(c);
}
}
- public static void addConnectionOut(ConnectionOut c) {
+ public void addConnectionOut(ConnectionOut c) {
synchronized (outConnections) {
outConnections.add(c);
}
}
- public static void removeConnectionIn(ConnectionIn c) {
+ public void removeConnectionIn(ConnectionIn c) {
synchronized (inConnections) {
inConnections.remove(c);
}
}
- public static void removeConnectionOut(ConnectionOut c) {
+ public void removeConnectionOut(ConnectionOut c) {
synchronized (outConnections) {
outConnections.remove(c);
}
}
- public static String getFromCache(String hostname) {
+ public String getFromCache(String hostname) {
CacheEntry ret = null;
synchronized (outCache) {
for (Iterator<CacheEntry> i = outCache.iterator(); i.hasNext();) {
@@ -84,7 +91,7 @@ public class XMPPComponent implements ServletContextListener {
return (ret != null) ? ret.xml : null;
}
- public static ConnectionOut getConnectionOut(String hostname, boolean needReady) {
+ public ConnectionOut getConnectionOut(String hostname, boolean needReady) {
synchronized (outConnections) {
for (ConnectionOut c : outConnections) {
if (c.to != null && c.to.equals(hostname) && (!needReady || c.streamReady)) {
@@ -95,7 +102,7 @@ public class XMPPComponent implements ServletContextListener {
return null;
}
- public static ConnectionIn getConnectionIn(String streamID) {
+ public ConnectionIn getConnectionIn(String streamID) {
synchronized (inConnections) {
for (ConnectionIn c : inConnections) {
if (c.streamID != null && c.streamID.equals(streamID)) {
@@ -106,11 +113,11 @@ public class XMPPComponent implements ServletContextListener {
return null;
}
- public static void sendOut(Stanza s) {
+ public void sendOut(Stanza s) {
sendOut(s.to.Host, s.toString());
}
- public static void sendOut(String hostname, String xml) {
+ public void sendOut(String hostname, String xml) {
boolean haveAnyConn = false;
ConnectionOut connOut = null;
@@ -131,7 +138,7 @@ public class XMPPComponent implements ServletContextListener {
try {
connOut.sendStanza(xml);
} catch (IOException e) {
- LOGGER.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString());
+ logger.warning("STREAM TO " + connOut.to + " " + connOut.streamID + " ERROR: " + e.toString());
}
return;
}
@@ -152,12 +159,11 @@ public class XMPPComponent implements ServletContextListener {
}
if (!haveAnyConn) {
- ConnectionOut connectionOut = null;
try {
- connectionOut = new ConnectionOut(hostname);
- XMPPComponent.executorService.submit(connectionOut);
+ ConnectionOut connectionOut = new ConnectionOut(this, hostname);
+ executorService.submit(connectionOut);
} catch (CertificateException | UnrecoverableKeyException | NoSuchAlgorithmException | XmlPullParserException | KeyStoreException | KeyManagementException | IOException e) {
- LOGGER.log(Level.SEVERE, "s2s out error", e);
+ logger.log(Level.SEVERE, "s2s out error", e);
}
}
}
@@ -165,45 +171,74 @@ public class XMPPComponent implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
- LOGGER.info("component initialized");
- executorService.submit(() -> {
- Properties conf = new Properties();
- try {
- conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf"));
- HOSTNAME = conf.getProperty("hostname");
- String componentName = conf.getProperty("componentname");
- STATSFILE = conf.getProperty("statsfile");
- keystore = conf.getProperty("keystore");
- keystorePassword = conf.getProperty("keystore_password");
- brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(","));
- Class.forName("com.mysql.jdbc.Driver");
- sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" +
- conf.getProperty("mysql_username", "") + "&password=" + conf.getProperty("mysql_password", ""));
-
- childParsers.put(JuickMessage.XMLNS, new JuickMessage());
- executorService.submit(() -> connRouter = new ConnectionRouter(componentName, conf.getProperty("xmpp_password")));
- executorService.submit(new ConnectionListener());
- executorService.submit(new CleaningUp());
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, "XMPPComponent error", e);
- }
- });
+ logger.info("component initialized");
+ Properties conf = new Properties();
+ try {
+ conf.load(sce.getServletContext().getResourceAsStream("/WEB-INF/juick.conf"));
+ HOSTNAME = conf.getProperty("hostname");
+ componentName = conf.getProperty("componentname");
+ JID Jid = new JID(conf.getProperty("xmppbot_jid"));
+ STATSFILE = conf.getProperty("statsfile");
+ keystore = conf.getProperty("keystore");
+ keystorePassword = conf.getProperty("keystore_password");
+ brokenSSLhosts = Arrays.asList(conf.getProperty("broken_ssl_hosts", "").split(","));
+ DriverManagerDataSource dataSource = new DriverManagerDataSource();
+ dataSource.setDriverClassName(conf.getProperty("datasource_driver", "com.mysql.jdbc.Driver"));
+ dataSource.setUrl(conf.getProperty("datasource_url"));
+ sql = new JdbcTemplate(dataSource);
+ bot = new JuickBot(this, Jid);
+
+ childParsers.put(JuickMessage.XMLNS, new JuickMessage());
+
+ executorService.submit(() -> {
+ Socket routerSocket = null;
+ try {
+ routerSocket = new Socket("localhost", 5347);
+ router = new StreamComponent(new JID("s2s"), routerSocket.getInputStream(), routerSocket.getOutputStream(), conf.getProperty("xmpp_password"));
+ router.addChildParser(new JuickMessage());
+ router.addListener((Stream.StreamListener) this);
+ router.addListener((Message.MessageListener) this);
+ router.addListener((Iq.IqListener) this);
+ router.startParsing();
+ } catch (IOException e) {
+ logger.log(Level.SEVERE, "router error", e);
+ }
+ });
+ executorService.submit(() -> {
+ final ServerSocket listener = new ServerSocket(5269);
+ logger.info("s2s listener ready");
+ while (true) {
+ try {
+ Socket socket = listener.accept();
+ ConnectionIn client = new ConnectionIn(this, bot, socket);
+ addConnectionIn(client);
+ executorService.submit(client);
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "s2s error", e);
+ }
+ }
+ });
+ executorService.submit(new CleaningUp(this));
+
+ } catch (Exception e) {
+ logger.log(Level.SEVERE, "XMPPComponent error", e);
+ }
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
- synchronized (XMPPComponent.outConnections) {
- for (Iterator<ConnectionOut> i = XMPPComponent.outConnections.iterator(); i.hasNext();) {
+ synchronized (outConnections) {
+ for (Iterator<ConnectionOut> i = outConnections.iterator(); i.hasNext();) {
ConnectionOut c = i.next();
c.closeConnection();
i.remove();
}
}
- synchronized (XMPPComponent.inConnections) {
- for (Iterator<ConnectionIn> i = XMPPComponent.inConnections.iterator(); i.hasNext();) {
+ synchronized (inConnections) {
+ for (Iterator<ConnectionIn> i = inConnections.iterator(); i.hasNext();) {
ConnectionIn c = i.next();
c.closeConnection();
i.remove();
@@ -211,42 +246,198 @@ public class XMPPComponent implements ServletContextListener {
}
try {
- XMPPComponent.connRouter.closeConnection();
+ closeRouterConnection();
} catch (IOException e) {
- LOGGER.log(Level.WARNING, "router warning", e);
+ logger.log(Level.WARNING, "router warning", e);
}
+ executorService.shutdown();
+ logger.info("component destroyed");
+ }
+ public void closeRouterConnection() throws IOException {
+ router.logoff();
+ }
- synchronized (XMPPComponent.sqlSync) {
- if (XMPPComponent.sql != null) {
- try {
- XMPPComponent.sql.close();
- XMPPComponent.sql = null;
- } catch (SQLException e) {
- LOGGER.warning("SQL ERROR: " + e);
+ public void sendJuickMessage(JuickMessage jmsg) {
+ List<String> jids = new ArrayList<>();
+
+ if (jmsg.FriendsOnly) {
+ jids = SubscriptionsQueries.getJIDSubscribedToUser(sql, jmsg.getUser().getUID(), jmsg.FriendsOnly);
+ } else {
+ List<User> users = SubscriptionsQueries.getSubscribedUsers(sql, jmsg.getUser().getUID(), jmsg.getMID());
+ for (User user : users) {
+ for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) {
+ jids.add(jid);
}
}
}
- // Now deregister JDBC drivers in this context's ClassLoader:
- // Get the webapp's ClassLoader
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- // Loop through all drivers
- Enumeration<Driver> drivers = DriverManager.getDrivers();
- while (drivers.hasMoreElements()) {
- Driver driver = drivers.nextElement();
- if (driver.getClass().getClassLoader() == cl) {
- // This driver was registered by the webapp's ClassLoader, so deregister it:
- try {
- LOGGER.info(String.format("Deregistering JDBC driver %s", driver.toString()));
- DriverManager.deregisterDriver(driver);
- } catch (SQLException ex) {
- LOGGER.log(Level.SEVERE, String.format("Error deregistering JDBC driver %s", driver), ex);
- }
+
+ String txt = "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n";
+ String attachment = jmsg.getAttachmentURL();
+ if (attachment != null) {
+ txt += attachment + "\n";
+ }
+ txt += jmsg.getText() + "\n\n";
+ txt += "#" + jmsg.getMID() + " http://juick.com/" + jmsg.getMID();
+
+ Nickname nick = new Nickname();
+ nick.Nickname = "@" + jmsg.getUser().getUName();
+
+ com.juick.xmpp.Message msg = new com.juick.xmpp.Message();
+ msg.from = bot.JuickJID;
+ msg.body = txt;
+ msg.type = Message.Type.chat;
+ msg.thread = "juick-" + jmsg.getMID();
+ msg.addChild(jmsg);
+ msg.addChild(nick);
+ if (attachment != null) {
+ XOOB oob = new XOOB();
+ oob.URL = attachment;
+ msg.addChild(oob);
+ }
+
+ for (String jid : jids) {
+ msg.to = new JID(jid);
+ sendOut(msg);
+ }
+ }
+
+ public void sendJuickComment(JuickMessage jmsg) {
+ List<User> users;
+ String replyQuote;
+ String replyTo;
+
+ users = SubscriptionsQueries.getUsersSubscribedToComments(sql, jmsg.getMID(), jmsg.getUser().getUID());
+ com.juick.Message replyMessage = jmsg.ReplyTo > 0 ? MessagesQueries.getReply(sql, jmsg.getMID(), jmsg.ReplyTo)
+ : MessagesQueries.getMessage(sql, jmsg.getMID());
+ replyTo = replyMessage.getUser().getUName();
+ replyQuote = getReplyQuote(replyMessage);
+
+ String txt = "Reply by @" + jmsg.getUser().getUName() + ":\n" + replyQuote + "\n@" + replyTo + " ";
+ String attachment = jmsg.getAttachmentURL();
+ if (attachment != null) {
+ txt += attachment + "\n";
+ }
+ txt += jmsg.getText() + "\n\n" + "#" + jmsg.getMID() + "/" + jmsg.getRID() + " http://juick.com/" + jmsg.getMID() + "#" + jmsg.getRID();
+
+ com.juick.xmpp.Message msg = new com.juick.xmpp.Message();
+ msg.from = bot.JuickJID;
+ msg.body = txt;
+ msg.type = Message.Type.chat;
+ msg.addChild(jmsg);
+ for (User user : users) {
+ for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) {
+ msg.to = new JID(jid);
+ sendOut(msg);
+ }
+ }
+ }
+
+ private String getReplyQuote(com.juick.Message q) {
+ String quote = q.getText();
+ if (quote.length() > 50) {
+ quote = ">" + quote.substring(0, 47).replace('\n', ' ') + "...\n";
+ } else if (quote.length() > 0) {
+ quote = ">" + quote.replace('\n', ' ') + "\n";
+ }
+ return quote;
+ }
+
+ public void sendJuickRecommendation(JuickMessage recomm) {
+ List<User> users;
+ JuickMessage jmsg;
+ jmsg = new JuickMessage(MessagesQueries.getMessage(sql, recomm.getMID()));
+ users = SubscriptionsQueries.getUsersSubscribedToUserRecommendations(sql,
+ recomm.getUser().getUID(), recomm.getMID(), jmsg.getUser().getUID());
+
+ String txt = "Recommended by @" + recomm.getUser().getUName() + ":\n";
+ txt += "@" + jmsg.getUser().getUName() + ":" + jmsg.getTagsString() + "\n";
+ String attachment = jmsg.getAttachmentURL();
+ if (attachment != null) {
+ txt += attachment + "\n";
+ }
+ txt += jmsg.getText() + "\n\n";
+ txt += "#" + jmsg.getMID();
+ if (jmsg.Replies > 0) {
+ if (jmsg.Replies % 10 == 1 && jmsg.Replies % 100 != 11) {
+ txt += " (" + jmsg.Replies + " reply)";
} else {
- // driver was not registered by the webapp's ClassLoader and may be in use elsewhere
- LOGGER.log(Level.SEVERE, String.format("Not deregistering JDBC driver %s as it does not belong to this webapp's ClassLoader", driver));
+ txt += " (" + jmsg.Replies + " replies)";
}
}
- executorService.shutdown();
- LOGGER.info("component destroyed");
+ txt += " http://juick.com/" + jmsg.getMID();
+
+ Nickname nick = new Nickname();
+ nick.Nickname = "@" + jmsg.getUser().getUName();
+
+ com.juick.xmpp.Message msg = new com.juick.xmpp.Message();
+ msg.from = bot.JuickJID;
+ msg.body = txt;
+ msg.type = Message.Type.chat;
+ msg.thread = "juick-" + jmsg.getMID();
+ msg.addChild(jmsg);
+ msg.addChild(nick);
+ if (attachment != null) {
+ XOOB oob = new XOOB();
+ oob.URL = attachment;
+ msg.addChild(oob);
+ }
+
+ for (User user : users) {
+ for (String jid : UserQueries.getJIDsbyUID(sql, user.getUID())) {
+ msg.to = new JID(jid);
+ sendOut(msg);
+ }
+ }
+ }
+
+ @Override
+ public boolean onIq(Iq iq) {
+ JID jid = iq.to;
+ if (!jid.Host.equals(componentName)) {
+ logger.info("STREAM ROUTER (IQ): " + iq.toString());
+ sendOut(iq);
+ }
+ return false;
+ }
+
+ @Override
+ public void onMessage(Message xmsg) {
+ logger.info("STREAM ROUTER (PROCESS): " + xmsg.toString());
+ JuickMessage jmsg = (JuickMessage) xmsg.getChild(JuickMessage.XMLNS);
+ JID jid = xmsg.to;
+ if (jid.Host.equals(componentName)) {
+ if (jmsg != null) {
+ if (jid.Username != null && jid.Username.equals("recomm")) {
+ sendJuickRecommendation(jmsg);
+ } else {
+ if (jmsg.getRID() > 0) {
+ sendJuickComment(jmsg);
+ } else if (jmsg.getMID() > 0) {
+ sendJuickMessage(jmsg);
+ }
+ }
+ }
+ } else {
+ sendOut(xmsg);
+ }
+ }
+
+ @Override
+ public void onPresence(Presence presence) {
+ JID jid = presence.to;
+ if (!jid.Host.equals(componentName)) {
+ logger.info("STREAM ROUTER (PRESENCE): " + presence.toString());
+ sendOut(presence);
+ }
+ }
+
+ @Override
+ public void onStreamReady() {
+ logger.info("STREAM ROUTER (READY)");
+ }
+
+ @Override
+ public void onStreamFail(Exception ex) {
+ logger.log(Level.SEVERE, "STREAM ROUTER (FAIL)", ex);
}
}