/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.poller; import com.liferay.portal.kernel.json.JSONFactoryUtil; import com.liferay.portal.kernel.json.JSONObject; import com.liferay.portal.kernel.log.Log; import com.liferay.portal.kernel.log.LogFactoryUtil; import com.liferay.portal.kernel.messaging.DestinationNames; import com.liferay.portal.kernel.messaging.Message; import com.liferay.portal.kernel.messaging.MessageBusUtil; import com.liferay.portal.kernel.messaging.MessageListener; import com.liferay.portal.kernel.model.BrowserTracker; import com.liferay.portal.kernel.model.Company; import com.liferay.portal.kernel.poller.PollerHeader; import com.liferay.portal.kernel.poller.PollerProcessor; import com.liferay.portal.kernel.poller.PollerRequest; import com.liferay.portal.kernel.poller.PollerResponse; import com.liferay.portal.kernel.service.BrowserTrackerLocalServiceUtil; import com.liferay.portal.kernel.service.CompanyLocalServiceUtil; import com.liferay.portal.kernel.util.GetterUtil; import com.liferay.portal.kernel.util.StringPool; import com.liferay.portal.kernel.util.StringUtil; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.kernel.uuid.PortalUUIDUtil; import com.liferay.util.Encryptor; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.http.HttpServletRequest; /** * @author Michael C. Han * @author Brian Wing Shun Chan * @author Edward Han */ public class PollerRequestHandlerImpl implements PollerRequestHandler, MessageListener { @Override public PollerHeader getPollerHeader(String pollerRequestString) { if (Validator.isNull(pollerRequestString)) { return null; } Map<String, Object>[] pollerRequestChunks = parsePollerRequestParameters(pollerRequestString); return parsePollerRequestHeader(pollerRequestChunks); } @Override public JSONObject processRequest( HttpServletRequest request, String pollerRequestString) throws Exception { if (Validator.isNull(pollerRequestString)) { return null; } Map<String, Object>[] pollerRequestChunks = parsePollerRequestParameters(pollerRequestString); PollerHeader pollerHeader = parsePollerRequestHeader( pollerRequestChunks); if (!isValidPollerHeader(pollerHeader)) { if (_log.isWarnEnabled()) { _log.warn( "Invalid poller header for request " + pollerRequestString); } return null; } boolean receiveRequest = isReceiveRequest(request.getPathInfo()); String pollerSessionId = getPollerSessionId(pollerHeader); PollerSession pollerSession = null; synchronized (_pollerSessions) { pollerSession = _pollerSessions.get(pollerSessionId); if ((pollerSession == null) && receiveRequest) { pollerSession = new PollerSession(pollerSessionId); _pollerSessions.put(pollerSessionId, pollerSession); } } List<PollerRequest> pollerRequests = createPollerRequests( pollerHeader, pollerRequestChunks, receiveRequest); executePollerRequests(pollerSession, pollerRequests); if (receiveRequest) { return createPollerResponseHeader(pollerHeader); } else { return null; } } @Override public void receive(Message message) { Object messagePayload = message.getPayload(); if (!(messagePayload instanceof PollerResponse)) { return; } PollerResponse pollerResponse = (PollerResponse)messagePayload; PollerHeader pollerHeader = pollerResponse.getPollerHeader(); String pollerSessionId = getPollerSessionId(pollerHeader); synchronized (_pollerSessions) { PollerSession pollerSession = _pollerSessions.get(pollerSessionId); if ((pollerSession != null) && pollerSession.completePortletProcessing( pollerResponse.getPortletId(), message.getResponseId())) { _pollerSessions.remove(pollerSessionId); } } } protected PollerRequest createPollerRequest( PollerHeader pollerHeader, String portletId, boolean receiveRequest) throws Exception { return createPollerRequest( pollerHeader, portletId, new HashMap<String, String>(), null, receiveRequest); } protected PollerRequest createPollerRequest( PollerHeader pollerHeader, String portletId, Map<String, String> parameterMap, String chunkId, boolean receiveRequest) throws Exception { PollerProcessor pollerProcessor = PollerProcessorUtil.getPollerProcessor(portletId); if (pollerProcessor == null) { if (_log.isWarnEnabled()) { _log.warn( "Poller processor not found for portlet " + portletId); } return null; } return new PollerRequest( pollerHeader, portletId, parameterMap, chunkId, receiveRequest); } protected List<PollerRequest> createPollerRequests( PollerHeader pollerHeader, Map<String, Object>[] pollerRequestChunks, boolean receiveRequest) throws Exception { Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap(); List<PollerRequest> pollerRequests = new ArrayList<>( portletIdsMap.size()); Set<String> receiveRequestPortletIds = null; if (receiveRequest) { receiveRequestPortletIds = new HashSet<>( (int)(pollerRequestChunks.length / 0.75) + 1); } for (int i = 1; i < pollerRequestChunks.length; i++) { Map<String, Object> pollerRequestChunk = pollerRequestChunks[i]; String portletId = (String)pollerRequestChunk.get("portletId"); Map<String, String> parameterMap = parseData(pollerRequestChunk); String chunkId = (String)pollerRequestChunk.get("chunkId"); try { PollerRequest pollerRequest = createPollerRequest( pollerHeader, portletId, parameterMap, chunkId, receiveRequest); pollerRequests.add(pollerRequest); if (receiveRequest) { receiveRequestPortletIds.add(portletId); } } catch (Exception e) { _log.error(e, e); } } if (receiveRequest) { Set<String> portletIds = portletIdsMap.keySet(); for (String portletId : portletIds) { if (receiveRequestPortletIds.contains(portletId)) { continue; } try { PollerRequest pollerRequest = createPollerRequest( pollerHeader, portletId, receiveRequest); pollerRequests.add(pollerRequest); } catch (Exception e) { _log.error(e, e); } } } return pollerRequests; } protected JSONObject createPollerResponseHeader(PollerHeader pollerHeader) { if (pollerHeader == null) { return null; } boolean suspendPolling = false; if (pollerHeader.isStartPolling()) { BrowserTrackerLocalServiceUtil.updateBrowserTracker( pollerHeader.getUserId(), pollerHeader.getBrowserKey()); } else { BrowserTracker browserTracker = BrowserTrackerLocalServiceUtil.getBrowserTracker( pollerHeader.getUserId(), pollerHeader.getBrowserKey()); if (browserTracker.getBrowserKey() != pollerHeader.getBrowserKey()) { suspendPolling = true; } } JSONObject pollerResponseHeaderJSONObject = JSONFactoryUtil.createJSONObject(); pollerResponseHeaderJSONObject.put("suspendPolling", suspendPolling); pollerResponseHeaderJSONObject.put("userId", pollerHeader.getUserId()); return pollerResponseHeaderJSONObject; } protected void executePollerRequests( PollerSession pollerSession, List<PollerRequest> pollerRequests) { for (PollerRequest pollerRequest : pollerRequests) { if (pollerRequest == null) { continue; } String responseId = null; if (pollerRequest.isReceiveRequest()) { responseId = PortalUUIDUtil.generate(); if (!pollerSession.beginPortletProcessing( pollerRequest, responseId)) { continue; } } Message message = new Message(); message.setPayload(pollerRequest); if (pollerRequest.isReceiveRequest()) { message.setResponseId(responseId); message.setResponseDestinationName( DestinationNames.POLLER_RESPONSE); } MessageBusUtil.sendMessage(DestinationNames.POLLER, message); } } protected String fixPollerRequestString(String pollerRequestString) { if (Validator.isNull(pollerRequestString)) { return null; } return StringUtil.replace( pollerRequestString, new String[] { StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE, _ESCAPED_OPEN_CURLY_BRACE, _ESCAPED_CLOSE_CURLY_BRACE }, new String[] { _OPEN_HASH_MAP_WRAPPER, StringPool.DOUBLE_CLOSE_CURLY_BRACE, StringPool.OPEN_CURLY_BRACE, StringPool.CLOSE_CURLY_BRACE }); } protected String getPollerSessionId(PollerHeader pollerHeader) { return String.valueOf(pollerHeader.getUserId()); } protected long getUserId(long companyId, String userIdString) { long userId = 0; try { Company company = CompanyLocalServiceUtil.getCompany(companyId); userId = GetterUtil.getLong( Encryptor.decrypt(company.getKeyObj(), userIdString)); } catch (Exception e) { _log.error( "Invalid credentials for company id " + companyId + " and user id " + userIdString); } return userId; } protected boolean isReceiveRequest(String path) { if ((path != null) && path.endsWith(_PATH_RECEIVE)) { return true; } else { return false; } } protected boolean isValidPollerHeader(PollerHeader pollerHeader) { if (pollerHeader == null) { return false; } Map<String, Boolean> portletIdsMap = pollerHeader.getPortletIdsMap(); if ((portletIdsMap == null) || portletIdsMap.isEmpty()) { return false; } return true; } protected Map<String, String> parseData( Map<String, Object> pollerRequestChunk) throws Exception { Map<String, Object> oldParameterMap = (Map<String, Object>)pollerRequestChunk.get("data"); Map<String, String> newParameterMap = new HashMap<>(); if (oldParameterMap == null) { return newParameterMap; } for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) { newParameterMap.put( entry.getKey(), String.valueOf(entry.getValue())); } return newParameterMap; } protected PollerHeader parsePollerRequestHeader( Map<String, Object>[] pollerRequestChunks) { if ((pollerRequestChunks == null) || (pollerRequestChunks.length < 1)) { return null; } Map<String, Object> pollerRequestChunk = pollerRequestChunks[0]; long browserKey = GetterUtil.getLong( String.valueOf(pollerRequestChunk.get("browserKey"))); long companyId = GetterUtil.getLong( String.valueOf(pollerRequestChunk.get("companyId"))); Map<String, Boolean> portletIdsMap = (Map<String, Boolean>)pollerRequestChunk.get("portletIdsMap"); boolean startPolling = GetterUtil.getBoolean( String.valueOf(pollerRequestChunk.get("startPolling"))); String userIdString = GetterUtil.getString( String.valueOf(pollerRequestChunk.get("userId"))); long userId = getUserId(companyId, userIdString); if (userId == 0) { return null; } return new PollerHeader( companyId, userId, browserKey, portletIdsMap, startPolling); } protected Map<String, Object>[] parsePollerRequestParameters( String pollerRequestString) { String fixedPollerRequestString = fixPollerRequestString( pollerRequestString); return (Map<String, Object>[])JSONFactoryUtil.deserialize( fixedPollerRequestString); } private static final String _ESCAPED_CLOSE_CURLY_BRACE = "[$CLOSE_CURLY_BRACE$]"; private static final String _ESCAPED_OPEN_CURLY_BRACE = "[$OPEN_CURLY_BRACE$]"; private static final String _OPEN_HASH_MAP_WRAPPER = "{\"javaClass\":\"java.util.HashMap\",\"map\":{"; private static final String _PATH_RECEIVE = "/receive"; private static final Log _log = LogFactoryUtil.getLog( PollerRequestHandlerImpl.class); private final Map<String, PollerSession> _pollerSessions = new HashMap<>(); }