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