Synchronization of UserContainer with UserService

The UserContainer class is now instantiated by OSGI.
It is notified via the event service each time a
user is added/modified within the current UserService
implementation.

Change-Id: I51811bf359abc6f477afe78190cdb24145ae1b19
Signed-off-by: Frederic SIVIGNON <frederic.sivignon@sap.com>
Signed-off-by: Michael Ochmann <michael.ochmann@sap.com>
diff --git a/org.eclipse.skalli.api/src/main/java/org/eclipse/skalli/services/user/EventUserUpdate.java b/org.eclipse.skalli.api/src/main/java/org/eclipse/skalli/services/user/EventUserUpdate.java
new file mode 100644
index 0000000..69d4d37
--- /dev/null
+++ b/org.eclipse.skalli.api/src/main/java/org/eclipse/skalli/services/user/EventUserUpdate.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2010-2017 SAP AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAP AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.skalli.services.user;
+
+import org.eclipse.skalli.model.User;
+import org.eclipse.skalli.services.event.Event;
+import org.eclipse.skalli.services.event.EventService;
+
+/**
+ * Event that notifies about changes to the user base cache. This event is fired by
+ * the {@link EventService} when a user is added or updated in the current UserService
+ * instance.
+ * <br>
+ * Any service that for example caches or shows {@link org.eclipse.skalli.model.User users}
+ * should register to this event
+ * (see {@link org.eclipse.skalli.services.event.EventService#registerListener(Class,
+ *  org.eclipse.skalli.services.event.EventListener)}).
+ */
+public class EventUserUpdate extends Event {
+
+    private final User user;
+
+    /**
+     * Creates a user update event.
+     *
+     * @param user  the user that has been updated
+     */
+    public EventUserUpdate(User user) {
+        this.user = user;
+    }
+
+    /**
+     * Returns the user that has been updated.
+     */
+    public User getUser() {
+        return user;
+    }
+}
diff --git a/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/LocalUserComponent.java b/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/LocalUserComponent.java
index 4e2a0d3..1f8da28 100644
--- a/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/LocalUserComponent.java
+++ b/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/LocalUserComponent.java
@@ -28,7 +28,9 @@
 import org.eclipse.skalli.model.User;
 import org.eclipse.skalli.model.ValidationException;
 import org.eclipse.skalli.services.entity.EntityServiceBase;
+import org.eclipse.skalli.services.event.EventService;
 import org.eclipse.skalli.services.extension.DataMigration;
+import org.eclipse.skalli.services.user.EventUserUpdate;
 import org.eclipse.skalli.services.user.UserService;
 import org.osgi.service.component.ComponentConstants;
 import org.osgi.service.component.ComponentContext;
@@ -50,6 +52,7 @@
     private static final Logger LOG = LoggerFactory.getLogger(LocalUserComponent.class);
 
     private static final int CURRENT_MODEL_VERISON = 20;
+    private EventService eventService;
 
     private Map<String, User> cache;
 
@@ -64,6 +67,11 @@
     }
 
     @Override
+    protected void bindEventService(EventService eventService) {
+        this.eventService = eventService;
+    }
+
+    @Override
     public Class<User> getEntityClass() {
         return User.class;
     }
@@ -92,6 +100,10 @@
             cache = new HashMap<String, User>();
             for (User user: getAll()) {
                 cache.put(user.getUserId(), user);
+
+                if (this.eventService != null) {
+                    this.eventService.fireEvent(new EventUserUpdate(user));
+                }
             }
         }
         return cache;
diff --git a/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/ldap/LDAPUserComponent.java b/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/ldap/LDAPUserComponent.java
index a216f6c..2784a24 100644
--- a/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/ldap/LDAPUserComponent.java
+++ b/org.eclipse.skalli.core/src/main/java/org/eclipse/skalli/core/user/ldap/LDAPUserComponent.java
@@ -41,6 +41,7 @@
 import org.eclipse.skalli.services.configuration.EventConfigUpdate;
 import org.eclipse.skalli.services.event.EventListener;
 import org.eclipse.skalli.services.event.EventService;
+import org.eclipse.skalli.services.user.EventUserUpdate;
 import org.eclipse.skalli.services.user.UserService;
 import org.eclipse.skalli.services.user.ldap.LdapContextProvider;
 import org.osgi.service.component.ComponentConstants;
@@ -72,6 +73,7 @@
     private String destination;
     private String baseDN;
     private String searchScope;
+    private EventService eventService;
 
     private Cache<String, User> cache;
 
@@ -98,10 +100,12 @@
 
     protected void bindEventService(EventService eventService) {
         eventService.registerListener(EventConfigUpdate.class, this);
+        this.eventService = eventService;
     }
 
     protected void unbindEventService(EventService eventService) {
         eventService.unregisterListener(EventConfigUpdate.class, this);
+        this.eventService = null;
     }
 
     protected void bindConfigurationService(ConfigurationService configurationService) {
@@ -152,12 +156,26 @@
         List<User> users = searchUserByName(searchText);
         for (User user : users) {
             if (user != null) {
-                cache.put(StringUtils.lowerCase(user.getUserId()), user);
+                addUserToCache(user);
             }
         }
         return users;
     }
 
+    /**
+     * Adds or updates the given user within the cache class data member.
+     * Also fires an event so other user cache instances can be updated (See UserContainer class).
+     * @param user
+     */
+    private void addUserToCache(User user){
+        cache.put(user.getUserId().toLowerCase(Locale.ENGLISH), user);
+
+        if (this.eventService != null) {
+            this.eventService.fireEvent(new EventUserUpdate(user));
+        }
+    }
+
+
     @Override
     public synchronized User getUserById(String userId) {
         if (StringUtils.isBlank(userId)) {
@@ -171,7 +189,7 @@
             }
             user = searchUserById(userId);
             if (user != null) {
-                cache.put(lowerCaseUserId, user);
+                addUserToCache(user);
             }
         }
         return user;
@@ -210,8 +228,8 @@
                 if (user != null) {
                     String userId = user.getUserId();
                     if (StringUtils.isNotBlank(userId)) {
-                        cache.put(userId.toLowerCase(Locale.ENGLISH), user);
                         users.add(user);
+                        addUserToCache(user);
                     }
                 }
             }
diff --git a/org.eclipse.skalli.view/META-INF/MANIFEST.MF b/org.eclipse.skalli.view/META-INF/MANIFEST.MF
index 98331ce..f3c6dec 100644
--- a/org.eclipse.skalli.view/META-INF/MANIFEST.MF
+++ b/org.eclipse.skalli.view/META-INF/MANIFEST.MF
@@ -31,6 +31,7 @@
  org.eclipse.skalli.services,
  org.eclipse.skalli.services.configuration,
  org.eclipse.skalli.services.entity,
+ org.eclipse.skalli.services.event,
  org.eclipse.skalli.services.extension,
  org.eclipse.skalli.services.extension.rest,
  org.eclipse.skalli.services.favorites,
@@ -57,7 +58,8 @@
  OSGI-INF/TopLinksConfigSection.xml,
  OSGI-INF/NewsConfigSection.xml,
  OSGI-INF/UserDetailsConfigSection.xml,
- OSGI-INF/BrandingConfigSection.xml
+ OSGI-INF/BrandingConfigSection.xml,
+ OSGI-INF/UserContainer.xml
 Web-ContextPath: /
 Webapp-Context: /
 Export-Package: org.eclipse.skalli.view,
diff --git a/org.eclipse.skalli.view/OSGI-INF/UserContainer.xml b/org.eclipse.skalli.view/OSGI-INF/UserContainer.xml
new file mode 100644
index 0000000..beffbb0
--- /dev/null
+++ b/org.eclipse.skalli.view/OSGI-INF/UserContainer.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (c) 2010-2017 SAP AG and others.
+    All rights reserved. This program and the accompanying materials
+    are made available under the terms of the Eclipse Public License v1.0
+    which accompanies this distribution, and is available at
+    http://www.eclipse.org/legal/epl-v10.html
+
+    Contributors:
+        SAP AG - initial API and implementation
+ -->
+<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.eclipse.skalli.view.internal.container.UserContainer">
+   <implementation class="org.eclipse.skalli.view.internal.container.UserContainer"/>
+   <reference
+        name="EventService"
+        interface="org.eclipse.skalli.services.event.EventService"
+        cardinality="0..1"
+        policy="dynamic"
+        bind="bindEventService"
+        unbind="unbindEventService" />
+</scr:component>
diff --git a/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/component/UserSelectCombo.java b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/component/UserSelectCombo.java
index d20274e..1ddb96b 100644
--- a/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/component/UserSelectCombo.java
+++ b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/component/UserSelectCombo.java
@@ -22,7 +22,7 @@
     public UserSelectCombo(String caption, String width) {
         super(caption);
         setWidth(width);
-        setContainerDataSource(UserContainer.createWithData());
+        setContainerDataSource(UserContainer.getInstance());
         setItemCaptionPropertyId(User.PROPERTY_DISPLAY_NAME);
         setFilteringMode(ComboBox.FILTERINGMODE_CONTAINS);
         setImmediate(true);
diff --git a/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/IndexedUserContainer.java b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/IndexedUserContainer.java
new file mode 100644
index 0000000..f95f2a9
--- /dev/null
+++ b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/IndexedUserContainer.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2010-2017 SAP AG and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     SAP AG - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.skalli.view.internal.container;
+
+import java.io.Serializable;
+
+import org.eclipse.skalli.model.PropertyName;
+import org.eclipse.skalli.model.User;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+
+public class IndexedUserContainer extends IndexedContainer implements Serializable {
+
+    private static final long serialVersionUID = 632503436661743735L;
+
+    @PropertyName(position = 0)
+    public static final Object PROPERTY_USER = "user"; //$NON-NLS-1$
+
+    public IndexedUserContainer() {
+        addContainerProperty(User.PROPERTY_FIRSTNAME, String.class, null);
+        addContainerProperty(User.PROPERTY_LASTNAME, String.class, null);
+        addContainerProperty(User.PROPERTY_EMAIL, String.class, null);
+        addContainerProperty(User.PROPERTY_DISPLAY_NAME, String.class, null);
+        addContainerProperty(PROPERTY_USER, User.class, null);
+    }
+
+    public synchronized Item addItem(User user) {
+        String userId = user.getUserId();
+        Item item = getItem(userId);
+        if (item == null) {
+            item = addItem(user.getUserId()); // IndexedContainer#addItem return null, if entry already exists!!!
+        }
+        if (item != null) {
+            item.getItemProperty(User.PROPERTY_FIRSTNAME).setValue(user.getFirstname());
+            item.getItemProperty(User.PROPERTY_LASTNAME).setValue(user.getLastname());
+            item.getItemProperty(User.PROPERTY_EMAIL).setValue(user.getEmail());
+            item.getItemProperty(User.PROPERTY_DISPLAY_NAME).setValue(user.getDisplayName());
+            item.getItemProperty(PROPERTY_USER).setValue(user);
+        }
+        return item;
+    }
+}
diff --git a/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/UserContainer.java b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/UserContainer.java
index e3a1304..fe9790d 100644
--- a/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/UserContainer.java
+++ b/org.eclipse.skalli.view/src/main/java/org/eclipse/skalli/view/internal/container/UserContainer.java
@@ -10,9 +10,9 @@
  *******************************************************************************/
 package org.eclipse.skalli.view.internal.container;
 
-import java.io.Serializable;
+import static org.eclipse.skalli.view.internal.container.IndexedUserContainer.PROPERTY_USER;
+
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -20,8 +20,10 @@
 import java.util.TreeSet;
 
 import org.eclipse.skalli.model.Member;
-import org.eclipse.skalli.model.PropertyName;
 import org.eclipse.skalli.model.User;
+import org.eclipse.skalli.services.event.EventListener;
+import org.eclipse.skalli.services.event.EventService;
+import org.eclipse.skalli.services.user.EventUserUpdate;
 import org.eclipse.skalli.services.user.UserService;
 import org.eclipse.skalli.services.user.UserServices;
 
@@ -29,58 +31,22 @@
 import com.vaadin.data.Property;
 import com.vaadin.data.util.IndexedContainer;
 
-public class UserContainer extends IndexedContainer implements Serializable {
+public class UserContainer implements EventListener<EventUserUpdate> {
 
-    private static final long serialVersionUID = 632503436661743735L;
-    private static UserContainer container;
+    private static IndexedUserContainer container = new IndexedUserContainer();
 
-    @PropertyName(position = 0)
-    public static final Object PROPERTY_USER = "user"; //$NON-NLS-1$
-
-    private UserContainer(Collection<User> users) {
-        super();
-        addContainerProperty(User.PROPERTY_FIRSTNAME, String.class, null);
-        addContainerProperty(User.PROPERTY_LASTNAME, String.class, null);
-        addContainerProperty(User.PROPERTY_EMAIL, String.class, null);
-        addContainerProperty(User.PROPERTY_DISPLAY_NAME, String.class, null);
-        addContainerProperty(PROPERTY_USER, User.class, null);
-        for (User user : users) {
-            addItem(user);
-        }
+    protected void bindEventService(EventService eventService) {
+        eventService.registerListener(EventUserUpdate.class, this);
     }
 
-    private Item addItem(User user) {
-        String userId = user.getUserId();
-        Item item = getItem(userId);
-        if (item == null) {
-            item = addItem(user.getUserId()); // IndexedContainer#addItem return null, if entry already exists!!!
-        }
-        if (item != null) {
-            item.getItemProperty(User.PROPERTY_FIRSTNAME).setValue(user.getFirstname());
-            item.getItemProperty(User.PROPERTY_LASTNAME).setValue(user.getLastname());
-            item.getItemProperty(User.PROPERTY_EMAIL).setValue(user.getEmail());
-            item.getItemProperty(User.PROPERTY_DISPLAY_NAME).setValue(user.getDisplayName());
-            item.getItemProperty(PROPERTY_USER).setValue(user);
-        }
-        return item;
+    protected void unbindEventService(EventService eventService) {
+        eventService.unregisterListener(EventUserUpdate.class, this);
     }
 
-    public static synchronized UserContainer createWithData() {
-        if (container == null) {
-            List<User> users = Collections.emptyList();
-            UserService userService = UserServices.getUserService();
-            if (userService != null) {
-                users = userService.getUsers();
-            }
-            container = new UserContainer(users);
-        }
+    public static IndexedContainer getInstance() {
         return container;
     }
 
-    public static void refresh() {
-        container = null;
-    }
-
     public static User getUser(Object userId) {
         if (userId instanceof User) {
             return (User) userId;
@@ -120,7 +86,6 @@
     }
 
     private static Item searchAndAddUser(String userId) {
-        UserContainer container = UserContainer.createWithData();
         Item item = container.getItem(userId);
         if (item == null) {
             User user = UserServices.getUser(userId.toString());
@@ -149,7 +114,6 @@
     }
 
     public static Set<User> getUsers(Set<Member> members) {
-        UserContainer container = UserContainer.createWithData();
         Set<User> users = new TreeSet<User>();
         Set<String> userIds = new HashSet<String>();
         for (Member member : members) {
@@ -178,4 +142,15 @@
         return users;
     }
 
+    /**
+     * When the user service notifies about a change in the user base
+     * update the indexed container.
+     */
+     @Override
+     public void onEvent(EventUserUpdate event) {
+         User user = event.getUser();
+         if (user != null) {
+            container.addItem(user);
+         }
+     }
 }