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