001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * The contents of this file are subject to the terms of the Liferay Enterprise
005     * Subscription License ("License"). You may not use this file except in
006     * compliance with the License. You can obtain a copy of the License by
007     * contacting Liferay, Inc. See the License for the specific language governing
008     * permissions and limitations under the License, including but not limited to
009     * distribution rights of the Software.
010     *
011     *
012     *
013     */
014    
015    package com.liferay.portal.poller;
016    
017    import com.liferay.portal.NoSuchLayoutException;
018    import com.liferay.portal.kernel.json.JSONArray;
019    import com.liferay.portal.kernel.json.JSONFactoryUtil;
020    import com.liferay.portal.kernel.json.JSONObject;
021    import com.liferay.portal.kernel.log.Log;
022    import com.liferay.portal.kernel.log.LogFactoryUtil;
023    import com.liferay.portal.kernel.messaging.DestinationNames;
024    import com.liferay.portal.kernel.poller.PollerHeader;
025    import com.liferay.portal.kernel.poller.PollerProcessor;
026    import com.liferay.portal.kernel.poller.PollerRequest;
027    import com.liferay.portal.kernel.util.ContentTypes;
028    import com.liferay.portal.kernel.util.GetterUtil;
029    import com.liferay.portal.kernel.util.ParamUtil;
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.model.BrowserTracker;
034    import com.liferay.portal.model.Company;
035    import com.liferay.portal.service.BrowserTrackerLocalServiceUtil;
036    import com.liferay.portal.service.CompanyLocalServiceUtil;
037    import com.liferay.portal.util.PortalUtil;
038    import com.liferay.portal.util.PropsValues;
039    import com.liferay.util.Encryptor;
040    import com.liferay.util.servlet.ServletResponseUtil;
041    
042    import java.io.IOException;
043    
044    import java.util.HashMap;
045    import java.util.HashSet;
046    import java.util.Map;
047    import java.util.Set;
048    
049    import javax.servlet.ServletException;
050    import javax.servlet.http.HttpServlet;
051    import javax.servlet.http.HttpServletRequest;
052    import javax.servlet.http.HttpServletResponse;
053    
054    /**
055     * @author Brian Wing Shun Chan
056     */
057    public class PollerServlet extends HttpServlet {
058    
059            public void service(
060                            HttpServletRequest request, HttpServletResponse response)
061                    throws IOException, ServletException {
062    
063                    try {
064                            String content = getContent(request);
065    
066                            if (content == null) {
067                                    PortalUtil.sendError(
068                                            HttpServletResponse.SC_NOT_FOUND,
069                                            new NoSuchLayoutException(), request, response);
070                            }
071                            else {
072                                    response.setContentType(ContentTypes.TEXT_PLAIN_UTF8);
073    
074                                    ServletResponseUtil.write(
075                                            response, content.getBytes(StringPool.UTF8));
076                            }
077                    }
078                    catch (Exception e) {
079                            _log.error(e, e);
080    
081                            PortalUtil.sendError(
082                                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e, request,
083                                    response);
084                    }
085            }
086    
087            protected String getContent(HttpServletRequest request) throws Exception {
088                    String pollerRequestString = getPollerRequestString(request);
089    
090                    if (Validator.isNull(pollerRequestString)) {
091                            return null;
092                    }
093    
094                    Map<String, Object>[] pollerRequestChunks =
095                            (Map<String, Object>[])JSONFactoryUtil.deserialize(
096                                    pollerRequestString);
097    
098                    PollerHeader pollerHeader = getPollerHeader(pollerRequestChunks);
099    
100                    if (pollerHeader == null) {
101                            return null;
102                    }
103    
104                    boolean receiveRequest = isReceiveRequest(request);
105    
106                    JSONArray pollerResponseChunksJSON = null;
107                    Set<String> portletIdsWithChunks = null;
108    
109                    if (receiveRequest) {
110                            pollerResponseChunksJSON = JSONFactoryUtil.createJSONArray();
111                            portletIdsWithChunks = new HashSet<String>();
112    
113                            boolean suspendPolling = false;
114    
115                            if (pollerHeader.isStartPolling()) {
116                                    BrowserTrackerLocalServiceUtil.updateBrowserTracker(
117                                            pollerHeader.getUserId(), pollerHeader.getBrowserKey());
118                            }
119                            else {
120                                    BrowserTracker browserTracker =
121                                            BrowserTrackerLocalServiceUtil.getBrowserTracker(
122                                                    pollerHeader.getUserId(), pollerHeader.getBrowserKey());
123    
124                                    if (browserTracker.getBrowserKey() !=
125                                                    pollerHeader.getBrowserKey()) {
126    
127                                            suspendPolling = true;
128                                    }
129                            }
130    
131                            JSONObject pollerResponseChunkJSON =
132                                    JSONFactoryUtil.createJSONObject();
133    
134                            pollerResponseChunkJSON.put("userId", pollerHeader.getUserId());
135                            pollerResponseChunkJSON.put(
136                                    "initialRequest", pollerHeader.isInitialRequest());
137                            pollerResponseChunkJSON.put("suspendPolling", suspendPolling);
138    
139                            pollerResponseChunksJSON.put(pollerResponseChunkJSON);
140                    }
141    
142                    PollerRequestManager pollerRequestManager = new PollerRequestManager(
143                            pollerResponseChunksJSON, DestinationNames.POLLER,
144                            DestinationNames.POLLER_RESPONSE,
145                            PropsValues.POLLER_REQUEST_TIMEOUT);
146    
147                    for (int i = 1; i < pollerRequestChunks.length; i++) {
148                            Map<String, Object> pollerRequestChunk = pollerRequestChunks[i];
149    
150                            String portletId = (String)pollerRequestChunk.get("portletId");
151                            Map<String, String> parameterMap = getData(pollerRequestChunk);
152                            String chunkId = (String)pollerRequestChunk.get("chunkId");
153    
154                            try {
155                                    PollerRequest pollerRequest = process(
156                                            portletIdsWithChunks, pollerHeader, portletId, parameterMap,
157                                            chunkId, receiveRequest);
158    
159                                    pollerRequestManager.addPollerRequest(pollerRequest);
160                            }
161                            catch (Exception e) {
162                                    _log.error(e, e);
163                            }
164                    }
165    
166                    pollerRequestManager.processRequests();
167    
168                    if (!receiveRequest) {
169                            return StringPool.BLANK;
170                    }
171    
172                    pollerRequestManager.clearRequests();
173    
174                    for (String portletId : pollerHeader.getPortletIds()) {
175                            if (portletIdsWithChunks.contains(portletId)) {
176                                    continue;
177                            }
178    
179                            try {
180                                    PollerRequest pollerRequest = process(
181                                            portletIdsWithChunks, pollerHeader, portletId,
182                                            new HashMap<String, String>(), null, receiveRequest);
183    
184                                    pollerRequestManager.addPollerRequest(pollerRequest);
185                            }
186                            catch (Exception e) {
187                                    _log.error(e, e);
188                            }
189                    }
190    
191                    pollerRequestManager.processRequests();
192    
193                    pollerResponseChunksJSON = pollerRequestManager.getPollerResponse();
194    
195                    return pollerResponseChunksJSON.toString();
196            }
197    
198            protected Map<String, String> getData(
199                            Map<String, Object> pollerRequestChunk)
200                    throws Exception {
201    
202                    Map<String, Object> oldParameterMap =
203                            (Map<String, Object>)pollerRequestChunk.get("data");
204    
205                    Map<String, String> newParameterMap = new HashMap<String, String>();
206    
207                    if (oldParameterMap == null) {
208                            return newParameterMap;
209                    }
210    
211                    for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
212                            newParameterMap.put(
213                                    entry.getKey(), String.valueOf(entry.getValue()));
214                    }
215    
216                    return newParameterMap;
217            }
218    
219            protected PollerHeader getPollerHeader(
220                            Map<String, Object>[] pollerRequestChunks)
221                    throws Exception {
222    
223                    if (pollerRequestChunks.length < 1) {
224                            return null;
225                    }
226    
227                    Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
228    
229                    long companyId = GetterUtil.getLong(
230                            String.valueOf(pollerRequestChunk.get("companyId")));
231                    String userIdString = GetterUtil.getString(
232                            String.valueOf(pollerRequestChunk.get("userId")));
233                    long browserKey = GetterUtil.getLong(
234                            String.valueOf(pollerRequestChunk.get("browserKey")));
235                    String[] portletIds = StringUtil.split(
236                            String.valueOf(pollerRequestChunk.get("portletIds")));
237                    boolean initialRequest = GetterUtil.getBoolean(
238                            String.valueOf(pollerRequestChunk.get("initialRequest")));
239                    boolean startPolling = GetterUtil.getBoolean(
240                            String.valueOf(pollerRequestChunk.get("startPolling")));
241    
242                    long userId = getUserId(companyId, userIdString);
243    
244                    if (userId == 0) {
245                            return null;
246                    }
247    
248                    return new PollerHeader(
249                            companyId, userId, browserKey, portletIds, initialRequest,
250                            startPolling);
251            }
252    
253            protected String getPollerRequestString(HttpServletRequest request)
254                    throws Exception {
255    
256                    String pollerRequestString = ParamUtil.getString(
257                            request, "pollerRequest");
258    
259                    if (Validator.isNull(pollerRequestString)) {
260                            return null;
261                    }
262    
263                    return StringUtil.replace(
264                            pollerRequestString,
265                            new String[] {
266                                    StringPool.OPEN_CURLY_BRACE,
267                                    StringPool.CLOSE_CURLY_BRACE,
268                                    _ESCAPED_OPEN_CURLY_BRACE,
269                                    _ESCAPED_CLOSE_CURLY_BRACE
270                            },
271                            new String[] {
272                                    _OPEN_HASH_MAP_WRAPPER,
273                                    StringPool.DOUBLE_CLOSE_CURLY_BRACE,
274                                    StringPool.OPEN_CURLY_BRACE,
275                                    StringPool.CLOSE_CURLY_BRACE
276                            });
277            }
278    
279            protected long getUserId(long companyId, String userIdString) {
280                    long userId = 0;
281    
282                    try {
283                            Company company = CompanyLocalServiceUtil.getCompany(companyId);
284    
285                            userId = GetterUtil.getLong(
286                                    Encryptor.decrypt(company.getKeyObj(), userIdString));
287                    }
288                    catch (Exception e) {
289                            _log.error(
290                                    "Invalid credentials for company id " + companyId +
291                                            " and user id " + userIdString);
292                    }
293    
294                    return userId;
295            }
296    
297            protected boolean isReceiveRequest(HttpServletRequest request)
298                    throws Exception {
299    
300                    String path = GetterUtil.getString(request.getPathInfo());
301    
302                    if (path.endsWith(_PATH_RECEIVE)) {
303                            return true;
304                    }
305                    else {
306                            return false;
307                    }
308            }
309    
310            protected PollerRequest process(
311                            Set<String> portletIdsWithChunks, PollerHeader pollerHeader,
312                            String portletId, Map<String, String> parameterMap, String chunkId,
313                            boolean receiveRequest)
314                    throws Exception {
315    
316                    PollerProcessor pollerProcessor =
317                            PollerProcessorUtil.getPollerProcessor(portletId);
318    
319                    if (pollerProcessor == null) {
320                            _log.error("Poller processor not found for portlet " + portletId);
321    
322                            return null;
323                    }
324    
325                    PollerRequest pollerRequest = new PollerRequest(
326                            pollerHeader, portletId, parameterMap, chunkId, receiveRequest);
327    
328                    if (receiveRequest) {
329                            portletIdsWithChunks.add(portletId);
330                    }
331    
332                    return pollerRequest;
333            }
334    
335            private static final String _ESCAPED_CLOSE_CURLY_BRACE =
336                    "[$CLOSE_CURLY_BRACE$]";
337    
338            private static final String _ESCAPED_OPEN_CURLY_BRACE =
339                    "[$OPEN_CURLY_BRACE$]";
340    
341            private static final String _OPEN_HASH_MAP_WRAPPER =
342                    "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
343    
344            private static final String _PATH_RECEIVE = "/receive";
345    
346            private static Log _log = LogFactoryUtil.getLog(PollerServlet.class);
347    
348    }