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

Last change on this file since 32357 was 32342, checked in by kjdon, 6 years ago

switch(string) is only java 7, and we are using java 6, so made this is/else instead

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