4A Server -  2.0
 All Classes Namespaces Files Functions Variables Enumerator
MessageProcessor.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: MessageProcessor.java
5  * Description: Static class which parses and process XML with messages
6  */
7 
8 /**
9  * @file MessageProcessor.java
10  *
11  * @brief Static class which parses and process XML with messages
12  */
13 
14 package cz.vutbr.fit.knot.annotations.comet;
15 
22 import java.io.IOException;
23 import java.io.StringReader;
24 import java.util.ArrayList;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.logging.Level;
28 import java.util.logging.Logger;
29 import java.util.regex.Matcher;
30 import java.util.regex.Pattern;
31 import javax.xml.parsers.DocumentBuilder;
32 import javax.xml.parsers.DocumentBuilderFactory;
33 import javax.xml.parsers.ParserConfigurationException;
34 import org.w3c.dom.*;
35 import org.xml.sax.InputSource;
36 import org.xml.sax.SAXException;
37 
38 /**
39  * Static class which parses and process XML with messages
40  *
41  * @brief Static class which parses and process XML with messages
42  * @author idytrych
43  */
44 public class MessageProcessor {
45 
46  // used as a patterns when removing escaped apostrophe from XML node content
47  private static String unescapeXMLApos = "'";
48  private static String unescapeXMLQuotes = """;
49  private static String unescapeXMLLT = "<";
50  private static String unescapeXMLGT = ">";
51  private static String unescapeXMLAmp = "&";
52 
53  /**
54  * Parses and process XML with messages from client
55  *
56  * @param XMLString String with XML with messages from client
57  * @return Object with informations about client request
58  */
59  public static RequestInfo processXML(String XMLString) {
60  RequestInfo requestInfo = new RequestInfo();
61 
62  // parse XML
63  Document doc = parseXml(XMLString);
64  if (doc == null) {
66  String msg = "Bad XML from client: " + XMLString;
67  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
68  }
69  return null; // bad request
70  }
71 
72  Element docEl = doc.getDocumentElement();
73  requestInfo.setRequestString(XMLString);
74  requestInfo.setRequestDocument(doc);
75 
76  EditorSession session;
77  Flier flier = new Flier(); // create flier for comet handlers
78  requestInfo.setFlier(flier);
79 
80  // get session id or create session
81  NodeList sessNodes = docEl.getElementsByTagName("session");
82  ArrayList<Element> sessElements = new ArrayList<Element>();
83  for (int i = 0; i < sessNodes.getLength(); i++) {
84  Element sessEl = (Element) sessNodes.item(i);
85  sessElements.add(sessEl);
86  }
87 
88  String idString = docEl.getAttribute("sessionID");
89 
90  if ((sessElements.isEmpty() && idString.isEmpty())
91  || (sessElements.isEmpty() && idString.equals("undefined")) ) {
92  // Session id not presented - new session?
93  NodeList connNodes = docEl.getElementsByTagName("connect");
94  Element connEl = (Element) connNodes.item(0);
95  if (connEl == null) { // if connect message not sent
97  String msg = "Unknown session (not sent).";
98  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
99  }
101  return requestInfo; // nothing else can be done without valid session
102  }
103  // get protocol version from attribute of connect
104  String protVersion = connEl.getAttribute("protocolVersion");
105  if (protVersion.length() == 0) { // if protocol version is invalid
107  String msg = "Bad protocol version.";
108  Logger.getLogger(MessageProcessor.class.getName()).log(Level.SEVERE, msg);
109  }
111  return requestInfo; // nothing else can be done without valid session
112  }
113  String attachCometToStr = connEl.getAttribute("attachCometTo");
114  EditorSession attachCometSession = null;
115  if (attachCometToStr != null && !attachCometToStr.isEmpty()) {
116  try {
117  Long attachCometTo = Long.decode(attachCometToStr);
118  if (attachCometTo != null) {
119  attachCometSession = AppBean.getSession(attachCometTo);
120  }
121  } catch (NumberFormatException ex) {
123  String msg = "Can not attach comet to another session.";
124  Logger.getLogger(MessageProcessor.class.getName()).log(Level.SEVERE, msg);
125  }
126  }
127  }
128 
129  // create new session
130  session = new EditorSession(protVersion);
131  requestInfo.setSession(session);
132  AppBean.addSession(session);
133  flier.setCreatedInSessId(session.getSessionId());
134  flier.setCreatedInSession(session);
135  // prepare connected message for client
136  if(!protVersion.startsWith("1.") && !protVersion.startsWith("2.") && !protVersion.equals("1") && !protVersion.equals("2")){
138  String msg = "Unsupported protocol version. Client is trying to connect with version = " + protVersion;
139  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
140  }
142  return requestInfo;
143  }
144  if (session.getProtocolLOD() == Constants.PROTOCOL_LOD_V2) {
145  requestInfo.appendToReply("<connected protocolVersion=\"2.0\" sessionID=\""
146  + session.getSessionId() + "\"/>");
147  } else if (session.getProtocolLOD() == Constants.PROTOCOL_LOD_V1_1) {
148  requestInfo.appendToReply("<connected protocolVersion=\"1.1\" sessionID=\""
149  + session.getSessionId() + "\"/>");
150  } else {
151  requestInfo.appendToReply("<connected protocolVersion=\"1.0\" sessionID=\""
152  + session.getSessionId() + "\"/>");
153  }
154 
155  if (attachCometSession != null) {
156  session.setCometContext(attachCometSession.getCometContext());
157  AnnotHandler handler = attachCometSession.getCometHandler();
158  if (handler != null) {
159  session.setCometHandler(handler);
160  handler.addSessionInSessionList(session);
161  }
162  }
163 
164  // set information about protocol version
165  requestInfo.setConnectWPV(protVersion);
166  } else if(!sessElements.isEmpty() && sessElements.size() < 2) { // If one session message was sent
167  Element sessEl = sessElements.get(0);
168  // get session id and convert to Long
169  String sessIdStr = sessEl.getAttribute("id");
170  if (sessIdStr.isEmpty()) {
172  String msg = "Unknown session (id not sent).";
173  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
174  }
176  return requestInfo; // nothing else can be done without valid session
177  }
178 
179  long sessId;
180 
181  try{
182  sessId = Long.decode(sessIdStr);
183  }catch(Exception ex){
185  String msg = "Can't decode session ID.";
186  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
187  }
188 
189  requestInfo.addError(Constants.PROTOCOL_LOD_V1, Localisation.LANGUAGE_ENG, Localisation.ERROR_31_SESSION_EXPIRED, "", "Can't decode session ID.");
190  return requestInfo; // nothing else can be done without valid session
191  }
192 
193  // find session
194  session = AppBean.getSession(sessId);
195  if (session == null) {
196  // if session not found, prepare error message
199  String msg = "Unknown session (not found).";
200  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
201  }
202  return requestInfo; // nothing else can be done without valid session
203  }
204 
205  requestInfo.setSession(session);
206  flier.setCreatedInSessId(sessId);
207  flier.setCreatedInSession(session);
208 
209  // Check whether there are sibling nodes
210  if (sessEl.getNextSibling() == null && sessEl.getPreviousSibling() == null) {
211  // If only one message in this request is session, set appropriate information
212  requestInfo.setSessionOnly(true);
213  requestInfo.setCanNotify(false); // nothing to notify
214  return requestInfo; // all messages was processed
215  }
216 
217  // Check whether sibling nodes aren't text or comment nodes (no other messages)
218  Node auxNode = sessEl;
219  auxNode = auxNode.getNextSibling();
220  if (auxNode != null) {
221  while (auxNode.getNextSibling() != null && auxNode.getNodeType() != Node.ELEMENT_NODE) {
222  auxNode = auxNode.getNextSibling();
223  }
224  }
225  if (auxNode == null || auxNode.getNodeType() != Node.ELEMENT_NODE) {
226  auxNode = sessEl;
227  auxNode = auxNode.getPreviousSibling();
228  if (auxNode != null) {
229  while (auxNode.getPreviousSibling() != null && auxNode.getNodeType() != Node.ELEMENT_NODE) {
230  auxNode = auxNode.getPreviousSibling();
231  }
232  }
233  if (auxNode == null || auxNode.getNodeType() != Node.ELEMENT_NODE) {
234  // If only one message in this request is session, set appropriate information
235  requestInfo.setSessionOnly(true);
236  requestInfo.setCanNotify(false); // nothing to notify
237  return requestInfo; // all messages was processed
238  }
239  }
240  } else if (!sessElements.isEmpty()) { // If more session messages was sent
241 
242  //Check whethere there are any other elements in request
243  NodeList nl = docEl.getChildNodes();
244  for(int i = 0; i < nl.getLength(); i++){
245  Node n = nl.item(i);
246 
247  if(n.getNodeType() == Node.TEXT_NODE && n.getNodeValue().trim().length() == 0){
248  continue;
249  }
250 
251  if((!n.getNodeName().equals("session")) && (!n.getNodeName().equals("comet"))){
253  String msg = "Illegal elements in request.";
254  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
255  }
257  return requestInfo; // nothing else can be done without valid session
258  }
259  }
260 
261 
262  ArrayList<EditorSession> sessAL = new ArrayList<EditorSession>();
263  Iterator<Element> sessElIt = sessElements.iterator();
264  EditorSession tmpSess;
265 
266  while(sessElIt.hasNext()){
267  Element e = sessElIt.next();
268 
269  String sessIdStr = e.getAttribute("id");
270 
271  if (sessIdStr.isEmpty()) {
273  String msg = "Unknown session (id not sent).";
274  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
275  }
277  return requestInfo; // nothing else can be done without valid session
278  }
279 
280  Long sessId = null;
281  try {
282  sessId = Long.decode(sessIdStr);
283  } catch (NumberFormatException numberFormatException) {
286  String msg = "Bad session id: ";
287  if (sessIdStr != null) {
288  msg += sessIdStr;
289  }
290  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
291  }
292  return requestInfo; // nothing else can be done without valid session
293  }
294 
295  //Search for session
296  tmpSess = AppBean.getSession(sessId);
297 
298  if (tmpSess == null) {
299  // if session not found, prepare error message
302  String msg = "Unknown session (not found).";
303  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
304  }
305  return requestInfo; // nothing else can be done without valid session
306  }
307 
308  sessAL.add(tmpSess);
309  }
310 
311  requestInfo.setCometSessionList(sessAL);
312  session = sessAL.get(0);
313 
314  requestInfo.setSession(session);
315  requestInfo.setSessionOnly(true);
316  requestInfo.setCanNotify(false); // nothing to notify
317  return requestInfo; // all messages was processed
318 
319  } else {
320  if (idString.isEmpty()) {
322  String msg = "Unknown session (id not sent).";
323  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
324  }
326  return requestInfo; // nothing else can be done without valid session
327  }
328 
329  Long sessId = null;
330  try {
331  sessId = Long.decode(idString);
332  } catch (NumberFormatException numberFormatException) {
333  }
334 
335  // find session
336  session = null;
337  if (sessId != null) {
338  session = AppBean.getSession(sessId);
339  }
340  if (session == null) {
341  // if session not found, prepare error message
344  String msg = "Unknown session (not found).";
345  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg);
346  }
347  return requestInfo; // nothing else can be done without valid session
348  }
349 
350  requestInfo.setSession(session);
351  flier.setCreatedInSessId(sessId);
352  flier.setCreatedInSession(session);
353  }
354 
355  //choose which processor will proces the document (by version of protocol)
356  switch(session.getProtocolLOD()){
357  case Constants.PROTOCOL_LOD_V2 :{
358  // parse by processor for version 2
359  P2Processor proc = new P2Processor();
360  proc.processXMLDocument(docEl,requestInfo);
361  }break;
362 
363  case Constants.PROTOCOL_LOD_V1_1 :
364  case Constants.PROTOCOL_LOD_V1 :{
365  // parse by processor for version 1
366  P1Processor proc = new P1Processor();
367  return proc.processXML(docEl, requestInfo);
368  }
369  }
370 
371  return requestInfo;
372  } // processXML()
373 
374  /**
375  * Parses XML with messages from client
376  *
377  * @param XMLString String with XML with messages from client
378  * @return Parsed XML in w3c.dom.Document
379  */
380  private static Document parseXml(String XMLString) {
381  Document dom;
382  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
383  dbf.setNamespaceAware(true);
384 
385  try {
386  DocumentBuilder db = dbf.newDocumentBuilder();
387  InputSource is = new InputSource();
388  is.setCharacterStream(new StringReader(XMLString));
389  dom = db.parse(is);
390  } catch (ParserConfigurationException pce) {
392  String msg = "ParserConfigurationException while parsing messages from the client.";
393  Logger.getLogger(MessageProcessor.class.getName()).log(Level.SEVERE, msg, pce);
394  }
395  return null; // bad request
396  } catch (SAXException se) {
398  String msg = "SAXException while parsing messages from the client.";
399  Logger.getLogger(MessageProcessor.class.getName()).log(Level.ALL, msg, se);
400  }
401  return null;
402  } catch (IOException ioe) {
404  String msg = "IOException while parsing messages from the client.";
405  Logger.getLogger(MessageProcessor.class.getName()).log(Level.SEVERE, msg, ioe);
406  }
407  return null;
408  }
409 
410  return dom;
411  } // parseXml()
412 
413  /**
414  * Gets content of XML emement
415  * If element have only child with textual content, it returns this content.
416  * If element have CDATA section eventually surrounded with text nodes,
417  * content of CDATA section is returned.
418  *
419  * @param contentEl Element with content
420  * @return Content of XML emement
421  */
422  public static String getElementContent(Element contentEl) {
423  String content = null;
424  if (contentEl != null) { // if element is not null
425  // get child node(s) with content
426  NodeList contentCSNL = contentEl.getChildNodes();
427  if (contentCSNL.getLength() == 3) { // if it has 3 childs (text, CDATA, text)
428  // get CDATA section
429  Node contentCSEl = contentCSNL.item(1);
430  if (contentCSEl != null) {
431  content = contentCSEl.getNodeValue(); // get content of CDATA section
432  }
433  } else if (contentCSNL.getLength() == 2) { // if it has 2 childs (text, CDATA or CDATA, text)
434  // assume that CDATA is first
435  Node contentCSEl = contentCSNL.item(0);
436  if (contentCSEl != null) {
437  content = contentCSEl.getNodeValue(); // get content of CDATA
438  }
439  if ((content.trim()).isEmpty()) { // if content contains only white spaces
440  // CDATA is second (or missing, but it has a same effect)
441  contentCSEl = contentCSNL.item(1);
442  if (contentCSEl != null) {
443  content = contentCSEl.getNodeValue(); // get content of CDATA
444  }
445  }
446  } else if (contentCSNL.getLength() == 1) { // if it has 1 child (CDATA or text)
447  Node contentCSEl = contentCSNL.item(0);
448  if (contentCSEl != null) {
449  content = contentCSEl.getNodeValue(); // get content of child node
450  }
451  } else { // if it hasn't childs
452  content = contentEl.getNodeValue(); // get content of node
453  }
454  } // if element is not null
455  return unescapeXML(content);
456  } // getElementContent()
457 
458  /**
459  * Replaces arrows with spaces in given string by slashes
460  * " -> ", " ->", "-> " and "->" are replaced by "/"
461  *
462  * @param aString String with arrows
463  * @return Returns string with slashes instead arrows
464  */
465  public static String replaceArrows(String aString) {
466  String retStr = aString.replace(" -> ", "/");
467  retStr = retStr.replace(" ->", "/");
468  retStr = retStr.replace("-> ", "/");
469  retStr = retStr.replace("->", "/");
470  return retStr;
471  }
472 
473  /**
474  * Replaces escaped characters in XML with its original character representation.
475  *
476  * @param escaped Character string containing items escaped by XML processor
477  * @return Common character string free of escaped version of characters
478  */
479  public static String unescapeXML(String escaped) {
480  if (escaped != null) {
481  escaped = escaped.replaceAll(unescapeXMLApos, "\'");
482  escaped = escaped.replaceAll(unescapeXMLQuotes, "\"");
483  escaped = escaped.replaceAll(unescapeXMLLT, "<");
484  escaped = escaped.replaceAll(unescapeXMLGT, ">");
485  escaped = escaped.replaceAll(unescapeXMLAmp, "&");
486  }
487  return escaped;
488  }
489 
490  /**
491  * Converts array of bytes to hex string
492  * from http://rgagnon.com/javadetails/java-0596.html
493  *
494  * @param b Array of bytes
495  * @return Hex string
496  */
497  public static String getHexString(byte[] b) {
498  String result = "";
499  for (int i = 0; i < b.length; i++) {
500  result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
501  }
502  return result;
503  }
504 
505  /**
506  * Checks whether id of user group is included in beginning of given string
507  *
508  * @param gStr Checked string
509  * @return If id of user group is included, returns true, false otherwise
510  */
511  public static boolean isGroupIncluded(String gStr) {
512  if (!gStr.startsWith("g")) {
513  // if string doesn't start with letter g, id of group can't be correctly included
514  return false;
515  }
516  gStr = MessageProcessor.replaceArrows(gStr);
517  // find end of group id
518  int index = gStr.indexOf("/");
519  if (index < 1) { // if group id doesn't end, it's incorrect or it's not id of group
520  return false;
521  }
522  // get id of group from string
523  String gIdStr = gStr.substring(1, index);
524  // parse id of user group
525  Integer gId = null;
526  try {
527  gId = Integer.parseInt(gIdStr);
528  } catch (NumberFormatException nfe) { // if it's not number, it's not id of group
529  return false;
530  }
531  // query database for user group
532  Object[] params = new Object[2];
533  params[0] = "id";
534  params[1] = gId;
535  List gList = AppBean.getPersistenceManager().queryDB("UserGroup.findById", params);
536  if (gList != null && !gList.isEmpty()) { // if group was not found, it's not id of group
537  return true;
538  }
539  return false; // id of group is included
540  } // isGroupIncluded()
541 } // class MessageProcessor
Singleton for storing global variables.
Definition: AppBean.java:47
Class which process XML with messages in 4A protocol v 1.x.
Static class which parses and process XML with messages.
Flier with informations for comet handlers.
Definition: Flier.java:31
Processed informations about client request.
Class which parses and process XML with messages for protocol version 2.
Class responsible for localised strings.
Informations about client session.
static RequestInfo processXML(String XMLString)