001    /**
002     * Copyright (c) 2000-2012 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            public PollerHeader getPollerHeader(String pollerRequestString) {
060                    if (Validator.isNull(pollerRequestString)) {
061                            return null;
062                    }
063    
064                    Map<String, Object>[] pollerRequestChunks =
065                            parsePollerRequestParameters(pollerRequestString);
066    
067                    return parsePollerRequestHeader(pollerRequestChunks);
068            }
069    
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                            request, 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            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                            HttpServletRequest request, boolean receiveRequest,
150                            PollerHeader pollerHeader, String portletId)
151                    throws Exception {
152    
153                    return createPollerRequest(
154                            request, receiveRequest, pollerHeader, portletId,
155                            new HashMap<String, String>(), null);
156            }
157    
158            protected PollerRequest createPollerRequest(
159                            HttpServletRequest request, boolean receiveRequest,
160                            PollerHeader pollerHeader, String portletId,
161                            Map<String, String> parameterMap, String chunkId)
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                            request, pollerHeader, portletId, parameterMap, chunkId,
178                            receiveRequest);
179            }
180    
181            protected List<PollerRequest> createPollerRequests(
182                            HttpServletRequest request, PollerHeader pollerHeader,
183                            Map<String, Object>[] pollerRequestChunks, boolean receiveRequest)
184                    throws Exception {
185    
186                    Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap();
187    
188                    List<PollerRequest> pollerRequests = new ArrayList<PollerRequest>(
189                            portletIdsMap.size());
190    
191                    Set<String> receiveRequestPortletIds = null;
192    
193                    if (receiveRequest) {
194                            receiveRequestPortletIds = new HashSet<String>(
195                                    (int)(pollerRequestChunks.length / 0.75) + 1);
196                    }
197    
198                    for (int i = 1; i < pollerRequestChunks.length; i++) {
199                            Map<String, Object> pollerRequestChunk = pollerRequestChunks[i];
200    
201                            String portletId = (String)pollerRequestChunk.get("portletId");
202                            Map<String, String> parameterMap = parseData(pollerRequestChunk);
203                            String chunkId = (String)pollerRequestChunk.get("chunkId");
204    
205                            try {
206                                    PollerRequest pollerRequest = createPollerRequest(
207                                            request, receiveRequest, pollerHeader, portletId,
208                                            parameterMap, chunkId);
209    
210                                    pollerRequests.add(pollerRequest);
211    
212                                    if (receiveRequest) {
213                                            receiveRequestPortletIds.add(portletId);
214                                    }
215                            }
216                            catch (Exception e) {
217                                    _log.error(e, e);
218                            }
219                    }
220    
221                    if (receiveRequest) {
222                            Set<String> portletIds = portletIdsMap.keySet();
223    
224                            for (String portletId : portletIds) {
225                                    if (receiveRequestPortletIds.contains(portletId)) {
226                                            continue;
227                                    }
228    
229                                    try {
230                                            PollerRequest pollerRequest = createPollerRequest(
231                                                    request, receiveRequest, pollerHeader, portletId);
232    
233                                            pollerRequests.add(pollerRequest);
234                                    }
235                                    catch (Exception e) {
236                                            _log.error(e, e);
237                                    }
238                            }
239                    }
240    
241                    return pollerRequests;
242            }
243    
244            protected JSONObject createPollerResponseHeader(PollerHeader pollerHeader)
245                    throws SystemException {
246    
247                    if (pollerHeader == null) {
248                            return null;
249                    }
250    
251                    boolean suspendPolling = false;
252    
253                    if (pollerHeader.isStartPolling()) {
254                            BrowserTrackerLocalServiceUtil.updateBrowserTracker(
255                                    pollerHeader.getUserId(), pollerHeader.getBrowserKey());
256                    }
257                    else {
258                            BrowserTracker browserTracker =
259                                    BrowserTrackerLocalServiceUtil.getBrowserTracker(
260                                            pollerHeader.getUserId(), pollerHeader.getBrowserKey());
261    
262                            if (browserTracker.getBrowserKey() !=
263                                    pollerHeader.getBrowserKey()) {
264    
265                                    suspendPolling = true;
266                            }
267                    }
268    
269                    JSONObject pollerResponseHeaderJSONObject =
270                            JSONFactoryUtil.createJSONObject();
271    
272                    pollerResponseHeaderJSONObject.put("userId", pollerHeader.getUserId());
273                    pollerResponseHeaderJSONObject.put("suspendPolling", suspendPolling);
274    
275                    return pollerResponseHeaderJSONObject;
276            }
277    
278            protected void executePollerRequests(
279                    PollerSession pollerSession, List<PollerRequest> pollerRequests) {
280    
281                    for (PollerRequest pollerRequest : pollerRequests) {
282                            PollerRequestResponsePair pollerRequestResponsePair =
283                                    new PollerRequestResponsePair(pollerRequest);
284    
285                            String responseId = null;
286    
287                            if (pollerRequest.isReceiveRequest()) {
288                                    responseId = PortalUUIDUtil.generate();
289    
290                                    PollerResponse pollerResponse = new DefaultPollerResponse(
291                                            pollerRequest.getPollerHeader(),
292                                            pollerRequest.getPortletId(), pollerRequest.getChunkId());
293    
294                                    pollerRequestResponsePair.setPollerResponse(pollerResponse);
295    
296                                    if (!pollerSession.beginPortletProcessing(
297                                                    pollerRequestResponsePair, responseId)) {
298    
299                                            continue;
300                                    }
301                            }
302    
303                            Message message = new Message();
304    
305                            message.setPayload(pollerRequestResponsePair);
306    
307                            if (pollerRequest.isReceiveRequest()) {
308                                    message.setResponseId(responseId);
309    
310                                    message.setResponseDestinationName(
311                                            DestinationNames.POLLER_RESPONSE);
312                            }
313    
314                            MessageBusUtil.sendMessage(DestinationNames.POLLER, message);
315                    }
316            }
317    
318            protected String fixPollerRequestString(String pollerRequestString) {
319                    if (Validator.isNull(pollerRequestString)) {
320                            return null;
321                    }
322    
323                    return StringUtil.replace(
324                            pollerRequestString,
325                            new String[] {
326                                    StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE,
327                                    _ESCAPED_OPEN_CURLY_BRACE, _ESCAPED_CLOSE_CURLY_BRACE
328                            },
329                            new String[] {
330                                    _OPEN_HASH_MAP_WRAPPER, StringPool.DOUBLE_CLOSE_CURLY_BRACE,
331                                    StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE
332                            });
333            }
334    
335            protected String getPollerSessionId(PollerHeader pollerHeader) {
336                    return String.valueOf(pollerHeader.getUserId());
337            }
338    
339            protected long getUserId(long companyId, String userIdString) {
340                    long userId = 0;
341    
342                    try {
343                            Company company = CompanyLocalServiceUtil.getCompany(companyId);
344    
345                            userId = GetterUtil.getLong(
346                                    Encryptor.decrypt(company.getKeyObj(), userIdString));
347                    }
348                    catch (Exception e) {
349                            _log.error(
350                                    "Invalid credentials for company id " + companyId +
351                                            " and user id " + userIdString);
352                    }
353    
354                    return userId;
355            }
356    
357            protected boolean isReceiveRequest(String path) {
358                    if ((path != null) && path.endsWith(_PATH_RECEIVE)) {
359                            return true;
360                    }
361                    else {
362                            return false;
363                    }
364            }
365    
366            protected boolean isValidPollerHeader(PollerHeader pollerHeader) {
367                    if (pollerHeader == null) {
368                            return false;
369                    }
370    
371                    Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap();
372    
373                    if ((portletIdsMap == null) || portletIdsMap.isEmpty()) {
374                            return false;
375                    }
376    
377                    return true;
378            }
379    
380            protected Map<String, String> parseData(
381                            Map<String, Object> pollerRequestChunk)
382                    throws Exception {
383    
384                    Map<String, Object> oldParameterMap =
385                            (Map<String, Object>)pollerRequestChunk.get("data");
386    
387                    Map<String, String> newParameterMap = new HashMap<String, String>();
388    
389                    if (oldParameterMap == null) {
390                            return newParameterMap;
391                    }
392    
393                    for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
394                            newParameterMap.put(
395                                    entry.getKey(), String.valueOf(entry.getValue()));
396                    }
397    
398                    return newParameterMap;
399            }
400    
401            protected PollerHeader parsePollerRequestHeader(
402                    Map<String, Object>[] pollerRequestChunks) {
403    
404                    if ((pollerRequestChunks == null) || (pollerRequestChunks.length < 1)) {
405                            return null;
406                    }
407    
408                    Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
409    
410                    long browserKey = GetterUtil.getLong(
411                            String.valueOf(pollerRequestChunk.get("browserKey")));
412                    long companyId = GetterUtil.getLong(
413                            String.valueOf(pollerRequestChunk.get("companyId")));
414                    Map<String, Boolean> portletIdsMap =
415                            (Map<String, Boolean>)pollerRequestChunk.get("portletIdsMap");
416                    boolean startPolling = GetterUtil.getBoolean(
417                            String.valueOf(pollerRequestChunk.get("startPolling")));
418                    String userIdString = GetterUtil.getString(
419                            String.valueOf(pollerRequestChunk.get("userId")));
420    
421                    long userId = getUserId(companyId, userIdString);
422    
423                    if (userId == 0) {
424                            return null;
425                    }
426    
427                    return new PollerHeader(
428                            companyId, userId, browserKey, portletIdsMap, startPolling);
429            }
430    
431            protected Map<String, Object>[] parsePollerRequestParameters(
432                    String pollerRequestString) {
433    
434                    String fixedPollerRequestString = fixPollerRequestString(
435                            pollerRequestString);
436    
437                    return (Map<String, Object>[])JSONFactoryUtil.deserialize(
438                            fixedPollerRequestString);
439            }
440    
441            private static final String _ESCAPED_CLOSE_CURLY_BRACE =
442                    "[$CLOSE_CURLY_BRACE$]";
443    
444            private static final String _ESCAPED_OPEN_CURLY_BRACE =
445                    "[$OPEN_CURLY_BRACE$]";
446    
447            private static final String _OPEN_HASH_MAP_WRAPPER =
448                    "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
449    
450            private static final String _PATH_RECEIVE = "/receive";
451    
452            private static Log _log = LogFactoryUtil.getLog(
453                    PollerRequestHandlerImpl.class);
454    
455            private Map<String, PollerSession> _pollerSessions =
456                    new HashMap<String, PollerSession>();
457    
458    }