001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.liveusers;
016    
017    import com.liferay.portal.kernel.cluster.ClusterExecutorUtil;
018    import com.liferay.portal.kernel.cluster.ClusterNode;
019    import com.liferay.portal.kernel.concurrent.ConcurrentHashSet;
020    import com.liferay.portal.kernel.dao.orm.QueryUtil;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.model.Group;
024    import com.liferay.portal.kernel.model.UserTracker;
025    import com.liferay.portal.kernel.service.GroupLocalServiceUtil;
026    import com.liferay.portal.kernel.service.UserTrackerLocalServiceUtil;
027    import com.liferay.portal.kernel.service.persistence.UserTrackerUtil;
028    import com.liferay.portal.kernel.servlet.PortalSessionContext;
029    import com.liferay.portal.kernel.util.Validator;
030    import com.liferay.portal.util.PropsValues;
031    
032    import java.util.ArrayList;
033    import java.util.Date;
034    import java.util.Iterator;
035    import java.util.LinkedHashMap;
036    import java.util.List;
037    import java.util.Map;
038    import java.util.Set;
039    import java.util.concurrent.ConcurrentHashMap;
040    
041    import javax.servlet.http.HttpSession;
042    
043    /**
044     * @author Charles May
045     * @author Brian Wing Shun Chan
046     */
047    public class LiveUsers {
048    
049            public static void addClusterNode(
050                    String clusterNodeId, Map<Long, Map<Long, Set<String>>> clusterUsers) {
051    
052                    _instance._addClusterNode(clusterNodeId, clusterUsers);
053            }
054    
055            public static void deleteGroup(long companyId, long groupId) {
056                    _instance._deleteGroup(companyId, groupId);
057            }
058    
059            public static Set<Long> getGroupUsers(long companyId, long groupId) {
060                    return _instance._getGroupUsers(
061                            _instance._getLiveUsers(companyId), groupId);
062            }
063    
064            public static int getGroupUsersCount(long companyId, long groupId) {
065                    return getGroupUsers(companyId, groupId).size();
066            }
067    
068            public static Map<Long, Map<Long, Set<String>>> getLocalClusterUsers() {
069                    return _instance._getLocalClusterUsers();
070            }
071    
072            public static Map<String, UserTracker> getSessionUsers(long companyId) {
073                    return _instance._getSessionUsers(companyId);
074            }
075    
076            public static int getSessionUsersCount(long companyId) {
077                    return getSessionUsers(companyId).size();
078            }
079    
080            public static UserTracker getUserTracker(long companyId, String sessionId) {
081                    return _instance._getUserTracker(companyId, sessionId);
082            }
083    
084            public static void joinGroup(long companyId, long groupId, long userId) {
085                    _instance._joinGroup(companyId, groupId, userId);
086            }
087    
088            public static void joinGroup(long companyId, long groupId, long[] userIds) {
089                    _instance._joinGroup(companyId, groupId, userIds);
090            }
091    
092            public static void leaveGroup(long companyId, long groupId, long userId) {
093                    _instance._leaveGroup(companyId, groupId, userId);
094            }
095    
096            public static void leaveGroup(
097                    long companyId, long groupId, long[] userIds) {
098    
099                    _instance._leaveGroup(companyId, groupId, userIds);
100            }
101    
102            public static void removeClusterNode(String clusterNodeId) {
103                    _instance._removeClusterNode(clusterNodeId);
104            }
105    
106            public static void signIn(
107                    String clusterNodeId, long companyId, long userId, String sessionId,
108                    String remoteAddr, String remoteHost, String userAgent) {
109    
110                    _instance._signIn(
111                            clusterNodeId, companyId, userId, sessionId, remoteAddr, remoteHost,
112                            userAgent);
113            }
114    
115            public static void signOut(
116                    String clusterNodeId, long companyId, long userId, String sessionId) {
117    
118                    _instance._signOut(clusterNodeId, companyId, userId, sessionId);
119            }
120    
121            private LiveUsers() {
122            }
123    
124            private void _addClusterNode(
125                    String clusterNodeId,
126                    Map<Long, Map<Long, Set<String>>> clusterUsers) {
127    
128                    if (Validator.isNull(clusterNodeId)) {
129                            return;
130                    }
131    
132                    for (Map.Entry<Long, Map<Long, Set<String>>> companyUsers :
133                                    clusterUsers.entrySet()) {
134    
135                            long companyId = companyUsers.getKey();
136                            Map<Long, Set<String>> userSessionsMap = companyUsers.getValue();
137    
138                            for (Map.Entry<Long, Set<String>> userSessions :
139                                            userSessionsMap.entrySet()) {
140    
141                                    long userId = userSessions.getKey();
142    
143                                    for (String sessionId : userSessions.getValue()) {
144                                            _signIn(
145                                                    clusterNodeId, companyId, userId, sessionId, null, null,
146                                                    null);
147                                    }
148                            }
149                    }
150            }
151    
152            private void _addClusterUser(
153                    String clusterNodeId, long companyId, long userId, String sessionId) {
154    
155                    if (Validator.isNull(clusterNodeId)) {
156                            return;
157                    }
158    
159                    Map<Long, Map<Long, Set<String>>> clusterUsers = _clusterUsers.get(
160                            clusterNodeId);
161    
162                    if (clusterUsers == null) {
163                            clusterUsers = new ConcurrentHashMap<>();
164    
165                            _clusterUsers.put(clusterNodeId, clusterUsers);
166                    }
167    
168                    Map<Long, Set<String>> companyUsers = clusterUsers.get(companyId);
169    
170                    if (companyUsers == null) {
171                            companyUsers = new ConcurrentHashMap<>();
172    
173                            clusterUsers.put(companyId, companyUsers);
174                    }
175    
176                    Set<String> userSessions = companyUsers.get(userId);
177    
178                    if (userSessions == null) {
179                            userSessions = new ConcurrentHashSet<>();
180    
181                            companyUsers.put(userId, userSessions);
182                    }
183    
184                    userSessions.add(sessionId);
185            }
186    
187            private void _addUserTracker(
188                    long companyId, long userId, UserTracker userTracker) {
189    
190                    List<UserTracker> userTrackers = _getUserTrackers(companyId, userId);
191    
192                    if (userTrackers != null) {
193                            userTrackers.add(userTracker);
194                    }
195                    else {
196                            userTrackers = new ArrayList<>();
197    
198                            userTrackers.add(userTracker);
199    
200                            Map<Long, List<UserTracker>> userTrackersMap = _getUserTrackersMap(
201                                    companyId);
202    
203                            userTrackersMap.put(userId, userTrackers);
204                    }
205            }
206    
207            private void _deleteGroup(long companyId, long groupId) {
208                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
209    
210                    liveUsers.remove(groupId);
211            }
212    
213            private Set<Long> _getGroupUsers(
214                    Map<Long, Set<Long>> liveUsers, long groupId) {
215    
216                    Set<Long> groupUsers = liveUsers.get(groupId);
217    
218                    if (groupUsers == null) {
219                            groupUsers = new ConcurrentHashSet<>();
220    
221                            liveUsers.put(groupId, groupUsers);
222                    }
223    
224                    return groupUsers;
225            }
226    
227            private Map<Long, Set<Long>> _getLiveUsers(long companyId) {
228                    Map<Long, Set<Long>> liveUsers = _liveUsers.get(companyId);
229    
230                    if (liveUsers == null) {
231                            liveUsers = new ConcurrentHashMap<>();
232    
233                            _liveUsers.put(companyId, liveUsers);
234                    }
235    
236                    return liveUsers;
237            }
238    
239            private Map<Long, Map<Long, Set<String>>> _getLocalClusterUsers() {
240                    ClusterNode clusterNode = ClusterExecutorUtil.getLocalClusterNode();
241    
242                    if (clusterNode == null) {
243                            return null;
244                    }
245    
246                    return _clusterUsers.get(clusterNode.getClusterNodeId());
247            }
248    
249            private Map<String, UserTracker> _getSessionUsers(long companyId) {
250                    Map<String, UserTracker> sessionUsers = _sessionUsers.get(companyId);
251    
252                    if (sessionUsers == null) {
253                            sessionUsers = new ConcurrentHashMap<>();
254    
255                            _sessionUsers.put(companyId, sessionUsers);
256                    }
257    
258                    return sessionUsers;
259            }
260    
261            private UserTracker _getUserTracker(long companyId, String sessionId) {
262                    Map<String, UserTracker> sessionUsers = _getSessionUsers(companyId);
263    
264                    return sessionUsers.get(sessionId);
265            }
266    
267            private List<UserTracker> _getUserTrackers(long companyId, long userId) {
268                    Map<Long, List<UserTracker>> userTrackersMap = _getUserTrackersMap(
269                            companyId);
270    
271                    return userTrackersMap.get(userId);
272            }
273    
274            private Map<Long, List<UserTracker>> _getUserTrackersMap(long companyId) {
275                    Map<Long, List<UserTracker>> userTrackersMap = _userTrackers.get(
276                            companyId);
277    
278                    if (userTrackersMap == null) {
279                            userTrackersMap = new ConcurrentHashMap<>();
280    
281                            _userTrackers.put(companyId, userTrackersMap);
282                    }
283    
284                    return userTrackersMap;
285            }
286    
287            private void _joinGroup(long companyId, long groupId, long userId) {
288                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
289    
290                    Set<Long> groupUsers = _getGroupUsers(liveUsers, groupId);
291    
292                    if (_getUserTrackers(companyId, userId) != null) {
293                            groupUsers.add(userId);
294                    }
295            }
296    
297            private void _joinGroup(long companyId, long groupId, long[] userIds) {
298                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
299    
300                    Set<Long> groupUsers = _getGroupUsers(liveUsers, groupId);
301    
302                    for (long userId : userIds) {
303                            if (_getUserTrackers(companyId, userId) != null) {
304                                    groupUsers.add(userId);
305                            }
306                    }
307            }
308    
309            private void _leaveGroup(long companyId, long userId, long groupId) {
310                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
311    
312                    Set<Long> groupUsers = _getGroupUsers(liveUsers, groupId);
313    
314                    groupUsers.remove(userId);
315            }
316    
317            private void _leaveGroup(long companyId, long groupId, long[] userIds) {
318                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
319    
320                    Set<Long> groupUsers = _getGroupUsers(liveUsers, groupId);
321    
322                    for (long userId : userIds) {
323                            groupUsers.remove(userId);
324                    }
325            }
326    
327            private void _removeClusterNode(String clusterNodeId) {
328                    if (Validator.isNull(clusterNodeId)) {
329                            return;
330                    }
331    
332                    Map<Long, Map<Long, Set<String>>> clusterUsers = _clusterUsers.remove(
333                            clusterNodeId);
334    
335                    if (clusterUsers == null) {
336                            return;
337                    }
338    
339                    for (Map.Entry<Long, Map<Long, Set<String>>> companyUsers :
340                                    clusterUsers.entrySet()) {
341    
342                            long companyId = companyUsers.getKey();
343                            Map<Long, Set<String>> userSessionsMap = companyUsers.getValue();
344    
345                            for (Map.Entry<Long, Set<String>> userSessions :
346                                            userSessionsMap.entrySet()) {
347    
348                                    long userId = userSessions.getKey();
349    
350                                    for (String sessionId : userSessions.getValue()) {
351                                            _signOut(clusterNodeId, companyId, userId, sessionId);
352                                    }
353                            }
354                    }
355            }
356    
357            private void _removeClusterUser(
358                    String clusterNodeId, long companyId, long userId, String sessionId) {
359    
360                    if (Validator.isNull(clusterNodeId)) {
361                            return;
362                    }
363    
364                    Map<Long, Map<Long, Set<String>>> clusterUsers = _clusterUsers.get(
365                            clusterNodeId);
366    
367                    if (clusterUsers == null) {
368                            return;
369                    }
370    
371                    Map<Long, Set<String>> companyUsers = clusterUsers.get(companyId);
372    
373                    if (companyUsers == null) {
374                            return;
375                    }
376    
377                    Set<String> userSessions = companyUsers.get(userId);
378    
379                    if (userSessions == null) {
380                            return;
381                    }
382    
383                    userSessions.remove(sessionId);
384            }
385    
386            private void _removeUserTracker(
387                    long companyId, long userId, UserTracker userTracker) {
388    
389                    List<UserTracker> userTrackers = _getUserTrackers(companyId, userId);
390    
391                    if (userTrackers == null) {
392                            return;
393                    }
394    
395                    String sessionId = userTracker.getSessionId();
396    
397                    Iterator<UserTracker> itr = userTrackers.iterator();
398    
399                    while (itr.hasNext()) {
400                            UserTracker curUserTracker = itr.next();
401    
402                            if (sessionId.equals(curUserTracker.getSessionId())) {
403                                    itr.remove();
404                            }
405                    }
406    
407                    if (userTrackers.isEmpty()) {
408                            Map<Long, List<UserTracker>> userTrackersMap = _getUserTrackersMap(
409                                    companyId);
410    
411                            userTrackersMap.remove(userId);
412                    }
413            }
414    
415            private void _signIn(
416                    String clusterNodeId, long companyId, long userId, String sessionId,
417                    String remoteAddr, String remoteHost, String userAgent) {
418    
419                    _addClusterUser(clusterNodeId, companyId, userId, sessionId);
420    
421                    _updateGroupStatus(companyId, userId, true);
422    
423                    Map<String, UserTracker> sessionUsers = _getSessionUsers(companyId);
424    
425                    UserTracker userTracker = sessionUsers.get(sessionId);
426    
427                    if ((userTracker == null) &&
428                            PropsValues.SESSION_TRACKER_MEMORY_ENABLED) {
429    
430                            userTracker = UserTrackerUtil.create(0);
431    
432                            userTracker.setCompanyId(companyId);
433                            userTracker.setUserId(userId);
434                            userTracker.setModifiedDate(new Date());
435                            userTracker.setSessionId(sessionId);
436                            userTracker.setRemoteAddr(remoteAddr);
437                            userTracker.setRemoteHost(remoteHost);
438                            userTracker.setUserAgent(userAgent);
439    
440                            sessionUsers.put(sessionId, userTracker);
441    
442                            _addUserTracker(companyId, userId, userTracker);
443                    }
444            }
445    
446            private void _signOut(
447                    String clusterNodeId, long companyId, long userId, String sessionId) {
448    
449                    _removeClusterUser(clusterNodeId, companyId, userId, sessionId);
450    
451                    List<UserTracker> userTrackers = _getUserTrackers(companyId, userId);
452    
453                    if ((userTrackers == null) || (userTrackers.size() <= 1)) {
454                            _updateGroupStatus(companyId, userId, false);
455                    }
456    
457                    Map<String, UserTracker> sessionUsers = _getSessionUsers(companyId);
458    
459                    UserTracker userTracker = sessionUsers.remove(sessionId);
460    
461                    if (userTracker == null) {
462                            return;
463                    }
464    
465                    try {
466                            UserTrackerLocalServiceUtil.addUserTracker(
467                                    userTracker.getCompanyId(), userTracker.getUserId(),
468                                    userTracker.getModifiedDate(), sessionId,
469                                    userTracker.getRemoteAddr(), userTracker.getRemoteHost(),
470                                    userTracker.getUserAgent(), userTracker.getPaths());
471                    }
472                    catch (Exception e) {
473                            if (_log.isWarnEnabled()) {
474                                    _log.warn(e.getMessage());
475                            }
476                    }
477    
478                    try {
479                            HttpSession session = PortalSessionContext.get(sessionId);
480    
481                            if (session != null) {
482                                    session.invalidate();
483                            }
484                    }
485                    catch (Exception e) {
486                    }
487    
488                    _removeUserTracker(companyId, userId, userTracker);
489            }
490    
491            private Map<Long, Set<Long>> _updateGroupStatus(
492                    long companyId, long userId, boolean signedIn) {
493    
494                    Map<Long, Set<Long>> liveUsers = _getLiveUsers(companyId);
495    
496                    LinkedHashMap<String, Object> groupParams = new LinkedHashMap<>();
497    
498                    groupParams.put("usersGroups", userId);
499    
500                    List<Group> groups = GroupLocalServiceUtil.search(
501                            companyId, null, null, groupParams, QueryUtil.ALL_POS,
502                            QueryUtil.ALL_POS);
503    
504                    for (Group group : groups) {
505                            Set<Long> groupUsers = _getGroupUsers(
506                                    liveUsers, group.getGroupId());
507    
508                            if (signedIn) {
509                                    groupUsers.add(userId);
510                            }
511                            else {
512                                    groupUsers.remove(userId);
513                            }
514                    }
515    
516                    return liveUsers;
517            }
518    
519            private static final Log _log = LogFactoryUtil.getLog(LiveUsers.class);
520    
521            private static final LiveUsers _instance = new LiveUsers();
522    
523            private final Map<String, Map<Long, Map<Long, Set<String>>>> _clusterUsers =
524                    new ConcurrentHashMap<>();
525            private final Map<Long, Map<Long, Set<Long>>> _liveUsers =
526                    new ConcurrentHashMap<>();
527            private final Map<Long, Map<String, UserTracker>> _sessionUsers =
528                    new ConcurrentHashMap<>();
529            private final Map<Long, Map<Long, List<UserTracker>>> _userTrackers =
530                    new ConcurrentHashMap<>();
531    
532    }