source: greenstone3/trunk/src/java/org/greenstone/gsdl3/OAIServer.java@ 16688

Last change on this file since 16688 was 16688, checked in by davidb, 16 years ago

Changed 'Element process(Element)' in ModuleInterface to 'Node process(Node)'. After some deliberation is was decided this is a more useful (generic) layer of the DOM to pass information around in. Helps with the DocType problem when producing XSL Transformed pages, for example. When this was an Element, it would loose track of its DocType. Supporting method provided in XMLConverter 'Element nodeToElement(Node)' which checks a nodes docType and casts to Element if appropriate, or if a Document, typecasts to that and then extracts the top-level Element. With this fundamental change in ModuleInterface, around 20 files needed to be updated (Actions, Services, etc) that build on top of 'process()' to reflect this change, and use nodeToElement where necessary.

File size: 14.6 KB
Line 
1/*
2 * OAIServer.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3;
20
21import org.greenstone.gsdl3.comms.*;
22import org.greenstone.gsdl3.core.*;
23import org.greenstone.gsdl3.util.*;
24import org.greenstone.gsdl3.action.PageAction; // used to get the default action
25import org.w3c.dom.*;
26import java.io.*;
27import javax.servlet.*;
28import javax.servlet.http.*;
29import java.util.Enumeration;
30import java.util.HashSet;
31import java.io.File;
32
33import org.apache.log4j.*;
34
35/** a servlet to serve the OAI metadata harvesting - we are using servlets instead
36 * of cgi
37 * the init method is called only once - the first time the servlet classes
38 * are loaded. Each time a request comes in to the servlet, the session()
39 * method is called in a new thread (calls doGet/doPut etc)
40 * takes the verb= type args and builds a simple request to send to
41 * the oai receptionist, which returns a result in xml, conforming to the OAI-PMH
42 * protocol.
43 * @see Receptionist
44 */
45/**
46 * OAI server configuration instructions *
47 *
48 */
49public class OAIServer extends HttpServlet {
50
51 /** the receptionist to send messages to */
52 protected OAIReceptionist recept=null;
53 /** the default language - is specified by setting a servlet param,
54 * otherwise DEFAULT_LANG is used*/
55 protected String default_lang= null;
56 /** The default default - used if a default lang is not specified
57 * in the servlet params */
58 protected final String DEFAULT_LANG = "en";
59
60 /** a converter class to parse XML and create Docs
61 * This is only used for generating internal requests passed to MessageRouter.
62 * The response message is generated by parsing an existing xml skeleton file (web/WEB-INF/oaixml/oaiversion2.xml, for example).
63 */
64 protected XMLConverter converter=null;
65 /** container Document to create XML Nodes (but only request to the oai receptionist,
66 * not response (which is created in OAIXML.java) created by converter class */
67 protected Document doc=null;
68
69 /** A HashSet which contains all the legal verbs. */
70 protected HashSet verb_set = null;
71 /** A HashSet which contains all the legal oai keys in the key/value argument pair. */
72 protected HashSet param_set = null;
73 /** The name of the site with which we will finally be dealing, whether it is a local site or a remote site through a communicator.*/
74 protected String site = "";
75
76 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.OAIServer.class.getName());
77
78 /** initialise the servlet
79 */
80 public void init(ServletConfig config) throws ServletException {
81 // always call super.init, i.e., HttpServlet.;
82 super.init(config);
83 this.default_lang = config.getInitParameter(GSConstants.DEFAULT_LANG);
84
85 initVerbs();
86 initParams();
87
88 String site_name = config.getInitParameter(GSConstants.SITE_NAME);
89 String remote_site_name = null;
90 String remote_site_type = null;
91 String remote_site_address = null;
92
93 if (site_name == null) {
94 // no local site, try for communicator (remote site)
95 remote_site_name = config.getInitParameter("remote_site_name");
96 remote_site_type = config.getInitParameter("remote_site_type");
97 remote_site_address = config.getInitParameter("remote_site_address");
98 if (remote_site_name == null || remote_site_type == null || remote_site_address == null) {
99 System.err.println("initialisation paramters not all set!");
100 System.err.println("if site_name is not set, then you must have remote_site_name, remote_site_type and remote_site_address set");
101 System.exit(1);
102 }
103 }
104
105 if (this.default_lang == null) {
106 // choose english
107 this.default_lang = DEFAULT_LANG;
108 }
109
110 // the receptionist -the servlet will talk to this
111 this.recept = new OAIReceptionist();
112
113 // the receptionist uses a MessageRouter or Communicator to send its requests to. We either create a MessageRouter here for the designated site (if site_name set), or we create a Communicator for a remote site. The is given to teh Receptionist, and the servlet never talks to it again.directly.
114 if (site_name != null) {
115 //this site_name could consist of comma separated more than one site name.
116 String mr_name = (String)config.getInitParameter("messagerouter_class");
117 MessageRouter message_router = null;
118 if (mr_name == null) { // just use the normal MR *********
119 message_router = new MessageRouter();
120 } else { // try the specified one
121 try {
122 message_router = (MessageRouter)Class.forName("org.greenstone.gsdl3.core."+mr_name).newInstance();
123 } catch (Exception e) { // cant use this new one, so use normal one
124 System.err.println("OAIServlet configure exception when trying to use a new MessageRouter "+mr_name+": "+e.getMessage());
125 e.printStackTrace();
126 message_router = new MessageRouter();
127 }
128 }
129
130 message_router.setSiteName(site_name);
131 // lots of work is done in this step; see MessageRouter.java
132 message_router.configure();
133 this.recept.setSiteName(site_name);
134 this.recept.setMessageRouter(message_router);
135
136 } else {
137 // talking to a remote site, create a communicator
138 Communicator communicator = null;
139 // we need to create the XML to configure the communicator
140 Element site_elem = this.doc.createElement(GSXML.SITE_ELEM);
141 site_elem.setAttribute(GSXML.TYPE_ATT, remote_site_type);
142 site_elem.setAttribute(GSXML.NAME_ATT, remote_site_name);
143 site_elem.setAttribute(GSXML.ADDRESS_ATT, remote_site_address);
144
145 if (remote_site_type.equals(GSXML.COMM_TYPE_SOAP_JAVA)) {
146 communicator = new SOAPCommunicator();
147 } else {
148 System.err.println("OAIServlet.init Error: invalid Communicator type: "+remote_site_type);
149 System.exit(1);
150 }
151
152 if (!communicator.configure(site_elem)) {
153 System.err.println("OAIServlet.init Error: Couldn't configure communicator");
154 System.exit(1);
155 }
156 this.recept.setSiteName(remote_site_name);
157 this.recept.setMessageRouter(communicator);
158 }
159 // used for composing internal xml requests, but not xml responses.
160 // the converter may be used to get pretty xml, though.
161 this.converter = new XMLConverter();
162 this.doc = this.converter.newDOM();
163
164 // Read in OAIConfig.xml (residing web/WEB-INF/classes/) and
165 //use it to configure the receptionist. The init() is also called in which
166 //the resumption token file is read in and all expired tokens cleared.
167 Element oai_config = OAIXML.getOAIConfigXML();
168 if (oai_config == null) {
169 logger.error("Fail to parse oai config file OAIConfig.xml.");
170 return;
171 }
172 // pass it to the receptionist
173 this.recept.configure(oai_config);
174
175 }//end of init()
176
177 private void initVerbs() {
178 verb_set = new HashSet();
179 verb_set.add(OAIXML.GET_RECORD);
180 verb_set.add(OAIXML.LIST_RECORDS);
181 verb_set.add(OAIXML.LIST_IDENTIFIERS);
182 verb_set.add(OAIXML.LIST_SETS);
183 verb_set.add(OAIXML.LIST_METADATA_FORMATS);
184 verb_set.add(OAIXML.IDENTIFY);
185 }
186
187 private void initParams() {
188 param_set = new HashSet();
189 param_set.add(OAIXML.METADATA_PREFIX);
190 param_set.add(OAIXML.FROM);
191 param_set.add(OAIXML.UNTIL);
192 param_set.add(OAIXML.SET);
193 param_set.add(OAIXML.RESUMPTION_TOKEN);
194 param_set.add(OAIXML.IDENTIFIER);
195 }
196 private void logUsageInfo(HttpServletRequest request){
197 String usageInfo = "";
198
199 //logged info = general-info + session-info
200 usageInfo =
201 request.getContextPath()+" "+ //session id
202 request.getServletPath()+" "+ //serlvet
203 "["+request.getQueryString()+"]" +" "+ //the query string
204 "["+usageInfo.trim()+"]" +" "+ // params stored in a session
205 request.getRemoteAddr()+" "+ //remote address
206 request.getHeader("user-agent")+" "; //the remote brower info
207
208 logger.info(usageInfo);
209 }
210 /** return true if the url is in the form of baseURL?verb=...,
211 */
212 private boolean validate(String query, String verb) {
213 //Here in OAIServer, only the verbs are validated. All the validation for individual verb
214 // is taken in their doXXX() methods.
215 if(query == null || !query.startsWith(OAIXML.VERB+"=")) {
216 return false;
217 }
218 if (!verb_set.contains(verb)) {
219 return false;
220 }
221 return true;
222 }
223 private String getVerb(String query) {
224 if (query == null) return "";
225 int verb_start_index = query.indexOf("=") + 1;// first occurence of '='
226 int verb_end_index = query.indexOf("&");
227 if(verb_end_index == -1) {
228 return query.substring(verb_start_index);
229 }
230 return query.substring(verb_start_index, verb_end_index);
231 }
232 public void doGet(HttpServletRequest request, HttpServletResponse response)
233 throws ServletException, IOException {
234 PrintWriter out = response.getWriter();
235 logUsageInfo(request);
236 //out.println("url="+request.getRequestURL());// /oaiserver
237 //out.println("query="+request.getQueryString());// is /greenstone3
238
239 // oai always requires the content type be text/xml
240 request.setCharacterEncoding("UTF-8");
241 response.setContentType("text/xml;charset=UTF-8");
242
243 //
244 String lang = request.getParameter(GSParams.LANGUAGE);
245 if (lang==null || lang.equals("")) {
246 // use the default
247 lang = this.default_lang;
248 }
249 //we don't get the baseURL from the http request because what we get might be different from the one known publicly due to local network redirection.
250 //For example, puka.cs.waikato.ac.nz vs www.greenstone.org
251 //String base_url = request.getRequestURL().toString();
252 String query = request.getQueryString();
253 String[] pairs = (query==null)? null : query.split("&");//split into key/value pairs
254 String verb = getVerb(query);
255 Element xml_response = OAIXML.createBasicResponse(verb, pairs);
256 Element verb_elem = null;
257
258 if (validate(query, verb) == false) {
259 if (verb_set.contains(verb) == false) {
260 logger.error(OAIXML.BAD_VERB + ": " + query);
261 verb_elem = OAIXML.createErrorElement(OAIXML.BAD_VERB, OAIXML.ILLEGAL_OAI_VERB);
262 } else {
263 //must be something else other than bad verbs caused an error, so bad argument
264 logger.error(OAIXML.BAD_ARGUMENT + ": " + query);
265 verb_elem = OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, "");
266 }
267 xml_response.appendChild(verb_elem);
268 //this line never got displayed
269 //out.println("<?xml version='1.0' encoding='UTF-8' ?>");
270 out.println(this.converter.getPrettyString(xml_response));
271 return;
272 }//end of if(validate
273
274 // The query is valid, we can now
275 // compose the request message to the receptionist
276 Element xml_message = this.doc.createElement(GSXML.MESSAGE_ELEM);
277 Element xml_request = this.doc.createElement(GSXML.REQUEST_ELEM);
278 // The type attribute is set to be 'oaiService' from OAIServer to OAIReceptionist.
279 //xml_request.setAttribute(GSXML.TYPE_ATT, OAIXML.OAI_SERVICE);
280 xml_request.setAttribute(GSXML.LANG_ATT, lang);
281 xml_request.setAttribute(GSXML.TO_ATT, verb);
282 addParams(xml_request, pairs);
283
284 //xml_request.setAttribute(GSXML.OUTPUT_ATT, output);????
285 xml_message.appendChild(xml_request);
286
287 Node xml_result = this.recept.process (xml_message);
288 if (xml_result == null) {
289 logger.info("xml_result is null");
290 verb_elem = OAIXML.createErrorElement("Internal error", "");
291 xml_response.appendChild(verb_elem);
292 } else {
293
294 /** All response elements are in the form (with a corresponding verb name):
295 * <message>
296 <response>
297 <verb>
298 ...
299 * <resumptionToken>
300 * .. this is optional!
301 * </resumptionToken>
302 * </verb>
303 * </response>
304 * </message>
305 */
306 Node res = GSXML.getChildByTagName(xml_result, OAIXML.RESPONSE);
307 if(res == null) {
308 logger.info("response element in xml_result is null");
309 verb_elem = OAIXML.createErrorElement("Internal error", "");
310 } else {
311 verb_elem = GSXML.getFirstElementChild(res);
312 }
313
314 if(OAIXML.oai_version.equals(OAIXML.OAI_VERSION2) ||
315 verb_elem.getTagName().equals(OAIXML.ERROR)) {
316 xml_response.appendChild(xml_response.getOwnerDocument().importNode(verb_elem, true));
317 } else {
318 GSXML.copyAllChildren(xml_response, verb_elem);
319 }
320 }
321 out.println (this.converter.getPrettyString (xml_response));
322 return;
323 }
324 /** append parameter elements to the request sent to the receptionist*/
325 public void addParams(Element request, String[] pairs) {
326 // no params apart from the verb
327 if (pairs == null || pairs.length < 2) return ;
328
329 /**the request xml is composed in the form: <request>
330 * <param name=.../>
331 * <param name=.../>
332 * </request>
333 *(No paramList element in between).
334 */
335 for (int i=1; i<pairs.length; i++) {
336 //the first pair in pairs is the verb=xxx
337 int index = pairs[i].indexOf("=");
338 if(index != -1){ //just a double check
339 Element param = this.doc.createElement(OAIXML.PARAM);
340 param.setAttribute(OAIXML.NAME, pairs[i].substring(0, index));
341 param.setAttribute(OAIXML.VALUE, OAIXML.oaiDecode(pairs[i].substring(index + 1)));
342 request.appendChild(param);
343 }
344 }
345 }
346 public void doPost(HttpServletRequest request,
347 HttpServletResponse response)
348 throws ServletException, IOException {
349 doGet(request,response);
350 }
351}
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
Note: See TracBrowser for help on using the repository browser.