source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/OAIReceptionist.java@ 28854

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

adding new functionality to identify request

File size: 47.2 KB
Line 
1package org.greenstone.gsdl3.core;
2
3import org.greenstone.gsdl3.util.*;
4import org.greenstone.gsdl3.action.*;
5// XML classes
6import org.w3c.dom.Node;
7import org.w3c.dom.NodeList;
8import org.w3c.dom.Document;
9import org.w3c.dom.Element;
10
11// other java classes
12import java.io.File;
13import java.util.*;
14
15import org.apache.log4j.*;
16
17/** a Receptionist, used for oai metadata response xml generation.
18 * This receptionist talks to the message router directly,
19 * instead of via any action, hence no action map is needed.
20 * @see the basic Receptionist
21 */
22public class OAIReceptionist implements ModuleInterface {
23
24 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.OAIReceptionist.class.getName());
25
26 /** Instead of a config_params object, only a site_name is needed by oai receptionist. */
27 protected String site_name = null;
28 /** The unique repository identifier */
29 protected String repository_id = null;
30
31 /** container Document to create XML Nodes for requests sent to message router
32 * Not used for response
33 */
34 protected Document doc=null;
35
36 /** a converter class to parse XML and create Docs */
37 protected XMLConverter converter=null;
38
39 /** the configure file of this receptionist passed from the oai servlet. */
40 protected Element oai_config = null;
41
42 /** contained in the OAIConfig.xml deciding whether the resumptionToken should be in use */
43 protected int resume_after = -1 ;
44
45 /** the message router that the Receptionist and Actions will talk to */
46 protected ModuleInterface mr = null;
47
48
49 // Some of the data/responses will not change while the servlet is running, so
50 // we can cache them
51
52 /** A list of all the collections available to this OAI server */
53 protected NodeList collection_list = null;
54
55 /** The identify response */
56 protected Element identify_response = null;
57
58 public OAIReceptionist() {
59 this.converter = new XMLConverter();
60 this.doc = this.converter.newDOM();
61
62 }
63
64 public void cleanUp() {}
65
66 public void setSiteName(String site_name) {
67 this.site_name = site_name;
68 }
69 /** sets the message router - it should already be created and
70 * configured in the init() of a servlet (OAIServer, for example) before being passed to the receptionist*/
71 public void setMessageRouter(ModuleInterface mr) {
72 this.mr = mr;
73 }
74
75 /** configures the receptionist */
76 public boolean configure(Element config) {
77
78 if (this.mr==null) {
79 logger.error(" message routers must be set before calling oai configure");
80 return false;
81 }
82 if (config == null) {
83 logger.error(" oai configure file is null");
84 return false;
85 }
86 oai_config = config;
87 resume_after = getResumeAfter();
88
89 repository_id = getRepositoryId();
90 collection_list = getOAICollectionList();
91
92 //clear out expired resumption tokens stored in OAIResumptionToken.xml
93 OAIXML.init();
94 OAIXML.clearExpiredTokens();
95
96 return true;
97 }
98 /** process using strings - just calls process using Elements */
99 public String process(String xml_in) {
100
101 Node message_node = this.converter.getDOM(xml_in);
102 Node page = process(message_node);
103 return this.converter.getString(page);
104 }
105
106 //Compose a message element used to send back to the OAIServer servlet.
107 //This method is only used within OAIReceptionist
108 private Element getMessage(Element e) {
109 Element msg = OAIXML.createElement(OAIXML.MESSAGE);
110 msg.appendChild(OAIXML.getResponse(e));
111 return msg;
112 }
113 /** process - produce xml data in response to a request
114 * if something goes wrong, it returns null -
115 */
116 public Node process(Node message_node) {
117 logger.error("OAIReceptionist received request");
118
119 Element message = this.converter.nodeToElement(message_node);
120 logger.error(this.converter.getString(message));
121
122 // check that its a correct message tag
123 if (!message.getTagName().equals(GSXML.MESSAGE_ELEM)) {
124 logger.error(" Invalid message. GSDL message should start with <"+GSXML.MESSAGE_ELEM+">, instead it starts with:"+message.getTagName()+".");
125 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
126 }
127
128 // get the request out of the message - assume that there is only one
129 Element request = (Element)GSXML.getChildByTagName(message, GSXML.REQUEST_ELEM);
130 if (request == null) {
131 logger.error(" message had no request!");
132 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
133 }
134 //At this stage, the value of 'to' attribute of the request must be the 'verb'
135 //The only thing that the oai receptionist can be sure is that these verbs are valid, nothing else.
136 String verb = request.getAttribute(GSXML.TO_ATT);
137 if (verb.equals(OAIXML.IDENTIFY)) {
138 return doIdentify();
139 }
140 if (verb.equals(OAIXML.LIST_METADATA_FORMATS)) {
141 return doListMetadataFormats(message);
142 }
143 if (verb.equals(OAIXML.LIST_SETS)) {
144 return doListSets(message);
145 }
146 if (verb.equals(OAIXML.GET_RECORD)) {
147 return doGetRecord(message);
148 }
149 if (verb.equals(OAIXML.LIST_IDENTIFIERS)) {
150 return doListIdentifiers(message);
151 }
152 if (verb.equals(OAIXML.LIST_RECORDS)) {
153 return doListRecords(message);
154 }
155 return getMessage(OAIXML.createErrorElement("Unexpected things happened", ""));
156
157 }
158 /** send a request to the message router asking for a list of collections that support oai
159 * The type attribute must be changed from 'oaiService' to 'oaiSetList'
160 */
161 private NodeList getOAICollectionList() {
162 Element message = this.doc.createElement(OAIXML.MESSAGE);
163 Element request = this.doc.createElement(OAIXML.REQUEST);
164 message.appendChild(request);
165 request.setAttribute(OAIXML.TYPE, OAIXML.OAI_SET_LIST);
166 request.setAttribute(OAIXML.TO, "");
167 Node msg_node = mr.process(message);
168
169 if (msg_node == null) {
170 logger.error("returned msg_node from mr is null");
171 return null;
172 }
173 Element resp = (Element)GSXML.getChildByTagName(msg_node, OAIXML.RESPONSE);
174 Element coll_list = (Element)GSXML.getChildByTagName(resp, OAIXML.COLLECTION_LIST);
175 if (coll_list == null) {
176 logger.error("coll_list is null");
177 return null;
178 }
179 //logger.info(GSXML.xmlNodeToString(coll_list));
180 NodeList list = coll_list.getElementsByTagName(OAIXML.COLLECTION);
181 int length = list.getLength();
182 if (length == 0) {
183 logger.error("length is 0");
184 return null;
185 }
186 return list;
187 }
188 /**Exclusively called by doListSets()*/
189 private void getSets(Element list_sets_elem, NodeList oai_coll, int start_point, int end_point) {
190 for (int i=start_point; i<end_point; i++) {
191 String coll_spec = ((Element)oai_coll.item(i)).getAttribute(OAIXML.NAME);
192 String coll_name = coll_spec.substring(coll_spec.indexOf(":") + 1);
193 Element set = OAIXML.createElement(OAIXML.SET);
194 Element set_spec = OAIXML.createElement(OAIXML.SET_SPEC);
195 GSXML.setNodeText(set_spec, coll_spec);
196 set.appendChild(set_spec);
197 Element set_name = OAIXML.createElement(OAIXML.SET_NAME);
198 GSXML.setNodeText(set_name, coll_name);
199 set.appendChild(set_name);
200 list_sets_elem.appendChild(set);
201 }
202 }
203 private int getResumeAfter() {
204 Element resume_after = (Element)GSXML.getChildByTagName(oai_config, OAIXML.RESUME_AFTER);
205 if(resume_after != null) return Integer.parseInt(GSXML.getNodeText(resume_after));
206 return -1;
207 }
208 private String getRepositoryId() {
209 Element ri = (Element)GSXML.getChildByTagName(oai_config, OAIXML.REPOSITORY_ID);
210 if (ri != null) {
211 return GSXML.getNodeText(ri);
212 }
213 return "";
214 }
215 /** method to compose a set element
216 */
217 private Element doListSets(Element msg){
218 logger.info("");
219 // option: resumptionToken
220 // exceptions: badArgument, badResumptionToken, noSetHierarchy
221 Element list_sets_elem = OAIXML.createElement(OAIXML.LIST_SETS);
222
223 int oai_coll_size = collection_list.getLength();
224 if (oai_coll_size == 0) {
225 return getMessage(list_sets_elem);
226 }
227
228 Element req = (Element)GSXML.getChildByTagName(msg, GSXML.REQUEST_ELEM);
229 if (req == null) {
230 logger.error("req is null");
231 return null;
232 }
233 //params list only contains the parameters other than the verb
234 NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM);
235 Element param = null;
236 int smaller = (oai_coll_size>resume_after)? resume_after : oai_coll_size;
237 if (params.getLength() > 1) {
238 //Bad argument. Can't be more than one parameters for ListMetadataFormats verb
239 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
240 }
241 if(params.getLength() == 0) {
242 //this is requesting a list of sets in the whole repository
243 /** there is no resumeptionToken in the request, we check whether we need
244 * to send out resumeptionToken by comparing the total number of sets in this
245 * repository and the specified value of resumeAfter
246 */
247 if(resume_after < 0 || oai_coll_size <= resume_after) {
248 //send the whole list of records
249 //all data are sent on the first request. Therefore there should be
250 //no resumeptionToken stored in OAIConfig.xml.
251 //As long as the verb is 'ListSets', we ignore the rest of the parameters
252 getSets(list_sets_elem, collection_list, 0, oai_coll_size);
253 return getMessage(list_sets_elem);
254 }
255
256 //append required sets to list_sets_elem (may be a complete or incomplete list)
257 getSets(list_sets_elem, collection_list, 0, smaller);
258
259 if(oai_coll_size > resume_after) {
260 //An incomplete list is sent; append a resumptionToken element
261 Element token = createResumptionTokenElement(oai_coll_size, 0, resume_after, true);
262 //store this token
263 OAIXML.addToken(token);
264
265 list_sets_elem.appendChild(token);
266 }
267
268 return getMessage(list_sets_elem);
269 }
270
271 // The url should contain only one param called resumptionToken
272 // This is requesting a subsequent part of a list, following a previously sent incomplete list
273 param = (Element)params.item(0);
274 String param_name = param.getAttribute(OAIXML.NAME);
275 if (!param_name.equals(OAIXML.RESUMPTION_TOKEN)) {
276 //Bad argument
277 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
278 }
279 //get the token
280 String token = param.getAttribute(OAIXML.VALUE);
281 //validate the token string (the string has already been decoded in OAIServer, e.g.,
282 // replace %3A with ':')
283 if(OAIXML.containsToken(token) == false) {
284 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_RESUMPTION_TOKEN, ""));
285 }
286 //take out the cursor value, which is the size of previously sent list
287 int index = token.indexOf(":");
288 int cursor = Integer.parseInt(token.substring(index + 1));
289 Element token_elem = null;
290
291 // are we sending the final part of a complete list?
292 if(cursor + resume_after >= oai_coll_size) {
293 //Yes, we are.
294 //append required sets to list_sets_elem (list is complete)
295 getSets(list_sets_elem, collection_list, cursor, oai_coll_size);
296 //An incomplete list is sent; append a resumptionToken element
297 token_elem = createResumptionTokenElement(oai_coll_size, cursor, -1, false);
298 list_sets_elem.appendChild(token_elem);
299 } else {
300 //No, we are not.
301 //append required sets to list_sets_elem (list is incomplete)
302 getSets(list_sets_elem, collection_list, cursor, cursor + resume_after);
303 token_elem = createResumptionTokenElement(oai_coll_size, cursor, cursor + resume_after, true);
304 //store this token
305 OAIXML.addToken(token_elem);
306 list_sets_elem.appendChild(token_elem);
307 }
308 return getMessage(list_sets_elem);
309 }
310 private Element createResumptionTokenElement(int total_size, int cursor, int so_far_sent, boolean set_expiration, String metadata_prefix) {
311 Element token = OAIXML.createElement(OAIXML.RESUMPTION_TOKEN);
312 token.setAttribute(OAIXML.COMPLETE_LIST_SIZE, "" + total_size);
313 token.setAttribute(OAIXML.CURSOR, "" + cursor);
314
315 if(set_expiration) {
316 /** read the resumptionTokenExpiration element in OAIConfig.xml and get the specified time value
317 * Use the time value plus the current system time to get the expiration date string.
318 */
319 String expiration_date = OAIXML.getTime(System.currentTimeMillis() + OAIXML.getTokenExpiration()); // in milliseconds
320 token.setAttribute(OAIXML.EXPIRATION_DATE, expiration_date);
321 }
322
323 if(so_far_sent > 0) {
324 //the format of resumptionToken is not defined by the OAI-PMH and should be
325 //considered opaque by the harvester (in other words, strictly follow what the
326 //data provider has to offer
327 //Here, we make use of the uniqueness of the system time
328 String tokenValue = OAIXML.GS3OAI + System.currentTimeMillis() + ":" + so_far_sent;
329 if(!metadata_prefix.equals("")) {
330 tokenValue = tokenValue + ":" + metadata_prefix;
331 }
332 GSXML.setNodeText(token, tokenValue);
333 }
334 return token;
335 }
336
337 private Element createResumptionTokenElement(int total_size, int cursor, int so_far_sent, boolean set_expiration) {
338 return createResumptionTokenElement(total_size, cursor, so_far_sent, set_expiration, ""); // empty metadata_prefix
339 }
340
341 /** if the param_map contains strings other than those in valid_strs, return false;
342 * otherwise true.
343 */
344 private boolean isValidParam(HashMap<String, String> param_map, HashSet<String> valid_strs) {
345 ArrayList<String> param_list = new ArrayList<String>(param_map.keySet());
346 for(int i=0; i<param_list.size(); i++) {
347 if (valid_strs.contains(param_list.get(i)) == false) {
348 return false;
349 }
350 }
351 return true;
352 }
353 private Element doListIdentifiers(Element msg) {
354 // option: from, until, set, metadataPrefix, resumptionToken
355 // exceptions: badArgument, badResumptionToken, cannotDisseminateFormat, noRecordMatch, and noSetHierarchy
356 HashSet<String> valid_strs = new HashSet<String>();
357 valid_strs.add(OAIXML.FROM);
358 valid_strs.add(OAIXML.UNTIL);
359 valid_strs.add(OAIXML.SET);
360 valid_strs.add(OAIXML.METADATA_PREFIX);
361 valid_strs.add(OAIXML.RESUMPTION_TOKEN);
362
363 Element list_identifiers = OAIXML.createElement(OAIXML.LIST_IDENTIFIERS);
364 Element req = (Element)GSXML.getChildByTagName(msg, GSXML.REQUEST_ELEM);
365 if (req == null) { logger.error("req is null"); return null; }
366 NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM);
367 String coll_name = "";
368 String token = "";
369
370 HashMap<String, String> param_map = OAIXML.getParamMap(params);
371 if (!isValidParam(param_map, valid_strs)) {
372 logger.error("One of the params is invalid");
373 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
374 }
375 // param keys are valid, but if there are any date params, check they're of the right format
376 String from = param_map.get(OAIXML.FROM);
377 if(from != null) {
378 Date from_date = OAIXML.getDate(from);
379 if(from_date == null) {
380 logger.error("invalid date: " + from);
381 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
382 }
383 }
384 String until = param_map.get(OAIXML.UNTIL);
385 if(until != null) {
386 Date until_date = OAIXML.getDate(until);
387 if(until_date == null) {
388 logger.error("invalid date: " + until);
389 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
390 }
391 }
392 if(from != null && until != null) { // check they are of the same date-time format (granularity)
393 if(from.length() != until.length()) {
394 logger.error("The request has different granularities (date-time formats) for the From and Until date parameters.");
395 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
396 }
397 }
398
399 //ask the message router for a list of oai collections
400 //NodeList oai_coll = collection_list; //getOAICollectionList();
401 int oai_coll_size = collection_list.getLength();
402 if (oai_coll_size == 0) {
403 logger.info("returned oai collection list is empty");
404 return getMessage(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, ""));
405 }
406
407 //Now we check if the optional argument 'set' has been specified in the params; if so,
408 //whether the specified setSpec is supported by this repository
409 boolean request_set = param_map.containsKey(OAIXML.SET);
410 if(request_set == true) {
411 boolean set_supported = false;
412 String set_spec_str = param_map.get(OAIXML.SET);
413 // get the collection name
414 //if setSpec is supported by this repository, it must be in the form: site_name:coll_name
415 String[] strs = splitSetSpec(set_spec_str);
416 coll_name = strs[1];
417
418 for(int i=0; i<oai_coll_size; i++) {
419 if(set_spec_str.equals(((Element)collection_list.item(i)).getAttribute(OAIXML.NAME))) {
420 set_supported = true;
421 }
422 }
423 if(set_supported == false) {
424 logger.error("requested set is not found in this repository");
425 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
426 }
427 }
428
429 //Is there a resumptionToken included which is requesting an incomplete list?
430 if(param_map.containsKey(OAIXML.RESUMPTION_TOKEN)) {
431 // validate resumptionToken
432 token = param_map.get(OAIXML.RESUMPTION_TOKEN);
433 logger.info("has resumptionToken" + token);
434 if(OAIXML.containsToken(token) == false) {
435 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_RESUMPTION_TOKEN, ""));
436 }
437 }
438
439 // Custom test that expects a metadataPrefix comes here at end so that the official params can
440 // be tested first for errors and their error responses sent off. Required for OAI validation
441 if (!param_map.containsKey(OAIXML.METADATA_PREFIX)) {
442 logger.error("contains invalid params or no metadataPrefix");
443 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
444 }
445
446 //Now that we got a prefix, check and see if it's supported by this repository
447 String prefix_value = param_map.get(OAIXML.METADATA_PREFIX);
448 if (containsMetadataPrefix(prefix_value) == false) {
449 logger.error("requested prefix is not found in OAIConfig.xml");
450 return getMessage(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""));
451 }
452
453 //Now that all validation has been done, I hope, we can send request to the message router
454 Element result = null;
455 String verb = req.getAttribute(OAIXML.TO);
456 NodeList param_list = req.getElementsByTagName(OAIXML.PARAM);
457 ArrayList<Element> retain_param_list = new ArrayList<Element>();
458 for (int j=0; j<param_list.getLength(); j++) {
459 Element e = OAIXML.duplicateElement(msg.getOwnerDocument(), (Element)param_list.item(j), true);
460 retain_param_list.add(e);
461 }
462
463 //re-organize the request element
464 // reset the 'to' attribute
465 if (request_set == false) {
466 logger.info("requesting identifiers of all collections");
467 for(int i=0; i<oai_coll_size; i++) {
468 if(req == null) {
469 req = msg.getOwnerDocument().createElement(GSXML.REQUEST_ELEM);
470 msg.appendChild(req);
471 for (int j=0; j<retain_param_list.size(); j++) {
472 req.appendChild(retain_param_list.get(j));
473 }
474 }
475 String full_name = ((Element)collection_list.item(i)).getAttribute(OAIXML.NAME);
476 coll_name = full_name.substring(full_name.indexOf(":") + 1);
477 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
478 Node n = mr.process(msg);
479 Element e = converter.nodeToElement(n);
480 result = collectAll(result, e, verb, OAIXML.HEADER);
481
482 //clear the content of the old request element
483 msg.removeChild(req);
484 req = null;
485 }
486 } else {
487 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
488 Node result_node = mr.process(msg);
489 result = converter.nodeToElement(result_node);
490 }
491
492 if (result == null) {
493 logger.info("message router returns null");
494 return getMessage(OAIXML.createErrorElement("Internal service returns null", ""));
495 }
496 Element res = (Element)GSXML.getChildByTagName(result, OAIXML.RESPONSE);
497 if(res == null) {
498 logger.info("response element in xml_result is null");
499 return getMessage(OAIXML.createErrorElement("Internal service returns null", ""));
500 }
501 NodeList header_list = res.getElementsByTagName(OAIXML.HEADER);
502 int num_headers = header_list.getLength();
503 if(num_headers == 0) {
504 logger.info("message router returns 0 headers.");
505 return getMessage(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, ""));
506 }
507
508 //The request coming in does not contain a token, but we have to check the resume_after value and see if we need to issue a resumption token and
509 // save the token as well.
510 if (token.equals("") == true) {
511 if(resume_after < 0 || num_headers <= resume_after) {
512 //send the whole list of records
513 return result;
514 }
515
516 //append required number of records (may be a complete or incomplete list)
517 getRecords(list_identifiers, header_list, 0, resume_after);
518 //An incomplete list is sent; append a resumptionToken element
519 Element token_elem = createResumptionTokenElement(num_headers, 0, resume_after, true);
520 //store this token
521 OAIXML.addToken(token_elem);
522
523 list_identifiers.appendChild(token_elem);
524 return getMessage(list_identifiers);
525 }
526
527 if (token.equals("") == false) {
528 //get an appropriate number of records (partial list) according to the token
529 //take out the cursor value, which is the size of previously sent list
530 int index = token.indexOf(":");
531 int cursor = Integer.parseInt(token.substring(index + 1));
532 Element token_elem = null;
533
534 // are we sending the final part of a complete list?
535 if(cursor + resume_after >= num_headers) {
536 //Yes, we are.
537 //append required records to list_records (list is complete)
538 getRecords(list_identifiers, header_list, cursor, num_headers);
539 //An incomplete list is sent; append a resumptionToken element
540 token_elem = createResumptionTokenElement(num_headers, cursor, -1, false);
541 list_identifiers.appendChild(token_elem);
542 } else {
543 //No, we are not.
544 //append required records to list_records (list is incomplete)
545 getRecords(list_identifiers, header_list, cursor, cursor + resume_after);
546 token_elem = createResumptionTokenElement(num_headers, cursor, cursor + resume_after, true);
547 //store this token
548 OAIXML.addToken(token_elem);
549 list_identifiers.appendChild(token_elem);
550 }
551
552 return getMessage(list_identifiers);
553 }//end of if(!token.equals(""))
554
555 return result;
556 }
557 private Element doListRecords(Element msg){
558 logger.info("");
559 // option: from, until, set, metadataPrefix, and resumptionToken
560 // exceptions: badArgument, badResumptionToken, cannotDisseminateFormat, noRecordMatch, and noSetHierarchy
561 HashSet<String> valid_strs = new HashSet<String>();
562 valid_strs.add(OAIXML.FROM);
563 valid_strs.add(OAIXML.UNTIL);
564 valid_strs.add(OAIXML.SET);
565 valid_strs.add(OAIXML.METADATA_PREFIX);
566 valid_strs.add(OAIXML.RESUMPTION_TOKEN);
567
568 Element list_records = OAIXML.createElement(OAIXML.LIST_RECORDS);
569 Element req = (Element)GSXML.getChildByTagName(msg, GSXML.REQUEST_ELEM);
570 if (req == null) { logger.error("req is null"); return null; }
571 NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM);
572
573 String coll_name = "";
574 String token = "";
575
576 if(params.getLength() == 0) {
577 logger.error("must at least have the metadataPrefix parameter, can't be none");
578 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
579 }
580
581 HashMap<String, String> param_map = OAIXML.getParamMap(params);
582 if (!isValidParam(param_map, valid_strs)) {
583 logger.error("One of the params is invalid");
584 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
585 }
586 // param keys are valid, but if there are any date params, check they're of the right format
587 String from = param_map.get(OAIXML.FROM);
588 if(from != null) {
589 Date from_date = OAIXML.getDate(from);
590 if(from_date == null) {
591 logger.error("invalid date: " + from);
592 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
593 }
594 }
595 String until = param_map.get(OAIXML.UNTIL);
596 Date until_date = null;
597 if(until != null) {
598 until_date = OAIXML.getDate(until);
599 if(until_date == null) {
600 logger.error("invalid date: " + until);
601 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
602 }
603 }
604 if(from != null && until != null) { // check they are of the same date-time format (granularity)
605 if(from.length() != until.length()) {
606 logger.error("The request has different granularities (date-time formats) for the From and Until date parameters.");
607 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
608 }
609 }
610
611 //ask the message router for a list of oai collections
612 //NodeList oai_coll = getOAICollectionList();
613 int oai_coll_size = collection_list.getLength();
614 if (oai_coll_size == 0) {
615 logger.info("returned oai collection list is empty");
616 return getMessage(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, ""));
617 }
618
619 //Now we check if the optional argument 'set' has been specified in the params; if so,
620 //whether the specified setSpec is supported by this repository
621 boolean request_set = param_map.containsKey(OAIXML.SET);
622 if(request_set == true) {
623 boolean set_supported = false;
624 String set_spec_str = param_map.get(OAIXML.SET);
625 // get the collection name
626 //if setSpec is supported by this repository, it must be in the form: site_name:coll_name
627 String[] strs = splitSetSpec(set_spec_str);
628// name_of_site = strs[0];
629 coll_name = strs[1];
630 //logger.info("param contains set: "+coll_name);
631
632 for(int i=0; i<oai_coll_size; i++) {
633 if(set_spec_str.equals(((Element)collection_list.item(i)).getAttribute(OAIXML.NAME))) {
634 set_supported = true;
635 }
636 }
637 if(set_supported == false) {
638 logger.error("requested set is not found in this repository");
639 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
640 }
641 }
642
643 //Is there a resumptionToken included which is requesting an incomplete list?
644 if(param_map.containsKey(OAIXML.RESUMPTION_TOKEN)) {
645 // validate resumptionToken
646 //if (the token value is not found in the token xml file) {
647 // return getMessage(OAIXML.createErrorElement(OAIXML.BAD_RESUMPTION_TOKEN, ""));
648 //} else {
649 // use the request to get a complete list of records from the message router
650 // and issue the subsequent part of that complete list according to the token.
651 // store a new token if necessary.
652 //}
653 token = param_map.get(OAIXML.RESUMPTION_TOKEN);
654 logger.info("has resumptionToken: " + token);
655 if(OAIXML.containsToken(token) == false) {
656 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_RESUMPTION_TOKEN, ""));
657 }
658 }
659
660 // Moved the additional custom test that mandates the metadataPrefix here, since official
661 // errors should be caught first, so that their error responses can be sent off first
662 // such that GS2's oaiserver will validate properly.
663 if (!param_map.containsKey(OAIXML.METADATA_PREFIX)) {
664 if(!token.equals("")) { // resumptiontoken
665 int lastIndex = token.lastIndexOf(":");
666 if(lastIndex != token.indexOf(":")) { // if a meta_prefix is suffixed to the usual token,
667 // put that in the map and remove it from the end of the stored token
668 String meta_prefix = token.substring(lastIndex+1);
669 param_map.put(OAIXML.METADATA_PREFIX, meta_prefix);
670 token = token.substring(0, lastIndex);
671 param_map.put(OAIXML.RESUMPTION_TOKEN, token);
672
673 // Add to request <param name="metadataPrefix" value="oai_dc"/>
674 // need to add metaprefix as param to request, else a request
675 // for subsequent records when working with resumption tokens will fail
676 Element paramEl = req.getOwnerDocument().createElement(OAIXML.PARAM);
677 paramEl.setAttribute(OAIXML.NAME, OAIXML.METADATA_PREFIX);
678 paramEl.setAttribute(OAIXML.VALUE, meta_prefix);
679 req.appendChild(paramEl);
680 }
681 } else { // no metadata_prefix
682
683 // it must have a metadataPrefix
684 /** Here I disagree with the OAI specification: even if a resumptionToken is
685 * included in the request, the metadataPrefix is a must argument. Otherwise
686 * how would we know what metadataPrefix the harvester requested in his last request?
687 */
688 logger.error("no metadataPrefix");
689 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
690 }
691 }
692
693 //Now that we got a prefix, check and see if it's supported by this repository
694 String prefix_value = param_map.get(OAIXML.METADATA_PREFIX);
695 if (containsMetadataPrefix(prefix_value) == false) {
696 logger.error("requested prefix is not found in OAIConfig.xml");
697 return getMessage(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""));
698 }
699
700
701 //Now that all validation has been done, I hope, we can send request to the message router
702 Element result = null;
703 String verb = req.getAttribute(OAIXML.TO);
704 NodeList param_list = req.getElementsByTagName(OAIXML.PARAM);
705 ArrayList<Element> retain_param_list = new ArrayList<Element>();
706 for (int j=0; j<param_list.getLength(); j++) {
707 Element e = OAIXML.duplicateElement(msg.getOwnerDocument(), (Element)param_list.item(j), true);
708 retain_param_list.add(e);
709 }
710
711 //re-organize the request element
712 // reset the 'to' attribute
713 if (request_set == false) {
714 //coll_name could be "", which means it's requesting all records of all collections
715 //we send a request to each collection asking for its records
716 for(int i=0; i<oai_coll_size; i++) {
717 if(req == null) {
718 req = msg.getOwnerDocument().createElement(GSXML.REQUEST_ELEM);
719 msg.appendChild(req);
720 for (int j=0; j<retain_param_list.size(); j++) {
721 req.appendChild(retain_param_list.get(j));
722 }
723 }
724 String full_name = ((Element)collection_list.item(i)).getAttribute(OAIXML.NAME);
725 coll_name = full_name.substring(full_name.indexOf(":") + 1);
726 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
727 //logger.info(GSXML.xmlNodeToString(req));
728 Node n = mr.process(msg);
729 Element e = converter.nodeToElement(n);
730 result = collectAll(result, e, verb, OAIXML.RECORD);
731
732 //clear the content of the old request element
733 msg.removeChild(req);
734 req = null;
735 }
736 } else {
737 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
738
739 Node result_node = mr.process(msg);
740 result = converter.nodeToElement(result_node);
741 }
742
743 if (result == null) {
744 logger.info("message router returns null");
745 return getMessage(OAIXML.createErrorElement("Internal service returns null", ""));
746 }
747 Element res = (Element)GSXML.getChildByTagName(result, OAIXML.RESPONSE);
748 if(res == null) {
749 logger.info("response element in xml_result is null");
750 return getMessage(OAIXML.createErrorElement("Internal service returns null", ""));
751 }
752 NodeList record_list = res.getElementsByTagName(OAIXML.RECORD);
753 int num_records = record_list.getLength();
754 if(num_records == 0) {
755 logger.info("message router returns 0 records.");
756 return getMessage(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, ""));
757 }
758
759 //The request coming in does not contain a token, but we have to check the resume_after value and see if we need to issue a resumption token and
760 // save the token as well.
761 if (token.equals("") == true) {
762 if(resume_after < 0 || num_records <= resume_after) {
763 //send the whole list of records
764 return result;
765 }
766
767 //append required number of records (may be a complete or incomplete list)
768 getRecords(list_records, record_list, 0, resume_after);
769 //An incomplete list is sent; append a resumptionToken element
770 Element token_elem = createResumptionTokenElement(num_records, 0, resume_after, true, param_map.get(OAIXML.METADATA_PREFIX));
771 //store this token
772 OAIXML.addToken(token_elem);
773
774 list_records.appendChild(token_elem);
775 return getMessage(list_records);
776 }
777
778 if (token.equals("") == false) {
779 //get an appropriate number of records (partial list) according to the token
780 //take out the cursor value, which is the size of previously sent list
781 int index = token.indexOf(":");
782 int cursor = Integer.parseInt(token.substring(index + 1));
783 Element token_elem = null;
784
785 // are we sending the final part of a complete list?
786 if(cursor + resume_after >= num_records) {
787 //Yes, we are.
788 //append required records to list_records (list is complete)
789 getRecords(list_records, record_list, cursor, num_records);
790 //An incomplete list is sent; append a resumptionToken element
791 token_elem = createResumptionTokenElement(num_records, cursor, -1, false, param_map.get(OAIXML.METADATA_PREFIX));
792 list_records.appendChild(token_elem);
793
794 } else {
795 //No, we are not.
796 //append required records to list_records (list is incomplete)
797 getRecords(list_records, record_list, cursor, cursor + resume_after);
798 token_elem = createResumptionTokenElement(num_records, cursor, cursor + resume_after, true, param_map.get(OAIXML.METADATA_PREFIX));
799 //store this token
800 OAIXML.addToken(token_elem);
801 list_records.appendChild(token_elem);
802 }
803
804 return getMessage(list_records);
805 }//end of if(!token.equals(""))
806
807 return result;//a backup return
808 }
809 // method exclusively used by doListRecords/doListIdentifiers
810 private void getRecords(Element verb_elem, NodeList list, int start_point, int end_point) {
811 for (int i=start_point; i<end_point; i++) {
812 verb_elem.appendChild(verb_elem.getOwnerDocument().importNode(list.item(i), true));
813 }
814 }
815 private Element collectAll(Element result, Element msg, String verb, String elem_name) {
816 if(result == null) {
817 //in the first round, result is null
818 return msg;
819 }
820 Element res_in_result = (Element)GSXML.getChildByTagName(result, OAIXML.RESPONSE);
821 if(res_in_result == null) { // return the results of all other collections accumulated so far
822 return msg;
823 }
824 Element verb_elem = (Element)GSXML.getChildByTagName(res_in_result, verb);
825 if(msg == null) {
826 return result;
827 }
828
829 //e.g., get all <record> elements from the returned message. There may be none of
830 //such element, for example, the collection service returned an error message
831 NodeList elem_list = msg.getElementsByTagName(elem_name);
832
833 for (int i=0; i<elem_list.getLength(); i++) {
834 verb_elem.appendChild(res_in_result.getOwnerDocument().importNode(elem_list.item(i), true));
835 }
836 return result;
837 }
838 /** there are three possible exception conditions: bad argument, idDoesNotExist, and noMetadataFormats.
839 * The first one is handled here, and the last two are processed by OAIPMH.
840 */
841 private Element doListMetadataFormats(Element msg) {
842 //if the verb is ListMetadataFormats, there could be only one parameter: identifier
843 //, or there is no parameter; otherwise it is an error
844 //logger.info("" + this.converter.getString(msg));
845
846 Element list_metadata_formats = OAIXML.createElement(OAIXML.LIST_METADATA_FORMATS);
847
848 Element req = (Element)GSXML.getChildByTagName(msg, GSXML.REQUEST_ELEM);
849 if (req == null) { logger.error(""); return null; }
850 NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM);
851 Element param = null;
852 if(params.getLength() == 0) {
853 //this is requesting metadata formats for the whole repository
854 //read the oaiConfig.xml file, return the metadata formats specified there.
855 Element format_list = (Element)GSXML.getChildByTagName(oai_config, OAIXML.LIST_METADATA_FORMATS);
856 if(format_list == null) {
857 logger.error("OAIConfig.xml must contain the supported metadata formats");
858 return getMessage(list_metadata_formats);
859 }
860 NodeList formats = format_list.getElementsByTagName(OAIXML.METADATA_FORMAT);
861 for(int i=0; i<formats.getLength(); i++) {
862 Element meta_fmt = OAIXML.createElement(OAIXML.METADATA_FORMAT);
863 Element first_meta_format = (Element)formats.item(i);
864 //the element also contains mappings, but we don't want them
865 meta_fmt.appendChild(meta_fmt.getOwnerDocument().importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.METADATA_PREFIX), true));
866 meta_fmt.appendChild(meta_fmt.getOwnerDocument().importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.SCHEMA), true));
867 meta_fmt.appendChild(meta_fmt.getOwnerDocument().importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.METADATA_NAMESPACE), true));
868 list_metadata_formats.appendChild(meta_fmt);
869 }
870 return getMessage(list_metadata_formats);
871
872
873 }
874
875 if (params.getLength() > 1) {
876 //Bad argument. Can't be more than one parameters for ListMetadataFormats verb
877 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
878 }
879
880 // This is a request for the metadata of a particular item with an identifier
881 /**the request xml is in the form: <request>
882 * <param name=.../>
883 * </request>
884 *And there is a param element and one element only. (No paramList element in between).
885 */
886 param = (Element)params.item(0);
887 String param_name = param.getAttribute(OAIXML.NAME);
888 String identifier = "";
889 if (!param_name.equals(OAIXML.IDENTIFIER)) {
890 //Bad argument
891 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
892 } else {
893 identifier = param.getAttribute(OAIXML.VALUE);
894 // the identifier is in the form: <site_name>:<coll_name>:<OID>
895 // so it must contain at least two ':' characters
896 String[] strs = identifier.split(":");
897 if(strs == null || strs.length < 3) {
898 // the OID may also contain ':'
899 logger.error("identifier is not in the form site:coll:id" + identifier);
900 return getMessage(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, ""));
901 }
902
903 // send request to message router
904 // get the names
905 strs = splitNames(identifier);
906 if(strs == null || strs.length < 3) {
907 logger.error("identifier is not in the form site:coll:id" + identifier);
908 return getMessage(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, ""));
909 }
910 String name_of_site = strs[0];
911 String coll_name = strs[1];
912 String oid = strs[2];
913
914 //re-organize the request element
915 // reset the 'to' attribute
916 String verb = req.getAttribute(OAIXML.TO);
917 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
918 // reset the identifier element
919 param.setAttribute(OAIXML.NAME, OAIXML.OID);
920 param.setAttribute(OAIXML.VALUE, oid);
921
922 //Now send the request to the message router to process
923 Node result_node = mr.process(msg);
924 return converter.nodeToElement(result_node);
925 }
926
927
928 }
929
930
931 private void appendParam(Element req, String name, String value) {
932 Element param = req.getOwnerDocument().createElement(OAIXML.PARAM);
933 param.setAttribute(OAIXML.NAME, name);
934 param.setAttribute(OAIXML.VALUE, value);
935 req.appendChild(param);
936 }
937 private void copyNamedElementfromConfig(Element to_elem, String element_name) {
938 Element original_element = (Element)GSXML.getChildByTagName(oai_config, element_name);
939 if(original_element != null) {
940 copyNode(to_elem, original_element);
941 }
942 }
943
944 private void copyNode(Element to_elem, Node original_element) {
945 to_elem.appendChild(to_elem.getOwnerDocument().importNode(original_element, true));
946
947 }
948
949 private Element doIdentify() {
950 //The validation for this verb has been done in OAIServer.validate(). So no bother here.
951 logger.info("");
952 if (this.identify_response != null) {
953 // we have already created it
954 return getMessage(this.identify_response);
955 }
956
957 Element identify = OAIXML.createElement(OAIXML.IDENTIFY);
958 //do the repository name
959 copyNamedElementfromConfig(identify, OAIXML.REPOSITORY_NAME);
960 //do the baseurl
961 copyNamedElementfromConfig(identify, OAIXML.BASE_URL);
962 //do the protocol version
963 copyNamedElementfromConfig(identify, OAIXML.PROTOCOL_VERSION);
964
965 //There can be more than one admin email according to the OAI specification
966 NodeList admin_emails = GSXML.getChildrenByTagName(oai_config, OAIXML.ADMIN_EMAIL);
967 int num_admin = 0;
968 Element from_admin_email = null;
969 if (admin_emails != null) {
970 num_admin = admin_emails.getLength();
971 }
972 for (int i=0; i<num_admin; i++) {
973 copyNode(identify, admin_emails.item(i));
974 }
975
976 //do the earliestDatestamp
977 //send request to mr to search through the earliest datestamp amongst all oai collections in the repository.
978 //ask the message router for a list of oai collections
979 //NodeList oai_coll = getOAICollectionList();
980 long earliestDatestamp = getEarliestDateStamp(collection_list);
981 String earliestDatestamp_str = OAIXML.getTime(earliestDatestamp);
982 Element earliestDatestamp_elem = OAIXML.createElement(OAIXML.EARLIEST_DATESTAMP);
983 GSXML.setNodeText(earliestDatestamp_elem, earliestDatestamp_str);
984 identify.appendChild(earliestDatestamp_elem);
985
986 //do the deletedRecord
987 copyNamedElementfromConfig(identify, OAIXML.DELETED_RECORD);
988 //do the granularity
989 copyNamedElementfromConfig(identify, OAIXML.GRANULARITY);
990
991 // output the oai identifier
992 Element description = OAIXML.createElement(OAIXML.DESCRIPTION);
993 identify.appendChild(description);
994 Element oaiIdentifier = OAIXML.createOAIIdentifierXML(repository_id, "lucene-jdbm-demo", "ec159e");
995 description.appendChild(oaiIdentifier);
996
997 // if there are any oaiInfo metadata, add them in too.
998 Element info = (Element)GSXML.getChildByTagName(oai_config, OAIXML.OAI_INFO);
999 if (info != null) {
1000 NodeList meta = GSXML.getChildrenByTagName(info, OAIXML.INFO_METADATA);
1001 if (meta != null && meta.getLength() > 0) {
1002 Element gsdl = OAIXML.createGSDLElement();
1003 description.appendChild(gsdl);
1004 for (int m = 0; m<meta.getLength(); m++) {
1005 copyNode(gsdl, meta.item(m));
1006 }
1007
1008 }
1009 }
1010 this.identify_response = identify;
1011 return getMessage(identify);
1012 }
1013 //split setSpec (site_name:coll_name) into an array of strings
1014 //It has already been checked that the set_spec contains at least one ':'
1015 private String[] splitSetSpec(String set_spec) {
1016 logger.info(set_spec);
1017 String[] strs = new String[2];
1018 int colon_index = set_spec.indexOf(":");
1019 strs[0] = set_spec.substring(0, colon_index);
1020 strs[1] = set_spec.substring(colon_index + 1);
1021 return strs;
1022 }
1023 /** split the identifier into <site + collection + OID> as an array
1024 It has already been checked that the 'identifier' contains at least two ':'
1025 */
1026 private String[] splitNames(String identifier) {
1027 logger.info(identifier);
1028 String [] strs = new String[3];
1029 int first_colon = identifier.indexOf(":");
1030 if(first_colon == -1) {
1031 return null;
1032 }
1033 strs[0] = identifier.substring(0, first_colon);
1034
1035 String sr = identifier.substring(first_colon + 1);
1036 int second_colon = sr.indexOf(":");
1037 //logger.error(first_colon + " " + second_colon);
1038 strs[1] = sr.substring(0, second_colon);
1039
1040 strs[2] = sr.substring(second_colon + 1);
1041 return strs;
1042 }
1043 /** validate if the specified metadata prefix value is supported by the repository
1044 * by checking it in the OAIConfig.xml
1045 */
1046 private boolean containsMetadataPrefix(String prefix_value) {
1047 NodeList prefix_list = oai_config.getElementsByTagName(OAIXML.METADATA_PREFIX);
1048
1049 for(int i=0; i<prefix_list.getLength(); i++) {
1050 if(prefix_value.equals(GSXML.getNodeText((Element)prefix_list.item(i)).trim() )) {
1051 return true;
1052 }
1053 }
1054 return false;
1055 }
1056 private Element doGetRecord(Element msg){
1057 logger.info("");
1058 /** arguments:
1059 identifier: required
1060 metadataPrefix: required
1061 * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
1062 */
1063 Element get_record = OAIXML.createElement(OAIXML.GET_RECORD);
1064
1065 HashSet<String> valid_strs = new HashSet<String>();
1066 valid_strs.add(OAIXML.IDENTIFIER);
1067 valid_strs.add(OAIXML.METADATA_PREFIX);
1068
1069 Element req = (Element)GSXML.getChildByTagName(msg, GSXML.REQUEST_ELEM);
1070 NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM);
1071 HashMap<String, String> param_map = OAIXML.getParamMap(params);
1072
1073 if(!isValidParam(param_map, valid_strs) ||
1074 params.getLength() == 0 ||
1075 param_map.containsKey(OAIXML.IDENTIFIER) == false ||
1076 param_map.containsKey(OAIXML.METADATA_PREFIX) == false ) {
1077 logger.error("must have the metadataPrefix/identifier parameter.");
1078 return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
1079 }
1080
1081 String prefix = param_map.get(OAIXML.METADATA_PREFIX);
1082 String identifier = param_map.get(OAIXML.IDENTIFIER);
1083
1084 // verify the metadata prefix
1085 if (containsMetadataPrefix(prefix) == false) {
1086 logger.error("requested prefix is not found in OAIConfig.xml");
1087 return getMessage(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""));
1088 }
1089
1090 // get the names
1091 String[] strs = splitNames(identifier);
1092 if(strs == null || strs.length < 3) {
1093 logger.error("identifier is not in the form site:coll:id" + identifier);
1094 return getMessage(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, ""));
1095 }
1096 String name_of_site = strs[0];
1097 String coll_name = strs[1];
1098 String oid = strs[2];
1099
1100 //re-organize the request element
1101 // reset the 'to' attribute
1102 String verb = req.getAttribute(OAIXML.TO);
1103 req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
1104 // reset the identifier element
1105 Element param = GSXML.getNamedElement(req, OAIXML.PARAM, OAIXML.NAME, OAIXML.IDENTIFIER);
1106 if (param != null) {
1107 param.setAttribute(OAIXML.NAME, OAIXML.OID);
1108 param.setAttribute(OAIXML.VALUE, oid);
1109 }
1110
1111 //Now send the request to the message router to process
1112 Node result_node = mr.process(msg);
1113 return converter.nodeToElement(result_node);
1114 }
1115
1116 // See OAIConfig.xml
1117 // dynamically works out what the earliestDateStamp is, since it varies by collection
1118 // returns this time in *milliseconds*.
1119 protected long getEarliestDateStamp(NodeList oai_coll) {
1120 //do the earliestDatestamp
1121 long earliestDatestamp = System.currentTimeMillis();
1122 int oai_coll_size = oai_coll.getLength();
1123 if (oai_coll_size == 0) {
1124 logger.info("returned oai collection list is empty. Setting repository earliestDatestamp to be 1970-01-01.");
1125 earliestDatestamp = 0;
1126 }
1127 // the earliestDatestamp is now stored as a metadata element in the collection's buildConfig.xml file
1128 // we get the earliestDatestamp among the collections
1129 for(int i=0; i<oai_coll_size; i++) {
1130 long coll_earliestDatestamp = Long.parseLong(((Element)oai_coll.item(i)).getAttribute(OAIXML.EARLIEST_DATESTAMP));
1131 earliestDatestamp = (earliestDatestamp > coll_earliestDatestamp)? coll_earliestDatestamp : earliestDatestamp;
1132 }
1133
1134 return earliestDatestamp*1000; // converting from seconds to milliseconds
1135 }
1136}
Note: See TracBrowser for help on using the repository browser.