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