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.poller.PollerHeader;
32  import com.liferay.portal.kernel.poller.PollerProcessor;
33  import com.liferay.portal.kernel.poller.PollerRequest;
34  import com.liferay.portal.kernel.poller.PollerResponse;
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.util.Encryptor;
47  import com.liferay.util.servlet.ServletResponseUtil;
48  
49  import java.io.IOException;
50  
51  import java.util.HashMap;
52  import java.util.HashSet;
53  import java.util.Map;
54  import java.util.Set;
55  
56  import javax.servlet.ServletException;
57  import javax.servlet.http.HttpServlet;
58  import javax.servlet.http.HttpServletRequest;
59  import javax.servlet.http.HttpServletResponse;
60  
61  /**
62   * <a href="PollerServlet.java.html"><b><i>View Source</i></b></a>
63   *
64   * @author Brian Wing Shun Chan
65   *
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 pollerRequest = getPollerRequest(request);
99  
100         if (Validator.isNull(pollerRequest)) {
101             return null;
102         }
103 
104         Map<String, Object>[] pollerRequestChunks =
105             (Map<String, Object>[])JSONFactoryUtil.deserialize(pollerRequest);
106 
107         PollerHeader pollerHeader = getPollerHeader(pollerRequestChunks);
108 
109         if (pollerHeader == null) {
110             return null;
111         }
112 
113         boolean doReceive = isDoReceive(request);
114 
115         JSONArray pollerResponseChunksJSON = null;
116         Set<String> portletIdsWithChunks = null;
117 
118         if (doReceive) {
119             pollerResponseChunksJSON = JSONFactoryUtil.createJSONArray();
120             portletIdsWithChunks = new HashSet<String>();
121 
122             boolean suspendPolling = false;
123 
124             if (pollerHeader.isStartPolling()) {
125                 BrowserTrackerLocalServiceUtil.updateBrowserTracker(
126                     pollerHeader.getUserId(), pollerHeader.getBrowserKey());
127             }
128             else {
129                 BrowserTracker browserTracker =
130                     BrowserTrackerLocalServiceUtil.getBrowserTracker(
131                         pollerHeader.getUserId(), pollerHeader.getBrowserKey());
132 
133                 if (browserTracker.getBrowserKey() !=
134                         pollerHeader.getBrowserKey()) {
135 
136                     suspendPolling = true;
137                 }
138             }
139 
140             JSONObject pollerResponseChunkJSON =
141                 JSONFactoryUtil.createJSONObject();
142 
143             pollerResponseChunkJSON.put("userId", pollerHeader.getUserId());
144             pollerResponseChunkJSON.put(
145                 "initialRequest", pollerHeader.isInitialRequest());
146             pollerResponseChunkJSON.put("suspendPolling", suspendPolling);
147 
148             pollerResponseChunksJSON.put(pollerResponseChunkJSON);
149         }
150 
151         for (int i = 1; i < pollerRequestChunks.length; i++) {
152             Map<String, Object> pollerRequestChunk = pollerRequestChunks[i];
153 
154             String portletId = (String)pollerRequestChunk.get("portletId");
155             Map<String, String> parameterMap = getData(pollerRequestChunk);
156             String chunkId = (String)pollerRequestChunk.get("chunkId");
157 
158             try {
159                 process(
160                     doReceive, pollerResponseChunksJSON, portletIdsWithChunks,
161                     pollerHeader, portletId, parameterMap, chunkId);
162             }
163             catch (Exception e) {
164                 _log.error(e, e);
165             }
166         }
167 
168         if (!doReceive) {
169             return StringPool.BLANK;
170         }
171 
172         for (String portletId : pollerHeader.getPortletIds()) {
173             if (portletIdsWithChunks.contains(portletId)) {
174                 continue;
175             }
176 
177             try {
178                 process(
179                     doReceive, pollerResponseChunksJSON, portletIdsWithChunks,
180                     pollerHeader, portletId, new HashMap<String, String>(),
181                     null);
182             }
183             catch (Exception e) {
184                 _log.error(e, e);
185             }
186         }
187 
188         return pollerResponseChunksJSON.toString();
189     }
190 
191     protected Map<String, String> getData(
192             Map<String, Object> pollerRequestChunk)
193         throws Exception {
194 
195         Map<String, Object> oldParameterMap =
196             (Map<String, Object>)pollerRequestChunk.get("data");
197 
198         Map<String, String> newParameterMap = new HashMap<String, String>();
199 
200         if (oldParameterMap == null) {
201             return newParameterMap;
202         }
203 
204         for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
205             newParameterMap.put(
206                 entry.getKey(), String.valueOf(entry.getValue()));
207         }
208 
209         return newParameterMap;
210     }
211 
212     protected PollerHeader getPollerHeader(
213             Map<String, Object>[] pollerRequestChunks)
214         throws Exception {
215 
216         if (pollerRequestChunks.length < 1) {
217             return null;
218         }
219 
220         Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
221 
222         long companyId = GetterUtil.getLong(
223             String.valueOf(pollerRequestChunk.get("companyId")));
224         String userIdString = GetterUtil.getString(
225             String.valueOf(pollerRequestChunk.get("userId")));
226         long browserKey = GetterUtil.getLong(
227             String.valueOf(pollerRequestChunk.get("browserKey")));
228         String[] portletIds = StringUtil.split(
229             String.valueOf(pollerRequestChunk.get("portletIds")));
230         boolean initialRequest = GetterUtil.getBoolean(
231             String.valueOf(pollerRequestChunk.get("initialRequest")));
232         boolean startPolling = GetterUtil.getBoolean(
233             String.valueOf(pollerRequestChunk.get("startPolling")));
234 
235         long userId = getUserId(companyId, userIdString);
236 
237         if (userId == 0) {
238             return null;
239         }
240 
241         return new PollerHeader(
242             userId, browserKey, portletIds, initialRequest, startPolling);
243     }
244 
245     protected String getPollerRequest(HttpServletRequest request)
246         throws Exception {
247 
248         String pollerRequest = ParamUtil.getString(request, "pollerRequest");
249 
250         if (Validator.isNull(pollerRequest)) {
251             return null;
252         }
253 
254         return StringUtil.replace(
255             pollerRequest,
256             new String[] {
257                 StringPool.OPEN_CURLY_BRACE,
258                 StringPool.CLOSE_CURLY_BRACE,
259                 _ESCAPED_OPEN_CURLY_BRACE,
260                 _ESCAPED_CLOSE_CURLY_BRACE
261             },
262             new String[] {
263                 _OPEN_HASH_MAP_WRAPPER,
264                 StringPool.DOUBLE_CLOSE_CURLY_BRACE,
265                 StringPool.OPEN_CURLY_BRACE,
266                 StringPool.CLOSE_CURLY_BRACE
267             });
268     }
269 
270     protected long getUserId(long companyId, String userIdString) {
271         long userId = 0;
272 
273         try {
274             Company company = CompanyLocalServiceUtil.getCompany(companyId);
275 
276             userId = GetterUtil.getLong(
277                 Encryptor.decrypt(company.getKeyObj(), userIdString));
278         }
279         catch (Exception e) {
280             _log.error(
281                 "Invalid credentials for company id " + companyId +
282                     " and user id " + userIdString);
283         }
284 
285         return userId;
286     }
287 
288     protected boolean isDoReceive(HttpServletRequest request)
289         throws Exception {
290 
291         String path = GetterUtil.getString(request.getPathInfo());
292 
293         if (path.endsWith("/receive")) {
294             return true;
295         }
296         else {
297             return false;
298         }
299     }
300 
301     protected void process(
302             boolean doReceive, JSONArray pollerResponseChunksJSON,
303             Set<String> portletIdsWithChunks, PollerHeader pollerHeader,
304             String portletId, Map<String, String> parameterMap, String chunkId)
305         throws Exception {
306 
307         PollerProcessor pollerProcessor =
308             PollerProcessorUtil.getPollerProcessor(portletId);
309 
310         if (pollerProcessor == null) {
311             _log.error("Poller processor not found for portlet " + portletId);
312 
313             return;
314         }
315 
316         PollerRequest pollerRequest = new PollerRequest(
317             pollerHeader, portletId, parameterMap, chunkId);
318 
319         if (doReceive) {
320             PollerResponse pollerResponse = new PollerResponse(
321                 portletId, chunkId);
322 
323             pollerProcessor.receive(pollerRequest, pollerResponse);
324 
325             pollerResponseChunksJSON.put(pollerResponse.toJSONObject());
326             portletIdsWithChunks.add(portletId);
327         }
328         else {
329             pollerProcessor.send(pollerRequest);
330         }
331     }
332 
333     private static final String _ESCAPED_CLOSE_CURLY_BRACE =
334         "[$CLOSE_CURLY_BRACE$]";
335 
336     private static final String _ESCAPED_OPEN_CURLY_BRACE =
337         "[$OPEN_CURLY_BRACE$]";
338 
339     private static final String _OPEN_HASH_MAP_WRAPPER =
340         "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
341 
342     private static Log _log = LogFactoryUtil.getLog(PollerServlet.class);
343 
344 }