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