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.poller;
016    
017    import com.liferay.portal.kernel.json.JSONFactoryUtil;
018    import com.liferay.portal.kernel.json.JSONObject;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.messaging.DestinationNames;
022    import com.liferay.portal.kernel.messaging.Message;
023    import com.liferay.portal.kernel.messaging.MessageBusUtil;
024    import com.liferay.portal.kernel.messaging.MessageListener;
025    import com.liferay.portal.kernel.poller.PollerHeader;
026    import com.liferay.portal.kernel.poller.PollerProcessor;
027    import com.liferay.portal.kernel.poller.PollerRequest;
028    import com.liferay.portal.kernel.poller.PollerResponse;
029    import com.liferay.portal.kernel.util.GetterUtil;
030    import com.liferay.portal.kernel.util.StringPool;
031    import com.liferay.portal.kernel.util.StringUtil;
032    import com.liferay.portal.kernel.util.Validator;
033    import com.liferay.portal.kernel.uuid.PortalUUIDUtil;
034    import com.liferay.portal.model.BrowserTracker;
035    import com.liferay.portal.model.Company;
036    import com.liferay.portal.service.BrowserTrackerLocalServiceUtil;
037    import com.liferay.portal.service.CompanyLocalServiceUtil;
038    import com.liferay.util.Encryptor;
039    
040    import java.util.ArrayList;
041    import java.util.HashMap;
042    import java.util.HashSet;
043    import java.util.List;
044    import java.util.Map;
045    import java.util.Set;
046    
047    import javax.servlet.http.HttpServletRequest;
048    
049    /**
050     * @author Michael C. Han
051     * @author Brian Wing Shun Chan
052     * @author Edward Han
053     */
054    public class PollerRequestHandlerImpl
055            implements PollerRequestHandler, MessageListener {
056    
057            @Override
058            public PollerHeader getPollerHeader(String pollerRequestString) {
059                    if (Validator.isNull(pollerRequestString)) {
060                            return null;
061                    }
062    
063                    Map<String, Object>[] pollerRequestChunks =
064                            parsePollerRequestParameters(pollerRequestString);
065    
066                    return parsePollerRequestHeader(pollerRequestChunks);
067            }
068    
069            @Override
070            public JSONObject processRequest(
071                            HttpServletRequest request, String pollerRequestString)
072                    throws Exception {
073    
074                    if (Validator.isNull(pollerRequestString)) {
075                            return null;
076                    }
077    
078                    Map<String, Object>[] pollerRequestChunks =
079                            parsePollerRequestParameters(pollerRequestString);
080    
081                    PollerHeader pollerHeader = parsePollerRequestHeader(
082                            pollerRequestChunks);
083    
084                    if (!isValidPollerHeader(pollerHeader)) {
085                            if (_log.isWarnEnabled()) {
086                                    _log.warn(
087                                            "Invalid poller header for request " + pollerRequestString);
088                            }
089    
090                            return null;
091                    }
092    
093                    boolean receiveRequest = isReceiveRequest(request.getPathInfo());
094    
095                    String pollerSessionId = getPollerSessionId(pollerHeader);
096    
097                    PollerSession pollerSession = null;
098    
099                    synchronized (_pollerSessions) {
100                            pollerSession = _pollerSessions.get(pollerSessionId);
101    
102                            if ((pollerSession == null) && receiveRequest) {
103                                    pollerSession = new PollerSession(pollerSessionId);
104    
105                                    _pollerSessions.put(pollerSessionId, pollerSession);
106                            }
107                    }
108    
109                    List<PollerRequest> pollerRequests = createPollerRequests(
110                            pollerHeader, pollerRequestChunks, receiveRequest);
111    
112                    executePollerRequests(pollerSession, pollerRequests);
113    
114                    if (receiveRequest) {
115                            return createPollerResponseHeader(pollerHeader);
116                    }
117                    else {
118                            return null;
119                    }
120            }
121    
122            @Override
123            public void receive(Message message) {
124                    Object messagePayload = message.getPayload();
125    
126                    if (!(messagePayload instanceof PollerResponse)) {
127                            return;
128                    }
129    
130                    PollerResponse pollerResponse = (PollerResponse)messagePayload;
131    
132                    PollerHeader pollerHeader = pollerResponse.getPollerHeader();
133    
134                    String pollerSessionId = getPollerSessionId(pollerHeader);
135    
136                    synchronized (_pollerSessions) {
137                            PollerSession pollerSession = _pollerSessions.get(pollerSessionId);
138    
139                            if ((pollerSession != null) &&
140                                    pollerSession.completePortletProcessing(
141                                            pollerResponse.getPortletId(), message.getResponseId())) {
142    
143                                    _pollerSessions.remove(pollerSessionId);
144                            }
145                    }
146            }
147    
148            protected PollerRequest createPollerRequest(
149                            PollerHeader pollerHeader, String portletId, boolean receiveRequest)
150                    throws Exception {
151    
152                    return createPollerRequest(
153                            pollerHeader, portletId, new HashMap<String, String>(), null,
154                            receiveRequest);
155            }
156    
157            protected PollerRequest createPollerRequest(
158                            PollerHeader pollerHeader, String portletId,
159                            Map<String, String> parameterMap, String chunkId,
160                            boolean receiveRequest)
161                    throws Exception {
162    
163                    PollerProcessor pollerProcessor =
164                            PollerProcessorUtil.getPollerProcessor(portletId);
165    
166                    if (pollerProcessor == null) {
167                            if (_log.isWarnEnabled()) {
168                                    _log.warn(
169                                            "Poller processor not found for portlet " + portletId);
170                            }
171    
172                            return null;
173                    }
174    
175                    return new PollerRequest(
176                            pollerHeader, portletId, parameterMap, chunkId, receiveRequest);
177            }
178    
179            protected List<PollerRequest> createPollerRequests(
180                            PollerHeader pollerHeader,
181                            Map<String, Object>[] pollerRequestChunks, boolean receiveRequest)
182                    throws Exception {
183    
184                    Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap();
185    
186                    List<PollerRequest> pollerRequests = new ArrayList<>(
187                            portletIdsMap.size());
188    
189                    Set<String> receiveRequestPortletIds = null;
190    
191                    if (receiveRequest) {
192                            receiveRequestPortletIds = new HashSet<>(
193                                    (int)(pollerRequestChunks.length / 0.75) + 1);
194                    }
195    
196                    for (int i = 1; i < pollerRequestChunks.length; i++) {
197                            Map<String, Object> pollerRequestChunk = pollerRequestChunks[i];
198    
199                            String portletId = (String)pollerRequestChunk.get("portletId");
200                            Map<String, String> parameterMap = parseData(pollerRequestChunk);
201                            String chunkId = (String)pollerRequestChunk.get("chunkId");
202    
203                            try {
204                                    PollerRequest pollerRequest = createPollerRequest(
205                                            pollerHeader, portletId, parameterMap, chunkId,
206                                            receiveRequest);
207    
208                                    pollerRequests.add(pollerRequest);
209    
210                                    if (receiveRequest) {
211                                            receiveRequestPortletIds.add(portletId);
212                                    }
213                            }
214                            catch (Exception e) {
215                                    _log.error(e, e);
216                            }
217                    }
218    
219                    if (receiveRequest) {
220                            Set<String> portletIds = portletIdsMap.keySet();
221    
222                            for (String portletId : portletIds) {
223                                    if (receiveRequestPortletIds.contains(portletId)) {
224                                            continue;
225                                    }
226    
227                                    try {
228                                            PollerRequest pollerRequest = createPollerRequest(
229                                                    pollerHeader, portletId, receiveRequest);
230    
231                                            pollerRequests.add(pollerRequest);
232                                    }
233                                    catch (Exception e) {
234                                            _log.error(e, e);
235                                    }
236                            }
237                    }
238    
239                    return pollerRequests;
240            }
241    
242            protected JSONObject createPollerResponseHeader(PollerHeader pollerHeader) {
243                    if (pollerHeader == null) {
244                            return null;
245                    }
246    
247                    boolean suspendPolling = false;
248    
249                    if (pollerHeader.isStartPolling()) {
250                            BrowserTrackerLocalServiceUtil.updateBrowserTracker(
251                                    pollerHeader.getUserId(), pollerHeader.getBrowserKey());
252                    }
253                    else {
254                            BrowserTracker browserTracker =
255                                    BrowserTrackerLocalServiceUtil.getBrowserTracker(
256                                            pollerHeader.getUserId(), pollerHeader.getBrowserKey());
257    
258                            if (browserTracker.getBrowserKey() !=
259                                            pollerHeader.getBrowserKey()) {
260    
261                                    suspendPolling = true;
262                            }
263                    }
264    
265                    JSONObject pollerResponseHeaderJSONObject =
266                            JSONFactoryUtil.createJSONObject();
267    
268                    pollerResponseHeaderJSONObject.put("userId", pollerHeader.getUserId());
269                    pollerResponseHeaderJSONObject.put("suspendPolling", suspendPolling);
270    
271                    return pollerResponseHeaderJSONObject;
272            }
273    
274            protected void executePollerRequests(
275                    PollerSession pollerSession, List<PollerRequest> pollerRequests) {
276    
277                    for (PollerRequest pollerRequest : pollerRequests) {
278                            String responseId = null;
279    
280                            if (pollerRequest.isReceiveRequest()) {
281                                    responseId = PortalUUIDUtil.generate();
282    
283                                    if (!pollerSession.beginPortletProcessing(
284                                                    pollerRequest, responseId)) {
285    
286                                            continue;
287                                    }
288                            }
289    
290                            Message message = new Message();
291    
292                            message.setPayload(pollerRequest);
293    
294                            if (pollerRequest.isReceiveRequest()) {
295                                    message.setResponseId(responseId);
296    
297                                    message.setResponseDestinationName(
298                                            DestinationNames.POLLER_RESPONSE);
299                            }
300    
301                            MessageBusUtil.sendMessage(DestinationNames.POLLER, message);
302                    }
303            }
304    
305            protected String fixPollerRequestString(String pollerRequestString) {
306                    if (Validator.isNull(pollerRequestString)) {
307                            return null;
308                    }
309    
310                    return StringUtil.replace(
311                            pollerRequestString,
312                            new String[] {
313                                    StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE,
314                                    _ESCAPED_OPEN_CURLY_BRACE, _ESCAPED_CLOSE_CURLY_BRACE
315                            },
316                            new String[] {
317                                    _OPEN_HASH_MAP_WRAPPER, StringPool.DOUBLE_CLOSE_CURLY_BRACE,
318                                    StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE
319                            });
320            }
321    
322            protected String getPollerSessionId(PollerHeader pollerHeader) {
323                    return String.valueOf(pollerHeader.getUserId());
324            }
325    
326            protected long getUserId(long companyId, String userIdString) {
327                    long userId = 0;
328    
329                    try {
330                            Company company = CompanyLocalServiceUtil.getCompany(companyId);
331    
332                            userId = GetterUtil.getLong(
333                                    Encryptor.decrypt(company.getKeyObj(), userIdString));
334                    }
335                    catch (Exception e) {
336                            _log.error(
337                                    "Invalid credentials for company id " + companyId +
338                                            " and user id " + userIdString);
339                    }
340    
341                    return userId;
342            }
343    
344            protected boolean isReceiveRequest(String path) {
345                    if ((path != null) && path.endsWith(_PATH_RECEIVE)) {
346                            return true;
347                    }
348                    else {
349                            return false;
350                    }
351            }
352    
353            protected boolean isValidPollerHeader(PollerHeader pollerHeader) {
354                    if (pollerHeader == null) {
355                            return false;
356                    }
357    
358                    Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap();
359    
360                    if ((portletIdsMap == null) || portletIdsMap.isEmpty()) {
361                            return false;
362                    }
363    
364                    return true;
365            }
366    
367            protected Map<String, String> parseData(
368                            Map<String, Object> pollerRequestChunk)
369                    throws Exception {
370    
371                    Map<String, Object> oldParameterMap =
372                            (Map<String, Object>)pollerRequestChunk.get("data");
373    
374                    Map<String, String> newParameterMap = new HashMap<>();
375    
376                    if (oldParameterMap == null) {
377                            return newParameterMap;
378                    }
379    
380                    for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
381                            newParameterMap.put(
382                                    entry.getKey(), String.valueOf(entry.getValue()));
383                    }
384    
385                    return newParameterMap;
386            }
387    
388            protected PollerHeader parsePollerRequestHeader(
389                    Map<String, Object>[] pollerRequestChunks) {
390    
391                    if ((pollerRequestChunks == null) || (pollerRequestChunks.length < 1)) {
392                            return null;
393                    }
394    
395                    Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
396    
397                    long browserKey = GetterUtil.getLong(
398                            String.valueOf(pollerRequestChunk.get("browserKey")));
399                    long companyId = GetterUtil.getLong(
400                            String.valueOf(pollerRequestChunk.get("companyId")));
401                    Map<String, Boolean> portletIdsMap =
402                            (Map<String, Boolean>)pollerRequestChunk.get("portletIdsMap");
403                    boolean startPolling = GetterUtil.getBoolean(
404                            String.valueOf(pollerRequestChunk.get("startPolling")));
405                    String userIdString = GetterUtil.getString(
406                            String.valueOf(pollerRequestChunk.get("userId")));
407    
408                    long userId = getUserId(companyId, userIdString);
409    
410                    if (userId == 0) {
411                            return null;
412                    }
413    
414                    return new PollerHeader(
415                            companyId, userId, browserKey, portletIdsMap, startPolling);
416            }
417    
418            protected Map<String, Object>[] parsePollerRequestParameters(
419                    String pollerRequestString) {
420    
421                    String fixedPollerRequestString = fixPollerRequestString(
422                            pollerRequestString);
423    
424                    return (Map<String, Object>[])JSONFactoryUtil.deserialize(
425                            fixedPollerRequestString);
426            }
427    
428            private static final String _ESCAPED_CLOSE_CURLY_BRACE =
429                    "[$CLOSE_CURLY_BRACE$]";
430    
431            private static final String _ESCAPED_OPEN_CURLY_BRACE =
432                    "[$OPEN_CURLY_BRACE$]";
433    
434            private static final String _OPEN_HASH_MAP_WRAPPER =
435                    "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
436    
437            private static final String _PATH_RECEIVE = "/receive";
438    
439            private static final Log _log = LogFactoryUtil.getLog(
440                    PollerRequestHandlerImpl.class);
441    
442            private final Map<String, PollerSession> _pollerSessions = new HashMap<>();
443    
444    }