/****************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one   *
 * or more contributor license agreements.  See the NOTICE file *
 * distributed with this work for additional information        *
 * regarding copyright ownership.  The ASF licenses this file   *
 * to you 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.                                           *
 ****************************************************************/

package org.apache.james.imapserver;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.james.api.user.UserMetaDataRespository;
import org.apache.james.api.user.UserRepositoryException;
import org.apache.james.imap.api.display.HumanReadableText;
import org.apache.james.imap.mailbox.SubscriptionException;
import org.apache.james.imap.store.Subscriber;

/**
 *
 */
public class UserMetaDataRepositorySubscripter implements Subscriber {

    public static final String META_DATA_KEY = "org.apache.james.IMAP_SUBSCRIPTIONS";

    private Log log = LogFactory.getLog(UserMetaDataRepositorySubscripter.class);

    private UserMetaDataRespository repository;
    private final Map<String,UserSubscription> userSubscriptionsByUser;

    public UserMetaDataRepositorySubscripter() {
        userSubscriptionsByUser = new HashMap<String, UserSubscription>();
    }

    @Resource(name = "userMetaDataRepository")
    public void setUserMetaDataRespository(UserMetaDataRespository repository) {
        this.repository = repository;
    }
    
    /*
     * (non-Javadoc)
     * @see org.apache.james.imap.store.Subscriber#subscribe(java.lang.String, java.lang.String)
     */
    public void subscribe(String user, String mailbox) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user);
            subscription.subscribe(mailbox);
        } catch (UserRepositoryException e) {
            throw new SubscriptionException(HumanReadableText.GENERIC_SUBSCRIPTION_FAILURE, e);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.imap.store.Subscriber#subscriptions(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    public Collection<String> subscriptions(String user) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user);
            final Collection<String> results = (Collection) subscription.subscriptions().clone();
            return results;
        } catch (UserRepositoryException e) {
            throw new SubscriptionException(HumanReadableText.GENERIC_SUBSCRIPTION_FAILURE, e);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.apache.james.imap.store.Subscriber#unsubscribe(java.lang.String, java.lang.String)
     */
    public void unsubscribe(String user, String mailbox) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user);
            subscription.unsubscribe(mailbox);
        } catch (UserRepositoryException e) {
            throw new SubscriptionException(HumanReadableText.GENERIC_UNSUBSCRIPTION_FAILURE, e);
        }
    }

    private synchronized UserSubscription getUserSubscription(final String user) {
        UserSubscription subscription = (UserSubscription) userSubscriptionsByUser.get(user);
        if (subscription == null) {
            subscription = new UserSubscription(user, repository, log);
        }
        return subscription;
    }

    /**
     * Manages subscriptions for a user. Subscriptions are stored in a single
     * collection. This class synchronizes access for write operations.
     */
    private static final class UserSubscription {

        private final String user;
        private final UserMetaDataRespository repository;
        private Log log;

        public UserSubscription(final String user, final UserMetaDataRespository repository, Log log) {
            super();
            this.user = user;
            this.repository = repository;
        }

        public synchronized void subscribe(String mailbox) throws UserRepositoryException {
            final HashSet<String> existingSubscriptions = subscriptions();
            if (!existingSubscriptions.contains(mailbox)) {
                final HashSet<String> newSubscriptions;
                if (existingSubscriptions == null) {
                    newSubscriptions = new HashSet<String>();
                } else {
                    existingSubscriptions.add(mailbox);
                    newSubscriptions = existingSubscriptions;
                }
                repository.setAttribute(user, newSubscriptions, META_DATA_KEY);
            }
        }

        public synchronized void unsubscribe(String mailbox) throws UserRepositoryException {
            final HashSet<String> subscriptions = subscriptions();
            if (subscriptions.remove(mailbox)) {
                repository.setAttribute(user, subscriptions, META_DATA_KEY);
            }
        }

        public HashSet<String> subscriptions() throws UserRepositoryException {
            try {
                final HashSet<String> storedSubscriptions = (HashSet<String>) repository.getAttribute(user, META_DATA_KEY);
                final HashSet<String> results;
                if (storedSubscriptions == null) {
                    results = new HashSet<String>();
                } else {
                    results = storedSubscriptions;
                }
                return results;
            } catch (ClassCastException e) {
                log.error("ClassCastException during retrieval. Reseting subscriptions for user " + user);
                log.debug("HashSet expected but not retrieved.", e);
                return new HashSet<String>();
            }
        }
    }

}

@@ -1673,32 +1673,90 @@
 ptionException;%0A
+import org.apache.james.imap.mailbox.MailboxSession.User;%0A
 import org.apach
@@ -2473,140 +2473,8 @@
    %0A
-    /*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#subscribe(java.lang.String, java.lang.String)%0A     */
 %0A   
@@ -2488,38 +2488,36 @@
  void subscribe(
-String
+User
  user, String ma
@@ -2634,32 +2634,46 @@
 ubscription(user
+.getUserName()
 );%0A            s
@@ -2867,126 +2867,8 @@
  %7D%0A%0A
-    /*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#subscriptions(java.lang.String)%0A     */
 %0A   
@@ -2943,22 +2943,20 @@
 iptions(
-String
+User
  user) t
@@ -3065,32 +3065,46 @@
 ubscription(user
+.getUserName()
 );%0A            f
@@ -3379,143 +3379,8 @@
  %7D%0A%0A
-    /*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#unsubscribe(java.lang.String, java.lang.String)%0A     */%0A
     
@@ -3403,22 +3403,20 @@
 bscribe(
-String
+User
  user, S
@@ -3545,24 +3545,38 @@
 ription(user
+.getUserName()
 );%0A         

Update for latest changes in imap trunk