Changeset 28857


Ignore:
Timestamp:
2014-02-27T14:00:16+13:00 (10 years ago)
Author:
kjdon
Message:

removed this.doc as DOM is not thread safe. adding oai super set handling. set list is created during configure as it is static. this means that if you add a new collection you need to restart the server. Merged methods for listIdentifiers and ListRecords as the code is pretty much identical. better resumption token handling. big code tidy up.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/OAIReceptionist.java

    r27672 r28857  
     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
    120package org.greenstone.gsdl3.core;
    221
     
    2847  /** The unique  repository identifier */
    2948  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  
     49   
    3650  /** a converter class to parse XML and create Docs */
    3751  protected XMLConverter converter=null;
     
    4660  protected ModuleInterface mr = null;
    4761 
    48 
    4962  // Some of the data/responses will not change while the servlet is running, so
    5063  // we can cache them
     
    5265  /** A list of all the collections available to this OAI server */
    5366  protected NodeList collection_list = null;
    54 
     67  /** a vector of the names, for convenience */
     68  protected Vector<String> collection_name_list = null;
     69  /** If this is true, then there are no OAI enabled collections, so can always return noRecordsMatch (after validating the request params) */
     70  protected boolean noRecordsMatch = false;
     71     
     72  /** A set of all known 'sets' */
     73  protected HashSet<String> set_set = null;
     74
     75  protected boolean has_super_colls = false;
     76  /** a hash of super set-> collection list */
     77  protected HashMap<String, Vector<String>> super_coll_map = null;
    5578  /** The identify response */
    5679  protected Element identify_response = null;
    57  
     80  /** The list set response */
     81  protected Element listsets_response = null;
     82  /** the list metadata formats response */
     83  protected Element listmetadataformats_response = null;
     84
    5885  public OAIReceptionist() {
    5986    this.converter = new XMLConverter();
    60     this.doc = this.converter.newDOM();
    61 
    6287  }
    6388 
     
    87112    resume_after = getResumeAfter();
    88113   
    89     repository_id = getRepositoryId();
    90     collection_list = getOAICollectionList();
     114    repository_id = getRepositoryIdentifier();
     115    if (!configureSetInfo()) {
     116      // there are no sets
     117      logger.error("No sets (collections) available for OAI");
     118      return false;
     119    }
    91120
    92121    //clear out expired resumption tokens stored in OAIResumptionToken.xml
    93     OAIXML.init();
    94     OAIXML.clearExpiredTokens();
     122    OAIResumptionToken.init();
     123    OAIResumptionToken.clearExpiredTokens();
    95124   
    96125    return true;
    97126  }
     127
     128  // assuming that sets are static. If collections change then the servlet
     129  // should be restarted.
     130  private boolean configureSetInfo() {
     131    // do we have any super colls listed in web/WEB-INF/classes/OAIConfig.xml?
     132    // Will be like
     133    // <oaiSuperSet>
     134    //   <SetSpec>xxx</SetSpec>
     135    //   <setName>xxx</SetName>
     136    //   <SetDescription>xxx</setDescription>
     137    // </oaiSuperSet>
     138    // The super set is listed in OAIConfig, and collections themselves state
     139    // whether they are part of the super set or not.
     140    NodeList super_coll_list = this.oai_config.getElementsByTagName(OAIXML.OAI_SUPER_SET);
     141    HashMap<String, Element> super_coll_data = new HashMap<String, Element>();
     142    if (super_coll_list.getLength() > 0) {
     143      this.has_super_colls = true;
     144      for (int i=0; i<super_coll_list.getLength(); i++) {
     145    Element super_coll = (Element)super_coll_list.item(i);
     146    Element set_spec = (Element)GSXML.getChildByTagName(super_coll, OAIXML.SET_SPEC);
     147    if (set_spec != null) {
     148      String name = GSXML.getNodeText(set_spec);
     149      if (!name.equals("")) {
     150        super_coll_data.put(name, super_coll);
     151        logger.error("adding in super coll "+name);
     152      }
     153    }
     154      }
     155   
     156      if (super_coll_data.size()==0) {
     157    this.has_super_colls = false;
     158      }
     159    }
     160    if (this.has_super_colls == true) {
     161      this.super_coll_map = new HashMap<String, Vector<String>>();
     162    }
     163    this.set_set = new HashSet<String>();
     164
     165    // next, we get a list of all the OAI enabled collections
     166    // We get this by sending a listSets request to the MR
     167    Document doc = this.converter.newDOM();
     168    Element message = doc.createElement(GSXML.MESSAGE_ELEM);
     169   
     170    Element request = GSXML.createBasicRequest(doc, OAIXML.OAI_SET_LIST, "", null);
     171    message.appendChild(request);
     172    Node msg_node = mr.process(message);
     173   
     174    if (msg_node == null) {
     175      logger.error("returned msg_node from mr is null");
     176      return false;
     177    }
     178    Element resp = (Element)GSXML.getChildByTagName(msg_node, GSXML.RESPONSE_ELEM);
     179    Element coll_list = (Element)GSXML.getChildByTagName(resp, GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
     180    if (coll_list == null) {
     181      logger.error("coll_list is null");
     182      return false;
     183    }
     184
     185    NodeList list = coll_list.getElementsByTagName(GSXML.COLLECTION_ELEM);
     186    int length = list.getLength();
     187    if (length == 0) {
     188      logger.error("length is 0");
     189      noRecordsMatch = true;
     190      return false;
     191    }
     192
     193    this.collection_list = list;
     194    this.collection_name_list = new Vector<String>();
     195   
     196    Document listsets_doc = this.converter.newDOM();
     197    Element listsets_element = listsets_doc.createElement(OAIXML.LIST_SETS);
     198    this.listsets_response = getMessage(listsets_doc, listsets_element);
     199   
     200    // Now, for each collection, get a list of all its sets
     201    // might include subsets (classifiers) or super colls
     202    // We'll reuse the first message, changing its type and to atts
     203    request.setAttribute(GSXML.TYPE_ATT, "");
     204    StringBuffer to = new StringBuffer();
     205    for (int i=0; i<collection_list.getLength(); i++) {
     206      if (i!=0) {
     207    to.append(',');
     208      }
     209      String coll_id =((Element) collection_list.item(i)).getAttribute(GSXML.NAME_ATT);
     210      logger.error("coll_id = "+coll_id);
     211      to.append(coll_id+"/"+OAIXML.LIST_SETS);
     212      this.collection_name_list.add(coll_id);
     213    }
     214    logger.error ("to att = "+to.toString());
     215    request.setAttribute(GSXML.TO_ATT, to.toString());
     216    // send to MR
     217    msg_node = mr.process(message);
     218    logger.error(this.converter.getPrettyString(msg_node));
     219    NodeList response_list =  ((Element)msg_node).getElementsByTagName(GSXML.RESPONSE_ELEM);
     220    for (int c=0; c<response_list.getLength(); c++) {
     221      // for each collection's response
     222      Element response = (Element)response_list.item(c);
     223      String coll_name = GSPath.getFirstLink(response.getAttribute(GSXML.FROM_ATT));
     224      logger.error("coll from response "+coll_name);
     225      NodeList set_list = response.getElementsByTagName(OAIXML.SET);
     226      for (int j=0; j<set_list.getLength(); j++) {
     227    // now check if it a super collection
     228    Element set = (Element)set_list.item(j);
     229    String set_spec = GSXML.getNodeText((Element)GSXML.getChildByTagName(set, OAIXML.SET_SPEC));
     230    logger.error("set spec = "+set_spec);
     231    // this may change if we add site name back in
     232    // setSpecs will be collname or collname:subset or supercollname
     233    if (set_spec.indexOf(":")==-1 && ! set_spec.equals(coll_name)) {
     234      // it must be a super coll spec
     235      logger.error("found super coll, "+set_spec);
     236      // check that it is a valid one from config
     237      if (this.has_super_colls == true && super_coll_data.containsKey(set_spec)) {
     238        Vector <String> subcolls = this.super_coll_map.get(set_spec);
     239        if (subcolls == null) {
     240          logger.error("its new!!");
     241          // not in there yet
     242          subcolls = new Vector<String>();
     243          this.set_set.add(set_spec);
     244          this.super_coll_map.put(set_spec, subcolls);
     245          // the first time a supercoll is mentioned, add into the set list
     246          logger.error("finding the set info "+this.converter.getPrettyString(super_coll_data.get(set_spec)));
     247          listsets_element.appendChild(GSXML.duplicateWithNewName(listsets_doc, super_coll_data.get(set_spec), OAIXML.SET, true));
     248        }
     249        // add this collection to the list for the super coll
     250        subcolls.add(coll_name);
     251      }
     252    } else { // its either the coll itself or a subcoll
     253      // add in the set
     254      listsets_element.appendChild(listsets_doc.importNode(set, true));
     255      this.set_set.add(set_spec);
     256    }
     257      } // for each set in the collection
     258    } // for each OAI enabled collection
     259    return true;
     260  }
     261
    98262  /** process using strings - just calls process using Elements */
    99263  public String process(String xml_in) {
     
    104268  }
    105269
    106   //Compose a message element used to send back to the OAIServer servlet.
     270  //Compose a message/response element used to send back to the OAIServer servlet.
    107271  //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));
     272  private Element getMessage(Document doc, Element e) {
     273    Element msg = doc.createElement(GSXML.MESSAGE_ELEM);
     274    Element response = doc.createElement(GSXML.RESPONSE_ELEM);
     275    msg.appendChild(response);
     276    response.appendChild(e);
    111277    return msg;
    112278  }
     279
    113280  /** process - produce xml data in response to a request
    114281   * if something goes wrong, it returns null -
     
    123290    if (!message.getTagName().equals(GSXML.MESSAGE_ELEM)) {
    124291      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, ""));
     292      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "Internal messaging error");
    126293    }
    127294   
     
    130297    if (request == null) {
    131298      logger.error(" message had no request!");
    132       return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
     299      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "Internal messaging error");
    133300    }
    134301    //At this stage, the value of 'to' attribute of the request must be the 'verb'
     
    139306    }
    140307    if (verb.equals(OAIXML.LIST_METADATA_FORMATS)) {
    141       return doListMetadataFormats(message);
     308      return doListMetadataFormats(request);
    142309    }
    143310    if (verb.equals(OAIXML.LIST_SETS)) {
    144       return doListSets(message);
     311      // we have composed the list sets response on init
     312      // Note this means that list sets never uses resumption tokens
     313      return this.listsets_response;
    145314    }
    146315    if (verb.equals(OAIXML.GET_RECORD)) {
    147       return doGetRecord(message);
     316      return doGetRecord(request);
    148317    }
    149318    if (verb.equals(OAIXML.LIST_IDENTIFIERS)) {
    150       return doListIdentifiers(message);
     319      return doListIdentifiersOrRecords(request,OAIXML.LIST_IDENTIFIERS , OAIXML.HEADER);
    151320    }
    152321    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   }
     322      return doListIdentifiersOrRecords(request, OAIXML.LIST_RECORDS, OAIXML.RECORD);
     323    }
     324    // should never get here as verbs were checked in OAIServer
     325    return OAIXML.createErrorMessage(OAIXML.BAD_VERB, "Unexpected things happened");
     326   
     327  }
     328
     329
    203330  private int getResumeAfter() {
    204331    Element resume_after = (Element)GSXML.getChildByTagName(oai_config, OAIXML.RESUME_AFTER);
     
    206333    return -1;
    207334  }
    208   private String getRepositoryId() {
    209     Element ri = (Element)GSXML.getChildByTagName(oai_config, OAIXML.REPOSITORY_ID);
     335  private String getRepositoryIdentifier() {
     336    Element ri = (Element)GSXML.getChildByTagName(oai_config, OAIXML.REPOSITORY_IDENTIFIER);
    210337    if (ri != null) {
    211338      return GSXML.getNodeText(ri);
     
    213340    return "";
    214341  }
    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     }
     342
    340343
    341344  /** if the param_map contains strings other than those in valid_strs, return false;
    342345   *  otherwise true.
    343346   */
    344   private boolean isValidParam(HashMap<String, String> param_map, HashSet<String> valid_strs) {
     347  private boolean areAllParamsValid(HashMap<String, String> param_map, HashSet<String> valid_strs) {
    345348    ArrayList<String> param_list = new ArrayList<String>(param_map.keySet());
    346349    for(int i=0; i<param_list.size(); i++) {
     350      logger.error("param, key =  "+param_list.get(i)+", value = "+param_map.get(param_list.get(i)));
    347351      if (valid_strs.contains(param_list.get(i)) == false) {
    348352        return false;
     
    351355    return true;
    352356  }
    353   private Element doListIdentifiers(Element msg) {
    354     // option: from, until, set, metadataPrefix, resumptionToken
     357
     358  private Element doListIdentifiersOrRecords(Element req, String verb, String record_type) {
     359    // options: from, until, set, metadataPrefix, resumptionToken
    355360    // exceptions: badArgument, badResumptionToken, cannotDisseminateFormat, noRecordMatch, and noSetHierarchy
    356361    HashSet<String> valid_strs = new HashSet<String>();
     
    360365    valid_strs.add(OAIXML.METADATA_PREFIX);
    361366    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) { 
     367   
     368    Document result_doc = this.converter.newDOM();
     369    Element result_element = result_doc.createElement(verb);
     370    boolean result_token_needed = false; // does this result need to include a
     371    // resumption token
     372
     373    NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
     374
     375    HashMap<String, String> param_map = GSXML.getParamMap(params);
     376   
     377    // are all the params valid?
     378    if (!areAllParamsValid(param_map, valid_strs)) {
     379      logger.error("One of the params is invalid");
     380      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "There was an invalid parameter");
     381      // TODO, need to tell the user which one was invalid ??
     382    }     
     383   
     384    // Do we have a resumption token??
     385    String token = null;
     386    String from = null;
     387    String until = null;
     388    boolean set_requested = false;
     389    String set_spec_str = null;
     390    String prefix_value = null;
     391    int cursor = 0;
     392    int current_cursor = 0;
     393    String current_set = null;
     394   
     395    int total_size = -1; // we are only going to set this in resumption
     396    // token if it is easy to work out, i.e. not sending extra requests to
     397    // MR just to calculate total size
     398
     399    if(param_map.containsKey(OAIXML.RESUMPTION_TOKEN)) {
     400      // Is it an error to have other arguments? Do we need to check to make sure that resumptionToken is the only arg??
     401      // validate resumptionToken
     402      token = param_map.get(OAIXML.RESUMPTION_TOKEN);
     403      logger.info("has resumptionToken " + token);
     404      if(OAIResumptionToken.isValidToken(token) == false) {
     405    logger.error("token is not valid");
     406        return OAIXML.createErrorMessage(OAIXML.BAD_RESUMPTION_TOKEN, "");
     407      }
     408      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
     409      // initialise the request params from the stored token data
     410      HashMap<String, String> token_data = OAIResumptionToken.getTokenData(token);
     411      from = token_data.get(OAIXML.FROM);
     412      until = token_data.get(OAIXML.UNTIL);
     413      set_spec_str = token_data.get(OAIXML.SET);
     414      if (set_spec_str != null) {
     415    set_requested = true;
     416      }
     417      prefix_value = token_data.get(OAIXML.METADATA_PREFIX);
     418      current_set = token_data.get(OAIResumptionToken.CURRENT_SET);
     419      try {
     420    cursor = Integer.parseInt(token_data.get(OAIXML.CURSOR));
     421    cursor = cursor + resume_after; // increment cursor
     422    current_cursor = Integer.parseInt(token_data.get(OAIResumptionToken.CURRENT_CURSOR));
     423      } catch (NumberFormatException e) {
     424    logger.error("tried to parse int from cursor data and failed");
     425      }
     426     
     427    }
     428    else {
     429      // no resumption token, lets check the other params
     430      // there must be a metadataPrefix
     431      if (!param_map.containsKey(OAIXML.METADATA_PREFIX)) {
     432    logger.error("metadataPrefix param required");
     433    return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "metadataPrefix param required");
     434      }
     435
     436      //if there are any date params, check they're of the right format
     437      from = param_map.get(OAIXML.FROM);
     438      if(from != null) {   
    378439    Date from_date = OAIXML.getDate(from);
    379440    if(from_date == null) {
    380         logger.error("invalid date: " + from);
    381         return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
     441      logger.error("invalid date: " + from);
     442      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid format for "+ OAIXML.FROM);
    382443    }
    383     }
    384     String until = param_map.get(OAIXML.UNTIL);
    385     if(until != null) {
     444      }
     445      until = param_map.get(OAIXML.UNTIL);
     446      if(until != null) {   
    386447    Date until_date = OAIXML.getDate(until);
    387448    if(until_date == null) {
    388         logger.error("invalid date: " + until);
    389         return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
     449      logger.error("invalid date: " + until);
     450      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid format for "+ OAIXML.UNTIL);
    390451    }
    391     }   
    392     if(from != null && until != null) { // check they are of the same date-time format (granularity)
     452      }   
     453      if(from != null && until != null) { // check they are of the same date-time format (granularity)
    393454    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, ""));
     455      logger.error("The request has different granularities (date-time formats) for the From and Until date parameters.");
     456      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "The request has different granularities (date-time formats) for the From and Until date parameters.");
    396457    }
    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);
     458      }
     459 
     460      // check the set arg is a set we know about
     461      set_requested = param_map.containsKey(OAIXML.SET);
     462      set_spec_str = null;
     463      if(set_requested == true) {
     464    set_spec_str = param_map.get(OAIXML.SET);
     465    if (!this.set_set.contains(set_spec_str)) {
     466      // the set is not one we know about
     467      logger.error("requested set is not found in this repository");
     468      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "invalid set parameter");   
     469     
     470    }
     471      }
     472      // Is the metadataPrefix arg one this repository supports?
     473      prefix_value = param_map.get(OAIXML.METADATA_PREFIX);
     474      if (repositorySupportsMetadataPrefix(prefix_value) == false) {
     475    logger.error("requested metadataPrefix is not found in OAIConfig.xml");
     476    return OAIXML.createErrorMessage(OAIXML.CANNOT_DISSEMINATE_FORMAT, "metadata format "+prefix_value+" not supported by this repository");
     477      }
     478     
     479    } // else no resumption token, check other params   
     480   
     481    // Whew. Now we have validated the params, we can work on doing the actual
     482    // request
     483
     484
     485    Document doc = this.converter.newDOM();
     486    Element mr_msg = doc.createElement(GSXML.MESSAGE_ELEM);
     487    Element mr_req = doc.createElement(GSXML.REQUEST_ELEM);
     488    // TODO does this need a type???
     489    mr_msg.appendChild(mr_req);
     490
     491    // copy in the from/until params if there
     492    if (from != null) {
     493      mr_req.appendChild(GSXML.createParameter(doc, OAIXML.FROM, from));
     494    }
     495    if (until != null) {
     496      mr_req.appendChild(GSXML.createParameter(doc, OAIXML.UNTIL, until));
     497    }
     498    // add metadataPrefix
     499    mr_req.appendChild(GSXML.createParameter(doc, OAIXML.METADATA_PREFIX, prefix_value));
     500   
     501    // do we have a set???
     502    // if no set, we send to all collections in the collection list
     503    // if super set, we send to all collections in super set list
     504    // if a single collection, send to it
     505    // if a subset, send to the collection
     506    Vector<String> current_coll_list = null;
     507    boolean single_collection = false;
     508    if (set_requested == false) {
     509      // just do all colls
     510      current_coll_list = collection_name_list;
     511    }
     512    else if (has_super_colls && super_coll_map.containsKey(set_spec_str)) {
     513      current_coll_list = super_coll_map.get(set_spec_str);
     514    }
     515    else {
     516      current_coll_list = new Vector<String>();
     517      if (set_spec_str.indexOf(":") != -1) {
     518    // we have a subset
     519    //add the set param back into the request, but send the request to the collection
     520    String col_name = set_spec_str.substring(0, set_spec_str.indexOf(":"));
     521    current_coll_list.add(col_name);
     522    mr_req.appendChild(GSXML.createParameter(doc, OAIXML.SET, set_spec_str));
     523    single_collection = true;
     524      }
     525      else {
     526    // it must be a single collection name
     527    current_coll_list.add(set_spec_str);
     528    single_collection = true;
     529      }
     530    }
     531
     532    int num_collected_records = 0;
     533    int start_point = current_cursor; // may not be 0 if we are using a resumption token
     534    String resumption_collection = "";
     535    boolean empty_result_token = false; // if we are sending the last part of a list, then the token value will be empty
     536   
     537    // iterate through the list of collections and send the request to each
     538
     539    int start_coll=0;
     540    if (current_set != null) {
     541      // we are resuming a previous request, need to locate the first collection
     542      for (int i=0; i<current_coll_list.size(); i++) {
     543    if (current_set.equals(current_coll_list.get(i))) {
     544      start_coll = i;
     545      break;
     546    }
     547      }
     548    }
     549   
     550    for (int i=start_coll; i<current_coll_list.size(); i++) {
     551      String current_coll = current_coll_list.get(i);
     552      mr_req.setAttribute(GSXML.TO_ATT, current_coll+"/"+verb);
     553
     554      Element result = (Element)mr.process(mr_msg);
     555      logger.error(verb+ " result for coll "+current_coll);
     556      logger.error(this.converter.getPrettyString(result));
     557      if (result == null) {
     558    logger.info("message router returns null");
     559    // do what??? carry on? fail??
     560    return OAIXML.createErrorMessage("Internal service returns null", "");
     561      }
     562      Element res = (Element)GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
    497563      if(res == null) {
    498564        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       }
     565        return OAIXML.createErrorMessage("Internal service returns null", "");
     566      }
     567      NodeList record_list = res.getElementsByTagName(record_type);
     568      int num_records = record_list.getLength();
     569      if(num_records == 0) {
     570    logger.info("message router returns 0 records for coll "+current_coll);
     571    continue; // try the next collection
     572      } 
     573      if (single_collection) {
     574    total_size = num_records;
     575      }
     576      int records_to_add = (resume_after > 0 ? resume_after - num_collected_records : num_records);
     577      if (records_to_add > (num_records-start_point)) {
     578    records_to_add = num_records-start_point;
     579      }
     580      addRecordsToList(result_doc, result_element, record_list, start_point, records_to_add);
     581      num_collected_records += records_to_add;
    515582     
    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;
     583      // do we need to stop here, and do we need to issue a resumption token?
     584      if (resume_after > 0 && num_collected_records == resume_after) {
     585    // we have finished collecting records at the moment.
     586    // but are we conincidentally at the end? or are there more to go?
     587    if (records_to_add < (num_records - start_point)) {
     588      // we have added less than this collection had
     589      start_point += records_to_add;
     590      resumption_collection = current_coll;
     591      result_token_needed = true;
     592    }
     593    else {
     594      // we added all this collection had to offer
     595      // is there another collection in the list??
     596      if (i<current_coll_list.size()-1) {
     597        result_token_needed = true;
     598        start_point = 0;
     599        resumption_collection = current_coll_list.get(i+1);
     600      }
     601      else {
     602        // we have finished one collection and there are no more collection
     603        // if we need to send a resumption token (in this case, only because we started with one, then it will be empty
     604        logger.error("at end of list, need empty result token");
     605        empty_result_token = true;
     606      }
     607    }
     608    break;
     609      }
     610      start_point = 0; // only the first one will have start non-zero, if we
     611      // have a resumption token
    533612     
    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);
     613    } // for each collection
     614
     615    if (num_collected_records ==0) {
     616      // there were no matching results
     617      return OAIXML.createErrorMessage(OAIXML.NO_RECORDS_MATCH, "");
     618    }
     619
     620    if (num_collected_records < resume_after) {
     621      // we have been through all collections, and there are no more
     622      // 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
     623      if (result_token_needed) {
     624    empty_result_token = true;
     625      }
     626    }
     627   
     628    if (result_token_needed) {
     629      // we need a resumption token
     630      if (empty_result_token) {
     631    logger.error("have empty result token");
     632    token = "";
    542633      } 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, ""));
     634    if (token != null) {
     635      // we had a token for this request, we can just update it
     636      token = OAIResumptionToken.updateToken(token, ""+cursor, resumption_collection, ""+start_point);
     637    } else {
     638      // we are generating a new one
     639      token = OAIResumptionToken.createAndStoreResumptionToken(set_spec_str, prefix_value, from, until, ""+cursor, resumption_collection, ""+start_point );
    593640    }
    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 
     641      }
     642
     643      // result token XML
     644      long expiration_date = -1;
     645      if (empty_result_token) {
     646    // we know how many records in total as we have sent them all
     647    total_size = cursor+num_collected_records;
    794648      } 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   }
     649    // non-empty token, set the expiration date
     650    expiration_date = OAIResumptionToken.getExpirationDate(token);
     651      }
     652      Element token_elem = OAIXML.createResumptionTokenElement(result_doc, token, total_size, cursor, expiration_date);
     653      // OAIXML.addToken(token_elem); // store it
     654      result_element.appendChild(token_elem); // add to the result
     655    }
     656 
     657
     658    return getMessage(result_doc, result_element);
     659  }
     660
     661  private void addRecordsToList(Document doc, Element result_element, NodeList
     662                record_list, int start_point, int num_records) {
     663    int end_point = start_point + num_records;
     664    for (int i=start_point; i<end_point; i++) {
     665      result_element.appendChild(doc.importNode(record_list.item(i), true));
     666    }
     667  }
     668
     669
    809670  // method exclusively used by doListRecords/doListIdentifiers
    810671  private void getRecords(Element verb_elem, NodeList list, int start_point, int end_point) {
     
    818679      return msg;
    819680    }
    820     Element res_in_result = (Element)GSXML.getChildByTagName(result, OAIXML.RESPONSE);
     681    Element res_in_result = (Element)GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
    821682    if(res_in_result == null) { // return the results of all other collections accumulated so far
    822     return msg;
     683      return msg;
    823684    }
    824685    Element verb_elem = (Element)GSXML.getChildByTagName(res_in_result, verb);
     
    836697    return result;
    837698  }
    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) {
     699
     700 
     701  /** there are three possible exception conditions: bad argument, idDoesNotExist, and noMetadataFormat.
     702   * The first one is handled here, and the last two are processed by OAIPMH.
     703   */
     704  private Element doListMetadataFormats(Element req) {
    842705    //if the verb is ListMetadataFormats, there could be only one parameter: identifier
    843706    //, or there is no parameter; otherwise it is an error
    844707    //logger.info("" + this.converter.getString(msg));
    845708   
    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);
     709    NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
    851710    Element param = null;
     711    Document lmf_doc = this.converter.newDOM();
    852712    if(params.getLength() == 0) {
    853713      //this is requesting metadata formats for the whole repository
    854714      //read the oaiConfig.xml file, return the metadata formats specified there.
     715      if (this.listmetadataformats_response != null) {
     716    // we have already created it
     717    return this.listmetadataformats_response;
     718      }
     719
     720      Element list_metadata_formats = lmf_doc.createElement(OAIXML.LIST_METADATA_FORMATS);
     721     
    855722      Element format_list = (Element)GSXML.getChildByTagName(oai_config, OAIXML.LIST_METADATA_FORMATS);
    856723      if(format_list == null) {
    857724    logger.error("OAIConfig.xml must contain the supported metadata formats");
    858     return getMessage(list_metadata_formats);
     725    // TODO this is internal error, what to do???
     726    return getMessage(lmf_doc, list_metadata_formats);
    859727      }
    860728      NodeList formats = format_list.getElementsByTagName(OAIXML.METADATA_FORMAT);
    861729      for(int i=0; i<formats.getLength(); i++) {
    862     Element meta_fmt = OAIXML.createElement(OAIXML.METADATA_FORMAT);
     730    Element meta_fmt = lmf_doc.createElement(OAIXML.METADATA_FORMAT);
    863731    Element first_meta_format = (Element)formats.item(i);
    864732    //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));
     733    meta_fmt.appendChild(lmf_doc.importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.METADATA_PREFIX), true));
     734    meta_fmt.appendChild(lmf_doc.importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.SCHEMA), true));
     735    meta_fmt.appendChild(lmf_doc.importNode(GSXML.getChildByTagName(first_meta_format, OAIXML.METADATA_NAMESPACE), true));
    868736    list_metadata_formats.appendChild(meta_fmt);
    869         }
    870       return getMessage(list_metadata_formats);
     737      }
     738      return getMessage(lmf_doc, list_metadata_formats);
    871739     
    872740     
     
    875743    if (params.getLength() > 1) {
    876744      //Bad argument. Can't be more than one parameters for ListMetadataFormats verb
    877       return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
     745      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
    878746    }
    879747   
    880748    // 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         }
     749    /**the request xml is in the form: <request>
     750     *                                   <param name=.../>
     751     *                                 </request>
     752     *And there is a param element and one element only. (No paramList element in between).
     753     */
     754    param = (Element)params.item(0);
     755    String param_name = param.getAttribute(GSXML.NAME_ATT);
     756    String identifier = "";
     757    if (!param_name.equals(OAIXML.IDENTIFIER)) {
     758      //Bad argument
     759      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
     760    }
     761     
     762    identifier = param.getAttribute(GSXML.VALUE_ATT);
     763    // the identifier is in the form: <coll_name>:<OID>
     764    // so it must contain at least two ':' characters
     765    String[] strs = identifier.split(":");
     766    if(strs == null || strs.length < 2) {
     767      // the OID may also contain ':'
     768      logger.error("identifier is not in the form coll:id" + identifier);
     769      return OAIXML.createErrorMessage(OAIXML.ID_DOES_NOT_EXIST, "");
     770    }
    902771       
    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   }
     772    // send request to message router
     773    // get the names
     774    strs = splitNames(identifier);
     775    if(strs == null || strs.length < 2) {
     776      logger.error("identifier is not in the form coll:id" + identifier);
     777      return OAIXML.createErrorMessage(OAIXML.ID_DOES_NOT_EXIST, "");
     778    }
     779    //String name_of_site = strs[0];
     780    String coll_name = strs[0];
     781    String oid = strs[1];
     782
     783    //re-organize the request element
     784    // reset the 'to' attribute
     785    String verb = req.getAttribute(GSXML.TO_ATT);
     786    req.setAttribute(GSXML.TO_ATT, coll_name + "/" + verb);
     787    // reset the identifier element
     788    param.setAttribute(GSXML.NAME_ATT, OAIXML.OID);
     789    param.setAttribute(GSXML.VALUE_ATT, oid);
     790
     791    // TODO is this the best way to do this???? should we create a new request???
     792    Element message = req.getOwnerDocument().createElement(GSXML.MESSAGE_ELEM);
     793    message.appendChild(req);
     794    //Now send the request to the message router to process
     795    Node result_node = mr.process(message);
     796    return converter.nodeToElement(result_node);
     797  }
     798 
     799   
     800
     801
    937802  private void copyNamedElementfromConfig(Element to_elem, String element_name) {
    938803    Element original_element = (Element)GSXML.getChildByTagName(oai_config, element_name);
     
    952817    if (this.identify_response != null) {
    953818      // we have already created it
    954       return getMessage(this.identify_response);
    955     }
    956    
    957     Element identify = OAIXML.createElement(OAIXML.IDENTIFY);
     819      return this.identify_response;
     820    }
     821    Document doc = this.converter.newDOM();
     822    Element identify = doc.createElement(OAIXML.IDENTIFY);
    958823    //do the repository name
    959824    copyNamedElementfromConfig(identify, OAIXML.REPOSITORY_NAME);
     
    980845    long earliestDatestamp = getEarliestDateStamp(collection_list);
    981846    String earliestDatestamp_str = OAIXML.getTime(earliestDatestamp);
    982     Element earliestDatestamp_elem = OAIXML.createElement(OAIXML.EARLIEST_DATESTAMP);
     847    Element earliestDatestamp_elem = doc.createElement(OAIXML.EARLIEST_DATESTAMP);
    983848    GSXML.setNodeText(earliestDatestamp_elem, earliestDatestamp_str);
    984849    identify.appendChild(earliestDatestamp_elem);
     
    990855     
    991856    // output the oai identifier
    992     Element description = OAIXML.createElement(OAIXML.DESCRIPTION);
     857    Element description = doc.createElement(OAIXML.DESCRIPTION);
    993858    identify.appendChild(description);
    994     Element oaiIdentifier = OAIXML.createOAIIdentifierXML(repository_id, "lucene-jdbm-demo", "ec159e");
     859    // TODO, make this a valid id
     860    Element oaiIdentifier = OAIXML.createOAIIdentifierXML(doc, repository_id, "lucene-jdbm-demo", "ec159e");
    995861    description.appendChild(oaiIdentifier);
    996862
     
    998864    Element info = (Element)GSXML.getChildByTagName(oai_config, OAIXML.OAI_INFO);
    999865    if (info != null) {
    1000       NodeList meta = GSXML.getChildrenByTagName(info, OAIXML.INFO_METADATA);
     866      NodeList meta = GSXML.getChildrenByTagName(info, OAIXML.METADATA);
    1001867      if (meta != null && meta.getLength() > 0) {
    1002     Element gsdl = OAIXML.createGSDLElement();
     868    Element gsdl = OAIXML.createGSDLElement(doc);
    1003869    description.appendChild(gsdl);
    1004870    for (int m = 0; m<meta.getLength(); m++) {
     
    1009875    }
    1010876    this.identify_response = identify;
    1011     return getMessage(identify);
     877    return getMessage(doc, identify);
    1012878  }
    1013879  //split setSpec (site_name:coll_name) into an array of strings
     
    1021887    return strs;
    1022888  }
    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    */
     889  /** split the identifier into <collection + OID> as an array
     890      It has already been checked that the 'identifier' contains at least one ':'
     891  */
    1026892  private String[] splitNames(String identifier) {
    1027893    logger.info(identifier);
    1028     String [] strs = new String[3];
     894    String [] strs = new String[2];
    1029895    int first_colon = identifier.indexOf(":");
    1030896    if(first_colon == -1) {
    1031     return null;
     897      return null;
    1032898    }
    1033899    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);
     900    strs[1] = identifier.substring(first_colon + 1);
    1041901    return strs;
    1042902  }
     
    1044904   *  by checking it in the OAIConfig.xml
    1045905   */
    1046   private boolean containsMetadataPrefix(String prefix_value) {
     906  private boolean repositorySupportsMetadataPrefix(String prefix_value) {
    1047907    NodeList prefix_list = oai_config.getElementsByTagName(OAIXML.METADATA_PREFIX);
    1048908   
     
    1054914    return false;
    1055915  }
    1056   private Element doGetRecord(Element msg){
     916  private Element doGetRecord(Element req){
    1057917    logger.info("");
    1058918    /** arguments:
    1059919        identifier: required
    1060920        metadataPrefix: required
    1061      *  Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
    1062      */
    1063     Element get_record = OAIXML.createElement(OAIXML.GET_RECORD);
     921    *  Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
     922    */
     923    Document doc = this.converter.newDOM();
     924    Element get_record = doc.createElement(OAIXML.GET_RECORD);
    1064925
    1065926    HashSet<String> valid_strs = new HashSet<String>();
     
    1067928    valid_strs.add(OAIXML.METADATA_PREFIX);
    1068929
    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 ) {
     930    NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
     931    HashMap<String, String> param_map = GSXML.getParamMap(params);   
     932   
     933    if(!areAllParamsValid(param_map, valid_strs) ||
     934       params.getLength() == 0 ||
     935       param_map.containsKey(OAIXML.IDENTIFIER) == false ||
     936       param_map.containsKey(OAIXML.METADATA_PREFIX) == false ) {
    1077937      logger.error("must have the metadataPrefix/identifier parameter.");
    1078       return getMessage(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, ""));
     938      return OAIXML.createErrorMessage(OAIXML.BAD_ARGUMENT, "");
    1079939    }
    1080940   
     
    1083943   
    1084944    // verify the metadata prefix
    1085     if (containsMetadataPrefix(prefix) == false) {
     945    if (repositorySupportsMetadataPrefix(prefix) == false) {
    1086946      logger.error("requested prefix is not found in OAIConfig.xml");
    1087       return getMessage(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, ""));
     947      return OAIXML.createErrorMessage(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
    1088948    }
    1089949
    1090950    // get the names
    1091951    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, ""));
     952    if(strs == null || strs.length < 2) {
     953      logger.error("identifier is not in the form coll:id" + identifier);
     954      return OAIXML.createErrorMessage(OAIXML.ID_DOES_NOT_EXIST, "");
    1095955    }   
    1096     String name_of_site = strs[0];
    1097     String coll_name = strs[1];
    1098     String oid = strs[2];
     956    //String name_of_site = strs[0];
     957    String coll_name = strs[0];
     958    String oid = strs[1];
    1099959   
    1100960    //re-organize the request element
    1101961    // reset the 'to' attribute
    1102     String verb = req.getAttribute(OAIXML.TO);
    1103     req.setAttribute(OAIXML.TO, coll_name + "/" + verb);
     962    String verb = req.getAttribute(GSXML.TO_ATT);
     963    req.setAttribute(GSXML.TO_ATT, coll_name + "/" + verb);
    1104964    // reset the identifier element
    1105     Element param = GSXML.getNamedElement(req, OAIXML.PARAM, OAIXML.NAME, OAIXML.IDENTIFIER);
     965    Element param = GSXML.getNamedElement(req, GSXML.PARAM_ELEM, GSXML.NAME_ATT, OAIXML.IDENTIFIER);
    1106966    if (param != null) {
    1107       param.setAttribute(OAIXML.NAME, OAIXML.OID);
    1108       param.setAttribute(OAIXML.VALUE, oid);
     967      param.setAttribute(GSXML.NAME_ATT, OAIXML.OID);
     968      param.setAttribute(GSXML.VALUE_ATT, oid);
    1109969    }   
    1110970
    1111971    //Now send the request to the message router to process
     972    Element msg = doc.createElement(GSXML.MESSAGE_ELEM);
     973    msg.appendChild(doc.importNode(req, true));
    1112974    Node result_node = mr.process(msg);
    1113975    return converter.nodeToElement(result_node);
    1114976  }
    1115977
    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     }
     978  // See OAIConfig.xml
     979  // dynamically works out what the earliestDateStamp is, since it varies by collection
     980  // returns this time in *milliseconds*.
     981  protected long getEarliestDateStamp(NodeList oai_coll) {
     982    //do the earliestDatestamp
     983    long earliestDatestamp = System.currentTimeMillis();   
     984    int oai_coll_size = oai_coll.getLength();
     985    if (oai_coll_size == 0) {
     986      logger.info("returned oai collection list is empty. Setting repository earliestDatestamp to be 1970-01-01.");
     987      earliestDatestamp = 0;
     988    }
     989    // the earliestDatestamp is now stored as a metadata element in the collection's buildConfig.xml file
     990    // we get the earliestDatestamp among the collections
     991    for(int i=0; i<oai_coll_size; i++) {
     992      long coll_earliestDatestamp = Long.parseLong(((Element)oai_coll.item(i)).getAttribute(OAIXML.EARLIEST_DATESTAMP));
     993      earliestDatestamp = (earliestDatestamp > coll_earliestDatestamp)? coll_earliestDatestamp : earliestDatestamp;
     994    }
     995
     996    return earliestDatestamp*1000; // converting from seconds to milliseconds
     997  }
    1136998}
     999
     1000 
Note: See TracChangeset for help on using the changeset viewer.