Changeset 28857

Show
Ignore:
Timestamp:
27.02.2014 14:00:16 (6 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.

Files:
1 modified

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