001/** 002 * 003 * Copyright 2003-2007 Jive Software. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.jivesoftware.smackx.bookmarks; 019 020import java.util.Collections; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Map; 024import java.util.WeakHashMap; 025 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.XMPPException.XMPPErrorException; 030 031import org.jivesoftware.smackx.iqprivate.PrivateDataManager; 032 033import org.jxmpp.jid.EntityBareJid; 034import org.jxmpp.jid.parts.Resourcepart; 035 036 037/** 038 * Provides methods to manage bookmarks in accordance with XEP-0048. Methods for managing URLs and 039 * Conferences are provided. 040 * 041 * It should be noted that some extensions have been made to the XEP. There is an attribute on URLs 042 * that marks a url as a news feed and also a sub-element can be added to either a URL or conference 043 * indicated that it is shared amongst all users on a server. 044 * 045 * @author Alexander Wenckus 046 */ 047public final class BookmarkManager { 048 private static final Map<XMPPConnection, BookmarkManager> bookmarkManagerMap = new WeakHashMap<>(); 049 050 static { 051 PrivateDataManager.addPrivateDataProvider("storage", "storage:bookmarks", 052 new Bookmarks.Provider()); 053 } 054 055 /** 056 * Returns the <i>BookmarkManager</i> for a connection, if it doesn't exist it is created. 057 * 058 * @param connection the connection for which the manager is desired. 059 * @return Returns the <i>BookmarkManager</i> for a connection, if it doesn't 060 * exist it is created. 061 * @throws IllegalArgumentException when the connection is null. 062 */ 063 public synchronized static BookmarkManager getBookmarkManager(XMPPConnection connection) 064 { 065 BookmarkManager manager = bookmarkManagerMap.get(connection); 066 if (manager == null) { 067 manager = new BookmarkManager(connection); 068 bookmarkManagerMap.put(connection, manager); 069 } 070 return manager; 071 } 072 073 private final PrivateDataManager privateDataManager; 074 private Bookmarks bookmarks; 075 private final Object bookmarkLock = new Object(); 076 077 /** 078 * Default constructor. Registers the data provider with the private data manager in the 079 * storage:bookmarks namespace. 080 * 081 * @param connection the connection for persisting and retrieving bookmarks. 082 */ 083 private BookmarkManager(XMPPConnection connection) { 084 privateDataManager = PrivateDataManager.getInstanceFor(connection); 085 } 086 087 /** 088 * Returns all currently bookmarked conferences. 089 * 090 * @return returns all currently bookmarked conferences 091 * @throws XMPPErrorException 092 * @throws NoResponseException 093 * @throws NotConnectedException 094 * @throws InterruptedException 095 * @see BookmarkedConference 096 */ 097 public List<BookmarkedConference> getBookmarkedConferences() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 098 retrieveBookmarks(); 099 return Collections.unmodifiableList(bookmarks.getBookmarkedConferences()); 100 } 101 102 /** 103 * Adds or updates a conference in the bookmarks. 104 * 105 * @param name the name of the conference 106 * @param jid the jid of the conference 107 * @param isAutoJoin whether or not to join this conference automatically on login 108 * @param nickname the nickname to use for the user when joining the conference 109 * @param password the password to use for the user when joining the conference 110 * @throws XMPPErrorException thrown when there is an issue retrieving the current bookmarks from 111 * the server. 112 * @throws NoResponseException if there was no response from the server. 113 * @throws NotConnectedException 114 * @throws InterruptedException 115 */ 116 public void addBookmarkedConference(String name, EntityBareJid jid, boolean isAutoJoin, 117 Resourcepart nickname, String password) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException 118 { 119 retrieveBookmarks(); 120 BookmarkedConference bookmark 121 = new BookmarkedConference(name, jid, isAutoJoin, nickname, password); 122 List<BookmarkedConference> conferences = bookmarks.getBookmarkedConferences(); 123 if (conferences.contains(bookmark)) { 124 BookmarkedConference oldConference = conferences.get(conferences.indexOf(bookmark)); 125 if (oldConference.isShared()) { 126 throw new IllegalArgumentException("Cannot modify shared bookmark"); 127 } 128 oldConference.setAutoJoin(isAutoJoin); 129 oldConference.setName(name); 130 oldConference.setNickname(nickname); 131 oldConference.setPassword(password); 132 } 133 else { 134 bookmarks.addBookmarkedConference(bookmark); 135 } 136 privateDataManager.setPrivateData(bookmarks); 137 } 138 139 /** 140 * Removes a conference from the bookmarks. 141 * 142 * @param jid the jid of the conference to be removed. 143 * @throws XMPPErrorException thrown when there is a problem with the connection attempting to 144 * retrieve the bookmarks or persist the bookmarks. 145 * @throws NoResponseException if there was no response from the server. 146 * @throws NotConnectedException 147 * @throws InterruptedException 148 * @throws IllegalArgumentException thrown when the conference being removed is a shared 149 * conference 150 */ 151 public void removeBookmarkedConference(EntityBareJid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 152 retrieveBookmarks(); 153 Iterator<BookmarkedConference> it = bookmarks.getBookmarkedConferences().iterator(); 154 while (it.hasNext()) { 155 BookmarkedConference conference = it.next(); 156 if (conference.getJid().equals(jid)) { 157 if (conference.isShared()) { 158 throw new IllegalArgumentException("Conference is shared and can't be removed"); 159 } 160 it.remove(); 161 privateDataManager.setPrivateData(bookmarks); 162 return; 163 } 164 } 165 } 166 167 /** 168 * Returns an unmodifiable collection of all bookmarked urls. 169 * 170 * @return returns an unmodifiable collection of all bookmarked urls. 171 * @throws XMPPErrorException thrown when there is a problem retriving bookmarks from the server. 172 * @throws NoResponseException if there was no response from the server. 173 * @throws NotConnectedException 174 * @throws InterruptedException 175 */ 176 public List<BookmarkedURL> getBookmarkedURLs() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 177 retrieveBookmarks(); 178 return Collections.unmodifiableList(bookmarks.getBookmarkedURLS()); 179 } 180 181 /** 182 * Adds a new url or updates an already existing url in the bookmarks. 183 * 184 * @param URL the url of the bookmark 185 * @param name the name of the bookmark 186 * @param isRSS whether or not the url is an rss feed 187 * @throws XMPPErrorException thrown when there is an error retriving or saving bookmarks from or to 188 * the server 189 * @throws NoResponseException if there was no response from the server. 190 * @throws NotConnectedException 191 * @throws InterruptedException 192 */ 193 public void addBookmarkedURL(String URL, String name, boolean isRSS) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 194 retrieveBookmarks(); 195 BookmarkedURL bookmark = new BookmarkedURL(URL, name, isRSS); 196 List<BookmarkedURL> urls = bookmarks.getBookmarkedURLS(); 197 if (urls.contains(bookmark)) { 198 BookmarkedURL oldURL = urls.get(urls.indexOf(bookmark)); 199 if (oldURL.isShared()) { 200 throw new IllegalArgumentException("Cannot modify shared bookmarks"); 201 } 202 oldURL.setName(name); 203 oldURL.setRss(isRSS); 204 } 205 else { 206 bookmarks.addBookmarkedURL(bookmark); 207 } 208 privateDataManager.setPrivateData(bookmarks); 209 } 210 211 /** 212 * Removes a url from the bookmarks. 213 * 214 * @param bookmarkURL the url of the bookmark to remove 215 * @throws XMPPErrorException thrown if there is an error retriving or saving bookmarks from or to 216 * the server. 217 * @throws NoResponseException if there was no response from the server. 218 * @throws NotConnectedException 219 * @throws InterruptedException 220 */ 221 public void removeBookmarkedURL(String bookmarkURL) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 222 retrieveBookmarks(); 223 Iterator<BookmarkedURL> it = bookmarks.getBookmarkedURLS().iterator(); 224 while (it.hasNext()) { 225 BookmarkedURL bookmark = it.next(); 226 if (bookmark.getURL().equalsIgnoreCase(bookmarkURL)) { 227 if (bookmark.isShared()) { 228 throw new IllegalArgumentException("Cannot delete a shared bookmark."); 229 } 230 it.remove(); 231 privateDataManager.setPrivateData(bookmarks); 232 return; 233 } 234 } 235 } 236 237 /** 238 * Check if the service supports bookmarks using private data. 239 * 240 * @return true if the service supports private data, false otherwise. 241 * @throws NoResponseException 242 * @throws NotConnectedException 243 * @throws InterruptedException 244 * @throws XMPPErrorException 245 * @see PrivateDataManager#isSupported() 246 * @since 4.2 247 */ 248 public boolean isSupported() throws NoResponseException, NotConnectedException, 249 XMPPErrorException, InterruptedException { 250 return privateDataManager.isSupported(); 251 } 252 253 private Bookmarks retrieveBookmarks() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { 254 synchronized (bookmarkLock) { 255 if (bookmarks == null) { 256 bookmarks = (Bookmarks) privateDataManager.getPrivateData("storage", 257 "storage:bookmarks"); 258 } 259 return bookmarks; 260 } 261 } 262}