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

Last change on this file since 32475 was 32447, checked in by kjdon, 6 years ago

initialise params class earlier, so we can pass to MR as well as to recpt. - services now need to add any params that they want saving in the session

  • Property svn:keywords set to Author Date Id Revision
File size: 39.8 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
552 // We clean up the cache session_ids_table if system
553 // commands are issued (and also don't need to do caching for this request)
554 boolean should_cache = true;
555 if (action != null && action.equals(GSParams.SYSTEM_ACTION)
556 && !subaction.equals(GSXML.SYSTEM_TYPE_PING)
557 && !subaction.equals(GSXML.SYSTEM_TYPE_AUTHENTICATED_PING)) // don't 'clean' anything on a mere ping
558 {
559 should_cache = false;
560
561 // we may want to remove all collection cache info, or just a specific collection
562 boolean clean_all = true;
563 String clean_collection = null;
564 // system commands are to activate/deactivate stuff
565 // collection param is in the sc parameter.
566 // don't like the fact that it is hard coded here
567 String coll = getFirstParam(GSParams.SYSTEM_CLUSTER, queryMap);
568 if (coll != null && !coll.equals(""))
569 {
570 clean_all = false;
571 clean_collection = coll;
572 }
573 else
574 {
575 // check other system types
576 if (subaction.equals("a") || subaction.equals("d"))
577 {
578 String module_name = getFirstParam("sn", queryMap);
579 if (module_name != null && !module_name.equals(""))
580 {
581 clean_all = false;
582 clean_collection = module_name;
583 }
584 }
585 }
586 if (clean_all)
587 {
588 // TODO
589 session_ids_table = new Hashtable<String, UserSessionCache>();
590 session.removeAttribute(GSXML.USER_SESSION_CACHE_ATT); // triggers valueUnbound(), which removes the session id from the session_ids_table
591 }
592 else
593 {
594 // just clean up info for clean_collection
595 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(session_ids_table.values());
596 for (int i = 0; i < cache_list.size(); i++)
597 {
598 UserSessionCache cache = cache_list.get(i);
599 cache.cleanupCache(clean_collection);
600 }
601
602 }
603 }
604
605 // cache_key is the collection name, or service name
606 String cache_key = collection;
607 if (cache_key == null || cache_key.equals(""))
608 {
609 cache_key = service;
610 }
611
612 // logger.info("should_cache= " + should_cache);
613
614 //clear the collection-specific cache in the session, since we have no way to know whether this session is
615 //about the same collection as the last session or not.
616 Enumeration attributeNames = session.getAttributeNames();
617 while (attributeNames.hasMoreElements())
618 {
619 String name = (String) attributeNames.nextElement();
620 if (!name.equals(GSXML.USER_SESSION_CACHE_ATT) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
621 {
622 session.removeAttribute(name);
623 }
624 }
625
626 UserSessionCache session_cache = null;
627 Hashtable<String, Hashtable<String, String>> param_table = null;
628 Hashtable<String, String> table = null;
629 String sid = session.getId();
630 if (should_cache == true && cache_key != null && !cache_key.equals(""))
631 {
632 if (session_ids_table.containsKey(sid))
633 {
634 session_cache = session_ids_table.get(sid);
635 param_table = session_cache.getParamsTable();
636 logger.info("collections in table: " + tableToString(param_table));
637 if (param_table.containsKey(cache_key))
638 {
639 //logger.info("existing table: " + collection);
640 table = param_table.get(cache_key);
641 }
642 else
643 {
644 table = new Hashtable<String, String>();
645 param_table.put(cache_key, table);
646 //logger.info("new table: " + collection);
647 }
648 }
649 else
650 {
651 param_table = new Hashtable<String, Hashtable<String, String>>();
652 table = new Hashtable<String, String>();
653 param_table.put(cache_key, table);
654 session_cache = new UserSessionCache(sid, param_table);
655 session_ids_table.put(sid, session_cache);
656 session.setAttribute(GSXML.USER_SESSION_CACHE_ATT, session_cache);
657 //logger.info("new session id");
658 }
659 }
660
661 if (action == null || action.equals(""))
662 {
663 // should we do all the following stuff if using default page?
664 // display the home page - the default page
665 xml_request.setAttribute(GSXML.ACTION_ATT, "p");
666 xml_request.setAttribute(GSXML.SUBACTION_ATT, PageAction.HOME_PAGE);
667 }
668 else
669 {
670 xml_request.setAttribute(GSXML.ACTION_ATT, action);
671 if (subaction != null)
672 {
673 xml_request.setAttribute(GSXML.SUBACTION_ATT, subaction);
674 }
675
676 // create the param list for the greenstone request - includes
677 // the params from the current request and any others from the saved session
678 Element xml_param_list = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
679 xml_request.appendChild(xml_param_list);
680
681 if (queryMap.containsKey("s1.collection") || queryMap.containsKey("s1.group")){
682 table.remove("s1.collection");
683 table.remove("s1.group");
684 }
685 for (String name : queryMap.keySet())
686 {
687 if (!name.equals(GSParams.ACTION) && !name.equals(GSParams.SUBACTION) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSParams.OUTPUT))
688 {// we have already dealt with these
689
690 String value = "";
691 String[] values = queryMap.get(name);
692 value = values[0];
693 if (values.length > 1)
694 {
695 for (int i = 1; i < values.length; i++)
696 {
697 value += "," + values[i];
698 }
699 }
700 // either add it to the param list straight away, or save it to the session and add it later
701 if (this.params.shouldSave(name) && table != null)
702 {
703 table.put(name, value);
704 }
705 else
706 {
707 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
708 param.setAttribute(GSXML.NAME_ATT, name);
709 param.setAttribute(GSXML.VALUE_ATT, GSXML.xmlSafe(value));
710 if (this.params.isSensitive(name)) {
711 param.setAttribute(GSXML.SENSITIVE_ATT, "true");
712 }
713 xml_param_list.appendChild(param);
714
715 }
716 }
717 }
718 //put everything in the table into the session
719 // do we need to do this? why not just put from table into param list
720 if (table != null)
721 {
722 Enumeration<String> keys = table.keys();
723 while (keys.hasMoreElements())
724 {
725 String name = keys.nextElement();
726 session.setAttribute(name, table.get(name));
727 }
728 }
729
730 // put in all the params from the session cache
731 Enumeration params = session.getAttributeNames();
732 while (params.hasMoreElements())
733 {
734 String name = (String) params.nextElement();
735
736 if (!name.equals(GSXML.USER_SESSION_CACHE_ATT) && !name.equals(GSParams.LANGUAGE) && !name.equals(GSXML.USER_ID_ATT))
737 {
738
739 // lang and uid are stored but we dont want it in the param list cos its already in the request
740 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
741 param.setAttribute(GSXML.NAME_ATT, name);
742 String value = GSXML.xmlSafe((String) session.getAttribute(name));
743
744 // ugly hack to undo : escaping
745 value = StringUtils.replace(value, "%3A", "\\:");
746 param.setAttribute(GSXML.VALUE_ATT, value);
747 xml_param_list.appendChild(param);
748 }
749 }
750 }
751
752 if (output.equals("json"))
753 {
754 response.setContentType("application/json");
755 }
756 else if (!output.equals("html") && !output.equals("server") && !output.equals("xsltclient"))
757 {
758 response.setContentType("text/xml"); // for now use text
759 }
760
761 //Add custom HTTP headers if requested
762 String httpHeadersParam = getFirstParam(GSParams.HTTP_HEADER_FIELDS, queryMap);
763 if (httpHeadersParam != null && httpHeadersParam.length() > 0)
764 {
765 Gson gson = new Gson();
766 Type type = new TypeToken<List<Map<String, String>>>()
767 {
768 }.getType();
769 List<Map<String, String>> httpHeaders = gson.fromJson(httpHeadersParam, type);
770 if (httpHeaders != null && httpHeaders.size() > 0)
771 {
772
773 for (int j = 0; j < httpHeaders.size(); j++)
774 {
775 Map nameValueMap = httpHeaders.get(j);
776 String name = (String) nameValueMap.get("name");
777 String value = (String) nameValueMap.get("value");
778
779 if (name != null && value != null)
780 {
781 response.setHeader(name, value);
782 }
783 }
784 }
785 }
786
787 String requestedURL = request.getRequestURL().toString();
788 String baseURL = "";
789 if (requestedURL.indexOf(library_name) != -1)
790 {
791 baseURL = requestedURL.substring(0, requestedURL.indexOf(library_name));
792 xml_request.setAttribute("baseURL", baseURL);
793 }
794 String fullURL;
795 if (request.getQueryString() != null)
796 {
797 fullURL = requestedURL + "?" + request.getQueryString();
798 }
799 else
800 {
801 fullURL = requestedURL;
802 }
803
804 xml_request.setAttribute("remoteAddress", request.getRemoteAddr());
805 xml_request.setAttribute("fullURL", fullURL.replace("&", "&amp;"));
806
807 if (!runSecurityChecks(request, xml_request, userContext, out, baseURL, collection, document, queryMap))
808 {
809 return;
810 }
811
812 Node xml_result = this.recept.process(xml_message);
813 encodeURLs(xml_result, response);
814
815 String xml_string = XMLConverter.getPrettyString(xml_result);
816
817 if (output.equals("json"))
818 {
819 try
820 {
821 JSONObject json_obj = org.json.XML.toJSONObject(xml_string);
822
823 out.println(json_obj.toString());
824 }
825 catch (Exception e)
826 {
827 e.printStackTrace();
828 out.println("Error: failed to convert output XML to JSON format");
829 }
830 }
831 else
832 {
833 out.println(xml_string);
834 }
835
836 displaySize(session_ids_table);
837 }
838
839 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
840 {
841 doGetOrPost(request, response, request.getParameterMap());
842 } //end of doGet(HttpServletRequest, HttpServletResponse)
843
844 private boolean runSecurityChecks(HttpServletRequest request, Element xml_request, UserContext userContext, PrintWriter out, String baseURL, String collection, String document, Map<String, String[]> queryMap) throws ServletException
845 {
846 //Check if we need to login or logout
847 String username = getFirstParam(GSParams.USERNAME, queryMap);
848 String password = getFirstParam(GSParams.PASSWORD, queryMap);
849 String logout = getFirstParam(GSParams.LOGOUT, queryMap);
850
851 if (logout != null)
852 {
853 request.logout();
854 }
855
856 if (username != null && password != null)
857 {
858 //We are changing to another user, so log out first
859 if (request.getAuthType() != null)
860 {
861 request.logout();
862 }
863
864 //This try/catch block catches when the login request fails (e.g. The user enters an incorrect password).
865 try
866 {
867 //Try a global login first
868 password = Authentication.hashPassword(password);
869 request.login(username, password);
870 }
871 catch (Exception ex)
872 {
873 try
874 {
875 //If the global login fails then try a site-level login
876 String siteName = (String) this.recept.getConfigParams().get(GSConstants.SITE_NAME);
877 request.login(siteName + "-" + username, password);
878 }
879 catch (Exception exc)
880 {
881 //The user entered in either the wrong username or the wrong password
882 Document loginPageDoc = XMLConverter.newDOM();
883 Element loginPageMessage = loginPageDoc.createElement(GSXML.MESSAGE_ELEM);
884 Element loginPageRequest = GSXML.createBasicRequest(loginPageDoc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
885 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
886 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
887 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, "html");
888 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
889 loginPageMessage.appendChild(loginPageRequest);
890
891 Element paramList = loginPageDoc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
892 loginPageRequest.appendChild(paramList);
893
894 Element messageParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
895 messageParam.setAttribute(GSXML.NAME_ATT, "loginMessage");
896 messageParam.setAttribute(GSXML.VALUE_ATT, "Either your username or password was incorrect, please try again.");
897 paramList.appendChild(messageParam);
898
899 Element urlParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
900 urlParam.setAttribute(GSXML.NAME_ATT, "redirectURL");
901 String queryString = "";
902 if (request.getQueryString() != null)
903 {
904 queryString = "?" + request.getQueryString().replace("&", "&amp;");
905 }
906 urlParam.setAttribute(GSXML.VALUE_ATT, library_name + queryString);
907 paramList.appendChild(urlParam);
908
909 Node loginPageResponse = this.recept.process(loginPageMessage);
910 out.println(XMLConverter.getPrettyString(loginPageResponse));
911
912 return false;
913 }
914 }
915 }
916
917 //If a user is logged in
918 if (request.getAuthType() != null)
919 {
920 Element userInformation = xml_request.getOwnerDocument().createElement(GSXML.USER_INFORMATION_ELEM);
921 userInformation.setAttribute(GSXML.USERNAME_ATT, request.getUserPrincipal().getName());
922
923 Document msg_doc = XMLConverter.newDOM();
924 Element userInfoMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
925 Element userInfoRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_SECURITY, "GetUserInformation", userContext);
926 userInfoMessage.appendChild(userInfoRequest);
927
928 Element paramList = msg_doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
929 userInfoRequest.appendChild(paramList);
930
931 Element param = msg_doc.createElement(GSXML.PARAM_ELEM);
932 param.setAttribute(GSXML.NAME_ATT, GSXML.USERNAME_ATT);
933 param.setAttribute(GSXML.VALUE_ATT, request.getUserPrincipal().getName());
934 paramList.appendChild(param);
935
936 Element userInformationResponse = (Element) GSXML.getChildByTagName(this.recept.process(userInfoMessage), GSXML.RESPONSE_ELEM);
937 Element responseParamList = (Element) GSXML.getChildByTagName(userInformationResponse, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
938 if (responseParamList == null)
939 {
940 logger.error("Can't get the groups for user " + request.getUserPrincipal().getName());
941 }
942 else
943 {
944 HashMap<String, Serializable> responseParams = GSXML.extractParams(responseParamList, true);
945 String groups = (String) responseParams.get(GSXML.GROUPS_ATT);
946 String editEnabled = (String) responseParams.get("editEnabled");
947
948 userInformation.setAttribute(GSXML.GROUPS_ATT, groups);
949 userInformation.setAttribute("editEnabled", (editEnabled != null) ? editEnabled : "false");
950 xml_request.appendChild(userInformation);
951 }
952 }
953
954 //If we are in a collection-related page then make sure this user is allowed to access it
955 if (collection != null && !collection.equals(""))
956 {
957 //Get the security info for this collection
958 Document msg_doc = XMLConverter.newDOM();
959 Element securityMessage = msg_doc.createElement(GSXML.MESSAGE_ELEM);
960 Element securityRequest = GSXML.createBasicRequest(msg_doc, GSXML.REQUEST_TYPE_SECURITY, collection, userContext);
961 securityMessage.appendChild(securityRequest);
962 if (document != null && !document.equals(""))
963 {
964 securityRequest.setAttribute(GSXML.NODE_OID, document);
965 }
966
967 Element securityResponse = (Element) GSXML.getChildByTagName(this.recept.process(securityMessage), GSXML.RESPONSE_ELEM);
968 if (securityResponse == null)
969 {
970 return false;
971 }
972
973 ArrayList<String> groups = GSXML.getGroupsFromSecurityResponse(securityResponse);
974
975 //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
976 if (!groups.contains(""))
977 {
978 boolean found = false;
979 for (String group : groups)
980 {
981 if (request.isUserInRole(group))
982 {
983 found = true;
984 break;
985 }
986 }
987
988 //The current user is not allowed to access the page so produce a login page
989 if (!found)
990 {
991 Document loginPageDoc = XMLConverter.newDOM();
992 Element loginPageMessage = loginPageDoc.createElement(GSXML.MESSAGE_ELEM);
993 Element loginPageRequest = GSXML.createBasicRequest(loginPageDoc, GSXML.REQUEST_TYPE_PAGE, "", userContext);
994 loginPageRequest.setAttribute(GSXML.ACTION_ATT, "p");
995 loginPageRequest.setAttribute(GSXML.SUBACTION_ATT, "login");
996 loginPageRequest.setAttribute(GSXML.OUTPUT_ATT, "html");
997 loginPageRequest.setAttribute(GSXML.BASE_URL, baseURL);
998 loginPageMessage.appendChild(loginPageRequest);
999
1000 Element paramList = loginPageDoc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1001 loginPageRequest.appendChild(paramList);
1002
1003 Element messageParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
1004 messageParam.setAttribute(GSXML.NAME_ATT, "loginMessage");
1005 if (request.getAuthType() == null)
1006 {
1007 messageParam.setAttribute(GSXML.VALUE_ATT, "Please log in to view this page");
1008 }
1009 else
1010 {
1011 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?");
1012 }
1013 paramList.appendChild(messageParam);
1014
1015 Element urlParam = loginPageDoc.createElement(GSXML.PARAM_ELEM);
1016 urlParam.setAttribute(GSXML.NAME_ATT, "redirectURL");
1017 if (request.getQueryString() != null && request.getQueryString().length() > 0)
1018 {
1019 urlParam.setAttribute(GSXML.VALUE_ATT, request.getRequestURL() + "?" + request.getQueryString().replace("&", "&amp;"));
1020 }
1021 else
1022 {
1023 urlParam.setAttribute(GSXML.VALUE_ATT, request.getRequestURL().toString());
1024 }
1025 paramList.appendChild(urlParam);
1026
1027 Node loginPageResponse = this.recept.process(loginPageMessage);
1028 out.println(XMLConverter.getPrettyString(loginPageResponse));
1029
1030 return false;
1031 }
1032 }
1033 }
1034 return true;
1035 }
1036
1037 //a debugging method
1038 private void displaySize(Hashtable<String, UserSessionCache> table)
1039 {
1040 if (table == null)
1041 {
1042 logger.info("cached table is null");
1043 return;
1044 }
1045 if (table.size() == 0)
1046 {
1047 logger.info("cached table size is zero");
1048 return;
1049 }
1050 int num_cached_coll = 0;
1051 ArrayList<UserSessionCache> cache_list = new ArrayList<UserSessionCache>(table.values());
1052 for (int i = 0; i < cache_list.size(); i++)
1053 {
1054 num_cached_coll += cache_list.get(i).tableSize();
1055 }
1056 logger.info("Number of sessions : total number of cached collection info = " + table.size() + " : " + num_cached_coll);
1057 }
1058
1059 /** merely a debugging method! */
1060 private String tableToString(Hashtable<String, Hashtable<String, String>> table)
1061 {
1062 String str = "";
1063 Enumeration<String> keys = table.keys();
1064 while (keys.hasMoreElements())
1065 {
1066 String name = keys.nextElement();
1067 str += name + ", ";
1068 }
1069 return str;
1070 }
1071
1072 /**
1073 * this goes through each URL and adds in a session id if needed-- its
1074 * needed if the browser doesn't accept cookies also escapes things if
1075 * needed
1076 */
1077 protected void encodeURLs(Node dataNode, HttpServletResponse response)
1078 {
1079 if (dataNode == null)
1080 {
1081 return;
1082 }
1083
1084 Element data = null;
1085
1086 short nodeType = dataNode.getNodeType();
1087 if (nodeType == Node.DOCUMENT_NODE)
1088 {
1089 Document docNode = (Document) dataNode;
1090 data = docNode.getDocumentElement();
1091 }
1092 else
1093 {
1094 data = (Element) dataNode;
1095 }
1096
1097 if (data != null)
1098 {
1099
1100 // get all the <a> elements
1101 NodeList hrefs = data.getElementsByTagName("a");
1102 // Instead of calculating each iteration...
1103 int hrefscount = hrefs.getLength();
1104
1105 for (int i = 0; hrefs != null && i < hrefscount; i++)
1106 {
1107 Element a = (Element) hrefs.item(i);
1108 // ugly hack to get rid of : in the args - interferes with session handling
1109 String href = a.getAttribute("href");
1110 if (!href.equals(""))
1111 {
1112 if (href.indexOf("?") != -1)
1113 {
1114 String[] parts = StringUtils.split(href, "\\?", -1);
1115 if (parts.length == 1)
1116 {
1117 parts[0] = StringUtils.replace(parts[0], ":", "%3A");
1118 href = "?" + parts[0];
1119 }
1120 else
1121 {
1122 parts[1] = StringUtils.replace(parts[1], ":", "%3A");
1123 href = parts[0] + "?" + parts[1];
1124 }
1125
1126 }
1127 a.setAttribute("href", response.encodeURL(href));
1128 }
1129 }
1130
1131 // now find any submit bits - get all the <form> elements
1132 NodeList forms = data.getElementsByTagName("form");
1133 int formscount = forms.getLength();
1134 for (int i = 0; forms != null && i < formscount; i++)
1135 {
1136 Element form = (Element) forms.item(i);
1137 form.setAttribute("action", response.encodeURL(form.getAttribute("action")));
1138 }
1139 // are these the only cases where URLs occur??
1140 // we should only do this for greenstone urls?
1141 }
1142
1143 }
1144
1145 protected String getFirstParam(String name, Map<String, String[]> map)
1146 {
1147 String[] val = map.get(name);
1148 if (val == null || val.length == 0)
1149 {
1150 return null;
1151 }
1152
1153 return val[0];
1154 }
1155
1156 synchronized protected int getNextUserId()
1157 {
1158 next_user_id++;
1159 return next_user_id;
1160 }
1161
1162 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
1163 {
1164 //Check if we need to process a file upload
1165 if (ServletFileUpload.isMultipartContent(request))
1166 {
1167 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
1168
1169 int sizeLimit = System.getProperties().containsKey("servlet.upload.filesize.limit") ? Integer.parseInt(System.getProperty("servlet.upload.filesize.limit")) : 100 * 1024 * 1024;
1170
1171 File tempDir = new File(GlobalProperties.getGSDL3Home() + File.separator + "tmp");
1172 if (!tempDir.exists())
1173 {
1174 tempDir.mkdirs();
1175 }
1176
1177 //We want all files to be stored on disk (hence the 0)
1178 fileItemFactory.setSizeThreshold(0);
1179 fileItemFactory.setRepository(tempDir);
1180
1181 ServletFileUpload uploadHandler = new ServletFileUpload(fileItemFactory);
1182 uploadHandler.setFileSizeMax(sizeLimit);
1183
1184 HashMap<String, String[]> queryMap = new HashMap<String, String[]>();
1185 try
1186 {
1187 List items = uploadHandler.parseRequest(request);
1188 Iterator iter = items.iterator();
1189 while (iter.hasNext())
1190 {
1191 FileItem current = (FileItem) iter.next();
1192 if (current.isFormField())
1193 {
1194 queryMap.put(current.getFieldName(), new String[] { current.getString() });
1195 }
1196 else if (current.getName() != null && !current.getName().equals(""))
1197 {
1198 File file = new File(tempDir, current.getName());
1199 current.write(file);
1200
1201 queryMap.put("md___ex.Filesize", new String[] { "" + file.length() });
1202 queryMap.put("md___ex.Filename", new String[] { "" + current.getName() });
1203 }
1204 }
1205 }
1206 catch (Exception e)
1207 {
1208 e.printStackTrace();
1209 }
1210
1211 doGetOrPost(request, response, queryMap);
1212 }
1213 else
1214 {
1215 doGetOrPost(request, response, request.getParameterMap());
1216 }
1217 }
1218}
Note: See TracBrowser for help on using the repository browser.