{"action":"created","comment":{"url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/comments/44917260","id":44917260,"diff_hunk":"@@ -20,103 +20,112 @@\n import com.hazelcast.client.impl.protocol.ClientMessage;\n import com.hazelcast.client.spi.EventHandler;\n import com.hazelcast.client.spi.impl.ClientInvocation;\n-import com.hazelcast.client.spi.impl.ClientInvocationFuture;\n import com.hazelcast.client.spi.impl.ListenerMessageCodec;\n+import com.hazelcast.core.HazelcastException;\n+import com.hazelcast.core.InitialMembershipEvent;\n+import com.hazelcast.core.InitialMembershipListener;\n import com.hazelcast.core.Member;\n import com.hazelcast.core.MemberAttributeEvent;\n import com.hazelcast.core.MembershipEvent;\n import com.hazelcast.nio.Address;\n-import com.hazelcast.util.ConcurrencyUtil;\n-import com.hazelcast.util.ConstructorFunction;\n-import com.hazelcast.util.EmptyStatement;\n import com.hazelcast.util.UuidUtil;\n \n import java.util.Collection;\n import java.util.Collections;\n-import java.util.Iterator;\n+import java.util.HashSet;\n+import java.util.LinkedList;\n+import java.util.Map;\n import java.util.Set;\n import java.util.concurrent.ConcurrentHashMap;\n-import java.util.concurrent.ConcurrentMap;\n import java.util.concurrent.Future;\n \n-public class ClientSmartListenerService extends ClientListenerServiceImpl {\n+public class ClientSmartListenerService extends ClientListenerServiceImpl implements InitialMembershipListener {\n \n-    private static final ConstructorFunction<ClientRegistrationKey, Set<ClientEventRegistration>> CONSTRUCTOR_FUNCTION\n-            = new ConstructorFunction<ClientRegistrationKey, Set<ClientEventRegistration>>() {\n-        @Override\n-        public Set<ClientEventRegistration> createNew(ClientRegistrationKey arg) {\n-            return Collections.newSetFromMap(new ConcurrentHashMap<ClientEventRegistration, Boolean>());\n-        }\n-    };\n-    private final ConcurrentMap<ClientRegistrationKey, Set<ClientEventRegistration>> registrations\n-            = new ConcurrentHashMap<ClientRegistrationKey, Set<ClientEventRegistration>>();\n-    private final Object regMutex = new Object();\n+    private final Set<Member> members = new HashSet<Member>();\n+    private final Map<ClientRegistrationKey, Map<Address, ClientEventRegistration>> registrations\n+            = new ConcurrentHashMap<ClientRegistrationKey, Map<Address, ClientEventRegistration>>();\n+    private final Object listenerRegLock = new Object();\n \n-    public ClientSmartListenerService(HazelcastClientInstanceImpl client, int eventThreadCount, int eventQueueCapacity) {\n+    public ClientSmartListenerService(HazelcastClientInstanceImpl client,\n+                                      int eventThreadCount, int eventQueueCapacity) {\n         super(client, eventThreadCount, eventQueueCapacity);\n+        client.getClientClusterService().addMembershipListener(this);\n     }\n \n     @Override\n     public String registerListener(ListenerMessageCodec codec, EventHandler handler) {\n-        ClientMessage request = codec.encodeAddRequest(true);\n-\n-        Collection<Member> members = client.getClientClusterService().getMemberList();\n-\n         String userRegistrationId = UuidUtil.newUnsecureUuidString();\n-        ClientRegistrationKey registrationKey = new ClientRegistrationKey(userRegistrationId, request, handler, codec);\n-        for (Member member : members) {\n-            invoke(registrationKey, member.getAddress());\n+        synchronized (listenerRegLock) {\n+\n+            ClientRegistrationKey registrationKey = new ClientRegistrationKey(userRegistrationId, handler, codec);\n+            registrations.put(registrationKey, new ConcurrentHashMap<Address, ClientEventRegistration>());\n+            try {\n+                for (Member member : this.members) {\n+                    invoke(registrationKey, member.getAddress());\n+                }\n+            } catch (Exception e) {\n+                deregisterListener(userRegistrationId);\n+                throw new HazelcastException(\"Listener can not be added\", e);\n+            }\n+            return userRegistrationId;\n         }\n-        return userRegistrationId;\n     }\n \n-    private void invoke(ClientRegistrationKey registrationKey, Address address) {\n-        ClientMessage request = registrationKey.getRequest();\n+    private void invoke(ClientRegistrationKey registrationKey, Address address) throws Exception {\n+        ListenerMessageCodec codec = registrationKey.getCodec();\n+        ClientMessage request = codec.encodeAddRequest(true);\n         EventHandler handler = registrationKey.getHandler();\n         handler.beforeListenerRegister();","path":"hazelcast-client/src/main/java/com/hazelcast/client/spi/impl/listener/ClientSmartListenerService.java","position":87,"original_position":87,"commit_id":"6439887e42e14e7b408bd80718bb9a498434d6f4","original_commit_id":"6439887e42e14e7b408bd80718bb9a498434d6f4","user":{"login":"cangencer","id":510278,"avatar_url":"https://avatars.githubusercontent.com/u/510278?v=3","gravatar_id":"","url":"https://api.github.com/users/cangencer","html_url":"https://github.com/cangencer","followers_url":"https://api.github.com/users/cangencer/followers","following_url":"https://api.github.com/users/cangencer/following{/other_user}","gists_url":"https://api.github.com/users/cangencer/gists{/gist_id}","starred_url":"https://api.github.com/users/cangencer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cangencer/subscriptions","organizations_url":"https://api.github.com/users/cangencer/orgs","repos_url":"https://api.github.com/users/cangencer/repos","events_url":"https://api.github.com/users/cangencer/events{/privacy}","received_events_url":"https://api.github.com/users/cangencer/received_events","type":"User","site_admin":false},"body":"No I meant that the beforeListenerRegister and onListenerRegister events will get fired twice, i.e. the cache will be invalidated every time a new member is added","created_at":"2015-11-16T12:23:17Z","updated_at":"2015-11-16T12:23:17Z","html_url":"https://github.com/hazelcast/hazelcast/pull/6728#discussion_r44917260","pull_request_url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728","_links":{"self":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/comments/44917260"},"html":{"href":"https://github.com/hazelcast/hazelcast/pull/6728#discussion_r44917260"},"pull_request":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728"}}},"pull_request":{"url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728","id":50569099,"html_url":"https://github.com/hazelcast/hazelcast/pull/6728","diff_url":"https://github.com/hazelcast/hazelcast/pull/6728.diff","patch_url":"https://github.com/hazelcast/hazelcast/pull/6728.patch","issue_url":"https://api.github.com/repos/hazelcast/hazelcast/issues/6728","number":6728,"state":"open","locked":false,"title":"Listener registration race fixed","user":{"login":"sancar","id":570462,"avatar_url":"https://avatars.githubusercontent.com/u/570462?v=3","gravatar_id":"","url":"https://api.github.com/users/sancar","html_url":"https://github.com/sancar","followers_url":"https://api.github.com/users/sancar/followers","following_url":"https://api.github.com/users/sancar/following{/other_user}","gists_url":"https://api.github.com/users/sancar/gists{/gist_id}","starred_url":"https://api.github.com/users/sancar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sancar/subscriptions","organizations_url":"https://api.github.com/users/sancar/orgs","repos_url":"https://api.github.com/users/sancar/repos","events_url":"https://api.github.com/users/sancar/events{/privacy}","received_events_url":"https://api.github.com/users/sancar/received_events","type":"User","site_admin":false},"body":"Lots of tests are added to verify the correct behaviour.\r\nSince listener registration and deregistration are not in hot paths\r\nand simplicity a lock on member variable is used in the fix.\r\n\r\nfixes #6724 #6695 #6694 #6654 #6622 #6620 #6580 #6545","created_at":"2015-11-12T21:17:43Z","updated_at":"2015-11-16T12:23:17Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3221d48ee30ded6604c4893a7c94a216effaf925","assignee":{"login":"sancar","id":570462,"avatar_url":"https://avatars.githubusercontent.com/u/570462?v=3","gravatar_id":"","url":"https://api.github.com/users/sancar","html_url":"https://github.com/sancar","followers_url":"https://api.github.com/users/sancar/followers","following_url":"https://api.github.com/users/sancar/following{/other_user}","gists_url":"https://api.github.com/users/sancar/gists{/gist_id}","starred_url":"https://api.github.com/users/sancar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sancar/subscriptions","organizations_url":"https://api.github.com/users/sancar/orgs","repos_url":"https://api.github.com/users/sancar/repos","events_url":"https://api.github.com/users/sancar/events{/privacy}","received_events_url":"https://api.github.com/users/sancar/received_events","type":"User","site_admin":false},"milestone":{"url":"https://api.github.com/repos/hazelcast/hazelcast/milestones/61","html_url":"https://github.com/hazelcast/hazelcast/milestones/3.6","labels_url":"https://api.github.com/repos/hazelcast/hazelcast/milestones/61/labels","id":982584,"number":61,"title":"3.6","description":"","creator":{"login":"enesakar","id":234086,"avatar_url":"https://avatars.githubusercontent.com/u/234086?v=3","gravatar_id":"","url":"https://api.github.com/users/enesakar","html_url":"https://github.com/enesakar","followers_url":"https://api.github.com/users/enesakar/followers","following_url":"https://api.github.com/users/enesakar/following{/other_user}","gists_url":"https://api.github.com/users/enesakar/gists{/gist_id}","starred_url":"https://api.github.com/users/enesakar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/enesakar/subscriptions","organizations_url":"https://api.github.com/users/enesakar/orgs","repos_url":"https://api.github.com/users/enesakar/repos","events_url":"https://api.github.com/users/enesakar/events{/privacy}","received_events_url":"https://api.github.com/users/enesakar/received_events","type":"User","site_admin":false},"open_issues":109,"closed_issues":808,"state":"open","created_at":"2015-02-17T09:27:09Z","updated_at":"2015-11-16T11:08:03Z","due_on":null,"closed_at":null},"commits_url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728/commits","review_comments_url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728/comments","review_comment_url":"https://api.github.com/repos/hazelcast/hazelcast/pulls/comments{/number}","comments_url":"https://api.github.com/repos/hazelcast/hazelcast/issues/6728/comments","statuses_url":"https://api.github.com/repos/hazelcast/hazelcast/statuses/6439887e42e14e7b408bd80718bb9a498434d6f4","head":{"label":"sancar:fix/listenerRegistrationRace/master","ref":"fix/listenerRegistrationRace/master","sha":"6439887e42e14e7b408bd80718bb9a498434d6f4","user":{"login":"sancar","id":570462,"avatar_url":"https://avatars.githubusercontent.com/u/570462?v=3","gravatar_id":"","url":"https://api.github.com/users/sancar","html_url":"https://github.com/sancar","followers_url":"https://api.github.com/users/sancar/followers","following_url":"https://api.github.com/users/sancar/following{/other_user}","gists_url":"https://api.github.com/users/sancar/gists{/gist_id}","starred_url":"https://api.github.com/users/sancar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sancar/subscriptions","organizations_url":"https://api.github.com/users/sancar/orgs","repos_url":"https://api.github.com/users/sancar/repos","events_url":"https://api.github.com/users/sancar/events{/privacy}","received_events_url":"https://api.github.com/users/sancar/received_events","type":"User","site_admin":false},"repo":{"id":12152804,"name":"hazelcast","full_name":"sancar/hazelcast","owner":{"login":"sancar","id":570462,"avatar_url":"https://avatars.githubusercontent.com/u/570462?v=3","gravatar_id":"","url":"https://api.github.com/users/sancar","html_url":"https://github.com/sancar","followers_url":"https://api.github.com/users/sancar/followers","following_url":"https://api.github.com/users/sancar/following{/other_user}","gists_url":"https://api.github.com/users/sancar/gists{/gist_id}","starred_url":"https://api.github.com/users/sancar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sancar/subscriptions","organizations_url":"https://api.github.com/users/sancar/orgs","repos_url":"https://api.github.com/users/sancar/repos","events_url":"https://api.github.com/users/sancar/events{/privacy}","received_events_url":"https://api.github.com/users/sancar/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/sancar/hazelcast","description":"Open Source In-Memory Data Grid","fork":true,"url":"https://api.github.com/repos/sancar/hazelcast","forks_url":"https://api.github.com/repos/sancar/hazelcast/forks","keys_url":"https://api.github.com/repos/sancar/hazelcast/keys{/key_id}","collaborators_url":"https://api.github.com/repos/sancar/hazelcast/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/sancar/hazelcast/teams","hooks_url":"https://api.github.com/repos/sancar/hazelcast/hooks","issue_events_url":"https://api.github.com/repos/sancar/hazelcast/issues/events{/number}","events_url":"https://api.github.com/repos/sancar/hazelcast/events","assignees_url":"https://api.github.com/repos/sancar/hazelcast/assignees{/user}","branches_url":"https://api.github.com/repos/sancar/hazelcast/branches{/branch}","tags_url":"https://api.github.com/repos/sancar/hazelcast/tags","blobs_url":"https://api.github.com/repos/sancar/hazelcast/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/sancar/hazelcast/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/sancar/hazelcast/git/refs{/sha}","trees_url":"https://api.github.com/repos/sancar/hazelcast/git/trees{/sha}","statuses_url":"https://api.github.com/repos/sancar/hazelcast/statuses/{sha}","languages_url":"https://api.github.com/repos/sancar/hazelcast/languages","stargazers_url":"https://api.github.com/repos/sancar/hazelcast/stargazers","contributors_url":"https://api.github.com/repos/sancar/hazelcast/contributors","subscribers_url":"https://api.github.com/repos/sancar/hazelcast/subscribers","subscription_url":"https://api.github.com/repos/sancar/hazelcast/subscription","commits_url":"https://api.github.com/repos/sancar/hazelcast/commits{/sha}","git_commits_url":"https://api.github.com/repos/sancar/hazelcast/git/commits{/sha}","comments_url":"https://api.github.com/repos/sancar/hazelcast/comments{/number}","issue_comment_url":"https://api.github.com/repos/sancar/hazelcast/issues/comments{/number}","contents_url":"https://api.github.com/repos/sancar/hazelcast/contents/{+path}","compare_url":"https://api.github.com/repos/sancar/hazelcast/compare/{base}...{head}","merges_url":"https://api.github.com/repos/sancar/hazelcast/merges","archive_url":"https://api.github.com/repos/sancar/hazelcast/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/sancar/hazelcast/downloads","issues_url":"https://api.github.com/repos/sancar/hazelcast/issues{/number}","pulls_url":"https://api.github.com/repos/sancar/hazelcast/pulls{/number}","milestones_url":"https://api.github.com/repos/sancar/hazelcast/milestones{/number}","notifications_url":"https://api.github.com/repos/sancar/hazelcast/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/sancar/hazelcast/labels{/name}","releases_url":"https://api.github.com/repos/sancar/hazelcast/releases{/id}","created_at":"2013-08-16T06:59:07Z","updated_at":"2015-04-23T16:11:55Z","pushed_at":"2015-11-14T21:31:31Z","git_url":"git://github.com/sancar/hazelcast.git","ssh_url":"git@github.com:sancar/hazelcast.git","clone_url":"https://github.com/sancar/hazelcast.git","svn_url":"https://github.com/sancar/hazelcast","homepage":"www.hazelcast.com","size":90848,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"hazelcast:master","ref":"master","sha":"885f1eefdc46401172ff94d6e64513355586739a","user":{"login":"hazelcast","id":1453152,"avatar_url":"https://avatars.githubusercontent.com/u/1453152?v=3","gravatar_id":"","url":"https://api.github.com/users/hazelcast","html_url":"https://github.com/hazelcast","followers_url":"https://api.github.com/users/hazelcast/followers","following_url":"https://api.github.com/users/hazelcast/following{/other_user}","gists_url":"https://api.github.com/users/hazelcast/gists{/gist_id}","starred_url":"https://api.github.com/users/hazelcast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hazelcast/subscriptions","organizations_url":"https://api.github.com/users/hazelcast/orgs","repos_url":"https://api.github.com/users/hazelcast/repos","events_url":"https://api.github.com/users/hazelcast/events{/privacy}","received_events_url":"https://api.github.com/users/hazelcast/received_events","type":"Organization","site_admin":false},"repo":{"id":3786237,"name":"hazelcast","full_name":"hazelcast/hazelcast","owner":{"login":"hazelcast","id":1453152,"avatar_url":"https://avatars.githubusercontent.com/u/1453152?v=3","gravatar_id":"","url":"https://api.github.com/users/hazelcast","html_url":"https://github.com/hazelcast","followers_url":"https://api.github.com/users/hazelcast/followers","following_url":"https://api.github.com/users/hazelcast/following{/other_user}","gists_url":"https://api.github.com/users/hazelcast/gists{/gist_id}","starred_url":"https://api.github.com/users/hazelcast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hazelcast/subscriptions","organizations_url":"https://api.github.com/users/hazelcast/orgs","repos_url":"https://api.github.com/users/hazelcast/repos","events_url":"https://api.github.com/users/hazelcast/events{/privacy}","received_events_url":"https://api.github.com/users/hazelcast/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/hazelcast/hazelcast","description":"Open Source In-Memory Data Grid","fork":false,"url":"https://api.github.com/repos/hazelcast/hazelcast","forks_url":"https://api.github.com/repos/hazelcast/hazelcast/forks","keys_url":"https://api.github.com/repos/hazelcast/hazelcast/keys{/key_id}","collaborators_url":"https://api.github.com/repos/hazelcast/hazelcast/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/hazelcast/hazelcast/teams","hooks_url":"https://api.github.com/repos/hazelcast/hazelcast/hooks","issue_events_url":"https://api.github.com/repos/hazelcast/hazelcast/issues/events{/number}","events_url":"https://api.github.com/repos/hazelcast/hazelcast/events","assignees_url":"https://api.github.com/repos/hazelcast/hazelcast/assignees{/user}","branches_url":"https://api.github.com/repos/hazelcast/hazelcast/branches{/branch}","tags_url":"https://api.github.com/repos/hazelcast/hazelcast/tags","blobs_url":"https://api.github.com/repos/hazelcast/hazelcast/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/hazelcast/hazelcast/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/hazelcast/hazelcast/git/refs{/sha}","trees_url":"https://api.github.com/repos/hazelcast/hazelcast/git/trees{/sha}","statuses_url":"https://api.github.com/repos/hazelcast/hazelcast/statuses/{sha}","languages_url":"https://api.github.com/repos/hazelcast/hazelcast/languages","stargazers_url":"https://api.github.com/repos/hazelcast/hazelcast/stargazers","contributors_url":"https://api.github.com/repos/hazelcast/hazelcast/contributors","subscribers_url":"https://api.github.com/repos/hazelcast/hazelcast/subscribers","subscription_url":"https://api.github.com/repos/hazelcast/hazelcast/subscription","commits_url":"https://api.github.com/repos/hazelcast/hazelcast/commits{/sha}","git_commits_url":"https://api.github.com/repos/hazelcast/hazelcast/git/commits{/sha}","comments_url":"https://api.github.com/repos/hazelcast/hazelcast/comments{/number}","issue_comment_url":"https://api.github.com/repos/hazelcast/hazelcast/issues/comments{/number}","contents_url":"https://api.github.com/repos/hazelcast/hazelcast/contents/{+path}","compare_url":"https://api.github.com/repos/hazelcast/hazelcast/compare/{base}...{head}","merges_url":"https://api.github.com/repos/hazelcast/hazelcast/merges","archive_url":"https://api.github.com/repos/hazelcast/hazelcast/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/hazelcast/hazelcast/downloads","issues_url":"https://api.github.com/repos/hazelcast/hazelcast/issues{/number}","pulls_url":"https://api.github.com/repos/hazelcast/hazelcast/pulls{/number}","milestones_url":"https://api.github.com/repos/hazelcast/hazelcast/milestones{/number}","notifications_url":"https://api.github.com/repos/hazelcast/hazelcast/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/hazelcast/hazelcast/labels{/name}","releases_url":"https://api.github.com/repos/hazelcast/hazelcast/releases{/id}","created_at":"2012-03-21T12:25:18Z","updated_at":"2015-11-15T14:48:30Z","pushed_at":"2015-11-16T11:35:25Z","git_url":"git://github.com/hazelcast/hazelcast.git","ssh_url":"git@github.com:hazelcast/hazelcast.git","clone_url":"https://github.com/hazelcast/hazelcast.git","svn_url":"https://github.com/hazelcast/hazelcast","homepage":"www.hazelcast.com","size":169507,"stargazers_count":1314,"watchers_count":1314,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":637,"mirror_url":null,"open_issues_count":514,"forks":637,"open_issues":514,"watchers":1314,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728"},"html":{"href":"https://github.com/hazelcast/hazelcast/pull/6728"},"issue":{"href":"https://api.github.com/repos/hazelcast/hazelcast/issues/6728"},"comments":{"href":"https://api.github.com/repos/hazelcast/hazelcast/issues/6728/comments"},"review_comments":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728/comments"},"review_comment":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/hazelcast/hazelcast/pulls/6728/commits"},"statuses":{"href":"https://api.github.com/repos/hazelcast/hazelcast/statuses/6439887e42e14e7b408bd80718bb9a498434d6f4"}}}}