PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/confluence/extra/impresence2/reporter/JabberPresenceReporter.java

https://bitbucket.org/xiaobguo/confluence-impresence-plugin
Java | 314 lines | 234 code | 44 blank | 36 comment | 30 complexity | cac00dde6228b360d599005437c76b26 MD5 | raw file
  1. /*
  2. * Copyright (c) 2006, Atlassian Software Systems Pty Ltd All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
  5. * following conditions are met:
  6. *
  7. * * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
  8. * disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
  9. * the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the
  10. * name of "Atlassian" nor the names of its contributors may be used to endorse or promote products derived from this
  11. * software without specific prior written permission.
  12. *
  13. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  14. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  15. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  16. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  17. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  18. * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  19. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  20. */
  21. package com.atlassian.confluence.extra.impresence2.reporter;
  22. import com.atlassian.bandana.BandanaManager;
  23. import com.atlassian.confluence.setup.bandana.ConfluenceBandanaContext;
  24. import com.atlassian.renderer.v2.RenderUtils;
  25. import com.atlassian.renderer.v2.macro.ResourceAware;
  26. import org.apache.commons.lang.StringUtils;
  27. import org.jivesoftware.smack.ConnectionConfiguration;
  28. import org.jivesoftware.smack.Roster;
  29. import org.jivesoftware.smack.RosterEntry;
  30. import org.jivesoftware.smack.XMPPConnection;
  31. import org.jivesoftware.smack.XMPPException;
  32. import org.jivesoftware.smack.packet.Presence;
  33. import org.jivesoftware.smack.packet.RosterPacket;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;
  36. import java.io.IOException;
  37. import java.util.Collections;
  38. import java.util.HashMap;
  39. import java.util.Map;
  40. /**
  41. * This class interfaces with Jabber (XMPP) IM services.
  42. */
  43. public class JabberPresenceReporter extends LoginPresenceReporter implements ResourceAware
  44. {
  45. private static final String DOMAIN_PREFIX = "extra.im.domain.";
  46. private static final String PORT_PREFIX = "extra.im.port.";
  47. private static final Logger logger = LoggerFactory.getLogger(JabberPresenceReporter.class);
  48. private static final Map<Presence.Mode, String> STATUS_MAP = Collections.unmodifiableMap(
  49. new HashMap<Presence.Mode, String>()
  50. {
  51. {
  52. put(Presence.Mode.available, "im_available");
  53. put(Presence.Mode.away, "im_away");
  54. put(Presence.Mode.available, "im_free_chat");
  55. put(Presence.Mode.dnd, "im_dnd");
  56. put(Presence.Mode.xa, "im_away");
  57. }
  58. }
  59. );
  60. public static final String KEY = "jabber";
  61. public static final String DEFAULT_JABBER_DOMAIN = "chat.example.com";
  62. public static final int DEFAULT_JABBER_PORT = 5222;
  63. private String resourcePath;
  64. private XMPPConnection xmppConnection;
  65. private BandanaManager bandanaManager;
  66. public void setBandanaManager(BandanaManager bandanaManager)
  67. {
  68. super.setBandanaManager(bandanaManager);
  69. this.bandanaManager = bandanaManager;
  70. }
  71. public String getKey()
  72. {
  73. return KEY;
  74. }
  75. public String getName()
  76. {
  77. return getText("presencereporter." + getKey() + ".name");
  78. }
  79. public String getServiceHomepage()
  80. {
  81. return getText("presencereporter." + getKey() + ".servicehomepage");
  82. }
  83. public String getPresenceXHTML(String id, boolean outputId) throws IOException, PresenceException
  84. {
  85. final XMPPConnection xmppConnection = checkCreateConnection();
  86. if (xmppConnection.isAuthenticated())
  87. {
  88. /* roster won't be null, because we've got an authentication check above */
  89. final Roster roster = xmppConnection.getRoster();
  90. if (!roster.contains(id))
  91. {
  92. try
  93. {
  94. roster.createEntry(id, id, null);
  95. return RenderUtils.error(getText("presencereporter." + getKey() + ".message.waitinbuddyaccept",
  96. new Object[]
  97. { id }));
  98. }
  99. catch (XMPPException e)
  100. {
  101. logger.error("Unable to add " + id + " to contact list of " + getId(), e);
  102. throw new PresenceException(getText("presencereporter." + getKey() + ".error.addbuddy",
  103. new Object[]
  104. { id, getId() }), e);
  105. }
  106. }
  107. else
  108. {
  109. final RosterEntry entry = roster.getEntry(id);
  110. if (RosterPacket.ItemStatus.SUBSCRIPTION_PENDING.equals(entry.getStatus()))
  111. {
  112. return getPresenceLink(id, "im_invisible", getText("presence.link.waitingunblock"), outputId);
  113. }
  114. else
  115. {
  116. final Presence presence = getPresence(id, roster);
  117. if (null != presence)
  118. {
  119. Presence.Mode presenceMode = presence.getMode();
  120. // Sometimes, available is sent with a null mode but available type.
  121. if (null == presenceMode && presence.getType().equals(Presence.Type.available))
  122. {
  123. presenceMode = Presence.Mode.available;
  124. }
  125. return getPresenceLink(id, getStatusImage(presenceMode), String.valueOf(presenceMode), outputId);
  126. }
  127. else
  128. {
  129. return getPresenceLink(id, "im_invisible", getText("presence.link.intermediate"), outputId);
  130. }
  131. }
  132. }
  133. }
  134. else
  135. {
  136. if (xmppConnection.isConnected())
  137. {
  138. xmppConnection.disconnect(); /* Since we failed to authenticate, we'll close the connection */
  139. }
  140. return RenderUtils.error(getText("presencereporter." + getKey() + ".error.login"));
  141. }
  142. }
  143. protected XMPPConnection checkCreateConnection()
  144. {
  145. if (null == xmppConnection || !xmppConnection.isConnected())
  146. {
  147. /* Close of the stale connction */
  148. if (null != xmppConnection)
  149. {
  150. xmppConnection.disconnect();
  151. }
  152. try
  153. {
  154. if (logger.isDebugEnabled())
  155. {
  156. logger.debug("Creating a new XMPPConnection for: " + getKey());
  157. }
  158. String trimmedId = getTrimmedId(getId());
  159. String password = getPassword();
  160. int lastIndexOfAlias = getId().lastIndexOf("@");
  161. String userDomainName;
  162. // If there's an '@' in the trimmed ID and it is not the last character, then we should use
  163. // that as the domain name. Otherwise, just use the jabber domain.
  164. if (-1 != lastIndexOfAlias && lastIndexOfAlias < getId().length() - 1)
  165. {
  166. userDomainName = getId().substring(lastIndexOfAlias + 1);
  167. }
  168. else
  169. {
  170. userDomainName = getDomain();
  171. }
  172. xmppConnection = createXmppConnection(userDomainName);
  173. xmppConnection.connect();
  174. xmppConnection.login(trimmedId, password);
  175. }
  176. catch (XMPPException xmppe)
  177. {
  178. logger.error("Unable to establish connection to " + getKey(), xmppe);
  179. }
  180. }
  181. return xmppConnection;
  182. }
  183. XMPPConnection createXmppConnection(String usernameDomain)
  184. {
  185. return new XMPPConnection(new ConnectionConfiguration(getDomain(), getPort(), usernameDomain));
  186. }
  187. private String getStatusImage(Presence.Mode mode)
  188. {
  189. final String img;
  190. if (null != mode && STATUS_MAP.containsKey(mode))
  191. {
  192. img = STATUS_MAP.get(mode);
  193. }
  194. else
  195. {
  196. img = "im_invisible";
  197. }
  198. if (null == img)
  199. {
  200. logger.info("Unrecognised " + getKey() + " status: " + mode);
  201. }
  202. return img;
  203. }
  204. /**
  205. * Removes the domain suffix from the specified ID.
  206. *
  207. * @param id
  208. * The ID to trim.
  209. * @return The ID with the domain suffix removed, if any. Otherwise, the return value would be the
  210. * same as the one specified to this method.
  211. */
  212. private String getTrimmedId(String id)
  213. {
  214. if (id.contains("@"))
  215. {
  216. id = id.substring(0, id.indexOf("@"));
  217. }
  218. return id;
  219. }
  220. private Presence getPresence(final String targetAddress, Roster roster)
  221. {
  222. if (logger.isDebugEnabled())
  223. {
  224. logger.debug("Roster for " + getId() + " is: " + roster);
  225. }
  226. if (null != roster)
  227. {
  228. return roster.getPresence(targetAddress);
  229. }
  230. else
  231. {
  232. return null;
  233. }
  234. }
  235. public String getResourcePath()
  236. {
  237. return resourcePath;
  238. }
  239. public void setResourcePath(String resourcePath)
  240. {
  241. this.resourcePath = resourcePath;
  242. }
  243. protected String getPresenceURL(String id)
  244. {
  245. return "jabber:" + id;
  246. }
  247. public String getDomain()
  248. {
  249. return StringUtils.defaultIfEmpty(
  250. (String) bandanaManager.getValue(ConfluenceBandanaContext.GLOBAL_CONTEXT, DOMAIN_PREFIX + getKey()),
  251. DEFAULT_JABBER_DOMAIN
  252. );
  253. }
  254. public void setDomain(String domain)
  255. {
  256. bandanaManager.setValue(ConfluenceBandanaContext.GLOBAL_CONTEXT, DOMAIN_PREFIX + getKey(), StringUtils.defaultString(StringUtils.trim(domain)));
  257. }
  258. public Integer getPort()
  259. {
  260. Object portObj = bandanaManager.getValue(ConfluenceBandanaContext.GLOBAL_CONTEXT, PORT_PREFIX + getKey());
  261. return null == portObj ? DEFAULT_JABBER_PORT : new Integer(portObj.toString());
  262. }
  263. public void setPort(Integer port)
  264. {
  265. if (null == port)
  266. bandanaManager.removeValue(ConfluenceBandanaContext.GLOBAL_CONTEXT, PORT_PREFIX + getKey());
  267. else
  268. bandanaManager.setValue(ConfluenceBandanaContext.GLOBAL_CONTEXT, PORT_PREFIX + getKey(), port.toString());
  269. }
  270. }