1   /**
2    * Copyright (c) 2000-2009 Liferay, Inc. All rights reserved.
3    *
4    * Permission is hereby granted, free of charge, to any person obtaining a copy
5    * of this software and associated documentation files (the "Software"), to deal
6    * in the Software without restriction, including without limitation the rights
7    * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    * copies of the Software, and to permit persons to whom the Software is
9    * furnished to do so, subject to the following conditions:
10   *
11   * The above copyright notice and this permission notice shall be included in
12   * all copies or substantial portions of the Software.
13   *
14   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20   * SOFTWARE.
21   */
22  
23  package com.liferay.portal.poller;
24  
25  import com.liferay.portal.NoSuchLayoutException;
26  import com.liferay.portal.kernel.json.JSONArray;
27  import com.liferay.portal.kernel.json.JSONFactoryUtil;
28  import com.liferay.portal.kernel.json.JSONObject;
29  import com.liferay.portal.kernel.log.Log;
30  import com.liferay.portal.kernel.log.LogFactoryUtil;
31  import com.liferay.portal.kernel.messaging.DestinationNames;
32  import com.liferay.portal.kernel.poller.PollerHeader;
33  import com.liferay.portal.kernel.poller.PollerProcessor;
34  import com.liferay.portal.kernel.poller.PollerRequest;
35  import com.liferay.portal.kernel.util.ContentTypes;
36  import com.liferay.portal.kernel.util.GetterUtil;
37  import com.liferay.portal.kernel.util.ParamUtil;
38  import com.liferay.portal.kernel.util.StringPool;
39  import com.liferay.portal.kernel.util.StringUtil;
40  import com.liferay.portal.kernel.util.Validator;
41  import com.liferay.portal.model.BrowserTracker;
42  import com.liferay.portal.model.Company;
43  import com.liferay.portal.service.BrowserTrackerLocalServiceUtil;
44  import com.liferay.portal.service.CompanyLocalServiceUtil;
45  import com.liferay.portal.util.PortalUtil;
46  import com.liferay.portal.util.PropsValues;
47  import com.liferay.util.Encryptor;
48  import com.liferay.util.servlet.ServletResponseUtil;
49  
50  import java.io.IOException;
51  
52  import java.util.HashMap;
53  import java.util.HashSet;
54  import java.util.Map;
55  import java.util.Set;
56  
57  import javax.servlet.ServletException;
58  import javax.servlet.http.HttpServlet;
59  import javax.servlet.http.HttpServletRequest;
60  import javax.servlet.http.HttpServletResponse;
61  
62  /**
63   * <a href="PollerServlet.java.html"><b><i>View Source</i></b></a>
64   *
65   * @author Brian Wing Shun Chan
66   */
67  public class PollerServlet extends HttpServlet {
68  
69      public void service(
70              HttpServletRequest request, HttpServletResponse response)
71          throws IOException, ServletException {
72  
73          try {
74              String content = getContent(request);
75  
76              if (content == null) {
77                  PortalUtil.sendError(
78                      HttpServletResponse.SC_NOT_FOUND,
79                      new NoSuchLayoutException(), request, response);
80              }
81              else {
82                  response.setContentType(ContentTypes.TEXT_PLAIN_UTF8);
83  
84                  ServletResponseUtil.write(
85                      response, content.getBytes(StringPool.UTF8));
86              }
87          }
88          catch (Exception e) {
89              _log.error(e, e);
90  
91              PortalUtil.sendError(
92                  HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e, request,
93                  response);
94          }
95      }
96  
97      protected String getContent(HttpServletRequest request) throws Exception {
98          String pollerRequestString = getPollerRequestString(request);
99  
100         if (Validator.isNull(pollerRequestString)) {
101             return null;
102         }
103 
104         Map<String, Object>[] pollerRequestChunks =
105             (Map<String, Object>[])JSONFactoryUtil.deserialize(
106                 pollerRequestString);
107 
108         PollerHeader pollerHeader = getPollerHeader(pollerRequestChunks);
109 
110         if (pollerHeader == null) {
111             return null;
112         }
113 
114         boolean receiveRequest = isReceiveRequest(request);
115 
116         JSONArray pollerResponseChunksJSON = null;
117         Set<String> portletIdsWithChunks = null;
118 
119         if (receiveRequest) {
120             pollerResponseChunksJSON = JSONFactoryUtil.createJSONArray();
121             portletIdsWithChunks = new HashSet<String>();
122 
123             boolean suspendPolling = false;
124 
125             if (pollerHeader.isStartPolling()) {
126                 BrowserTrackerLocalServiceUtil.updateBrowserTracker(
127                     pollerHeader.getUserId(), pollerHeader.getBrowserKey());
128             }
129             else {
130                 BrowserTracker browserTracker =
131                     BrowserTrackerLocalServiceUtil.getBrowserTracker(
132                         pollerHeader.getUserId(), pollerHeader.getBrowserKey());
133 
134                 if (browserTracker.getBrowserKey() !=
135                         pollerHeader.getBrowserKey()) {
136 
137                     suspendPolling = true;
138                 }
139             }
140 
141             JSONObject pollerResponseChunkJSON =
142                 JSONFactoryUtil.createJSONObject();
143 
144             pollerResponseChunkJSON.put("userId", pollerHeader.getUserId());
145             pollerResponseChunkJSON.put(
146                 "initialRequest", pollerHeader.isInitialRequest());
147             pollerResponseChunkJSON.put("suspendPolling", suspendPolling);
148 
149             pollerResponseChunksJSON.put(pollerResponseChunkJSON);
150         }
151 
152         PollerRequestManager pollerRequestManager = new PollerRequestManager(
153             pollerResponseChunksJSON, DestinationNames.POLLER,
154             DestinationNames.POLLER_RESPONSE,
155             PropsValues.POLLER_REQUEST_TIMEOUT);
156 
157         for (int i = 1; i < pollerRequestChunks.length; i++) {
158             Map<String, Object> pollerRequestChunk = pollerRequestChunks[i];
159 
160             String portletId = (String)pollerRequestChunk.get("portletId");
161             Map<String, String> parameterMap = getData(pollerRequestChunk);
162             String chunkId = (String)pollerRequestChunk.get("chunkId");
163 
164             try {
165                 PollerRequest pollerRequest = process(
166                     portletIdsWithChunks, pollerHeader, portletId, parameterMap,
167                     chunkId, receiveRequest);
168 
169                 pollerRequestManager.addPollerRequest(pollerRequest);
170             }
171             catch (Exception e) {
172                 _log.error(e, e);
173             }
174         }
175 
176         pollerRequestManager.processRequests();
177 
178         if (!receiveRequest) {
179             return StringPool.BLANK;
180         }
181 
182         pollerRequestManager.clearRequests();
183 
184         for (String portletId : pollerHeader.getPortletIds()) {
185             if (portletIdsWithChunks.contains(portletId)) {
186                 continue;
187             }
188 
189             try {
190                 PollerRequest pollerRequest = process(
191                     portletIdsWithChunks, pollerHeader, portletId,
192                     new HashMap<String, String>(), null, receiveRequest);
193 
194                 pollerRequestManager.addPollerRequest(pollerRequest);
195             }
196             catch (Exception e) {
197                 _log.error(e, e);
198             }
199         }
200 
201         pollerRequestManager.processRequests();
202 
203         pollerResponseChunksJSON = pollerRequestManager.getPollerResponse();
204 
205         return pollerResponseChunksJSON.toString();
206     }
207 
208     protected Map<String, String> getData(
209             Map<String, Object> pollerRequestChunk)
210         throws Exception {
211 
212         Map<String, Object> oldParameterMap =
213             (Map<String, Object>)pollerRequestChunk.get("data");
214 
215         Map<String, String> newParameterMap = new HashMap<String, String>();
216 
217         if (oldParameterMap == null) {
218             return newParameterMap;
219         }
220 
221         for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
222             newParameterMap.put(
223                 entry.getKey(), String.valueOf(entry.getValue()));
224         }
225 
226         return newParameterMap;
227     }
228 
229     protected PollerHeader getPollerHeader(
230             Map<String, Object>[] pollerRequestChunks)
231         throws Exception {
232 
233         if (pollerRequestChunks.length < 1) {
234             return null;
235         }
236 
237         Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
238 
239         long companyId = GetterUtil.getLong(
240             String.valueOf(pollerRequestChunk.get("companyId")));
241         String userIdString = GetterUtil.getString(
242             String.valueOf(pollerRequestChunk.get("userId")));
243         long browserKey = GetterUtil.getLong(
244             String.valueOf(pollerRequestChunk.get("browserKey")));
245         String[] portletIds = StringUtil.split(
246             String.valueOf(pollerRequestChunk.get("portletIds")));
247         boolean initialRequest = GetterUtil.getBoolean(
248             String.valueOf(pollerRequestChunk.get("initialRequest")));
249         boolean startPolling = GetterUtil.getBoolean(
250             String.valueOf(pollerRequestChunk.get("startPolling")));
251 
252         long userId = getUserId(companyId, userIdString);
253 
254         if (userId == 0) {
255             return null;
256         }
257 
258         return new PollerHeader(
259             userId, browserKey, portletIds, initialRequest, startPolling);
260     }
261 
262     protected String getPollerRequestString(HttpServletRequest request)
263         throws Exception {
264 
265         String pollerRequestString = ParamUtil.getString(
266             request, "pollerRequest");
267 
268         if (Validator.isNull(pollerRequestString)) {
269             return null;
270         }
271 
272         return StringUtil.replace(
273             pollerRequestString,
274             new String[] {
275                 StringPool.OPEN_CURLY_BRACE,
276                 StringPool.CLOSE_CURLY_BRACE,
277                 _ESCAPED_OPEN_CURLY_BRACE,
278                 _ESCAPED_CLOSE_CURLY_BRACE
279             },
280             new String[] {
281                 _OPEN_HASH_MAP_WRAPPER,
282                 StringPool.DOUBLE_CLOSE_CURLY_BRACE,
283                 StringPool.OPEN_CURLY_BRACE,
284                 StringPool.CLOSE_CURLY_BRACE
285             });
286     }
287 
288     protected long getUserId(long companyId, String userIdString) {
289         long userId = 0;
290 
291         try {
292             Company company = CompanyLocalServiceUtil.getCompany(companyId);
293 
294             userId = GetterUtil.getLong(
295                 Encryptor.decrypt(company.getKeyObj(), userIdString));
296         }
297         catch (Exception e) {
298             _log.error(
299                 "Invalid credentials for company id " + companyId +
300                     " and user id " + userIdString);
301         }
302 
303         return userId;
304     }
305 
306     protected boolean isReceiveRequest(HttpServletRequest request)
307         throws Exception {
308 
309         String path = GetterUtil.getString(request.getPathInfo());
310 
311         if (path.endsWith(_PATH_RECEIVE)) {
312             return true;
313         }
314         else {
315             return false;
316         }
317     }
318 
319     protected PollerRequest process(
320             Set<String> portletIdsWithChunks, PollerHeader pollerHeader,
321             String portletId, Map<String, String> parameterMap, String chunkId,
322             boolean receiveRequest)
323         throws Exception {
324 
325         PollerProcessor pollerProcessor =
326             PollerProcessorUtil.getPollerProcessor(portletId);
327 
328         if (pollerProcessor == null) {
329             _log.error("Poller processor not found for portlet " + portletId);
330 
331             return null;
332         }
333 
334         PollerRequest pollerRequest = new PollerRequest(
335             pollerHeader, portletId, parameterMap, chunkId, receiveRequest);
336 
337         if (receiveRequest) {
338             portletIdsWithChunks.add(portletId);
339         }
340 
341         return pollerRequest;
342     }
343 
344     private static final String _ESCAPED_CLOSE_CURLY_BRACE =
345         "[$CLOSE_CURLY_BRACE$]";
346 
347     private static final String _ESCAPED_OPEN_CURLY_BRACE =
348         "[$OPEN_CURLY_BRACE$]";
349 
350     private static final String _OPEN_HASH_MAP_WRAPPER =
351         "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
352 
353     private static final String _PATH_RECEIVE = "/receive";
354 
355     private static Log _log = LogFactoryUtil.getLog(PollerServlet.class);
356 
357 }