4A Server -  2.0
 All Classes Namespaces Files Functions Variables Enumerator
AnnotCometServlet.java
Go to the documentation of this file.
1 /*
2  * Project: Server for annotations sharing
3  * Author: Ing. Jaroslav Dytrych idytrych@fit.vutbr.cz
4  * File: AnnotCometServlet.java
5  * Description: Servlet for communication with clients (annotation editors)
6  */
7 
8 /**
9  * @file AnnotCometServlet.java
10  *
11  * @brief Servlet for communication with clients (annotation editors)
12  */
13 
14 /**
15  * @package cz.vutbr.fit.knot.annotations.comet
16  *
17  * @brief Classes related to communication with client, session handling etc.
18  */
19 package cz.vutbr.fit.knot.annotations.comet;
20 
21 import com.sun.grizzly.comet.CometContext;
22 import com.sun.grizzly.comet.CometEngine;
23 import com.sun.grizzly.comet.CometEvent;
24 import com.sun.grizzly.comet.CometHandler;
28 import java.io.BufferedReader;
29 import java.io.IOException;
30 import java.io.PrintWriter;
31 import java.io.StringWriter;
32 import java.util.ArrayList;
33 import java.util.Iterator;
34 import java.util.logging.Level;
35 import java.util.logging.Logger;
36 import javax.servlet.ServletConfig;
37 import javax.servlet.ServletContext;
38 import javax.servlet.ServletException;
39 import javax.servlet.http.HttpServlet;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 
43 /**
44  * Servlet for communication with clients (annotation editors)
45  *
46  * @brief Servlet for communication with clients (annotation editors)
47  * @author idytrych
48  */
49 public class AnnotCometServlet extends HttpServlet {
50 
51  /** Context path of servlet */
52  private String contextPath = null;
53 
54  /**
55  * Comet handler (handles comet request until timed out or sent data)
56  *
57  * This class allow Java components part of an HTTP request to be polled by
58  * the Grizzly Asynchronous Request Mechanism. It will be notified when
59  * another CometHandler notify them using CometContext.notify. With Servlet,
60  * it is recommended to attach the HTTPServletResponse and use this object
61  * to push back bytes to the client.
62  *
63  * @brief Comet handler
64  */
65  public class AnnotHandler implements CometHandler<HttpServletResponse> {
66 
67  /** Servlet response object */
68  private HttpServletResponse response;
69  /** Client session in which this handler was created */
70  private ArrayList<EditorSession> edSessionList = new ArrayList<EditorSession>();
71  /** Was message already prepared? */
72  private Boolean messagePrepared = false;
73 
74  /**
75  * Event handler
76  * Process notify events created in POST requests
77  *
78  * @param event Event
79  * @throws java.io.IOException
80  */
81  @Override
82  public void onEvent(CometEvent event) throws IOException {
83  synchronized (messagePrepared) {
84  if (messagePrepared) {
85  return;
86  }
87  messagePrepared = true;
88  if (CometEvent.NOTIFY == event.getType()) {
89  PrintWriter writer = response.getWriter();
90  // Flier flier = (Flier) event.attachment(); // gets flier with informations
91  // get messages for client
92  String responseString = null;
93  EditorSession sessionWithMes = null;
94  synchronized (edSessionList) {
95  sessionWithMes = edSessionList.get(0);
96  for (int i = 0; i < edSessionList.size(); i++) {
97  if (edSessionList.get(i).hasMessagesTSVC()) {
98  sessionWithMes = edSessionList.get(i);
99  responseString = sessionWithMes.getAndCleanMessages();
100  edSessionList.remove(i);
101  edSessionList.add(sessionWithMes);
102  break;
103  }
104  }
105  }
106 
107  // Recieve protocol version
108  int version = sessionWithMes.getNamespacesLOD();
109 
110  String sessAtt = " sessionID=\"" + sessionWithMes.getSessionId() + "\"";
111  if (sessionWithMes.getProtocolLOD() < Constants.PROTOCOL_LOD_V2) {
112  sessAtt = "";
113  }
114 
115  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
116 
117  //Attach namespaces
118  for (int i = 0; i < Constants.nameSpaces[version].length; i++) {
119  sb.append(Constants.nameSpaces[version][i]);
120  }
121 
122  if (responseString != null && !responseString.isEmpty()) {
123  sb.append(sessAtt).append(">").append(responseString).append("</messages>");
124  } else {
125  sb.append(sessAtt).append("><ok/></messages>");
126  }
127 
128  // send to client
129  synchronized (contextPath) {
130  writer.write(sb.toString());
131  writer.flush();
132  }
133 
134  event.getCometContext().resumeCometHandler(this);
135  }
136  }
137  } // onEvent()
138 
139  /**
140  * This method is called when connection is suspended by addCometHandler()
141  *
142  * @param event Event
143  */
144  @Override
145  public void onInitialize(CometEvent event) throws IOException {
146  synchronized (edSessionList) {
147  Iterator<EditorSession> edSessIt = edSessionList.iterator();
148  while(edSessIt.hasNext()){
149  EditorSession es = edSessIt.next();
150  es.setCometHandler(this);
151  es.setCometContext(contextPath);
152  }
153  }
154  }
155 
156  /**
157  * This method is called when connection timed out or if client closes
158  * connnection.
159  * If timed out, ok message is sent. If client closes connection, ok message
160  * is sent too (but possibly discarded).
161  *
162  * @param event Event
163  */
164  @Override
165  public void onInterrupt(CometEvent event) throws IOException {
166  synchronized (messagePrepared) {
167  if (messagePrepared) {
168  return;
169  }
170  messagePrepared = true;
171  EditorSession sess = this.getFirstSessionAndRotate();
172  int version = sess.getNamespacesLOD();
173  synchronized (edSessionList) {
174  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
175 
176  String sessAtt = " sessionID=\"" + sess.getSessionId() + "\"";
178  sessAtt = "";
179  }
180 
181  for (int i = 0; i < Constants.nameSpaces[version].length; i++) {
182  sb.append(Constants.nameSpaces[version][i]);
183  }
184  sb.append(sessAtt).append("><ok/></messages>");
185 
186  PrintWriter writer = response.getWriter();
187  writer.write(sb.toString());
188  writer.flush();
189  }
190  event.getCometContext().resumeCometHandler(this);
192  }
193  } // onInterrupt()
194 
195  /**
196  * This method is called when connection is resumed and terminated
197  *
198  * @param event Event
199  */
200  @Override
201  public void onTerminate(CometEvent event) throws IOException {
203  }
204 
205  /**
206  * Attaches an attachment to this class
207  *
208  * @param attachment
209  */
210  @Override
211  public void attach(HttpServletResponse attachment) {
212  this.response = attachment;
213  }
214 
215  /**
216  * Removes this hadler from comet context
217  */
218  private void removeThisFromContext() throws IOException {
219  response.getWriter().close();
220  CometContext context =
221  CometEngine.getEngine().getCometContext(contextPath);
222  context.removeCometHandler(this);
223  }
224 
225  /**
226  * Gets client session list in which this handler was created
227  *
228  * @return Returns client session list in which this handler was created
229  */
230  public ArrayList<EditorSession> getEdSessionList() {
231  return edSessionList;
232  }
233 
234  /**
235  * Sets client session list in which this handler was created
236  *
237  * @param edSessionList Client session list in which this handler was created
238  */
239  public void setEdSessionList(ArrayList<EditorSession> edSessionList) {
240  this.edSessionList = edSessionList;
241  }
242 
243  /**
244  * Gets the latest used session in list in which this handler was created
245  *
246  * @return Returns latest used client session in which this handler was created
247  */
249  EditorSession first = null;
250  synchronized (edSessionList) {
251  first = edSessionList.get(0);
252  edSessionList.remove(0);
253  edSessionList.add(first);
254  }
255  return first;
256  }
257 
258  /**
259  * Adds session to the session list
260  *
261  * @param session session for add
262  */
264  synchronized (edSessionList) {
265  this.edSessionList.add(session);
266  }
267  }
268  } // private class AnnotHandler
269 
270  /**
271  * Initializes servlet (sets path, timeout etc.)
272  *
273  * @param config Configuration info
274  */
275  @Override
276  public void init(ServletConfig config) throws ServletException {
277 
278  super.init(config);
279  ServletContext context = config.getServletContext();
280  contextPath = context.getContextPath() + Constants.COMET_CONTEXT_PATH;
281 
282  CometEngine engine = CometEngine.getEngine();
283  CometContext cometContext = engine.register(contextPath);
284  cometContext.setExpirationDelay(Constants.COMET_TIMEOUT_SEC * 1000); // in milliseconds
285  }
286 
287  /**
288  * Handles the HTTP <code>GET</code> method.
289  * This method is dedicated for comet requests. Session id is sent as URI
290  * parameter "session".
291  *
292  * @param request servlet request
293  * @param response servlet response
294  * @throws ServletException if a servlet-specific error occurs
295  * @throws IOException if an I/O error occurs
296  */
297  @Override
298  protected void doGet(HttpServletRequest request, HttpServletResponse response)
299  throws ServletException, IOException {
300 // request.setCharacterEncoding("UTF-8");
301  response.setContentType("text/xml;charset=UTF-8");
302 
303  // get session id and convert to Long
304  String sessionStr = request.getParameter("session");
305  if(sessionStr == null || sessionStr.isEmpty()){
306  sessionStr = request.getParameter("sessionID");
307  }
308 
309  Long sessionId = null;
310  try {
311  sessionId = Long.decode(sessionStr);
312  } catch (NumberFormatException numberFormatException) {
314  Logger.getLogger(AnnotCometServlet.class.getName()).log(Level.ALL, "Bad session id: " + sessionStr);
315  }
316  sessionId = null;
317  } catch (Exception e) {
319  Logger.getLogger(AnnotCometServlet.class.getName()).log(Level.ALL, "Bad session id", e);
320  }
321  sessionId = null;
322  }
323 
324  EditorSession session = null;
325  if (sessionId != null) {
326  session = AppBean.getSession(sessionId); // find session
327  }
328  if (session == null) { // if session not exists
330  Logger.getLogger(AnnotCometServlet.class.getName()).log(Level.ALL, "Session not found");
331  }
332  PrintWriter writer = response.getWriter();
333  // send error message
334  String err = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
335  + "<messages>\n"
337  + "</messages>";
338  writer.write(err);
339  writer.flush();
340  return;
341  }
342 
343  AppBean.getSessionsCleaner().setSessionConfirm(session.getSessionId());
344 
345  String resStr = session.getAndCleanMessages(); // get messages for client
346  if (!resStr.isEmpty()) { // if there are messages to send
347  // create response XML with messages
348  int version = session.getNamespacesLOD();
349  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
350 
351  String sessAtt = " sessionID=\"" + session.getSessionId() + "\"";
352  if (session.getProtocolLOD() < Constants.PROTOCOL_LOD_V2) {
353  sessAtt = "";
354  }
355 
356  for(int i = 0; i < Constants.nameSpaces[version].length; i++){
357  sb.append(Constants.nameSpaces[version][i]);
358  }
359 
360  sb.append(sessAtt).append(">").append(resStr).append("</messages>");
361 
362  // send to client
363  PrintWriter resWriter = response.getWriter();
364  resWriter.write(sb.toString());
365  resWriter.flush();
366  return;
367  } // if there are messages to send
368 
369  // create comet handler
370  AnnotHandler handler = new AnnotHandler();
371  handler.addSessionInSessionList(session);
372  handler.attach(response);
373 
374  // sleep and wait to data
375  CometEngine engine = CometEngine.getEngine();
376  CometContext context = engine.getCometContext(contextPath);
377  context.addCometHandler(handler);
378  } // doGet()
379 
380  /**
381  * Handles the HTTP <code>POST</code> method.
382  * If only one message (session) is sent, this is comet request, else this
383  * is AJAX request.
384  *
385  * @param request servlet request
386  * @param response servlet response
387  * @throws ServletException if a servlet-specific error occurs
388  * @throws IOException if an I/O error occurs
389  */
390  @Override
391  protected void doPost(HttpServletRequest request, HttpServletResponse response)
392  throws ServletException, IOException {
393 // request.setCharacterEncoding("UTF-8");
394  response.setContentType("text/xml;charset=UTF-8");
395 
396  BufferedReader bufReader = request.getReader();
397 
398  // read XML with messages from client to String
399  StringWriter writer = new StringWriter();
400  int nextChar;
401  while ((nextChar = bufReader.read()) != -1) {
402  writer.write(nextChar);
403  }
404  String messagesStr = writer.toString();
405 
406  // process messages from client and create object with informations about request
407  RequestInfo requestInfo = MessageProcessor.processXML(messagesStr);
408  if (requestInfo == null) { // if request is bad
409  PrintWriter resWriter = response.getWriter();
410  // send error message to client
411  String err = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
412  + "<messages>\n"
414  + "</messages>";
415  resWriter.write(err);
416  resWriter.flush();
417  return;
418  }
419 
420  // Gettion session
421  EditorSession session = requestInfo.getSession();
422 
423  // If request is not complete - <session id=""> is missing
424  if (session == null){
425  PrintWriter resWriter = response.getWriter();
426  // send error message to client
427  String err = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"
428  + "<messages>\n"
430  + "</messages>";
431  resWriter.write(err);
432  resWriter.flush();
433  return;
434  }
435 
436  // If only one message in this request is session, this is comet request
437  // through POST
438  if (requestInfo.getCometSessionList() != null && !requestInfo.getCometSessionList().isEmpty()
439  && requestInfo.isSessionOnly()) { // more sessions in one channel
440 
441  Iterator<EditorSession> edSessIt = requestInfo.getCometSessionList().iterator();
442  while (edSessIt.hasNext()) {
443  AppBean.getSessionsCleaner().setSessionConfirm(edSessIt.next().getSessionId());
444  }
445 
446  String responseString = null;
447  EditorSession sessionWithMes = null;
448  ArrayList<EditorSession> edSessionList = requestInfo.getCometSessionList();
449 
450  synchronized (edSessionList) {
451  sessionWithMes = edSessionList.get(0);
452  for (int i = 0; i < edSessionList.size(); i++) {
453  if (edSessionList.get(i).hasMessagesTSVC()) {
454  sessionWithMes = edSessionList.get(i);
455  responseString = sessionWithMes.getAndCleanMessages();
456  edSessionList.remove(i);
457  edSessionList.add(sessionWithMes);
458  break;
459  }
460  }
461  }
462 
463  if(responseString != null && !responseString.isEmpty()){
464  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
465 
466  int version = sessionWithMes.getNamespacesLOD();
467 
468 
469  String sessAtt = " sessionID=\"" + sessionWithMes.getSessionId() + "\"";
470  if (sessionWithMes.getProtocolLOD() < Constants.PROTOCOL_LOD_V2) {
471  sessAtt = "";
472  }
473  //Attach namespaces
474  for(int i = 0; i < Constants.nameSpaces[version].length; i++){
475  sb.append(Constants.nameSpaces[version][i]);
476  }
477 
478  sb.append(sessAtt).append(">").append(responseString).append("</messages>");
479 
480  //Send to client
481  PrintWriter resWriter = response.getWriter();
482  resWriter.write(sb.toString());
483  resWriter.flush();
484  return;
485  }
486 
487  // create comet handler
488  AnnotHandler handler = new AnnotHandler();
489  handler.setEdSessionList(edSessionList);
490  handler.attach(response);
491 
492  // sleep and wait to data
493  CometEngine engine = CometEngine.getEngine();
494  CometContext context = engine.getCometContext(contextPath);
495  context.addCometHandler(handler);
496  return;
497 
498  } else if (requestInfo.getSession() != null && requestInfo.isSessionOnly()) { // one session
499  AppBean.getSessionsCleaner().setSessionConfirm(requestInfo.getSession().getSessionId());
500 
501  // get messages for client
502  String resStr = requestInfo.getSession().getAndCleanMessages();
503  if (!resStr.isEmpty()) { // if there are messages to send
504  // create response XML with messages
505  int version = session.getNamespacesLOD();
506  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
507 
508  String sessAtt = " sessionID=\"" + session.getSessionId() + "\"";
509  if (session.getProtocolLOD() < Constants.PROTOCOL_LOD_V2) {
510  sessAtt = "";
511  }
512 
513  for(int i = 0; i < Constants.nameSpaces[version].length; i++){
514  sb.append(Constants.nameSpaces[version][i]);
515  }
516  sb.append(sessAtt).append(">").append(resStr).append("</messages>");
517 
518  // send to client
519  PrintWriter resWriter = response.getWriter();
520  resWriter.write(sb.toString());
521  resWriter.flush();
522  return;
523  } // if there are messages to send
524 
525  // create comet handler
526  AnnotHandler handler = new AnnotHandler();
527  handler.addSessionInSessionList(requestInfo.getSession());
528  handler.attach(response);
529 
530  // sleep and wait to data
531  CometEngine engine = CometEngine.getEngine();
532  CometContext context = engine.getCometContext(contextPath);
533  context.addCometHandler(handler);
534  return;
535  } // If only one message in this request is session, ...
536 
537  if (requestInfo.getSession() != null) {
538  AppBean.getSessionsCleaner().setSessionConfirm(requestInfo.getSession().getSessionId());
539  }
540 
541  if (requestInfo.getCometSessionList() != null && !requestInfo.getCometSessionList().isEmpty()){
542  Iterator<EditorSession> edSessIt = requestInfo.getCometSessionList().iterator();
543 
544  while(edSessIt.hasNext()){
545  AppBean.getSessionsCleaner().setSessionConfirm(edSessIt.next().getSessionId());
546  }
547  }
548 
549  // process data from request and create response messages
550  // This is done by calling modules and adding other messages (warnings, errors, ...)
551  String responseMessages = requestInfo.getReplyText();
552  responseMessages = responseMessages + AppBean.getResponseCreator().createPostResponse(requestInfo);
553  responseMessages = responseMessages + requestInfo.getWarnings();
554  responseMessages = responseMessages + requestInfo.getErrors();
555  if (responseMessages.isEmpty()) { // if no other messages, send <ok/>
556  responseMessages = "<ok/>";
557  }
558 
559  // Create response XML with messages and send to client
560 
561  PrintWriter resWriter = response.getWriter();
562 
563  int version = session.getNamespacesLOD();
564  StringBuilder sb = new StringBuilder("<?xml version=\"1.0\" encoding=\"utf-8\" ?><messages ");
565 
566  for(int i = 0; i < Constants.nameSpaces[version].length; i++){
567  sb.append(Constants.nameSpaces[version][i]);
568  }
569  sb.append(">").append(responseMessages).append("</messages>");
570 
571  synchronized(contextPath){
572  resWriter.write(sb.toString());
573  resWriter.flush();
574  }
575 
576  // if it's allowed, create responses for all other connected clients,
577  // store it to their sessions (it will be sent via comet) and notify
578  // comet handlers
579  if (requestInfo.getCanNotify() && requestInfo.getFlier() != null) {
580  ResponseCreator rc = AppBean.getResponseCreator();
581  synchronized(AppBean.getSessions()){
582  Iterator<EditorSession> sesIt = AppBean.getSessions().iterator();
583  while (sesIt.hasNext()) {
584  EditorSession es = sesIt.next();
585  es.addMessageTS(rc.createCometResponse(requestInfo.getFlier(), es));
586  }
587  }
588 
589  // notify comet handlers
590  CometEngine engine = CometEngine.getEngine();
591  CometContext<?> context = engine.getCometContext(contextPath);
592  context.notify(requestInfo.getFlier());
593  }
594 
595  } // doPost()
596 
597  /**
598  * Returns a short description of the servlet.
599  *
600  * @return a String containing servlet description
601  */
602  @Override
603  public String getServletInfo() {
604  return "Servlet for communication with annotation editors.";
605  }
606 } // class AnnotCometServlet
Singleton for storing global variables.
Definition: AppBean.java:47
void doPost(HttpServletRequest request, HttpServletResponse response)
Class that creates responses and persists data.
Servlet for communication with clients (annotation editors)
void doGet(HttpServletRequest request, HttpServletResponse response)
void setEdSessionList(ArrayList< EditorSession > edSessionList)
Processed informations about client request.
Class responsible for localised strings.
Informations about client session.