VCardManager.java 12.4 KB
Newer Older
1 2 3 4 5
/**
 * $RCSfile$
 * $Revision: 1651 $
 * $Date: 2005-07-20 00:20:39 -0300 (Wed, 20 Jul 2005) $
 *
6
 * Copyright (C) 2004-2008 Jive Software. All rights reserved.
7
 *
8 9 10 11 12 13 14 15 16 17 18
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
19 20
 */

21
package org.jivesoftware.openfire.vcard;
22

23 24 25 26 27
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

28
import org.dom4j.Element;
29 30 31 32 33 34
import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.BasicModule;
import org.jivesoftware.openfire.disco.ServerFeaturesProvider;
import org.jivesoftware.openfire.event.UserEventAdapter;
import org.jivesoftware.openfire.event.UserEventDispatcher;
import org.jivesoftware.openfire.user.User;
35 36 37 38 39 40
import org.jivesoftware.util.AlreadyExistsException;
import org.jivesoftware.util.ClassUtils;
import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.NotFoundException;
import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
41 42
import org.jivesoftware.util.cache.Cache;
import org.jivesoftware.util.cache.CacheFactory;
43 44
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
45 46 47 48 49 50

/**
 * Manages VCard information for users.
 *
 * @author Matt Tucker
 */
51
public class VCardManager extends BasicModule implements ServerFeaturesProvider {
52

53 54
	private static final Logger Log = LoggerFactory.getLogger(VCardManager.class);

55 56
    private VCardProvider provider;
    private static VCardManager instance;
57

58
    private EventHandler eventHandler;
59

60
    private Cache<String, Element> vcardCache;
61 62 63 64 65 66 67 68 69 70 71 72 73
    public static VCardManager getInstance() {
        return instance;
    }

    /**
     * Returns the currently-installed VCardProvider. <b>Warning:</b> in virtually all
     * cases the vcard provider should not be used directly. Instead, the appropriate
     * methods in VCardManager should be called. Direct access to the vcard provider is
     * only provided for special-case logic.
     *
     * @return the current VCardProvider.
     */
    public static VCardProvider getProvider() {
74
        return instance.provider;
75 76
    }

77 78
    public VCardManager() {
        super("VCard Manager");
79
        String cacheName = "VCard";
80
        vcardCache = CacheFactory.createCache(cacheName);
81
        this.eventHandler = new EventHandler();
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

        // Keeps the cache updated in case the vCard action was not performed by VCardManager
        VCardEventDispatcher.addListener(new VCardListener() {
            public void vCardCreated(String username, Element vCard) {
                // Since the vCard could be created by the provider, add it to the cache.
                vcardCache.put(username, vCard);
            }

            public void vCardUpdated(String username, Element vCard) {
                // Since the vCard could be updated by the provider, update it to the cache.
                vcardCache.put(username, vCard);
            }

            public void vCardDeleted(String username, Element vCard) {
                // Since the vCard could be delated by the provider, remove it to the cache.
                vcardCache.remove(username);
            }
        });
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
    }

    /**
     * Returns the user's vCard information for a given vcard property name. If the property
     * has no defined text then an empty string will be returned. However, if the property
     * does not exist then a <tt>null</tt> value will be answered. Advanced user systems can
     * use vCard information to link to user directory information or store other relevant
     * user information.</p>
     * Note that many elements in the vCard may have the same path so the returned value in that
     * case will be the first found element. For instance, "ADR:STREET" may be present in
     * many addresses of the user. Use {@link #getVCard(String)} to get the whole vCard of
     * the user.
     *
     * @param username The username of the user to return his vCard property.
     * @param name     The name of the vcard property to retrieve encoded with ':' to denote
     *                 the path.
     * @return The vCard value found
     */
    public String getVCardProperty(String username, String name) {
        String answer = null;
        Element vCardElement = getOrLoadVCard(username);
        if (vCardElement != null) {
            // A vCard was found for this user so now look for the correct element
            Element subElement = null;
            StringTokenizer tokenizer = new StringTokenizer(name, ":");
            while (tokenizer.hasMoreTokens()) {
                String tok = tokenizer.nextToken();
                if (subElement == null) {
                    subElement = vCardElement.element(tok);
                }
                else {
                    subElement = subElement.element(tok);
                }
                if (subElement == null) {
                    break;
                }
            }
            if (subElement != null) {
                answer = subElement.getTextTrim();
            }
        }
        return answer;
    }

    /**
     * Sets the user's vCard information. The new vCard information will be persistent. Advanced
     * user systems can use vCard information to link to user directory information or store
     * other relevant user information.
     *
     * @param username     The username of the user to set his new vCard.
     * @param vCardElement The DOM element sent by the user as his new vcard.
151
     * @throws Exception if an error occured while storing the new vCard.
152
     */
153 154 155 156
    public void setVCard(String username, Element vCardElement) throws Exception {
        boolean created = false;
        boolean updated = false;

157 158 159
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }
160
        Element oldVCard = getOrLoadVCard(username);
161
        Element newvCard = null;
162 163 164 165 166
        // See if we need to update the vCard or insert a new one.
        if (oldVCard != null) {
            // Only update the vCard in the database if the vCard has changed.
            if (!oldVCard.equals(vCardElement)) {
                try {
167
                    newvCard = provider.updateVCard(username, vCardElement);
168
                    vcardCache.put(username, newvCard);
169 170 171 172
                    updated = true;
                }
                catch (NotFoundException e) {
                    Log.warn("Tried to update a vCard that does not exist", e);
173
                    newvCard = provider.createVCard(username, vCardElement);
174
                    vcardCache.put(username, newvCard);
175 176 177 178 179 180
                    created = true;
                }
            }
        }
        else {
            try {
181
                newvCard = provider.createVCard(username, vCardElement);
182
                vcardCache.put(username, newvCard);
183 184 185 186
                created = true;
            }
            catch (AlreadyExistsException e) {
                Log.warn("Tried to create a vCard when one already exist", e);
187
                newvCard = provider.updateVCard(username, vCardElement);
188
                vcardCache.put(username, newvCard);
189 190 191 192 193 194
                updated = true;
            }
        }
        // Dispatch vCard events
        if (created) {
            // Alert listeners that a new vCard has been created
195
            VCardEventDispatcher.dispatchVCardCreated(username, newvCard);
196 197
        } else if (updated) {
            // Alert listeners that a vCard has been updated
198
            VCardEventDispatcher.dispatchVCardUpdated(username, newvCard);
199
        }
200 201 202 203 204 205
    }

    /**
     * Deletes the user's vCard from the user account.
     *
     * @param username The username of the user to delete his vCard.
206 207
     * @throws UnsupportedOperationException If the provider is read-only and the data
     *         cannot be deleted, this exception is thrown
208 209 210 211 212
     */
    public void deleteVCard(String username) {
        if (provider.isReadOnly()) {
            throw new UnsupportedOperationException("VCard provider is read-only.");
        }
213 214 215 216 217
        Element oldVCard = getOrLoadVCard(username);
        if (oldVCard != null) {
            vcardCache.remove(username);
            // Delete the property from the DB if it was present in memory
            provider.deleteVCard(username);
218
            // Alert listeners that a vCard has been deleted
219
            VCardEventDispatcher.dispatchVCardDeleted(username, oldVCard);
220 221 222 223 224 225 226 227
        }
    }

    /**
     * Returns the vCard of a given user or null if none was defined before. Changes to the
     * returned vCard will not be stored in the database. Use the returned vCard as a
     * read-only vCard.
     *
228
     * @param username Username (not full JID) whose vCard to retrieve.
229 230 231 232 233 234 235 236
     * @return the vCard of a given user.
     */
    public Element getVCard(String username) {
        Element vCardElement = getOrLoadVCard(username);
        return vCardElement == null ? null : vCardElement.createCopy();
    }

    private Element getOrLoadVCard(String username) {
237 238 239 240 241
        Element vCardElement = vcardCache.get(username);
        if (vCardElement == null) {
            vCardElement = provider.loadVCard(username);
            if (vCardElement != null) {
                vcardCache.put(username, vCardElement);
242 243
            }
        }
244
        return vCardElement;
245
    }
246

247 248
    @Override
	public void initialize(XMPPServer server) {
249 250
        instance = this;

251 252 253
        // Convert XML based provider setup to Database based
        JiveGlobals.migrateProperty("provider.vcard.className");

254
        // Load a VCard provider.
255
        String className = JiveGlobals.getProperty("provider.vcard.className",
256 257 258 259 260 261 262 263 264 265 266
                DefaultVCardProvider.class.getName());
        try {
            Class c = ClassUtils.forName(className);
            provider = (VCardProvider) c.newInstance();
        }
        catch (Exception e) {
            Log.error("Error loading vcard provider: " + className, e);
            provider = new DefaultVCardProvider();
        }
    }

267 268
    @Override
	public void start() {
269 270 271 272 273
        // Add this module as a user event listener so we can delete
        // all user properties when a user is deleted
        if (!provider.isReadOnly()) {
            UserEventDispatcher.addListener(eventHandler);
        }
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295

        // Detect when a new vcard provider class is set
        PropertyEventListener propListener = new PropertyEventListener() {
            public void propertySet(String property, Map params) {
                if ("provider.vcard.className".equals(property)) {
                    initialize(XMPPServer.getInstance());
                }
            }

            public void propertyDeleted(String property, Map params) {
                //Ignore
            }

            public void xmlPropertySet(String property, Map params) {
                //Ignore
            }

            public void xmlPropertyDeleted(String property, Map params) {
                //Ignore
            }
        };
        PropertyEventDispatcher.addListener(propListener);
296 297
    }

298 299
    @Override
	public void stop() {
300 301 302 303
        // Remove this module as a user event listener
        UserEventDispatcher.removeListener(eventHandler);
    }

304 305 306 307 308 309 310
    /**
     * Resets the manager state. The cache where loaded vCards are stored will be flushed.
     */
    public void reset() {
        vcardCache.clear();
    }

311 312 313 314 315 316
    public Iterator<String> getFeatures() {
        ArrayList<String> features = new ArrayList<String>();
        features.add("vcard-temp");
        return features.iterator();
    }

317
    private class EventHandler extends UserEventAdapter {
318 319
        @Override
		public void userDeleting(User user, Map params) {
320 321 322 323 324
            try {
                deleteVCard(user.getUsername());
            } catch (UnsupportedOperationException ue) { /* Do Nothing */ }
        }
    }
325
}