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