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

Last change on this file since 28295 was 28295, checked in by kjdon, 11 years ago

we can't use getServletName() to work out the servlet name in URL (eg library) as the servlet name is the name from web.xml and may not be the same as the url path. So store library_name from the initialisation params and use that instead.

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