PageRenderTime 59ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 1ms

/dumbhippo/tags/pre-data-model-stacker/server/src/com/dumbhippo/server/impl/MessengerGlueBean.java

https://gitlab.com/manoj-makkuboy/magnetism
Java | 487 lines | 365 code | 91 blank | 31 comment | 42 complexity | ca12dc3e658c3eb2e2b4fb983fe7ed66 MD5 | raw file
  1. package com.dumbhippo.server.impl;
  2. import java.util.ArrayList;
  3. import java.util.Collections;
  4. import java.util.Date;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.Set;
  9. import javax.ejb.EJB;
  10. import javax.ejb.Stateless;
  11. import javax.interceptor.AroundInvoke;
  12. import javax.interceptor.InvocationContext;
  13. import org.slf4j.Logger;
  14. import com.dumbhippo.GlobalSetup;
  15. import com.dumbhippo.Site;
  16. import com.dumbhippo.identity20.Guid;
  17. import com.dumbhippo.identity20.Guid.ParseException;
  18. import com.dumbhippo.persistence.Account;
  19. import com.dumbhippo.persistence.Group;
  20. import com.dumbhippo.persistence.InvitationToken;
  21. import com.dumbhippo.persistence.MembershipStatus;
  22. import com.dumbhippo.persistence.Post;
  23. import com.dumbhippo.persistence.SubscriptionStatus;
  24. import com.dumbhippo.persistence.User;
  25. import com.dumbhippo.persistence.ValidationException;
  26. import com.dumbhippo.persistence.XmppResource;
  27. import com.dumbhippo.server.AccountSystem;
  28. import com.dumbhippo.server.ClaimVerifier;
  29. import com.dumbhippo.server.GroupSystem;
  30. import com.dumbhippo.server.IdentitySpider;
  31. import com.dumbhippo.server.InvitationSystem;
  32. import com.dumbhippo.server.JabberUserNotFoundException;
  33. import com.dumbhippo.server.MessengerGlue;
  34. import com.dumbhippo.server.MusicSystem;
  35. import com.dumbhippo.server.NotFoundException;
  36. import com.dumbhippo.server.PersonViewer;
  37. import com.dumbhippo.server.PostingBoard;
  38. import com.dumbhippo.server.PromotionCode;
  39. import com.dumbhippo.server.ServerStatus;
  40. import com.dumbhippo.server.XmppMessageSender;
  41. import com.dumbhippo.server.blocks.PostBlockHandler;
  42. import com.dumbhippo.server.views.GroupView;
  43. import com.dumbhippo.server.views.PersonView;
  44. import com.dumbhippo.server.views.TrackView;
  45. import com.dumbhippo.server.views.UserViewpoint;
  46. import com.dumbhippo.tx.RetryException;
  47. @Stateless
  48. public class MessengerGlueBean implements MessengerGlue {
  49. static private final Logger logger = GlobalSetup.getLogger(MessengerGlueBean.class);
  50. @EJB
  51. private ClaimVerifier claimVerifier;
  52. @EJB
  53. private IdentitySpider identitySpider;
  54. @EJB
  55. private PersonViewer personViewer;
  56. @EJB
  57. private AccountSystem accountSystem;
  58. @EJB
  59. private PostingBoard postingBoard;
  60. @EJB
  61. private PostBlockHandler postBlockHandler;
  62. @EJB
  63. private MusicSystem musicSystem;
  64. @EJB
  65. private InvitationSystem invitationSystem;
  66. @EJB
  67. private GroupSystem groupSystem;
  68. @EJB
  69. private ServerStatus serverStatus;
  70. @EJB
  71. private XmppMessageSender xmppMessageSender;
  72. static final private long EXECUTION_WARN_MILLISECONDS = 5000;
  73. static private long tooBusyCount;
  74. static final private int MAX_ACTIVE_REQUESTS = 7;
  75. static private int activeRequestCount;
  76. static private int maxActiveRequestCount;
  77. static private synchronized void changeActiveRequestCount(int delta) {
  78. activeRequestCount += delta;
  79. if (activeRequestCount > maxActiveRequestCount)
  80. maxActiveRequestCount = activeRequestCount;
  81. }
  82. static public synchronized int getActiveRequestCount() {
  83. return activeRequestCount;
  84. }
  85. static public synchronized int getMaxActiveRequestCount() {
  86. return maxActiveRequestCount;
  87. }
  88. static private synchronized void incrementTooBusyCount() {
  89. tooBusyCount += 1;
  90. }
  91. static public synchronized long getTooBusyCount() {
  92. return tooBusyCount;
  93. }
  94. @AroundInvoke
  95. public Object timeAndCatchRuntimeExceptions(InvocationContext ctx) throws Exception {
  96. try {
  97. changeActiveRequestCount(1);
  98. long start = System.currentTimeMillis();
  99. Object result = ctx.proceed();
  100. long end = System.currentTimeMillis();
  101. if (end - start > EXECUTION_WARN_MILLISECONDS) {
  102. logger.warn("Execution of MessengerGlueBean.{} took {} milliseconds",
  103. ctx.getMethod().getName(), end - start);
  104. }
  105. return result;
  106. } finally {
  107. changeActiveRequestCount(-1);
  108. }
  109. }
  110. private Account accountFromUsername(String username) throws JabberUserNotFoundException {
  111. try {
  112. Guid guid = Guid.parseJabberId(username);
  113. return accountFromUserId(guid);
  114. } catch (ParseException e) {
  115. throw new JabberUserNotFoundException("corrupt username");
  116. }
  117. }
  118. private Account accountFromUserId(Guid userId) throws JabberUserNotFoundException {
  119. try {
  120. Account account = accountSystem.lookupAccountByOwnerId(userId);
  121. assert account.getOwner().getId().equals(userId);
  122. return account;
  123. } catch (NotFoundException e) {
  124. throw new JabberUserNotFoundException("username does not exist");
  125. }
  126. }
  127. @SuppressWarnings("unused")
  128. private User userFromTrustedUsername(String username) {
  129. try {
  130. return identitySpider.lookupGuid(User.class, Guid.parseTrustedJabberId(username));
  131. } catch (NotFoundException e) {
  132. throw new RuntimeException("trusted username doesn't appear to exist: " + username);
  133. }
  134. }
  135. public boolean authenticateJabberUser(String username, String token, String digest) {
  136. Account account;
  137. try {
  138. account = accountFromUsername(username);
  139. } catch (JabberUserNotFoundException e) {
  140. return false;
  141. }
  142. assert account != null;
  143. return !account.isAdminDisabled() && account.checkClientCookie(token, digest);
  144. }
  145. public long getJabberUserCount() {
  146. return accountSystem.getNumberOfActiveAccounts();
  147. }
  148. public void setName(String username, String name)
  149. throws JabberUserNotFoundException {
  150. // TODO Auto-generated method stub
  151. }
  152. public void setEmail(String username, String email)
  153. throws JabberUserNotFoundException {
  154. // TODO Auto-generated method stub
  155. }
  156. public JabberUser loadUser(String username) throws JabberUserNotFoundException {
  157. Account account = accountFromUsername(username);
  158. PersonView view = personViewer.getSystemView(account.getOwner());
  159. String email = null;
  160. if (view.getEmail() != null)
  161. email = view.getEmail().getEmail();
  162. JabberUser user = new JabberUser(username, account.getOwner().getNickname(), email);
  163. return user;
  164. }
  165. private void doShareLinkTutorial(UserViewpoint newUser) throws RetryException {
  166. logger.debug("We have a new user!!!!! WOOOOOOOOOOOOHOOOOOOOOOOOOOOO send them tutorial!");
  167. Account account = newUser.getViewer().getAccount();
  168. InvitationToken invite = invitationSystem.getCreatingInvitation(account);
  169. // see what feature the user was sold on originally, and share the right thing
  170. // with them accordingly
  171. User owner = newUser.getViewer();
  172. if (invite != null && invite.getPromotionCode() == PromotionCode.MUSIC_INVITE_PAGE_200602)
  173. postingBoard.doNowPlayingTutorialPost(newUser, owner);
  174. else {
  175. Set<Group> invitedToGroups = groupSystem.findRawGroups(newUser, owner, MembershipStatus.INVITED);
  176. Set<Group> invitedToFollowGroups = groupSystem.findRawGroups(newUser, owner, MembershipStatus.INVITED_TO_FOLLOW);
  177. invitedToGroups.addAll(invitedToFollowGroups);
  178. if (invitedToGroups.size() == 0) {
  179. postingBoard.doShareLinkTutorialPost(newUser, owner);
  180. } else {
  181. for (Group group : invitedToGroups) {
  182. postingBoard.doGroupInvitationPost(newUser, owner, group);
  183. }
  184. }
  185. }
  186. account.setWasSentShareLinkTutorial(true);
  187. }
  188. public void updateLoginDate(Guid userId, Date timestamp) {
  189. // account could be missing due to debug users or our own
  190. // send-notifications user. In fact any user on the jabber server
  191. // that we don't know about
  192. Account account;
  193. try {
  194. account = accountFromUserId(userId);
  195. } catch (JabberUserNotFoundException e) {
  196. logger.warn("username logged in that we don't know: {}", userId);
  197. return;
  198. }
  199. account.setNeedsDownload(false);
  200. account.setLastLoginDate(timestamp);
  201. }
  202. public void updateLogoutDate(Guid userId, Date timestamp) {
  203. Account account;
  204. try {
  205. account = accountFromUserId(userId);
  206. } catch (JabberUserNotFoundException e) {
  207. logger.warn("username logged out that we don't know: {}", userId);
  208. return;
  209. }
  210. account.setLastLogoutDate(timestamp);
  211. }
  212. public void sendConnectedResourceNotifications(Guid userId, boolean wasAlreadyConnected) throws RetryException {
  213. Account account;
  214. try {
  215. account = accountFromUserId(userId);
  216. } catch (JabberUserNotFoundException e) {
  217. logger.warn("username signed on that we don't know: {}", userId);
  218. return;
  219. }
  220. UserViewpoint viewpoint = new UserViewpoint(account.getOwner(), Site.XMPP);
  221. if (!account.getWasSentShareLinkTutorial()) {
  222. doShareLinkTutorial(viewpoint);
  223. }
  224. }
  225. private User getUserFromGuid(Guid guid) {
  226. try {
  227. return identitySpider.lookupGuid(User.class, guid);
  228. } catch (NotFoundException e) {
  229. throw new RuntimeException("User does not exist: " + guid, e);
  230. }
  231. }
  232. public Map<String, String> getCurrentMusicInfo(String username) {
  233. Map<String,String> musicInfo = new HashMap<String,String>();
  234. // would through an exception if the user does not exist
  235. User user = getUserFromGuid(Guid.parseTrustedJabberId(username));
  236. try {
  237. // because we are asking only for one recent track, we do not need to
  238. // pass a viewpoint
  239. TrackView trackView = musicSystem.getCurrentTrackView(null, user);
  240. musicInfo.put("name", trackView.getName());
  241. musicInfo.put("artist", trackView.getArtist());
  242. musicInfo.put("musicPlaying", Boolean.toString(trackView.isNowPlaying()));
  243. } catch (NotFoundException e) {
  244. // user does not have a music history
  245. return null;
  246. }
  247. return musicInfo;
  248. }
  249. public Map<String,String> getPrefs(String username) {
  250. Map<String,String> prefs = new HashMap<String,String>();
  251. Account account;
  252. try {
  253. account = accountFromUsername(username);
  254. } catch (JabberUserNotFoundException e) {
  255. logger.warn("Returning empty prefs for user we've never heard of");
  256. return prefs;
  257. }
  258. return accountSystem.getPrefs(account);
  259. }
  260. static final String RECENT_POSTS_ELEMENT_NAME = "recentPosts";
  261. static final String RECENT_POSTS_NAMESPACE = "http://dumbhippo.com/protocol/post";
  262. public void setPostIgnored(Guid userId, Guid postId, boolean ignore) throws NotFoundException, ParseException {
  263. User user = getUserFromGuid(userId);
  264. UserViewpoint viewpoint = new UserViewpoint(user, Site.XMPP);
  265. Post post = postingBoard.loadRawPost(viewpoint, postId);
  266. postBlockHandler.setPostHushed(viewpoint, post, ignore);
  267. }
  268. public void addGroupMember(Guid userId, Guid groupId, Guid inviteeId) throws NotFoundException {
  269. User user = getUserFromGuid(userId);
  270. UserViewpoint viewpoint = new UserViewpoint(user, Site.XMPP);
  271. GroupView groupView = groupSystem.loadGroup(viewpoint, groupId);
  272. User invitee = getUserFromGuid(inviteeId);
  273. groupSystem.addMember(user, groupView.getGroup(), invitee);
  274. }
  275. public boolean isServerTooBusy() {
  276. if (activeRequestCount >= MAX_ACTIVE_REQUESTS || serverStatus.throttleXmppConnections()) {
  277. incrementTooBusyCount();
  278. return true;
  279. } else {
  280. return false;
  281. }
  282. }
  283. public void handleMusicChanged(UserViewpoint viewpoint, Map<String, String> properties) throws RetryException {
  284. // empty properties map means that the music was stopped
  285. if (properties.size() > 0)
  286. musicSystem.setCurrentTrack(viewpoint.getViewer(), properties, true);
  287. }
  288. public void handleMusicPriming(UserViewpoint viewpoint, List<Map<String, String>> tracks) throws RetryException {
  289. User user = viewpoint.getViewer();
  290. if (identitySpider.getMusicSharingPrimed(user)) {
  291. // at log .info, since it isn't really a problem, but if it happened a lot we'd
  292. // want to investigate why
  293. logger.info("Ignoring priming data for music sharing, already primed");
  294. return;
  295. }
  296. // the tracks are in order from most to least highly-ranked, we want to
  297. // timestamp the most highly-ranked one as most recent, so do this backward
  298. tracks = new ArrayList<Map<String,String>>(tracks);
  299. Collections.reverse(tracks);
  300. for (Map<String,String> properties : tracks) {
  301. musicSystem.addHistoricalTrack(user, properties, true);
  302. }
  303. // don't do this again
  304. identitySpider.setMusicSharingPrimed(user, true);
  305. logger.debug("Primed user with {} tracks", tracks.size());
  306. }
  307. public void handlePresence(String localJid, String remoteJid, String type) throws RetryException {
  308. // FIXME: we aren't going to do exactly the right thing here if we get a RetryException when
  309. // saving the new status, since messages will get sent out multiple times. Our response XMPP
  310. // messages really should be done post-transaction
  311. logger.debug("Got presence of type '{}' from '{}' to local user '{}'", new Object[] { type, remoteJid, localJid });
  312. XmppResource remoteResource;
  313. try {
  314. remoteResource = identitySpider.getXmpp(remoteJid);
  315. } catch (ValidationException e) {
  316. logger.debug("Remote JID doesn't validate, ignoring", remoteJid);
  317. return; // Ignore
  318. }
  319. SubscriptionStatus oldStatus = xmppMessageSender.getSubscriptionStatus(localJid, remoteResource);
  320. SubscriptionStatus newStatus = oldStatus;
  321. // For unknown reasons, when we succesfully subscribe to a Google Talk user's
  322. // account, we sometimes dont' get a "subscribed" presence, we just immediately
  323. // get sent the user's current presence. This may have something to do with
  324. // us doing something wrong, but not knowing what, we just handle it by treating
  325. // getting a presence from a user we don't think we are yet subscribed to like
  326. // a "subscribed" presence
  327. //
  328. if ("subscribed".equals(type) || (null == type && !oldStatus.isSubscribedTo())) {
  329. switch (oldStatus) {
  330. case NONE:
  331. newStatus = SubscriptionStatus.TO;
  332. break;
  333. case TO:
  334. break;
  335. case FROM:
  336. newStatus = SubscriptionStatus.BOTH;
  337. break;
  338. case BOTH:
  339. break;
  340. }
  341. // Now that we've succesfully subscribed to the presence of the remote resource, we should
  342. // have the perms to send them a message, so check if we need to send anything out
  343. if (newStatus != oldStatus)
  344. claimVerifier.sendQueuedXmppLinks(localJid, remoteResource);
  345. } else if ("subscribe".equals(type)) {
  346. switch (oldStatus) {
  347. case NONE:
  348. newStatus = SubscriptionStatus.FROM;
  349. break;
  350. case TO:
  351. newStatus = SubscriptionStatus.BOTH;
  352. break;
  353. case FROM:
  354. break;
  355. case BOTH:
  356. break;
  357. }
  358. xmppMessageSender.sendAdminPresence(remoteResource.getJid(), localJid, "subscribed");
  359. /* Send an initial presence for ourself */
  360. xmppMessageSender.sendAdminPresence(remoteResource.getJid(), localJid, null);
  361. } else if ("unsubscribe".equals(type)) {
  362. switch (oldStatus) {
  363. case NONE:
  364. break;
  365. case TO:
  366. break;
  367. case FROM:
  368. newStatus = SubscriptionStatus.NONE;
  369. break;
  370. case BOTH:
  371. newStatus = SubscriptionStatus.TO;
  372. break;
  373. }
  374. xmppMessageSender.sendAdminPresence(remoteResource.getJid(), localJid, "unsubscribed");
  375. } else if ("unsubscribed".equals(type)) {
  376. switch (oldStatus) {
  377. case NONE:
  378. break;
  379. case TO:
  380. newStatus = SubscriptionStatus.NONE;
  381. break;
  382. case FROM:
  383. break;
  384. case BOTH:
  385. newStatus = SubscriptionStatus.FROM;
  386. break;
  387. }
  388. } else if ("probe".equals(type)) {
  389. // We're always available
  390. xmppMessageSender.sendAdminPresence(remoteResource.getJid(), localJid, null);
  391. }
  392. logger.debug("Old status was {}, new status is {}", oldStatus, newStatus);
  393. if (newStatus != oldStatus)
  394. xmppMessageSender.setSubscriptionStatus(localJid, remoteResource, newStatus);
  395. }
  396. }