/****************************************************************
 * 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.mailbox.MailboxSession.User;
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;
    }
    

    public void subscribe(User user, String mailbox) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user.getUserName());
            subscription.subscribe(mailbox);
        } catch (UserRepositoryException e) {
            throw new SubscriptionException(HumanReadableText.GENERIC_SUBSCRIPTION_FAILURE, e);
        }
    }


    @SuppressWarnings("unchecked")
    public Collection<String> subscriptions(User user) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user.getUserName());
            final Collection<String> results = (Collection) subscription.subscriptions().clone();
            return results;
        } catch (UserRepositoryException e) {
            throw new SubscriptionException(HumanReadableText.GENERIC_SUBSCRIPTION_FAILURE, e);
        }
    }

    public void unsubscribe(User user, String mailbox) throws SubscriptionException {
        try {
            final UserSubscription subscription = getUserSubscription(user.getUserName());
            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>();
            }
        }
    }

}

@@ -1658,34 +1658,27 @@
 mailbox.
-SubscriptionExcept
+MailboxSess
 ion;%0Aimp
@@ -1715,27 +1715,29 @@
 box.
-MailboxSession.User
+SubscriptionException
 ;%0Aim
@@ -2473,39 +2473,213 @@
     
-public void subscribe(User user
+/*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#subscribe(org.apache.james.imap.mailbox.MailboxSession, java.lang.String)%0A     */%0A    public void subscribe(MailboxSession session
 , St
@@ -2799,36 +2799,49 @@
 serSubscription(
-u
+session.getU
 ser
+()
 .getUserName());
@@ -3046,16 +3046,164 @@
     %7D%0A%0A%0A
+%0A    /*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#subscriptions(org.apache.james.imap.mailbox.MailboxSession)%0A     */%0A
     @Sup
@@ -3273,25 +3273,38 @@
 iptions(
-User user
+MailboxSession session
 ) throws
@@ -3404,36 +3404,49 @@
 serSubscription(
-u
+session.getU
 ser
+()
 .getUserName());
@@ -3727,24 +3727,187 @@
    %7D%0A    %7D%0A%0A
+    /*%0A     * (non-Javadoc)%0A     * @see org.apache.james.imap.store.Subscriber#unsubscribe(org.apache.james.imap.mailbox.MailboxSession, java.lang.String)%0A     */%0A
     public v
@@ -3926,17 +3926,30 @@
 ibe(
-User user
+MailboxSession session
 , St
@@ -4077,20 +4077,33 @@
 ription(
-u
+session.getU
 ser
+()
 .getUser
@@ -6162,32 +6162,71 @@
    %7D%0A        %7D%0A%0A
+        @SuppressWarnings(%22unchecked%22)%0A
         public H

rework to reflect api changes in imap trunk