source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/LibraryServlet.java@ 27074

Last change on this file since 27074 was 27074, checked in by sjm84, 11 years ago

The top level usercontext now includes the groups that the user is in

  • Property svn:keywords set to Author Date Id Revision
File size: 35.3 KB
Line 
1package org.greenstone.gsdl3;
2
3import java.io.IOException;
4import java.io.PrintWriter;
5import java.io.Serializable;
6import java.lang.reflect.Type;
7import java.util.ArrayList;
8import java.util.Enumeration;
9import java.util.HashMap;
10import java.util.Hashtable;
11import java.util.Iterator;
12import java.util.List;
13import java.util.Map;
14
15import javax.servlet.ServletConfig;
16import javax.servlet.ServletException;
17import javax.servlet.http.Cookie;
18import javax.servlet.http.HttpServletRequest;
19import javax.servlet.http.HttpServletResponse;
20import javax.servlet.http.HttpSession;
21import javax.servlet.http.HttpSessionBindingEvent;
22import javax.servlet.http.HttpSessionBindingListener;
23
24import org.apache.commons.lang3.StringUtils;
25import org.apache.log4j.Logger;
26import org.greenstone.gsdl3.action.PageAction;
27import org.greenstone.gsdl3.comms.Communicator;
28import org.greenstone.gsdl3.comms.SOAPCommunicator;
29import org.greenstone.gsdl3.core.DefaultReceptionist;
30import org.greenstone.gsdl3.core.MessageRouter;
31import org.greenstone.gsdl3.core.Receptionist;
32import org.greenstone.gsdl3.service.Authentication;
33import org.greenstone.gsdl3.util.GSConstants;
34import org.greenstone.gsdl3.util.GSParams;
35import org.greenstone.gsdl3.util.GSXML;
36import org.greenstone.gsdl3.util.UserContext;
37import org.greenstone.gsdl3.util.XMLConverter;
38import org.json.JSONObject;
39import org.w3c.dom.Document;
40import org.w3c.dom.Element;
41import org.w3c.dom.Node;
42import org.w3c.dom.NodeList;
43
44import com.google.gson.Gson;
45import com.google.gson.reflect.TypeToken;
46
47/**
48 * a servlet to serve the greenstone library - we are using servlets instead of
49 * cgi the init method is called only once - the first time the servlet classes
50 * are loaded. Each time a request comes in to the servlet, the session() method
51 * is called in a new thread (calls doGet/doPut etc) takes the a=p&p=home type
52 * args and builds a simple request to send to its receptionist, which returns a
53 * result in html, cos output=html is set in the request
54 *
55 * 18/Jul/07 xiao modify to make the cached parameters collection-specific. Most
56 * of the work is done in doGet(), except adding an inner class
57 * UserSessionCache.
58 *
59 * @see Receptionist
60 */
61public class LibraryServlet extends BaseGreenstoneServlet
62{
63 /** the receptionist to send messages to */
64 protected Receptionist recept = null;
65
66 /**
67 * the default language - is specified by setting a servlet param, otherwise
68 * DEFAULT_LANG is used
69 */
70 protected String default_lang = null;
71
72 /** Whether or not client-side XSLT support should be exposed */
73 protected boolean supports_client_xslt = false;
74
75 /**
76 * The default default - used if a default lang is not specified in the
77 * servlet params
78 */
79 protected final String DEFAULT_LANG = "en";
80
81 /** container Document to create XML Nodes */
82 protected Document doc = null;
83
84 /** a converter class to parse XML and create Docs */
85 protected XMLConverter converter = null;
86
87 /**
88 * the cgi stuff - the Receptionist can add new args to this
89 *
90 * its used by the servlet to determine what args to save
91 */
92 protected GSParams params = null;
93
94 /**
95 * user id - new one per session. This doesn't work if session state is
96 * saved between restarts - this requires this value to be saved too.
97 */
98 protected int next_user_id = 0;
99
100 /**
101 * a hash that contains all the active session IDs mapped to the cached
102 * items It is updated whenever the whole site or a particular collection is
103 * reconfigured using the command a=s&sa=c or a=s&sa=c&c=xxx It is in the
104 * form: sid -> (UserSessionCache object)
105 */
106 protected Hashtable<String, UserSessionCache> session_ids_table = new Hashtable<String, UserSessionCache>();
107
108 /**
109 * the maximum interval that the cached info remains in session_ids_table
110 * (in seconds) This is set in web.xml
111 */
112 protected int session_expiration = 1800;
113
114 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.LibraryServlet.class.getName());
115
116 /** initialise the servlet */
117 public void init(ServletConfig config) throws ServletException
118 {
119 // always call super.init;
120 super.init(config);
121 // disable preferences - does this work anyway??
122 //System.setProperty("java.util.prefs.PreferencesFactory", "org.greenstone.gsdl3.util.DisabledPreferencesFactory");
123
124 String library_name = config.getInitParameter(GSConstants.LIBRARY_NAME);
125 String interface_name = config.getInitParameter(GSConstants.INTERFACE_NAME);
126
127 String allowXslt = (String) config.getInitParameter(GSConstants.ALLOW_CLIENT_SIDE_XSLT);
128 supports_client_xslt = allowXslt != null && allowXslt.equals("true");
129
130 this.default_lang = config.getInitParameter(GSConstants.DEFAULT_LANG);
131 String sess_expire = config.getInitParameter(GSXML.SESSION_EXPIRATION);
132
133 if (sess_expire != null && !sess_expire.equals(""))
134 {
135 this.session_expiration = Integer.parseInt(sess_expire);
136 }
137
138 if (library_name == null || interface_name == null)
139 {
140 // must have this
141 System.err.println("initialisation parameters not all set!");
142 System.err.println(" you must have libraryname and interfacename");
143 System.exit(1);
144 }
145
146 String site_name = config.getInitParameter(GSConstants.SITE_NAME);
147 String remote_site_name = null;
148 String remote_site_type = null;
149 String remote_site_address = null;
150
151 if (site_name == null)
152 {
153 // no site, try for communicator
154 remote_site_name = config.getInitParameter("remote_site_name");
155 remote_site_type = config.getInitParameter("remote_site_type");
156 remote_site_address = config.getInitParameter("remote_site_address");
157 if (remote_site_name == null || remote_site_type == null || remote_site_address == null)
158 {
159 System.err.println("initialisation paramters not all set!");
160 System.err.println("if site_name is not set, then you must have remote_site_name, remote_site_type and remote_site_address set");
161 System.exit(1);
162 }
163 }
164
165 if (this.default_lang == null)
166 {
167 // choose english
168 this.default_lang = DEFAULT_LANG;
169 }
170
171 HashMap<String, Comparable> config_params = new HashMap<String, Comparable>();
172
173 config_params.put(GSConstants.LIBRARY_NAME, library_name);
174 config_params.put(GSConstants.INTERFACE_NAME, interface_name);
175 config_params.put(GSConstants.ALLOW_CLIENT_SIDE_XSLT, supports_client_xslt);
176
177 if (site_name != null)
178 {
179 config_params.put(GSConstants.SITE_NAME, site_name);
180 }
181 this.converter = new XMLConverter();
182 this.doc = this.converter.newDOM();
183
184 // the receptionist -the servlet will talk to this
185 String recept_name = (String) config.getInitParameter("receptionist_class");
186 if (recept_name == null)
187 {
188 this.recept = new DefaultReceptionist();
189 }
190 else
191 {
192 try
193 {
194 this.recept = (Receptionist) Class.forName("org.greenstone.gsdl3.core." + recept_name).newInstance();
195 }
196 catch (Exception e)
197 { // cant use this new one, so use normal one
198 System.err.println("LibraryServlet configure exception when trying to use a new Receptionist " + recept_name + ": " + e.getMessage());
199 e.printStackTrace();
200 this.recept = new DefaultReceptionist();
201 }
202 }
203 this.recept.setConfigParams(config_params);
204
205 // the receptionist uses a MessageRouter or Communicator to send its requests to. We either create a MessageRouter here for the designated site (if site_name set), or we create a Communicator for a remote site. The is given to teh Receptionist, and the servlet never talks to it again.directly.
206 if (site_name != null)
207 {
208 String mr_name = (String) config.getInitParameter("messagerouter_class");
209 MessageRouter message_router = null;
210 if (mr_name == null)
211 { // just use the normal MR
212 message_router = new MessageRouter();
213 }
214 else
215 { // try the specified one
216 try
217 {
218 message_router = (MessageRouter) Class.forName("org.greenstone.gsdl3.core." + mr_name).newInstance();
219 }
220 catch (Exception e)
221 { // cant use this new one, so use normal one
222 System.err.println("LibraryServlet configure exception when trying to use a new MessageRouter " + mr_name + ": " + e.getMessage());
223 e.printStackTrace();
224 message_router = new MessageRouter();
225 }
226 }
227
228 message_router.setSiteName(site_name);
229 message_router.setLibraryName(library_name);
230 message_router.configure();
231 this.recept.setMessageRouter(message_router);
232 }
233 else
234 {
235 // talking to a remote site, create a communicator
236 Communicator communicator = null;
237 // we need to create the XML to configure the communicator
238 Element site_elem = this.doc.createElement(GSXML.SITE_ELEM);
239 site_elem.setAttribute(GSXML.TYPE_ATT, remote_site_type);
240 site_elem.setAttribute(GSXML.NAME_ATT, remote_site_name);
241 site_elem.setAttribute(GSXML.ADDRESS_ATT, remote_site_address);
242
243 if (remote_site_type.equals(GSXML.COMM_TYPE_SOAP_JAVA))
244 {
245 communicator = new SOAPCommunicator();
246 }
247 else
248 {
249 System.err.println("LibraryServlet.init Error: invalid Communicator type: " + remote_site_type);
250 System.exit(1);
251 }
252
253 if (!communicator.configure(site_elem))
254 {
255 System.err.println("LibraryServlet.init Error: Couldn't configure communicator");
256 System.exit(1);
257 }
258 this.recept.setMessageRouter(communicator);
259 }
260
261 // the params arg thingy
262
263 String params_name = (String) config.getInitParameter("params_class");
264 if (params_name == null)
265 {
266 this.params = new GSParams();
267 }
268 else
269 {
270 try
271 {
272 this.params = (GSParams) Class.forName("org.greenstone.gsdl3.util." + params_name).newInstance();
273 }
274 catch (Exception e)
275 {
276 System.err.println("LibraryServlet configure exception when trying to use a new params thing " + params_name + ": " + e.getMessage());
277 e.printStackTrace();
278 this.params = new GSParams();
279 }
280 }
281 // pass it to the receptionist
282 this.recept.setParams(this.params);
283 this.recept.configure();
284
285 //Allow the message router and the document to be accessed from anywhere in this servlet context
286 this.getServletContext().setAttribute("GSRouter", this.recept.getMessageRouter());
287 this.getServletContext().setAttribute("GSDocument", this.doc);
288 }
289
290 private void logUsageInfo(HttpServletRequest request)
291 {
292 String usageInfo = "";
293
294 //session-info: get params stored in the session
295 HttpSession session = request.getSession(true);
296 Enumeration attributeNames = session.getAttributeNames();
297 while (attributeNames.hasMoreElements())
298 {
299 String name = (String) attributeNames.nextElement();
300 usageInfo += name + "=" + session.getAttribute(name) + " ";
301 }
302
303 //logged info = general-info + session-info
304 usageInfo = request.getServletPath() + " " + //serlvet
305 "[" + request.getQueryString() + "]" + " " + //the query string
306 "[" + usageInfo.trim() + "]" + " " + // params stored in a session
307 request.getRemoteAddr() + " " + //remote address
308 request.getRequestedSessionId() + " " + //session id
309 request.getHeader("user-agent") + " "; //the remote brower info
310
311 logger.info(usageInfo);
312
313 }
314
315 public class UserSessionCache implements HttpSessionBindingListener
316 {
317
318 String session_id = "";
319
320 /**
321 * a hash that maps the session ID to a hashtable that maps the
322 * coll_name to its parameters coll_name -> Hashtable (param_name ->
323 * param_value)
324 */
325 protected Hashtable<String, Hashtable<String, String>> coll_name_params_table = null;
326
327 public UserSessionCache(String id, Hashtable<String, Hashtable<String, String>> table)
328 {
329 session_id = id;
330 coll_name_params_table = (table == null) ? new Hashtable() : table;
331 }
332
333 protected void cleanupCache(String coll_name)
334 {
335 if (coll_name_params_table.containsKey(coll_name))
336 {
337 coll_name_params_table.remove(coll_name);
338 }
339 }
340
341 protected Hashtable<String, Hashtable<String, String>> getParamsTable()
342 {
343 return coll_name_params_table;
344 }
345
346 public void valueBound(HttpSessionBindingEvent event)
347 {
348 // Do nothing
349 }
350
351 public void valueUnbound(HttpSessionBindingEvent event)
352 {
353 if (session_ids_table.containsKey(session_id))
354 {
355 session_ids_table.remove(session_id);
356 }
357 }
358
359 public int tableSize()
360 {
361 return (coll_name_params_table == null) ? 0 : coll_name_params_table.size();
362 }
363 }
364
365 public void destroy()
366 {
367 recept.cleanUp();
368 }
369
370 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
371 {
372 logUsageInfo(request);
373
374 Map<String, String[]> queryMap = request.getParameterMap();
375 if (queryMap != null)
376 {
377 Iterator<String> queryIter = queryMap.keySet().iterator();
378 boolean redirect = false;
379 String href = null;
380 String rl = null;
381 String el = null;
382 while (queryIter.hasNext())
383 {
384 String q = queryIter.next();
385 if (q.equals(GSParams.EXTERNAL_LINK_TYPE))
386 {
387 el = queryMap.get(q)[0];
388 }
389 else if (q.equals(GSParams.HREF))
390 {
391 href = queryMap.get(q)[0];
392 href = StringUtils.replace(href, "%2f", "/");
393 href = StringUtils.replace(href, "%7e", "~");
394 href = StringUtils.replace(href, "%3f", "?");
395 href = StringUtils.replace(href, "%3A", "\\:");
396 }
397 else if (q.equals(GSParams.RELATIVE_LINK))
398 {
399 rl = queryMap.get(q)[0];
400 }
401 }
402
403 //if query_string contains "el=direct", an href is specified, and its not a relative link, then the web page will be redirected to the external URl, otherwise a greenstone page with an external URL will be displayed
404 //"rl=0" this is an external link
405 //"rl=1" this is an internal link
406 if ((href != null) && (rl.equals("0")))
407 {// This is an external link,
408
409 if (el.equals("framed"))
410 {
411 //TODO **** how best to change to a=p&sa=html&c=collection&url=href
412 // response.setContentType("text/xml");
413 //response.sendRedirect("http://localhost:8383/greenstone3/gs3library?a=p&sa=html&c=external&url="+href);
414 }
415 else
416 {
417 // el = '' or direct
418 //the web page is re-directed to the external URL (&el=&rl=0&href="http://...")
419 response.setContentType("text/xml");
420 response.sendRedirect(href);
421 }
422 }
423 }
424
425 // Nested Diagnostic Configurator to identify the client for
426 HttpSession session = request.getSession(true);
427 session.setMaxInactiveInterval(session_expiration);
428 String uid = (String) session.getAttribute(GSXML.USER_ID_ATT);
429 if (uid == null)
430 {
431 uid = "" + getNextUserId();
432 session.setAttribute(GSXML.USER_ID_ATT, uid);
433 }
434
435 request.setCharacterEncoding("UTF-8");
436 response.setContentType("text/html;charset=UTF-8");
437 PrintWriter out = response.getWriter();
438
439 String lang = request.getParameter(GSParams.LANGUAGE);
440 if (lang == null || lang.equals(""))
441 {
442 // try the session cached lang
443 lang = (String) session.getAttribute(GSParams.LANGUAGE);
444 if (lang == null || lang.equals(""))
445 {
446 // still not set, use the default
447 lang = this.default_lang;
448 }
449 }
450 UserContext userContext = new UserContext();
451 userContext.setLanguage(lang);
452 userContext.setUserID(uid);
453
454 if (request.getAuthType() != null)
455 {
456 //Get the groups for the user
457 Element acquireGroupMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
458 Element acquireGroupRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PROCESS, "GetUserInformation", userContext);
459 acquireGroupMessage.appendChild(acquireGroupRequest);
460
461 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
462 acquireGroupRequest.appendChild(paramList);
463 paramList.appendChild(GSXML.createParameter(this.doc, "username", request.getUserPrincipal().getName()));
464
465 Element aquireGroupsResponseMessage = (Element) this.recept.process(acquireGroupMessage);
466 Element aquireGroupsResponse = (Element) GSXML.getChildByTagName(aquireGroupsResponseMessage, GSXML.RESPONSE_ELEM);
467 Element param_list = (Element) GSXML.getChildByTagName(aquireGroupsResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
468
469 if (param_list != null)
470 {
471 HashMap<String, Serializable> params = GSXML.extractParams(param_list, false);
472 String groups = (String) params.get("groups");
473 userContext.setGroups(groups.split(","));
474 }
475 }
476
477 // set the lang in the session
478 session.setAttribute(GSParams.LANGUAGE, lang);
479
480 String output = request.getParameter(GSParams.OUTPUT);
481 if (output == null || output.equals(""))
482 {
483 output = "html"; // uses html by default
484 }
485
486 // If server output, force a switch to traditional interface
487 //output = (output.equals("server")) ? "html" : output;
488
489 // Force change the output mode if client-side XSLT is supported - server vs. client
490 // BUT only if the library allows client-side transforms
491 if (supports_client_xslt)
492 {
493 // MUST be done before the xml_message is built
494 Cookie[] cookies = request.getCookies();
495 Cookie xsltCookie = null;
496
497 // The client has cookies enabled and a value set - use it!
498 if (cookies != null)
499 {
500 for (Cookie c : cookies)
501 {
502 if (c.getName().equals("supportsXSLT"))
503 {
504 xsltCookie = c;
505 break;
506 }
507 }
508 output = (xsltCookie != null && xsltCookie.getValue().equals("true") && output.equals("html")) ? "xsltclient" : output;
509 }
510 }
511
512 // the request to the receptionist
513 Element xml_message = this.doc.createElement(GSXML.MESSAGE_ELEM);
514 Element xml_request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
515 xml_request.setAttribute(GSXML.OUTPUT_ATT, output);
516
517 xml_message.appendChild(xml_request);
518
519 String action = request.getParameter(GSParams.ACTION);
520 String subaction = request.getParameter(GSParams.SUBACTION);
521 String collection = request.getParameter(GSParams.COLLECTION);
522 String document = request.getParameter(GSParams.DOCUMENT);
523 String service = request.getParameter(GSParams.SERVICE);
524
525 // We clean up the cache session_ids_table if system
526 // commands are issued (and also don't need to do caching for this request)
527 boolean should_cache = true;
528 if (action != null && action.equals(GSParams.SYSTEM_ACTION))
529 {
530 should_cache = false;
531
532 // we may want to remove all collection cache info, or just a specific collection
533 boolean clean_all = true;
534 String clean_collection = null;
535 // system commands are to activate/deactivate stuff
536 // collection param is in the sc parameter.
537 // don't like the fact that it is hard coded here
538 String coll = request.getParameter(GSParams.SYSTEM_CLUSTER);
539 if (coll != null && !coll.equals(""))
540 {
541 clean_all = false;
542 clean_collection = coll;
543 }
544 else
545 {
546 // check other system types
547 if (subaction.equals("a") || subaction.equals("d"))
548 {
549 String module_name = request.getParameter("sn");
550 if (module_name != null && !module_name.equals(""))
551 {
552 clean_all = false;
553 clean_collection = module_name;
554 }
555 }
556 }
557 if (clean_all)
558 {
559 session_ids_table = new Hashtable<String, UserSessionCache>();
560 session.removeAttribute(GSXML.USER_SESSION_CACHE_ATT);
561 }
562 else
563 {
564 // just clean up info for clean_collection
565 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(session_ids_table.values());
566 for (int i = 0; i < cache_list.size(); i++)
567 {
568 UserSessionCache cache = cache_list.get(i);
569 cache.cleanupCache(clean_collection);
570 }
571
572 }
573 }
574
575 // cache_key is the collection name, or service name
576 String cache_key = collection;
577 if (cache_key == null || cache_key.equals(""))
578 {
579 cache_key = service;
580 }
581
582 // logger.info("should_cache= " + should_cache);
583
584 //clear the collection-specific cache in the session, since we have no way to know whether this session is
585 //about the same collection as the last session or not.
586 Enumeration attributeNames = session.getAttributeNames();
587 while (attributeNames.hasMoreElements())
588 {
589 String name = (String) attributeNames.nextElement();
590 if (!name.equals(GSXML.USER_SESSION_CACHE_ATT) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
591 {
592 session.removeAttribute(name);
593 }
594 }
595
596 UserSessionCache session_cache = null;
597 Hashtable<String, Hashtable<String, String>> param_table = null;
598 Hashtable<String, String> table = null;
599 String sid = session.getId();
600 if (should_cache == true && cache_key != null && !cache_key.equals(""))
601 {
602 if (session_ids_table.containsKey(sid))
603 {
604 session_cache = session_ids_table.get(sid);
605 param_table = session_cache.getParamsTable();
606 logger.info("collections in table: " + tableToString(param_table));
607 if (param_table.containsKey(cache_key))
608 {
609 //logger.info("existing table: " + collection);
610 table = param_table.get(cache_key);
611 }
612 else
613 {
614 table = new Hashtable<String, String>();
615 param_table.put(cache_key, table);
616 //logger.info("new table: " + collection);
617 }
618 }
619 else
620 {
621 param_table = new Hashtable<String, Hashtable<String, String>>();
622 table = new Hashtable<String, String>();
623 param_table.put(cache_key, table);
624 session_cache = new UserSessionCache(sid, param_table);
625 session_ids_table.put(sid, session_cache);
626 session.setAttribute(GSXML.USER_SESSION_CACHE_ATT, session_cache);
627 //logger.info("new session id");
628 }
629 }
630
631 if (action == null || action.equals(""))
632 {
633 // should we do all the following stuff if using default page?
634 // display the home page - the default page
635 xml_request.setAttribute(GSXML.ACTION_ATT, "p");
636 xml_request.setAttribute(GSXML.SUBACTION_ATT, PageAction.HOME_PAGE);
637 }
638 else
639 {
640 xml_request.setAttribute(GSXML.ACTION_ATT, action);
641 if (subaction != null)
642 {
643 xml_request.setAttribute(GSXML.SUBACTION_ATT, subaction);
644 }
645
646 // create the param list for the greenstone request - includes
647 // the params from the current request and any others from the saved session
648 Element xml_param_list = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
649 xml_request.appendChild(xml_param_list);
650
651 for (String name : queryMap.keySet())
652 {
653 if (!name.equals(GSParams.ACTION) && !name.equals(GSParams.SUBACTION) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSParams.OUTPUT))
654 {// we have already dealt with these
655
656 String value = "";
657 String[] values = request.getParameterValues(name);
658 value = values[0];
659 if (values.length > 1)
660 {
661 for (int i = 1; i < values.length; i++)
662 {
663 value += "," + values[i];
664 }
665 }
666 // either add it to the param list straight away, or save it to the session and add it later
667 if (this.params.shouldSave(name) && table != null)
668 {
669 table.put(name, value);
670 }
671 else
672 {
673 Element param = this.doc.createElement(GSXML.PARAM_ELEM);
674 param.setAttribute(GSXML.NAME_ATT, name);
675 param.setAttribute(GSXML.VALUE_ATT, GSXML.xmlSafe(value));
676 xml_param_list.appendChild(param);
677 }
678 }
679 }
680 //put everything in the table into the session
681 // do we need to do this? why not just put from table into param list
682 if (table != null)
683 {
684 Enumeration<String> keys = table.keys();
685 while (keys.hasMoreElements())
686 {
687 String name = keys.nextElement();
688 session.setAttribute(name, table.get(name));
689 }
690 }
691
692 // put in all the params from the session cache
693 Enumeration params = session.getAttributeNames();
694 while (params.hasMoreElements())
695 {
696 String name = (String) params.nextElement();
697
698 if (!name.equals(GSXML.USER_SESSION_CACHE_ATT) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
699 {
700
701 // lang and uid are stored but we dont want it in the param list cos its already in the request
702 Element param = this.doc.createElement(GSXML.PARAM_ELEM);
703 param.setAttribute(GSXML.NAME_ATT, name);
704 String value = GSXML.xmlSafe((String) session.getAttribute(name));
705
706 // ugly hack to undo : escaping
707 value = StringUtils.replace(value, "%3A", "\\:");
708 param.setAttribute(GSXML.VALUE_ATT, value);
709 xml_param_list.appendChild(param);
710 }
711 }
712 }
713
714 if (output.equals("json"))
715 {
716 response.setContentType("application/json");
717 }
718 else if (!output.equals("html") && !output.equals("server") && !output.equals("xsltclient"))
719 {
720 response.setContentType("text/xml"); // for now use text
721 }
722
723 //Add custom HTTP headers if requested
724 String httpHeadersParam = request.getParameter(GSParams.HTTP_HEADER_FIELDS);
725 if (httpHeadersParam != null && httpHeadersParam.length() > 0)
726 {
727 Gson gson = new Gson();
728 Type type = new TypeToken<List<Map<String, String>>>()
729 {
730 }.getType();
731 List<Map<String, String>> httpHeaders = gson.fromJson(httpHeadersParam, type);
732 if (httpHeaders != null && httpHeaders.size() > 0)
733 {
734
735 for (int j = 0; j < httpHeaders.size(); j++)
736 {
737 Map nameValueMap = httpHeaders.get(j);
738 String name = (String) nameValueMap.get("name");
739 String value = (String) nameValueMap.get("value");
740
741 if (name != null && value != null)
742 {
743 response.setHeader(name, value);
744 }
745 }
746 }
747 }
748
749 String requestedURL = request.getRequestURL().toString();
750 String baseURL = "";
751 if (requestedURL.indexOf(this.getServletName()) != -1)
752 {
753 baseURL = requestedURL.substring(0, requestedURL.indexOf(this.getServletName()));
754 xml_request.setAttribute("baseURL", baseURL);
755 }
756
757 String fullURL;
758 if (request.getQueryString() != null)
759 {
760 fullURL = requestedURL + "?" + request.getQueryString();
761 }
762 else
763 {
764 fullURL = requestedURL;
765 }
766
767 xml_request.setAttribute("remoteAddress", request.getRemoteAddr());
768 xml_request.setAttribute("fullURL", fullURL.replace("&", "&amp;"));
769
770 if (!runSecurityChecks(request, xml_request, userContext, out, baseURL, collection, document))
771 {
772 return;
773 }
774
775 Node xml_result = this.recept.process(xml_message);
776 encodeURLs(xml_result, response);
777
778 String xml_string = this.converter.getPrettyString(xml_result);
779
780 if (output.equals("json"))
781 {
782 try
783 {
784 JSONObject json_obj = org.json.XML.toJSONObject(xml_string);
785
786 out.println(json_obj.toString());
787 }
788 catch (Exception e)
789 {
790 e.printStackTrace();
791 out.println("Error: failed to convert output XML to JSON format");
792 }
793 }
794 else
795 {
796 out.println(xml_string);
797 }
798
799 displaySize(session_ids_table);
800
801 } //end of doGet(HttpServletRequest, HttpServletResponse)
802
803 private boolean runSecurityChecks(HttpServletRequest request, Element xml_request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document) throws ServletException
804 {
805 //Check if we need to login or logout
806 Map<String, String[]> params = request.getParameterMap();
807 String[] username = params.get("username");
808 String[] password = params.get("password");
809 String[] logout = params.get("logout");
810
811 if (logout != null)
812 {
813 request.logout();
814 }
815
816 if (username != null && password != null)
817 {
818 //We are changing to another user, so log out first
819 if (request.getAuthType() != null)
820 {
821 request.logout();
822 }
823
824 try
825 {
826 password[0] = Authentication.hashPassword(password[0]);
827 request.login(username[0], password[0]);
828 }
829 catch (Exception ex)
830 {
831 //The user entered in either the wrong username or the wrong password
832 Element loginPageMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
833 Element loginPageRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
834 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
835 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
836 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, "html");
837 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
838 loginPageMessage.appendChild(loginPageRequest);
839
840 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
841 loginPageRequest.appendChild(paramList);
842
843 Element messageParam = this.doc.createElement(GSXML.PARAM_ELEM);
844 messageParam.setAttribute(GSXML.NAME_ATT, "loginMessage");
845 messageParam.setAttribute(GSXML.VALUE_ATT, "Either your username or password was incorrect, please try again.");
846 paramList.appendChild(messageParam);
847
848 Element urlParam = this.doc.createElement(GSXML.PARAM_ELEM);
849 urlParam.setAttribute(GSXML.NAME_ATT, "redirectURL");
850 String queryString = "";
851 if (request.getQueryString() != null)
852 {
853 queryString = "?" + request.getQueryString().replace("&", "&amp;");
854 }
855 urlParam.setAttribute(GSXML.VALUE_ATT, this.getServletName() + queryString);
856 paramList.appendChild(urlParam);
857
858 Node loginPageResponse = this.recept.process(loginPageMessage);
859 out.println(this.converter.getPrettyString(loginPageResponse));
860
861 return false;
862 }
863 }
864
865 //If a user is logged in
866 if (request.getAuthType() != null)
867 {
868 Element userInformation = this.doc.createElement(GSXML.USER_INFORMATION_ELEM);
869 userInformation.setAttribute("username", request.getUserPrincipal().getName());
870
871 Element userInfoMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
872 Element userInfoRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_SECURITY, "GetUserInformation", userContext);
873 userInfoMessage.appendChild(userInfoRequest);
874
875 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
876 userInfoRequest.appendChild(paramList);
877
878 Element param = this.doc.createElement(GSXML.PARAM_ELEM);
879 param.setAttribute(GSXML.NAME_ATT, GSXML.USERNAME_ATT);
880 param.setAttribute(GSXML.VALUE_ATT, request.getUserPrincipal().getName());
881 paramList.appendChild(param);
882
883 Element userInformationResponse = (Element) GSXML.getChildByTagName(this.recept.process(userInfoMessage), GSXML.RESPONSE_ELEM);
884 Element responseParamList = (Element) GSXML.getChildByTagName(userInformationResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
885 if (responseParamList == null)
886 {
887 logger.error("Can't get the groups for user " + request.getUserPrincipal().getName());
888 }
889 else
890 {
891 HashMap<String, Serializable> responseParams = GSXML.extractParams(responseParamList, true);
892 String groups = (String) responseParams.get(GSXML.GROUPS_ATT);
893
894 userInformation.setAttribute(GSXML.GROUPS_ATT, groups);
895 xml_request.appendChild(userInformation);
896 }
897 }
898
899 //If we are in a collection-related page then make sure this user is allowed to access it
900 if (collection != null && !collection.equals(""))
901 {
902 //Get the security info for this collection
903 Element securityMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
904 Element securityRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_SECURITY, collection, userContext);
905 securityMessage.appendChild(securityRequest);
906 if (document != null && !document.equals(""))
907 {
908 securityRequest.setAttribute(GSXML.NODE_OID, document);
909 }
910
911 Element securityResponse = (Element) GSXML.getChildByTagName(this.recept.process(securityMessage), GSXML.RESPONSE_ELEM);
912 if (securityResponse == null)
913 {
914 return false;
915 }
916
917 ArrayList<String> groups = GSXML.getGroupsFromSecurityResponse(securityResponse);
918
919 //If guests are not allowed to access this page then check to see if the user is in a group that is allowed to access the page
920 if (!groups.contains(""))
921 {
922 boolean found = false;
923 for (String group : groups)
924 {
925 if (request.isUserInRole(group))
926 {
927 found = true;
928 break;
929 }
930 }
931
932 //The current user is not allowed to access the page so produce a login page
933 if (!found)
934 {
935 Element loginPageMessage = this.doc.createElement(GSXML.MESSAGE_ELEM);
936 Element loginPageRequest = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
937 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
938 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
939 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, "html");
940 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
941 loginPageMessage.appendChild(loginPageRequest);
942
943 Element paramList = this.doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
944 loginPageRequest.appendChild(paramList);
945
946 Element messageParam = this.doc.createElement(GSXML.PARAM_ELEM);
947 messageParam.setAttribute(GSXML.NAME_ATT, "loginMessage");
948 if (request.getAuthType() == null)
949 {
950 messageParam.setAttribute(GSXML.VALUE_ATT, "Please log in to view this page");
951 }
952 else
953 {
954 messageParam.setAttribute(GSXML.VALUE_ATT, "You are not in the correct group to view this page, would you like to log in as a different user?");
955 }
956 paramList.appendChild(messageParam);
957
958 Element urlParam = this.doc.createElement(GSXML.PARAM_ELEM);
959 urlParam.setAttribute(GSXML.NAME_ATT, "redirectURL");
960 if (request.getQueryString() != null && request.getQueryString().length() > 0)
961 {
962 urlParam.setAttribute(GSXML.VALUE_ATT, request.getRequestURL() + "?" + request.getQueryString().replace("&", "&amp;"));
963 }
964 else
965 {
966 urlParam.setAttribute(GSXML.VALUE_ATT, request.getRequestURL().toString());
967 }
968 paramList.appendChild(urlParam);
969
970 Node loginPageResponse = this.recept.process(loginPageMessage);
971 out.println(this.converter.getPrettyString(loginPageResponse));
972
973 return false;
974 }
975 }
976 }
977 return true;
978 }
979
980 //a debugging method
981 private void displaySize(Hashtable<String, UserSessionCache> table)
982 {
983 if (table == null)
984 {
985 logger.info("cached table is null");
986 return;
987 }
988 if (table.size() == 0)
989 {
990 logger.info("cached table size is zero");
991 return;
992 }
993 int num_cached_coll = 0;
994 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(table.values());
995 for (int i = 0; i < cache_list.size(); i++)
996 {
997 num_cached_coll += cache_list.get(i).tableSize();
998 }
999 logger.info("Number of sessions : total number of cached collection info = " + table.size() + " : " + num_cached_coll);
1000 }
1001
1002 /** merely a debugging method! */
1003 private String tableToString(Hashtable<String, Hashtable<String, String>> table)
1004 {
1005 String str = "";
1006 Enumeration<String> keys = table.keys();
1007 while (keys.hasMoreElements())
1008 {
1009 String name = keys.nextElement();
1010 str += name + ", ";
1011 }
1012 return str;
1013 }
1014
1015 /**
1016 * this goes through each URL and adds in a session id if needed-- its
1017 * needed if the browser doesn't accept cookies also escapes things if
1018 * needed
1019 */
1020 protected void encodeURLs(Node dataNode, HttpServletResponse response)
1021 {
1022
1023 if (dataNode == null)
1024 {
1025 return;
1026 }
1027
1028 Element data = null;
1029
1030 short nodeType = dataNode.getNodeType();
1031 if (nodeType == Node.DOCUMENT_NODE)
1032 {
1033 Document docNode = (Document) dataNode;
1034 data = docNode.getDocumentElement();
1035 }
1036 else
1037 {
1038 data = (Element) dataNode;
1039 }
1040
1041 if (data != null)
1042 {
1043
1044 // get all the <a> elements
1045 NodeList hrefs = data.getElementsByTagName("a");
1046 // Instead of calculating each iteration...
1047 int hrefscount = hrefs.getLength();
1048
1049 for (int i = 0; hrefs != null && i < hrefscount; i++)
1050 {
1051 Element a = (Element) hrefs.item(i);
1052 // ugly hack to get rid of : in the args - interferes with session handling
1053 String href = a.getAttribute("href");
1054 if (!href.equals(""))
1055 {
1056 if (href.indexOf("?") != -1)
1057 {
1058 String[] parts = StringUtils.split(href, "\\?", -1);
1059 if (parts.length == 1)
1060 {
1061 parts[0] = StringUtils.replace(parts[0], ":", "%3A");
1062 href = "?" + parts[0];
1063 }
1064 else
1065 {
1066 parts[1] = StringUtils.replace(parts[1], ":", "%3A");
1067 href = parts[0] + "?" + parts[1];
1068 }
1069
1070 }
1071 a.setAttribute("href", response.encodeURL(href));
1072 }
1073 }
1074
1075 // now find any submit bits - get all the <form> elements
1076 NodeList forms = data.getElementsByTagName("form");
1077 int formscount = forms.getLength();
1078 for (int i = 0; forms != null && i < formscount; i++)
1079 {
1080 Element form = (Element) forms.item(i);
1081 form.setAttribute("action", response.encodeURL(form.getAttribute("action")));
1082 }
1083 // are these the only cases where URLs occur??
1084 // we should only do this for greenstone urls?
1085 }
1086
1087 }
1088
1089 synchronized protected int getNextUserId()
1090 {
1091 next_user_id++;
1092 return next_user_id;
1093 }
1094
1095 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
1096 {
1097 doGet(request, response);
1098 }
1099}
Note: See TracBrowser for help on using the repository browser.