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         for (Map.Entry<String, Object> entry : oldParameterMap.entrySet()) {
201             newParameterMap.put(
202                 entry.getKey(), String.valueOf(entry.getValue()));
203         }
204 
205         return newParameterMap;
206     }
207 
208     protected PollerHeader getPollerHeader(
209             Map<String, Object>[] pollerRequestChunks)
210         throws Exception {
211 
212         if (pollerRequestChunks.length < 1) {
213             return null;
214         }
215 
216         Map<String, Object> pollerRequestChunk = pollerRequestChunks[0];
217 
218         long companyId = GetterUtil.getLong(
219             String.valueOf(pollerRequestChunk.get("companyId")));
220         String userIdString = GetterUtil.getString(
221             String.valueOf(pollerRequestChunk.get("userId")));
222         long browserKey = GetterUtil.getLong(
223             String.valueOf(pollerRequestChunk.get("browserKey")));
224         String[] portletIds = StringUtil.split(
225             String.valueOf(pollerRequestChunk.get("portletIds")));
226         boolean initialRequest = GetterUtil.getBoolean(
227             String.valueOf(pollerRequestChunk.get("initialRequest")));
228         boolean startPolling = GetterUtil.getBoolean(
229             String.valueOf(pollerRequestChunk.get("startPolling")));
230 
231         long userId = getUserId(companyId, userIdString);
232 
233         if (userId == 0) {
234             return null;
235         }
236 
237         return new PollerHeader(
238             userId, browserKey, portletIds, initialRequest, startPolling);
239     }
240 
241     protected String getPollerRequest(HttpServletRequest request)
242         throws Exception {
243 
244         String pollerRequest = ParamUtil.getString(request, "pollerRequest");
245 
246         if (Validator.isNull(pollerRequest)) {
247             return null;
248         }
249 
250         return StringUtil.replace(
251             pollerRequest,
252             new String[] {
253                 StringPool.OPEN_CURLY_BRACE,
254                 StringPool.CLOSE_CURLY_BRACE,
255                 _ESCAPED_OPEN_CURLY_BRACE,
256                 _ESCAPED_CLOSE_CURLY_BRACE
257             },
258             new String[] {
259                 _OPEN_HASH_MAP_WRAPPER,
260                 StringPool.DOUBLE_CLOSE_CURLY_BRACE,
261                 StringPool.OPEN_CURLY_BRACE,
262                 StringPool.CLOSE_CURLY_BRACE
263             });
264     }
265 
266     protected long getUserId(long companyId, String userIdString) {
267         long userId = 0;
268 
269         try {
270             Company company = CompanyLocalServiceUtil.getCompany(companyId);
271 
272             userId = GetterUtil.getLong(
273                 Encryptor.decrypt(company.getKeyObj(), userIdString));
274         }
275         catch (Exception e) {
276             _log.error(
277                 "Invalid credentials for company id " + companyId +
278                     " and user id " + userIdString);
279         }
280 
281         return userId;
282     }
283 
284     protected boolean isDoReceive(HttpServletRequest request)
285         throws Exception {
286 
287         String path = GetterUtil.getString(request.getPathInfo());
288 
289         if (path.endsWith("/receive")) {
290             return true;
291         }
292         else {
293             return false;
294         }
295     }
296 
297     protected void process(
298             boolean doReceive, JSONArray pollerResponseChunksJSON,
299             Set<String> portletIdsWithChunks, PollerHeader pollerHeader,
300             String portletId, Map<String, String> parameterMap, String chunkId)
301         throws Exception {
302 
303         PollerProcessor pollerProcessor =
304             PollerProcessorUtil.getPollerProcessor(portletId);
305 
306         if (pollerProcessor == null) {
307             _log.error("Poller processor not found for portlet " + portletId);
308 
309             return;
310         }
311 
312         PollerRequest pollerRequest = new PollerRequest(
313             pollerHeader, portletId, parameterMap, chunkId);
314 
315         if (doReceive) {
316             PollerResponse pollerResponse = new PollerResponse(
317                 portletId, chunkId);
318 
319             pollerProcessor.receive(pollerRequest, pollerResponse);
320 
321             pollerResponseChunksJSON.put(pollerResponse.toJSONObject());
322             portletIdsWithChunks.add(portletId);
323         }
324         else {
325             pollerProcessor.send(pollerRequest);
326         }
327     }
328 
329     private static final String _ESCAPED_CLOSE_CURLY_BRACE =
330         "[$CLOSE_CURLY_BRACE$]";
331 
332     private static final String _ESCAPED_OPEN_CURLY_BRACE =
333         "[$OPEN_CURLY_BRACE$]";
334 
335     private static final String _OPEN_HASH_MAP_WRAPPER =
336         "{\"javaClass\":\"java.util.HashMap\",\"map\":{";
337 
338     private static Log _log = LogFactoryUtil.getLog(PollerServlet.class);
339 
340 }