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

Last change on this file since 32716 was 32716, checked in by kjdon, 5 years ago

params are cached in a user's session per collection, or for eg cross collection search, by the service name. This lead to problems when doing cross collection search, the params eg query string, are cached under TextQuery, but when we view a document, we are getting the params form the colleciton cache. so teh query string is either not there or wrong, and search term highlighting either doesn't work or highlights the wrong things. now we can pass a cgi parameter, ck for 'cache key' - use that to get cached params instead of colleciton name. ccs searches need to pass that param to document links.

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