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

Last change on this file since 32830 was 32830, checked in by ak19, 5 years ago

Second and final part of fixing up OAI stuff so that there's no lock on the index db by OAI servlet side when a collection is being rebuilt. The bug was that the OAI servlet side would still keep a filelock on the db (index col db and etc oai-inf db, to be precise) when a collection was deactivated before moving build to index during the activate.pl stage. That's because the OAIMessageRouter does not responde to (de)activate messages sent to the regular library servlet's MessageRouter. This commit: Getting servercontrol.pm of activate.pl to send a request to (de)activate a collection to the OAIMessageRouter was far more involved: although OAIMessageRouter inherits from MessageRouter, it did not recognise the (de)activate query params because it specifically only recognises OAI verbs like Identify and the special case of 'reset' sent to the OAIMessageRouter. So added in pathways for activate and deactivate to be recognised and processed. Now servercontrol.pm will send (de)activate requests to both the MessageRouter and the OAIMessageRouter. And there's further support for if a collection is not an OAICollection (not part of the list of collections maintained by OAIReceptionist). What I don't have working, is that the collection is still enumerated by ListSets of the OAI servlet whereas attempting to view records and identifiers of the deactivated set fails. This misbehaviour doesn't impact rebuilding with activate.pl since it both deactivates then activates a collection, so a collection is not meant to remain in the deactivated state. The fix may be more complicated than removing the collection from OAIReceptionist's list of sets, since the OAI side deals with supercollections etc when it first loads OAICollections. So any fix has to take that into account.

File size: 46.1 KB
Line 
1/*
2 * OAIReceptionist.java
3 * Copyright (C) 2012 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 */
19
20package org.greenstone.gsdl3.core;
21
22import org.greenstone.gsdl3.util.*;
23import org.greenstone.gsdl3.action.*;
24// XML classes
25import org.w3c.dom.Node;
26import org.w3c.dom.NodeList;
27import org.w3c.dom.Document;
28import org.w3c.dom.Element;
29
30// other java classes
31import java.io.File;
32import java.util.*;
33
34import org.apache.log4j.*;
35
36/** a Receptionist, used for oai metadata response xml generation.
37 * This receptionist talks to the message router directly,
38 * instead of via any action, hence no action map is needed.
39 * @see the basic Receptionist
40 */
41public class OAIReceptionist implements ModuleInterface {
42
43 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.OAIReceptionist.class.getName());
44
45 /** Instead of a config_params object, only a site_name is needed by oai receptionist. */
46 protected String site_name = null;
47 /** The unique repository identifier */
48 protected String repository_id = null;
49
50 /** the configure file of this receptionist passed from the oai servlet. */
51 protected Element oai_config = null;
52
53 /** contained in the OAIConfig.xml deciding whether the resumptionToken should be in use */
54 protected int resume_after = -1 ;
55
56 /** the message router that the Receptionist and Actions will talk to */
57 protected ModuleInterface mr = null;
58
59 // Some of the data/responses will not change while the servlet is running, so
60 // we can cache them
61
62 /** A list of all the collections available to this OAI server */
63 protected Element collection_list = null;
64 /** a vector of the names, for convenience */
65 protected Vector<String> collection_name_list = null;
66 /** If this is true, then there are no OAI enabled collections, so can always return noRecordsMatch (after validating the request params) */
67 protected boolean noRecordsMatch = false;
68
69 /** A set of all known 'sets' */
70 protected HashSet<String> set_set = null;
71
72 protected boolean has_super_colls = false;
73 /** a hash of super set-> collection list */
74 protected HashMap<String, Vector<String>> super_coll_map = null;
75 /** store the super coll elements for convenience */
76 HashMap<String, Element> super_coll_data = null;
77 /** store the metadata formats ??????*/
78 /** The identify response */
79 protected Element identify_response = null;
80 /** The list set response */
81 protected Element listsets_response = null;
82 /** the list metadata formats response */
83 protected Element listmetadataformats_response = null;
84
85 public OAIReceptionist() {
86
87 }
88
89 public void cleanUp() {
90 if (this.mr != null) {
91
92 this.mr.cleanUp();
93 }
94 OAIResumptionToken.saveTokensToFile();
95 }
96
97 public void setSiteName(String site_name) {
98 this.site_name = site_name;
99 }
100 /** sets the message router - it should already be created and
101 * configured in the init() of a servlet (OAIServer, for example) before being passed to the receptionist*/
102 public void setMessageRouter(ModuleInterface mr) {
103 this.mr = mr;
104 }
105
106 /** configures the receptionist */
107 public boolean configure(Element config) {
108
109 if (this.mr==null) {
110 logger.error(" message routers must be set before calling oai configure");
111 return false;
112 }
113 if (config == null) {
114 logger.error(" oai configure file is null");
115 return false;
116 }
117 oai_config = config;
118 resume_after = getResumeAfter();
119
120 repository_id = getRepositoryIdentifier();
121 configureSuperSetInfo();
122 if (!configureSetInfo()) {
123 // there are no sets
124 logger.error("No sets (collections) available for OAI");
125 return false;
126 }
127
128 // load in tokens from OAIResumptionToken.xml, and then clear out any
129 // expired ones.
130 OAIResumptionToken.init();
131 OAIResumptionToken.clearExpiredTokens();
132
133 return true;
134 }
135
136 // assuming that sets are static. If collections change then the servlet
137 // should be restarted.
138 private boolean configureSuperSetInfo() {
139 // do we have any super colls listed in web/WEB-INF/classes/OAIConfig.xml?
140 // Will be like
141 // <oaiSuperSet>
142 // <SetSpec>xxx</SetSpec>
143 // <setName>xxx</SetName>
144 // <SetDescription>xxx</setDescription>
145 // </oaiSuperSet>
146 // The super set is listed in OAIConfig, and collections themselves state
147 // whether they are part of the super set or not.
148 NodeList super_coll_list = this.oai_config.getElementsByTagName(OAIXML.OAI_SUPER_SET);
149 this.super_coll_data = new HashMap<String, Element>();
150 if (super_coll_list.getLength() > 0) {
151 this.has_super_colls = true;
152 for (int i=0; i<super_coll_list.getLength(); i++) {
153 Element super_coll = (Element)super_coll_list.item(i);
154 Element set_spec = (Element)GSXML.getChildByTagName(super_coll, OAIXML.SET_SPEC);
155 if (set_spec != null) {
156 String name = GSXML.getNodeText(set_spec);
157 if (!name.equals("")) {
158 this.super_coll_data.put(name, super_coll);
159 logger.info("adding in super coll "+name);
160 }
161 }
162 }
163
164 if (this.super_coll_data.size()==0) {
165 this.has_super_colls = false;
166 }
167 }
168 if (this.has_super_colls == true) {
169 this.super_coll_map = new HashMap<String, Vector<String>>();
170 }
171 return true;
172
173 }
174 private boolean configureSetInfo() {
175 this.set_set = new HashSet<String>();
176
177 // First, we get a list of all the OAI enabled collections
178 // We get this by sending a listSets request to the MR
179 Document doc = XMLConverter.newDOM();
180 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
181
182 Element request = GSXML.createBasicRequest(doc, OAIXML.OAI_SET_LIST, "", null);
183 message.appendChild(request);
184 Node msg_node = mr.process(message);
185
186 if (msg_node == null) {
187 logger.error("returned msg_node from mr is null");
188 return false;
189 }
190 Element resp = (Element)GSXML.getChildByTagName(msg_node, GSXML.RESPONSE_ELEM);
191 Element coll_list = (Element)GSXML.getChildByTagName(resp, GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
192 if (coll_list == null) {
193 logger.error("coll_list is null");
194 return false;
195 }
196
197 this.collection_list = (Element)doc.importNode(coll_list, true);
198
199 // go through and store a list of collection names for convenience
200 // also create a 'to' attribute for the next request to the MR, which
201 // is a ListSets request to each collection
202 Node child = this.collection_list.getFirstChild();
203 if (child == null) {
204 logger.error("collection list has no children");
205 noRecordsMatch = true;
206 return false;
207 }
208
209 this.collection_name_list = new Vector<String>();
210 StringBuffer to = new StringBuffer();
211 boolean first = true;
212 while (child != null) {
213 if (child.getNodeName().equals(GSXML.COLLECTION_ELEM)) {
214 String coll_id =((Element) child).getAttribute(GSXML.NAME_ATT);
215 this.collection_name_list.add(coll_id);
216 if (!first) {
217 to.append(',');
218 }
219 first = false;
220 to.append(coll_id+"/"+OAIXML.LIST_SETS);
221 }
222 child = child.getNextSibling();
223 }
224 if (first) {
225 // we haven't found any collections
226 logger.error("found no collection elements in collectionList");
227 noRecordsMatch = true;
228 return false;
229 }
230 Document listsets_doc = XMLConverter.newDOM();
231 Element listsets_element = listsets_doc.createElement(OAIXML.LIST_SETS);
232 this.listsets_response = getMessage(listsets_doc, listsets_element);
233
234 // Now, for each collection, get a list of all its sets
235 // might include subsets (classifiers) or super colls
236 // We'll reuse the first message, changing its type and to atts
237 request.setAttribute(GSXML.TYPE_ATT, "");
238 request.setAttribute(GSXML.TO_ATT, to.toString());
239 // send to MR
240 msg_node = mr.process(message);
241 //logger.info("*** " + XMLConverter.getPrettyString(msg_node));
242 NodeList response_list = ((Element)msg_node).getElementsByTagName(GSXML.RESPONSE_ELEM);
243 for (int c=0; c<response_list.getLength(); c++) {
244 // for each collection's response
245 Element response = (Element)response_list.item(c);
246 String coll_name = GSPath.getFirstLink(response.getAttribute(GSXML.FROM_ATT));
247 logger.info("*** coll from response "+coll_name);
248 NodeList set_list = response.getElementsByTagName(OAIXML.SET);
249 for (int j=0; j<set_list.getLength(); j++) {
250 // now check if it a super collection
251 Element set = (Element)set_list.item(j);
252 String set_spec = GSXML.getNodeText((Element)GSXML.getChildByTagName(set, OAIXML.SET_SPEC));
253 logger.info("*** set spec = "+set_spec);
254 // this may change if we add site name back in
255 // setSpecs will be collname or collname:subset or supercollname
256 if (set_spec.indexOf(":")==-1 && ! set_spec.equals(coll_name)) {
257 // it must be a super coll spec
258 logger.info("*** found super coll, "+set_spec);
259 // check that it is a valid one from config
260 if (this.has_super_colls == true && this.super_coll_data.containsKey(set_spec)) {
261 Vector <String> subcolls = this.super_coll_map.get(set_spec);
262 if (subcolls == null) {
263 logger.info("*** its new!!");
264 // not in there yet
265 subcolls = new Vector<String>();
266 this.set_set.add(set_spec);
267 this.super_coll_map.put(set_spec, subcolls);
268 // the first time a supercoll is mentioned, add into the set list
269 logger.info("*** finding the set info "+XMLConverter.getPrettyString(this.super_coll_data.get(set_spec)));
270 listsets_element.appendChild(GSXML.duplicateWithNewName(listsets_doc, this.super_coll_data.get(set_spec), OAIXML.SET, true));
271 }
272 // add this collection to the list for the super coll
273 subcolls.add(coll_name);
274 }
275 } else { // its either the coll itself or a subcoll
276 // add in the set
277 listsets_element.appendChild(listsets_doc.importNode(set, true));
278 this.set_set.add(set_spec);
279 }
280 } // for each set in the collection
281 } // for each OAI enabled collection
282 return true;
283 }
284
285 protected void resetMessageRouter() {
286 // we just need to send a configure request to MR
287 Document doc = XMLConverter.newDOM();
288 Element mr_request_message = doc.createElement(GSXML.MESSAGE_ELEM);
289 Element mr_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", null);
290 mr_request_message.appendChild(mr_request);
291
292 Element system = doc.createElement(GSXML.SYSTEM_ELEM);
293 mr_request.appendChild(system);
294 system.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_CONFIGURE);
295
296 Element response = (Element) this.mr.process(mr_request_message);
297 logger.info("*** configure response = "+XMLConverter.getPrettyString(response));
298 }
299
300 protected boolean activateOrDeactivateCollection(String collName, int activationState) {
301 // Send a request like: a=s&sa=<a|d>&st=collection&sn=<collName>
302 Document doc = XMLConverter.newDOM();
303 Element mr_request_message = doc.createElement(GSXML.MESSAGE_ELEM);
304 Element mr_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_SYSTEM, "", null);
305 mr_request_message.appendChild(mr_request);
306
307 Element system = doc.createElement(GSXML.SYSTEM_ELEM);
308 mr_request.appendChild(system);
309 if(activationState == OAIXML.ACTIVATION) {
310 system.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_ACTIVATE);
311 } else {
312 system.setAttribute(GSXML.TYPE_ATT, GSXML.SYSTEM_TYPE_DEACTIVATE);
313 }
314 system.setAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT, GSXML.COLLECTION_ELEM);
315 system.setAttribute(GSXML.SYSTEM_MODULE_NAME_ATT, collName);
316
317 Element response = (Element) this.mr.process(mr_request_message);
318 logger.info("*** (de)activate response = "+XMLConverter.getPrettyString(response));
319
320 boolean success = false;
321 NodeList elements = response.getElementsByTagName(GSXML.STATUS_ELEM);
322 if(elements.getLength() <= 0) {
323 logger.error("***** No result status");
324 return false;
325 }
326
327 String result = GSXML.getNodeText((Element)elements.item(0));
328 if(result.contains("could not be")) { // could not be (de)activated
329 return false;
330 } else {
331 return true;
332 }
333 }
334
335 /** process using strings - just calls process using Elements */
336 public String process(String xml_in) {
337
338 Node message_node = XMLConverter.getDOM(xml_in);
339 Node page = process(message_node);
340 return XMLConverter.getString(page);
341 }
342
343 //Compose a message/response element used to send back to the OAIServer servlet.
344 //This method is only used within OAIReceptionist
345 private Element getMessage(Document doc, Element e) {
346 Element msg = doc.createElement(GSXML.MESSAGE_ELEM);
347 Element response = doc.createElement(GSXML.RESPONSE_ELEM);
348 msg.appendChild(response);
349 response.appendChild(e);
350 return msg;
351 }
352
353 /** process - produce xml data in response to a request
354 * if something goes wrong, it returns null -
355 */
356 public Node process(Node message_node) {
357 logger.info("*** OAIReceptionist received request");
358
359 Element message = GSXML.nodeToElement(message_node);
360 logger.info("*** " + XMLConverter.getString(message));
361
362 // check that its a correct message tag
363 if (!message.getTagName().equals(GSXML.MESSAGE_ELEM)) {
364 logger.error(" Invalid message. GSDL message should start with <"+GSXML.MESSAGE_ELEM+">, instead it starts with:"+message.getTagName()+".");
365 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "Internal messaging error");
366 }
367
368 // get the request out of the message - assume that there is only one
369 Element request = (Element)GSXML.getChildByTagName(message, GSXML.REQUEST_ELEM);
370 if (request == null) {
371 logger.error(" message had no request!");
372 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "Internal messaging error");
373 }
374
375 // Special cases: certain non-OAI commands/non-verbs are recognised
376 // special case, reset=true for reloading the MR and recept data
377 String reset = request.getAttribute("reset");
378 if (!reset.equals("")) {
379 resetMessageRouter();
380 configureSetInfo();
381 return OAIXML.createResetResponse(true);
382 }
383
384 // special case 2: activate=<collname> or deactivate=<collname> can be passed to oaiserver servlet too
385 if (request.hasAttribute(GSXML.SYSTEM_TYPE_ACTIVATE)) {
386 String collname = request.getAttribute(GSXML.SYSTEM_TYPE_ACTIVATE);
387 // don't bother activating if it's not an OAI collection
388 if (!this.set_set.contains(collname)) {
389 return OAIXML.createDeActivationOfNonOAICollResponse(OAIXML.ACTIVATION, collname);
390 }
391 boolean success = activateOrDeactivateCollection(collname, OAIXML.ACTIVATION);
392 return OAIXML.createActivationStateResponse(success, OAIXML.ACTIVATION, collname);
393 } else if (request.hasAttribute(GSXML.SYSTEM_TYPE_DEACTIVATE)) {
394 String collname = request.getAttribute(GSXML.SYSTEM_TYPE_DEACTIVATE);
395 // don't bother deactivating if it's not an OAI collection
396 if (!this.set_set.contains(collname)) {
397 return OAIXML.createDeActivationOfNonOAICollResponse(OAIXML.DEACTIVATION, collname);
398 }
399 boolean success = activateOrDeactivateCollection(collname, OAIXML.DEACTIVATION);
400 return OAIXML.createActivationStateResponse(success, OAIXML.DEACTIVATION, collname);
401 }
402
403 //At this stage, the value of 'to' attribute of the request must be the 'verb'
404 //The only thing that the oai receptionist can be sure is that these verbs are valid, nothing else.
405 String verb = request.getAttribute(GSXML.TO_ATT);
406 if (verb.equals(OAIXML.IDENTIFY)) {
407 return doIdentify();
408 }
409 if (verb.equals(OAIXML.LIST_METADATA_FORMATS)) {
410 return doListMetadataFormats(request);
411 }
412 if (verb.equals(OAIXML.LIST_SETS)) {
413 // we have composed the list sets response on init
414 // Note this means that list sets never uses resumption tokens
415 return this.listsets_response;
416 }
417 if (verb.equals(OAIXML.GET_RECORD)) {
418 return doGetRecord(request);
419 }
420 if (verb.equals(OAIXML.LIST_IDENTIFIERS)) {
421 return doListIdentifiersOrRecords(request,OAIXML.LIST_IDENTIFIERS , OAIXML.HEADER);
422 }
423 if (verb.equals(OAIXML.LIST_RECORDS)) {
424 return doListIdentifiersOrRecords(request, OAIXML.LIST_RECORDS, OAIXML.RECORD);
425 }
426 // should never get here as verbs were checked in OAIServer
427 return OAIXML.createErrorMessage(OAIXML.BAD_VERB, "Unexpected things happened");
428
429 }
430
431
432 private int getResumeAfter() {
433 Element resume_after = (Element)GSXML.getChildByTagName(oai_config, OAIXML.RESUME_AFTER);
434 if(resume_after != null) return Integer.parseInt(GSXML.getNodeText(resume_after));
435 return -1;
436 }
437 private String getRepositoryIdentifier() {
438 Element ri = (Element)GSXML.getChildByTagName(oai_config, OAIXML.REPOSITORY_IDENTIFIER);
439 if (ri != null) {
440 return GSXML.getNodeText(ri);
441 }
442 return "";
443 }
444
445
446 /** if the param_map contains strings other than those in valid_strs, return false;
447 * otherwise true.
448 */
449 private boolean areAllParamsValid(HashMap<String, String> param_map, HashSet<String> valid_strs) {
450 ArrayList<String> param_list = new ArrayList<String>(param_map.keySet());
451 for(int i=0; i<param_list.size(); i++) {
452 logger.info("*** param, key = "+param_list.get(i)+", value = "+param_map.get(param_list.get(i)));
453 if (valid_strs.contains(param_list.get(i)) == false) {
454 return false;
455 }
456 }
457 return true;
458 }
459
460 private Element doListIdentifiersOrRecords(Element req, String verb, String record_type) {
461 // options: from, until, set, metadataPrefix, resumptionToken
462 // exceptions: badArgument, badResumptionToken, cannotDisseminateFormat, noRecordMatch, and noSetHierarchy
463 HashSet<String> valid_strs = new HashSet<String>();
464 valid_strs.add(OAIXML.FROM);
465 valid_strs.add(OAIXML.UNTIL);
466 valid_strs.add(OAIXML.SET);
467 valid_strs.add(OAIXML.METADATA_PREFIX);
468 valid_strs.add(OAIXML.RESUMPTION_TOKEN);
469
470 Document result_doc = XMLConverter.newDOM();
471 Element result_element = result_doc.createElement(verb);
472 boolean result_token_needed = false; // does this result need to include a
473 // resumption token
474
475 NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
476
477 HashMap<String, String> param_map = GSXML.getParamMap(params);
478
479 // are all the params valid?
480 if (!areAllParamsValid(param_map, valid_strs)) {
481 logger.error("One of the params is invalid");
482 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "There was an invalid parameter");
483 // TODO, need to tell the user which one was invalid ??
484 }
485
486 // Do we have a resumption token??
487 String token = null;
488 String from = null;
489 String until = null;
490 boolean set_requested = false;
491 String set_spec_str = null;
492 String prefix_value = null;
493 int cursor = 0;
494 int current_cursor = 0;
495 String current_set = null;
496 long initial_time = 0;
497
498 int total_size = -1; // we are only going to set this in resumption
499 // token if it is easy to work out, i.e. not sending extra requests to
500 // MR just to calculate total size
501
502 if(param_map.containsKey(OAIXML.RESUMPTION_TOKEN)) {
503 // Is it an error to have other arguments? Do we need to check to make sure that resumptionToken is the only arg??
504 // validate resumptionToken
505 token = param_map.get(OAIXML.RESUMPTION_TOKEN);
506 logger.info("has resumptionToken " + token);
507 if(OAIResumptionToken.isValidToken(token) == false) {
508 logger.error("token is not valid");
509 return OAIXML.createErrorMessage(OAIXML.BAD_RESUMPTION_TOKEN, "");
510 }
511 result_token_needed = true; // we always need to send a token back if we have started with one. It may be empty if we are returning the end of the list
512 // initialise the request params from the stored token data
513 HashMap<String, String> token_data = OAIResumptionToken.getTokenData(token);
514 from = token_data.get(OAIXML.FROM);
515 until = token_data.get(OAIXML.UNTIL);
516 set_spec_str = token_data.get(OAIXML.SET);
517 if (set_spec_str != null) {
518 set_requested = true;
519 }
520 prefix_value = token_data.get(OAIXML.METADATA_PREFIX);
521 current_set = token_data.get(OAIResumptionToken.CURRENT_SET);
522 try {
523 cursor = Integer.parseInt(token_data.get(OAIXML.CURSOR));
524 cursor = cursor + resume_after; // increment cursor
525 current_cursor = Integer.parseInt(token_data.get(OAIResumptionToken.CURRENT_CURSOR));
526 initial_time = Long.parseLong(token_data.get(OAIResumptionToken.INITIAL_TIME));
527 } catch (NumberFormatException e) {
528 logger.error("tried to parse int from cursor data and failed");
529 }
530
531 // check that the collections/sets haven't changed since the token was issued
532 if (collectionsChangedSinceTime(set_spec_str, initial_time)) {
533 logger.error("one of the collections in set "+set_spec_str+" has changed since token issued. Expiring the token");
534 OAIResumptionToken.expireToken(token);
535 return OAIXML.createErrorMessage(OAIXML.BAD_RESUMPTION_TOKEN, "Repository data has changed since this token was issued. Resend original request");
536 }
537 }
538 else {
539 // no resumption token, lets check the other params
540 // there must be a metadataPrefix
541 if (!param_map.containsKey(OAIXML.METADATA_PREFIX)) {
542 logger.error("metadataPrefix param required");
543 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "metadataPrefix param required");
544 }
545
546 //if there are any date params, check they're of the right format
547 Date from_date = null;
548 Date until_date = null;
549
550 from = param_map.get(OAIXML.FROM);
551 if(from != null) {
552 from_date = OAIXML.getDate(from);
553 if(from_date == null) {
554 logger.error("invalid date: " + from);
555 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid format for "+ OAIXML.FROM);
556 }
557 }
558 until = param_map.get(OAIXML.UNTIL);
559 if(until != null) {
560 until_date = OAIXML.getDate(until);
561 if(until_date == null) {
562 logger.error("invalid date: " + until);
563 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid format for "+ OAIXML.UNTIL);
564 }
565 }
566
567 if(from != null && until != null) { // check they are of the same date-time format (granularity)
568 if(from.length() != until.length()) {
569 logger.error("The request has different granularities (date-time formats) for the From and Until date parameters.");
570 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "The request has different granularities (date-time formats) for the From and Until date parameters.");
571 }
572
573 if(from_date.compareTo(until_date) > 0) { // from date can't be later than until date
574 return OAIXML.createErrorMessage(OAIXML.NO_RECORDS_MATCH, "");
575 }
576 }
577
578 if(until_date != null) {
579
580 // Also call until_date.compareTo(earliestdatestamp) as the until date can't precede the earliest timestamp
581 // Unfortunately, this test has to be done after the granularity test
582 // compareTo() returns the value 0 if the argument Date is equal to this Date; a value less than 0 if this Date is before
583 // the Date argument; and a value greater than 0 if this Date is after the Date argument.
584 long earliestDatestamp = getEarliestDateStamp(collection_list);
585 String earliestDatestamp_str = OAIXML.getTime(earliestDatestamp);
586 Date earliestDatestamp_date = OAIXML.getDate(earliestDatestamp_str);
587
588 if(until_date.compareTo(earliestDatestamp_date) < 0) {
589 return OAIXML.createErrorMessage(OAIXML.NO_RECORDS_MATCH, "");
590 }
591 }
592
593
594 // check the set arg is a set we know about
595 set_requested = param_map.containsKey(OAIXML.SET);
596 set_spec_str = null;
597 if(set_requested == true) {
598 set_spec_str = param_map.get(OAIXML.SET);
599 if (!this.set_set.contains(set_spec_str)) {
600 // the set is not one we know about
601 logger.error("requested set is not found in this repository");
602 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid set parameter");
603
604 }
605 }
606 // Is the metadataPrefix arg one this repository supports?
607 prefix_value = param_map.get(OAIXML.METADATA_PREFIX);
608 if (repositorySupportsMetadataPrefix(prefix_value) == false) {
609 logger.error("requested metadataPrefix is not found in OAIConfig.xml");
610 return OAIXML.createErrorMessage(OAIXML.CANNOT_DISSEMINATE_FORMAT, "metadata format "+prefix_value+" not supported by this repository");
611 }
612
613 } // else no resumption token, check other params
614
615 // Whew. Now we have validated the params, we can work on doing the actual
616 // request
617
618
619 Document doc = XMLConverter.newDOM();
620 Element mr_msg = doc.createElement(GSXML.MESSAGE_ELEM);
621 Element mr_req = doc.createElement(GSXML.REQUEST_ELEM);
622 // TODO does this need a type???
623 mr_msg.appendChild(mr_req);
624
625 // copy in the from/until params if there
626 if (from != null) {
627 mr_req.appendChild(GSXML.createParameter(doc, OAIXML.FROM, from));
628 }
629 if (until != null) {
630 mr_req.appendChild(GSXML.createParameter(doc, OAIXML.UNTIL, until));
631 }
632 // add metadataPrefix
633 mr_req.appendChild(GSXML.createParameter(doc, OAIXML.METADATA_PREFIX, prefix_value));
634
635 // do we have a set???
636 // if no set, we send to all collections in the collection list
637 // if super set, we send to all collections in super set list
638 // if a single collection, send to it
639 // if a subset, send to the collection
640 Vector<String> current_coll_list = getCollectionListForSet(set_spec_str);
641 boolean single_collection = false;
642 if (current_coll_list.size() == 1) {
643 single_collection = true;
644 }
645 if (set_spec_str != null && set_spec_str.indexOf(":") != -1) {
646 // we have a subset - add the set param back in
647 mr_req.appendChild(GSXML.createParameter(doc, OAIXML.SET, set_spec_str));
648 }
649
650 int num_collected_records = 0;
651 int start_point = current_cursor; // may not be 0 if we are using a resumption token
652 String resumption_collection = "";
653 boolean empty_result_token = false; // if we are sending the last part of a list, then the token value will be empty
654
655 // iterate through the list of collections and send the request to each
656
657 int start_coll=0;
658 if (current_set != null) {
659 // we are resuming a previous request, need to locate the first collection
660 for (int i=0; i<current_coll_list.size(); i++) {
661 if (current_set.equals(current_coll_list.get(i))) {
662 start_coll = i;
663 break;
664 }
665 }
666 }
667
668 for (int i=start_coll; i<current_coll_list.size(); i++) {
669 String current_coll = current_coll_list.get(i);
670 mr_req.setAttribute(GSXML.TO_ATT, current_coll+"/"+verb);
671
672 Element result = (Element)mr.process(mr_msg);
673 logger.info("*** " + verb+ " result for coll "+current_coll);
674 logger.info("*** " + XMLConverter.getPrettyString(result));
675 if (result == null) {
676 logger.info("message router returns null");
677 // do what??? carry on? fail??
678 return OAIXML.createErrorMessage("Internal service returns null", "");
679 }
680 Element res = (Element)GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
681 if(res == null) {
682 logger.info("response element in xml_result is null");
683 return OAIXML.createErrorMessage("Internal service returns null", "");
684 }
685 NodeList record_list = res.getElementsByTagName(record_type);
686 int num_records = record_list.getLength();
687 if(num_records == 0) {
688 logger.info("message router returns 0 records for coll "+current_coll);
689 continue; // try the next collection
690 }
691 if (single_collection) {
692 total_size = num_records;
693 }
694 int records_to_add = (resume_after > 0 ? resume_after - num_collected_records : num_records);
695 if (records_to_add > (num_records-start_point)) {
696 records_to_add = num_records-start_point;
697 }
698 addRecordsToList(result_doc, result_element, record_list, start_point, records_to_add);
699 num_collected_records += records_to_add;
700
701 // do we need to stop here, and do we need to issue a resumption token?
702 if (resume_after > 0 && num_collected_records == resume_after) {
703 // we have finished collecting records at the moment.
704 // but are we conincidentally at the end? or are there more to go?
705 if (records_to_add < (num_records - start_point)) {
706 // we have added less than this collection had
707 start_point += records_to_add;
708 resumption_collection = current_coll;
709 result_token_needed = true;
710 }
711 else {
712 // we added all this collection had to offer
713 // is there another collection in the list??
714 if (i<current_coll_list.size()-1) {
715 result_token_needed = true;
716 start_point = 0;
717 resumption_collection = current_coll_list.get(i+1);
718 }
719 else {
720 // we have finished one collection and there are no more collection
721 // if we need to send a resumption token (in this case, only because we started with one, then it will be empty
722 logger.info("*** at end of list, need empty result token");
723 empty_result_token = true;
724 }
725 }
726 break;
727 }
728 start_point = 0; // only the first one will have start non-zero, if we
729 // have a resumption token
730
731 } // for each collection
732
733 if (num_collected_records ==0) {
734 // there were no matching results
735 return OAIXML.createErrorMessage(OAIXML.NO_RECORDS_MATCH, "");
736 }
737
738 if (num_collected_records < resume_after) {
739 // we have been through all collections, and there are no more
740 // if we need a result token - only because we started with one, so we need to send an empty one, then make sure everyone knows we are just sending an empty one
741 if (result_token_needed) {
742 empty_result_token = true;
743 }
744 }
745
746 if (result_token_needed) {
747 // we need a resumption token
748 if (empty_result_token) {
749 logger.info("*** have empty result token");
750 token = "";
751 } else {
752 if (token != null) {
753 // we had a token for this request, we can just update it
754 token = OAIResumptionToken.updateToken(token, ""+cursor, resumption_collection, ""+start_point);
755 } else {
756 // we are generating a new one
757 token = OAIResumptionToken.createAndStoreResumptionToken(set_spec_str, prefix_value, from, until, ""+cursor, resumption_collection, ""+start_point );
758 }
759 }
760
761 // result token XML
762 long expiration_date = -1;
763 if (empty_result_token) {
764 // we know how many records in total as we have sent them all
765 total_size = cursor+num_collected_records;
766 } else {
767 // non-empty token, set the expiration date
768 expiration_date = OAIResumptionToken.getExpirationDate(token);
769 }
770 Element token_elem = OAIXML.createResumptionTokenElement(result_doc, token, total_size, cursor, expiration_date);
771 // OAIXML.addToken(token_elem); // store it
772 result_element.appendChild(token_elem); // add to the result
773 }
774
775
776 return getMessage(result_doc, result_element);
777 }
778
779 private Vector<String> getCollectionListForSet(String set) {
780 if (set == null) {
781 // no set requested, need the complete collection list
782 return this.collection_name_list;
783 }
784 if (has_super_colls && super_coll_map.containsKey(set)) {
785 return super_coll_map.get(set);
786 }
787
788 Vector<String> coll_list = new Vector<String>();
789 if (set.indexOf(":") != -1) {
790 String col_name = set.substring(0, set.indexOf(":"));
791 coll_list.add(col_name);
792 }
793 else {
794 coll_list.add(set);
795 }
796 return coll_list;
797 }
798 private void addRecordsToList(Document doc, Element result_element, NodeList
799 record_list, int start_point, int num_records) {
800 int end_point = start_point + num_records;
801 for (int i=start_point; i<end_point; i++) {
802 result_element.appendChild(doc.importNode(record_list.item(i), true));
803 }
804 }
805
806 private Element collectAll(Element result, Element msg, String verb, String elem_name) {
807 if(result == null) {
808 //in the first round, result is null
809 return msg;
810 }
811 Element res_in_result = (Element)GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
812 if(res_in_result == null) { // return the results of all other collections accumulated so far
813 return msg;
814 }
815 Element verb_elem = (Element)GSXML.getChildByTagName(res_in_result, verb);
816 if(msg == null) {
817 return result;
818 }
819
820 //e.g., get all <record> elements from the returned message. There may be none of
821 //such element, for example, the collection service returned an error message
822 NodeList elem_list = msg.getElementsByTagName(elem_name);
823
824 for (int i=0; i<elem_list.getLength(); i++) {
825 verb_elem.appendChild(res_in_result.getOwnerDocument().importNode(elem_list.item(i), true));
826 }
827 return result;
828 }
829
830
831 /** there are three possible exception conditions: bad argument, idDoesNotExist, and noMetadataFormat.
832 * The first one is handled here, and the last two are processed by OAIPMH.
833 */
834 private Element doListMetadataFormats(Element req) {
835 //if the verb is ListMetadataFormats, there could be only one parameter: identifier
836 //, or there is no parameter; otherwise it is an error
837 //logger.info("" + XMLConverter.getString(msg));
838
839 NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
840 Element param = null;
841 Document lmf_doc = XMLConverter.newDOM();
842 if(params.getLength() == 0) {
843 //this is requesting metadata formats for the whole repository
844 //read the oaiConfig.xml file, return the metadata formats specified there.
845 if (this.listmetadataformats_response != null) {
846 // we have already created it
847 return this.listmetadataformats_response;
848 }
849
850 Element list_metadata_formats = lmf_doc.createElement(OAIXML.LIST_METADATA_FORMATS);
851 // get all the formats out of oai_config
852 NodeList formats = oai_config.getElementsByTagName(OAIXML.METADATA_FORMAT);
853 if (formats.getLength() ==0) {
854 logger.error("OAIConfig.xml must contain the supported metadata formats");
855 // TODO this is internal error, what to do???
856 return getMessage(lmf_doc, list_metadata_formats);
857 }
858
859 for(int i=0; i<formats.getLength(); i++) {
860 Element f = OAIXML.getMetadataFormatShort(lmf_doc, (Element)formats.item(i));
861 list_metadata_formats.appendChild(f);
862 }
863 this.listmetadataformats_response = getMessage(lmf_doc, list_metadata_formats);
864 return this.listmetadataformats_response;
865
866 }
867
868 if (params.getLength() > 1) {
869 //Bad argument. Can't be more than one parameters for ListMetadataFormats verb
870 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
871 }
872
873 // This is a request for the metadata of a particular item with an identifier
874 /**the request xml is in the form: <request>
875 * <param name=.../>
876 * </request>
877 *And there is a param element and one element only. (No paramList element in between).
878 */
879 param = (Element)params.item(0);
880 String param_name = param.getAttribute(GSXML.NAME_ATT);
881 String identifier = "";
882 if (!param_name.equals(OAIXML.IDENTIFIER)) {
883 //Bad argument
884 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
885 }
886
887 identifier = param.getAttribute(GSXML.VALUE_ATT);
888 // the identifier is in the form: <coll_name>:<OID>
889 // so it must contain at least one ':' characters
890 // (the oid itself may contain : chars)
891 String[] strs = identifier.split(":", 2);
892 if(strs.length != 2) {
893 logger.error("identifier is not in the form coll:id" + identifier);
894 return OAIXML.createErrorMessage(OAIXML.ID_DOES_NOT_EXIST, "");
895 }
896
897 // send request to message router
898 // get the names
899 String coll_name = strs[0];
900 String oid = strs[1];
901
902 Document msg_doc = XMLConverter.newDOM();
903 Element message = msg_doc.createElement(GSXML.MESSAGE_ELEM);
904 String verb = req.getAttribute(GSXML.TO_ATT);
905 String new_to = coll_name + "/" + verb;
906 Element request = GSXML.createBasicRequest(msg_doc, "oai???", new_to, null);
907 message.appendChild(request);
908 // add the id param
909 GSXML.addParameterToList(request, OAIXML.OID, oid);
910
911 //Now send the request to the message router to process
912 Node result_node = mr.process(message);
913 return GSXML.nodeToElement(result_node);
914 }
915
916 private void copyNamedElementfromConfig(Element to_elem, String element_name) {
917 Element original_element = (Element)GSXML.getChildByTagName(oai_config, element_name);
918 if(original_element != null) {
919 GSXML.copyNode(to_elem, original_element);
920 }
921 }
922
923
924 private Element doIdentify() {
925 //The validation for this verb has been done in OAIServer.validate(). So no bother here.
926 logger.info("");
927 if (this.identify_response != null) {
928 // we have already created it
929 return getMessage(this.identify_response.getOwnerDocument(), this.identify_response);
930 }
931 Document doc = XMLConverter.newDOM();
932 Element identify = doc.createElement(OAIXML.IDENTIFY);
933 //do the repository name
934 copyNamedElementfromConfig(identify, OAIXML.REPOSITORY_NAME);
935 //do the baseurl
936 copyNamedElementfromConfig(identify, OAIXML.BASE_URL);
937 //do the protocol version
938 copyNamedElementfromConfig(identify, OAIXML.PROTOCOL_VERSION);
939
940 //There can be more than one admin email according to the OAI specification
941 NodeList admin_emails = GSXML.getChildrenByTagName(oai_config, OAIXML.ADMIN_EMAIL);
942 int num_admin = 0;
943 Element from_admin_email = null;
944 if (admin_emails != null) {
945 num_admin = admin_emails.getLength();
946 }
947 for (int i=0; i<num_admin; i++) {
948 GSXML.copyNode(identify, admin_emails.item(i));
949 }
950
951 //do the earliestDatestamp
952 //send request to mr to search through the earliest datestamp amongst all oai collections in the repository.
953 //ask the message router for a list of oai collections
954 //NodeList oai_coll = getOAICollectionList();
955 long earliestDatestamp = getEarliestDateStamp(collection_list);
956 String earliestDatestamp_str = OAIXML.getTime(earliestDatestamp);
957 Element earliestDatestamp_elem = doc.createElement(OAIXML.EARLIEST_DATESTAMP);
958 GSXML.setNodeText(earliestDatestamp_elem, earliestDatestamp_str);
959 identify.appendChild(earliestDatestamp_elem);
960
961 //do the deletedRecord
962 copyNamedElementfromConfig(identify, OAIXML.DELETED_RECORD);
963 //do the granularity
964 copyNamedElementfromConfig(identify, OAIXML.GRANULARITY);
965
966 // output the oai identifier
967 Element description = doc.createElement(OAIXML.DESCRIPTION);
968 identify.appendChild(description);
969 // TODO, make this a valid id
970 Element oaiIdentifier = OAIXML.createOAIIdentifierXML(doc, repository_id, "lucene-jdbm-demo", "ec159e");
971 description.appendChild(oaiIdentifier);
972
973 // if there are any oaiInfo metadata, add them in too.
974 Element info = (Element)GSXML.getChildByTagName(oai_config, OAIXML.OAI_INFO);
975 if (info != null) {
976 NodeList meta = GSXML.getChildrenByTagName(info, OAIXML.METADATA);
977 if (meta != null && meta.getLength() > 0) {
978 Element gsdl = OAIXML.createGSDLElement(doc);
979 description.appendChild(gsdl);
980 for (int m = 0; m<meta.getLength(); m++) {
981 GSXML.copyNode(gsdl, meta.item(m));
982 }
983
984 }
985 }
986 this.identify_response = identify;
987 return getMessage(doc, identify);
988 }
989 /** split the identifier into <collection + OID> as an array
990 It has already been checked that the 'identifier' contains at least one ':'
991 */
992
993 /** validate if the specified metadata prefix value is supported by the repository
994 * by checking it in the OAIConfig.xml
995 */
996 private boolean repositorySupportsMetadataPrefix(String prefix_value) {
997 NodeList prefix_list = oai_config.getElementsByTagName(OAIXML.METADATA_PREFIX);
998
999 for(int i=0; i<prefix_list.getLength(); i++) {
1000 if(prefix_value.equals(GSXML.getNodeText((Element)prefix_list.item(i)).trim() )) {
1001 return true;
1002 }
1003 }
1004 return false;
1005 }
1006 private Element doGetRecord(Element req){
1007 logger.info("");
1008 /** arguments:
1009 identifier: required
1010 metadataPrefix: required
1011 * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
1012 */
1013 Document doc = XMLConverter.newDOM();
1014 Element get_record = doc.createElement(OAIXML.GET_RECORD);
1015
1016 HashSet<String> valid_strs = new HashSet<String>();
1017 valid_strs.add(OAIXML.IDENTIFIER);
1018 valid_strs.add(OAIXML.METADATA_PREFIX);
1019
1020 NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
1021 HashMap<String, String> param_map = GSXML.getParamMap(params);
1022
1023 if(!areAllParamsValid(param_map, valid_strs) ||
1024 params.getLength() == 0 ||
1025 param_map.containsKey(OAIXML.IDENTIFIER) == false ||
1026 param_map.containsKey(OAIXML.METADATA_PREFIX) == false ) {
1027 logger.error("must have the metadataPrefix/identifier parameter.");
1028 return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
1029 }
1030
1031 String prefix = param_map.get(OAIXML.METADATA_PREFIX);
1032 String identifier = param_map.get(OAIXML.IDENTIFIER);
1033
1034 // verify the metadata prefix
1035 if (repositorySupportsMetadataPrefix(prefix) == false) {
1036 logger.error("requested prefix is not found in OAIConfig.xml");
1037 return OAIXML.createErrorMessage(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
1038 }
1039
1040 // get the names
1041 String[] strs = identifier.split(":", 2);
1042 if(strs == null || strs.length < 2) {
1043 logger.error("identifier is not in the form coll:id" + identifier);
1044 return OAIXML.createErrorMessage(OAIXML.ID_DOES_NOT_EXIST, "");
1045 }
1046 //String name_of_site = strs[0];
1047 String coll_name = strs[0];
1048 String oid = strs[1];
1049
1050 //re-organize the request element
1051 // reset the 'to' attribute
1052 String verb = req.getAttribute(GSXML.TO_ATT);
1053 req.setAttribute(GSXML.TO_ATT, coll_name + "/" + verb);
1054 // reset the identifier element
1055 Element param = GSXML.getNamedElement(req, GSXML.PARAM_ELEM, GSXML.NAME_ATT, OAIXML.IDENTIFIER);
1056 if (param != null) {
1057 param.setAttribute(GSXML.NAME_ATT, OAIXML.OID);
1058 param.setAttribute(GSXML.VALUE_ATT, oid);
1059 }
1060
1061 //Now send the request to the message router to process
1062 Element msg = doc.createElement(GSXML.MESSAGE_ELEM);
1063 msg.appendChild(doc.importNode(req, true));
1064 Node result_node = mr.process(msg);
1065 return GSXML.nodeToElement(result_node);
1066 }
1067
1068 // See OAIConfig.xml
1069 // dynamically works out what the earliestDateStamp is, since it varies by collection
1070 // returns this time in *milliseconds*.
1071 protected long getEarliestDateStamp(Element oai_coll_list) {
1072 // config earliest datstamp
1073 long config_datestamp = 0;
1074 Element config_datestamp_elem = (Element)GSXML.getChildByTagName(this.oai_config, OAIXML.EARLIEST_DATESTAMP);
1075 if (config_datestamp_elem != null) {
1076 String datest = GSXML.getNodeText(config_datestamp_elem);
1077 config_datestamp = OAIXML.getTime(datest);
1078 if (config_datestamp == -1) {
1079 config_datestamp = 0;
1080 }
1081 }
1082 //do the earliestDatestamp
1083 long current_time = System.currentTimeMillis();
1084 long earliestDatestamp = current_time;
1085 NodeList oai_coll = oai_coll_list.getElementsByTagName(GSXML.COLLECTION_ELEM);
1086 int oai_coll_size = oai_coll.getLength();
1087 if (oai_coll_size == 0) {
1088 logger.info("returned oai collection list is empty. Setting repository earliestDatestamp to be the earliest datestamp from OAIConfig.xml, or 1970-01-01 if not specified.");
1089 return config_datestamp;
1090 }
1091 // the earliestDatestamp is now stored as a metadata element in the collection's buildConfig.xml file
1092 // we get the earliestDatestamp among the collections
1093 for(int i=0; i<oai_coll_size; i++) {
1094 String collName = collection_name_list.get(i);
1095 long coll_earliestDatestamp = Long.parseLong(((Element)oai_coll.item(i)).getAttribute(OAIXML.EARLIEST_OAI_DATESTAMP)); // Taken from oai-inf db's OAI_EARLIEST_TIMESTAMP_OID entry, -1 if not found
1096
1097 if (coll_earliestDatestamp > 0 && earliestDatestamp > coll_earliestDatestamp) {
1098 earliestDatestamp = coll_earliestDatestamp;
1099 //logger.info("@@@ Found earlier timestamp: " + earliestDatestamp + " ms");
1100 }
1101 }
1102
1103 // we're no longer trying fallbacks for earliestDatestamp (other than the extreme fallback of
1104 // unix epoch time) because, going forward, all collections will have oai-inf db containing
1105 // an entry for earliesttimestamp. And all OAICollections will moreover have them stored and
1106 // will return them upon calling getEarliestOAIDatestamp().
1107 /*
1108 if(earliestDatestamp == current_time) {
1109 logger.info("Can't determine earliesttimestamp from oai-inf.db for any OAI collection. Trying timestamps in build config...");
1110 for(int i=0; i<oai_coll_size; i++) {
1111 String collName = collection_name_list.get(i);
1112 long coll_earliestDatestamp = Long.parseLong(((Element)oai_coll.item(i)).getAttribute(OAIXML.EARLIEST_DATESTAMP)); // Taken from the earliest datestamp field in buildcfg
1113 if (coll_earliestDatestamp == 0) {
1114 // try last modified
1115 coll_earliestDatestamp = Long.parseLong(((Element)oai_coll.item(i)).getAttribute(OAIXML.LAST_MODIFIED));
1116 //logger.info("@@@ Falling back to using collection " + collName + "'s lastmodified date as its earliest timestamp: " + coll_earliestDatestamp);
1117 }
1118 if (coll_earliestDatestamp > 0) {
1119 earliestDatestamp = (earliestDatestamp > coll_earliestDatestamp)? coll_earliestDatestamp : earliestDatestamp;
1120 }
1121 }
1122 }
1123 */
1124
1125 if (earliestDatestamp == current_time) {
1126 logger.info("no collection had a real datestamp, using value from OAIConfig");
1127 return config_datestamp;
1128 }
1129 return earliestDatestamp;
1130 }
1131
1132 private boolean collectionsChangedSinceTime(String set_spec_str, long initial_time) {
1133
1134 // we need to look though all collections in the set to see if any have last modified dates > initial_time
1135 Vector<String> set_coll_list = getCollectionListForSet(set_spec_str);
1136
1137 Node child = this.collection_list.getFirstChild();
1138 while (child != null) {
1139 if (child.getNodeName().equals(GSXML.COLLECTION_ELEM)) {
1140 String coll_id =((Element) child).getAttribute(GSXML.NAME_ATT);
1141 if (set_coll_list.contains(coll_id)) {
1142 long last_modified = Long.parseLong(((Element)child).getAttribute(OAIXML.LAST_MODIFIED));
1143 if (initial_time < last_modified) {
1144 return true;
1145 }
1146 }
1147 }
1148 child = child.getNextSibling();
1149 }
1150 return false;
1151
1152 }
1153
1154}
1155
1156
Note: See TracBrowser for help on using the repository browser.