| 1 | /* |
|---|
| 2 | * OAIPMH.java |
|---|
| 3 | * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org |
|---|
| 4 | * |
|---|
| 5 | * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | * it under the terms of the GNU General Public License as published by |
|---|
| 7 | * the Free Software Foundation; either version 2 of the License, or |
|---|
| 8 | * (at your option) any later version. |
|---|
| 9 | * |
|---|
| 10 | * This program is distributed in the hope that it will be useful, |
|---|
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 13 | * GNU General Public License for more details. |
|---|
| 14 | * |
|---|
| 15 | * You should have received a copy of the GNU General Public License |
|---|
| 16 | * along with this program; if not, write to the Free Software |
|---|
| 17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|---|
| 18 | */ |
|---|
| 19 | package org.greenstone.gsdl3.service; |
|---|
| 20 | |
|---|
| 21 | // Greenstone classes |
|---|
| 22 | import org.greenstone.gsdl3.core.GSException; |
|---|
| 23 | import org.greenstone.gsdl3.util.GSXML; |
|---|
| 24 | import org.greenstone.gsdl3.util.OAIXML; |
|---|
| 25 | import org.greenstone.gsdl3.util.OID; |
|---|
| 26 | import org.greenstone.gsdl3.util.GSFile; |
|---|
| 27 | import org.greenstone.gsdl3.util.XMLConverter; |
|---|
| 28 | |
|---|
| 29 | import org.greenstone.gsdl3.util.SimpleCollectionDatabase; |
|---|
| 30 | import org.greenstone.gsdl3.util.DBInfo; |
|---|
| 31 | // XML classes |
|---|
| 32 | import org.w3c.dom.Document; |
|---|
| 33 | import org.w3c.dom.Element; |
|---|
| 34 | import org.w3c.dom.NodeList; |
|---|
| 35 | |
|---|
| 36 | // General Java classes |
|---|
| 37 | import java.io.File; |
|---|
| 38 | import java.util.StringTokenizer; |
|---|
| 39 | import java.util.Vector; |
|---|
| 40 | import java.util.Set; |
|---|
| 41 | import java.util.Iterator; |
|---|
| 42 | import java.util.ArrayList; |
|---|
| 43 | import java.util.Date; |
|---|
| 44 | import java.util.HashMap; |
|---|
| 45 | import java.util.Map.Entry; |
|---|
| 46 | |
|---|
| 47 | import org.apache.log4j.Logger; |
|---|
| 48 | |
|---|
| 49 | /** Implements the oai metadata retrieval service for GS3 collections. |
|---|
| 50 | * Dig into each collection's database and retrieve the metadata |
|---|
| 51 | * |
|---|
| 52 | * @author <a href="mailto:xiao@cs.waikato.ac.nz">Xiao</a> |
|---|
| 53 | */ |
|---|
| 54 | |
|---|
| 55 | public class OAIPMH extends ServiceRack { |
|---|
| 56 | |
|---|
| 57 | static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.OAIPMH.class.getName()); |
|---|
| 58 | |
|---|
| 59 | protected SimpleCollectionDatabase coll_db = null; |
|---|
| 60 | |
|---|
| 61 | protected String site_name = ""; |
|---|
| 62 | protected String coll_name = ""; |
|---|
| 63 | protected Element coll_config_xml = null; |
|---|
| 64 | |
|---|
| 65 | /** constructor */ |
|---|
| 66 | public OAIPMH() { |
|---|
| 67 | |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | public void cleanUp() { |
|---|
| 71 | super.cleanUp();//?? |
|---|
| 72 | this.coll_db.closeDatabase(); |
|---|
| 73 | } |
|---|
| 74 | /** configure this service */ |
|---|
| 75 | public boolean configure(Element info, Element extra_info) { |
|---|
| 76 | if (!super.configure(info, extra_info)){ |
|---|
| 77 | logger.info("Configuring ServiceRack.java returns false."); |
|---|
| 78 | return false; |
|---|
| 79 | } |
|---|
| 80 | |
|---|
| 81 | //get the names from ServiceRack.java |
|---|
| 82 | site_name = this.router.getSiteName(); |
|---|
| 83 | coll_name = this.cluster_name; |
|---|
| 84 | //get the collection-specific configurations from collectionConfig.xml |
|---|
| 85 | coll_config_xml = OAIXML.getCollectionConfigXML(site_name, coll_name); |
|---|
| 86 | |
|---|
| 87 | logger.info("Configuring OAIPMH..."); |
|---|
| 88 | // this call passes the indexStem in of ServiceRack element in buildConfig.xml to the super class. |
|---|
| 89 | this.config_info = info; |
|---|
| 90 | |
|---|
| 91 | // the index stem is either specified in the buildConfig.xml file or uses the collection name |
|---|
| 92 | Element index_stem_elem = (Element) GSXML.getChildByTagName(info, GSXML.INDEX_STEM_ELEM); |
|---|
| 93 | String index_stem = null; |
|---|
| 94 | if (index_stem_elem != null) { |
|---|
| 95 | index_stem = index_stem_elem.getAttribute(GSXML.NAME_ATT); |
|---|
| 96 | } |
|---|
| 97 | if (index_stem == null || index_stem.equals("")) { |
|---|
| 98 | index_stem = this.cluster_name; |
|---|
| 99 | } |
|---|
| 100 | |
|---|
| 101 | // find out what kind of database we have |
|---|
| 102 | Element database_type_elem = (Element) GSXML.getChildByTagName(info, GSXML.DATABASE_TYPE_ELEM); |
|---|
| 103 | String database_type = null; |
|---|
| 104 | if (database_type_elem != null) { |
|---|
| 105 | database_type = database_type_elem.getAttribute(GSXML.NAME_ATT); |
|---|
| 106 | } |
|---|
| 107 | if (database_type == null || database_type.equals("")) { |
|---|
| 108 | database_type = "gdbm"; // the default |
|---|
| 109 | } |
|---|
| 110 | coll_db = new SimpleCollectionDatabase(database_type); |
|---|
| 111 | if (coll_db == null) { |
|---|
| 112 | logger.error("Couldn't create the collection database of type "+database_type); |
|---|
| 113 | return false; |
|---|
| 114 | } |
|---|
| 115 | |
|---|
| 116 | // Open database for querying |
|---|
| 117 | String coll_db_file = GSFile.collectionDatabaseFile(this.site_home, this.cluster_name, index_stem, database_type); |
|---|
| 118 | if (!this.coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ)) { |
|---|
| 119 | logger.error("Could not open collection database!"); |
|---|
| 120 | return false; |
|---|
| 121 | } |
|---|
| 122 | |
|---|
| 123 | // the short_service_info is used by the message router to find the method names, |
|---|
| 124 | //so we just use the doc variable in class ServiceRack to create the xml; but |
|---|
| 125 | // in each method we will use OAIXML to create the response xml |
|---|
| 126 | // set up short_service_info_ - just the name |
|---|
| 127 | |
|---|
| 128 | Element identify = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 129 | |
|---|
| 130 | identify.setAttribute(OAIXML.NAME, OAIXML.IDENTIFY); |
|---|
| 131 | identify.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 132 | this.short_service_info.appendChild(identify); |
|---|
| 133 | |
|---|
| 134 | Element list_records = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 135 | list_records.setAttribute(OAIXML.NAME, OAIXML.LIST_RECORDS); |
|---|
| 136 | list_records.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 137 | this.short_service_info.appendChild(list_records); |
|---|
| 138 | |
|---|
| 139 | Element list_identifiers = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 140 | list_identifiers.setAttribute(OAIXML.NAME, OAIXML.LIST_IDENTIFIERS); |
|---|
| 141 | list_identifiers.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 142 | this.short_service_info.appendChild(list_identifiers); |
|---|
| 143 | |
|---|
| 144 | Element list_sets = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 145 | list_sets.setAttribute(OAIXML.NAME, OAIXML.LIST_SETS); |
|---|
| 146 | list_sets.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 147 | this.short_service_info.appendChild(list_sets); |
|---|
| 148 | |
|---|
| 149 | Element list_metadata_formats = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 150 | list_metadata_formats.setAttribute(OAIXML.NAME, OAIXML.LIST_METADATA_FORMATS); |
|---|
| 151 | list_metadata_formats.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 152 | this.short_service_info.appendChild(list_metadata_formats); |
|---|
| 153 | |
|---|
| 154 | Element get_record = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 155 | get_record.setAttribute(OAIXML.NAME, OAIXML.GET_RECORD); |
|---|
| 156 | get_record.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 157 | this.short_service_info.appendChild(get_record); |
|---|
| 158 | |
|---|
| 159 | return true; |
|---|
| 160 | } |
|---|
| 161 | /** returns a specific service description */ |
|---|
| 162 | public Element getServiceDescription(String service_id, String lang, String subset) { |
|---|
| 163 | |
|---|
| 164 | if (service_id.equals(OAIXML.IDENTIFY)) { |
|---|
| 165 | Element identify = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 166 | identify.setAttribute(OAIXML.NAME, OAIXML.IDENTIFY); |
|---|
| 167 | identify.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 168 | return identify; |
|---|
| 169 | } |
|---|
| 170 | if (service_id.equals(OAIXML.LIST_RECORDS)) { |
|---|
| 171 | Element list_records = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 172 | list_records.setAttribute(OAIXML.NAME, OAIXML.LIST_RECORDS); |
|---|
| 173 | list_records.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 174 | return list_records; |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | if (service_id.equals(OAIXML.LIST_IDENTIFIERS)) { |
|---|
| 178 | Element list_identifiers = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 179 | list_identifiers.setAttribute(OAIXML.NAME, OAIXML.LIST_IDENTIFIERS); |
|---|
| 180 | list_identifiers.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 181 | return list_identifiers; |
|---|
| 182 | } |
|---|
| 183 | if (service_id.equals(OAIXML.LIST_SETS)) { |
|---|
| 184 | Element list_sets = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 185 | list_sets.setAttribute(OAIXML.NAME, OAIXML.LIST_SETS); |
|---|
| 186 | list_sets.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 187 | return list_sets; |
|---|
| 188 | } |
|---|
| 189 | if (service_id.equals(OAIXML.LIST_METADATA_FORMATS)) { |
|---|
| 190 | Element list_metadata_formats = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 191 | list_metadata_formats.setAttribute(OAIXML.NAME, OAIXML.LIST_METADATA_FORMATS); |
|---|
| 192 | list_metadata_formats.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 193 | return list_metadata_formats; |
|---|
| 194 | } |
|---|
| 195 | |
|---|
| 196 | if (service_id.equals(OAIXML.GET_RECORD)) { |
|---|
| 197 | Element get_record = this.doc.createElement(OAIXML.SERVICE); |
|---|
| 198 | get_record.setAttribute(OAIXML.NAME, OAIXML.GET_RECORD); |
|---|
| 199 | get_record.setAttribute(GSXML.TYPE_ATT, "oai"); |
|---|
| 200 | return get_record; |
|---|
| 201 | } |
|---|
| 202 | |
|---|
| 203 | return null; |
|---|
| 204 | } |
|---|
| 205 | /** return the metadata information about this set of the repository */ |
|---|
| 206 | protected Element processIdentify(Element req) { |
|---|
| 207 | return null; |
|---|
| 208 | } |
|---|
| 209 | /** return the metadata information */ |
|---|
| 210 | protected Element processListSets(Element req) { |
|---|
| 211 | //This method is never called unless each set in the returned message contain a |
|---|
| 212 | //'description' element so that we need to ask each collection for their info |
|---|
| 213 | return null; |
|---|
| 214 | } |
|---|
| 215 | /** return the metadata information */ |
|---|
| 216 | protected Element processGetRecord(Element req) { |
|---|
| 217 | /** arguments: |
|---|
| 218 | identifier: required |
|---|
| 219 | metadataPrefix: required |
|---|
| 220 | * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist |
|---|
| 221 | */ |
|---|
| 222 | NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM); |
|---|
| 223 | HashMap param_map = OAIXML.getParamMap(params); |
|---|
| 224 | |
|---|
| 225 | String prefix = (String)param_map.get(OAIXML.METADATA_PREFIX); |
|---|
| 226 | if (prefix == null || prefix.equals("")) { |
|---|
| 227 | //Just a double-check |
|---|
| 228 | logger.error("the value of metadataPrefix att is not present in the request."); |
|---|
| 229 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 230 | } |
|---|
| 231 | |
|---|
| 232 | Element metadata_format = getMetadataFormatElement(prefix); |
|---|
| 233 | if(metadata_format == null) { |
|---|
| 234 | logger.error("metadata prefix is not supported."); |
|---|
| 235 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 236 | } |
|---|
| 237 | |
|---|
| 238 | String oid = (String)param_map.get(OAIXML.OID); |
|---|
| 239 | |
|---|
| 240 | //get a DBInfo object of the identifier; if this identifier is not present in the database, |
|---|
| 241 | // null is returned. |
|---|
| 242 | DBInfo info = this.coll_db.getInfo(oid); |
|---|
| 243 | if (info == null) { |
|---|
| 244 | logger.error("OID: " + oid + " is not present in the database."); |
|---|
| 245 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, "")); |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | ArrayList keys = new ArrayList(info.getKeys()); |
|---|
| 249 | String lastmodified = ""; |
|---|
| 250 | if(keys.contains(OAIXML.LASTMODIFIED)) { |
|---|
| 251 | lastmodified = info.getInfo(OAIXML.LASTMODIFIED); |
|---|
| 252 | } |
|---|
| 253 | lastmodified = OAIXML.getTime(Long.parseLong(lastmodified)); |
|---|
| 254 | |
|---|
| 255 | Element get_record = OAIXML.createElement(OAIXML.GET_RECORD); |
|---|
| 256 | Element record = OAIXML.createElement(OAIXML.RECORD); |
|---|
| 257 | //compose the header element |
|---|
| 258 | record.appendChild(createHeaderElement(oid, lastmodified)); |
|---|
| 259 | //compose the metadata element |
|---|
| 260 | record.appendChild(createMetadataElement(prefix, info, metadata_format)); |
|---|
| 261 | get_record.appendChild(record); |
|---|
| 262 | return OAIXML.getResponse(get_record); |
|---|
| 263 | } |
|---|
| 264 | /** return a list of identifiers */ |
|---|
| 265 | protected Element processListIdentifiers(Element req) { |
|---|
| 266 | /** arguments: |
|---|
| 267 | metadataPrefix: required |
|---|
| 268 | * from: optional |
|---|
| 269 | * until: optional |
|---|
| 270 | * set: optional |
|---|
| 271 | * resumptionToken: exclusive and optional (ignored as it has been handled by OAIReceptionist) |
|---|
| 272 | * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist |
|---|
| 273 | */ |
|---|
| 274 | NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM); |
|---|
| 275 | |
|---|
| 276 | if(params.getLength() == 0) { |
|---|
| 277 | logger.error("must at least have the metadataPrefix parameter, can't be none"); |
|---|
| 278 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, "")); |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | HashMap param_map = OAIXML.getParamMap(params); |
|---|
| 282 | |
|---|
| 283 | String prefix = ""; |
|---|
| 284 | Date from_date = null; |
|---|
| 285 | Date until_date = null; |
|---|
| 286 | |
|---|
| 287 | if(param_map.containsKey(OAIXML.METADATA_PREFIX) == false) { |
|---|
| 288 | //Just a double-check |
|---|
| 289 | logger.error("A param element containing the metadataPrefix is not present."); |
|---|
| 290 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 291 | } |
|---|
| 292 | prefix = (String)param_map.get(OAIXML.METADATA_PREFIX); |
|---|
| 293 | if (prefix == null || prefix.equals("")) { |
|---|
| 294 | //Just a double-check |
|---|
| 295 | logger.error("the value of metadataPrefix att is not present in the request."); |
|---|
| 296 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 297 | } |
|---|
| 298 | |
|---|
| 299 | if(param_map.containsKey(OAIXML.FROM)) { |
|---|
| 300 | String from = (String)param_map.get(OAIXML.FROM); |
|---|
| 301 | from_date = OAIXML.getDate(from); |
|---|
| 302 | } |
|---|
| 303 | if(param_map.containsKey(OAIXML.UNTIL)) { |
|---|
| 304 | String until = (String)param_map.get(OAIXML.UNTIL); |
|---|
| 305 | until_date = OAIXML.getDate(until); |
|---|
| 306 | } |
|---|
| 307 | |
|---|
| 308 | Element metadata_format = getMetadataFormatElement(prefix); |
|---|
| 309 | if(metadata_format == null) { |
|---|
| 310 | logger.error("metadata prefix is not supported."); |
|---|
| 311 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 312 | } |
|---|
| 313 | ArrayList oid_list = getChildrenIds(OAIXML.BROWSELIST); |
|---|
| 314 | if (oid_list == null) { |
|---|
| 315 | logger.error("No matched records found in collection: browselist is empty"); |
|---|
| 316 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, "")); |
|---|
| 317 | } |
|---|
| 318 | // all validation is done |
|---|
| 319 | Element list_identifiers = OAIXML.createElement(OAIXML.LIST_IDENTIFIERS); |
|---|
| 320 | for(int i=0; i<oid_list.size(); i++) { |
|---|
| 321 | String oid = (String)oid_list.get(i); |
|---|
| 322 | DBInfo info = this.coll_db.getInfo(oid); |
|---|
| 323 | if (info == null) { |
|---|
| 324 | logger.error("Database does not contains information about oid: " +oid); |
|---|
| 325 | continue; |
|---|
| 326 | } |
|---|
| 327 | ArrayList keys = new ArrayList(info.getKeys()); |
|---|
| 328 | String lastmodified = ""; |
|---|
| 329 | if(keys.contains(OAIXML.LASTMODIFIED)) { |
|---|
| 330 | lastmodified = info.getInfo(OAIXML.LASTMODIFIED); |
|---|
| 331 | } |
|---|
| 332 | lastmodified = OAIXML.getTime(Long.parseLong(lastmodified)); |
|---|
| 333 | |
|---|
| 334 | Date this_date = OAIXML.getDate(lastmodified); |
|---|
| 335 | if (from_date != null) { |
|---|
| 336 | if(this_date.before(from_date)) { |
|---|
| 337 | continue; |
|---|
| 338 | } |
|---|
| 339 | } |
|---|
| 340 | if (until_date != null) { |
|---|
| 341 | if (this_date.after(until_date)) { |
|---|
| 342 | continue; |
|---|
| 343 | } |
|---|
| 344 | } |
|---|
| 345 | //compose the header element and append it |
|---|
| 346 | list_identifiers.appendChild(createHeaderElement(oid, lastmodified)); |
|---|
| 347 | }//end of for(int i=0; i<oid_list.size(); i++) of doing thru each record |
|---|
| 348 | |
|---|
| 349 | return OAIXML.getResponse(list_identifiers); |
|---|
| 350 | } |
|---|
| 351 | /** return a list of records */ |
|---|
| 352 | protected Element processListRecords(Element req) { |
|---|
| 353 | /** the request sent here may contain optional 'from', 'untill', 'metadataPrefix', |
|---|
| 354 | * and 'resumptionToken' params. see doListSets() in OAIReceptionist. |
|---|
| 355 | * if the request contains 'resumptionToken' then it should have been handled by the |
|---|
| 356 | * OAIReceptionist. Therefore, the request sent here must not contain 'resumptionToken' |
|---|
| 357 | * argument but a 'metadataPrefix' param. The OAIReceptionist makes sure of this. |
|---|
| 358 | */ |
|---|
| 359 | NodeList params = GSXML.getChildrenByTagName(req, OAIXML.PARAM); |
|---|
| 360 | |
|---|
| 361 | if(params.getLength() == 0) { |
|---|
| 362 | logger.error("must at least have the metadataPrefix parameter, can't be none"); |
|---|
| 363 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.BAD_ARGUMENT, "")); |
|---|
| 364 | } |
|---|
| 365 | |
|---|
| 366 | HashMap param_map = OAIXML.getParamMap(params); |
|---|
| 367 | |
|---|
| 368 | String prefix = ""; |
|---|
| 369 | Date from_date = null; |
|---|
| 370 | Date until_date = null; |
|---|
| 371 | |
|---|
| 372 | if(param_map.containsKey(OAIXML.METADATA_PREFIX) == false) { |
|---|
| 373 | //Just a double-check |
|---|
| 374 | logger.error("A param element containing the metadataPrefix is not present."); |
|---|
| 375 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 376 | } |
|---|
| 377 | prefix = (String)param_map.get(OAIXML.METADATA_PREFIX); |
|---|
| 378 | if (prefix == null || prefix.equals("")) { |
|---|
| 379 | //Just a double-check |
|---|
| 380 | logger.error("the value of metadataPrefix att is not present in the request."); |
|---|
| 381 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 382 | } |
|---|
| 383 | |
|---|
| 384 | if(param_map.containsKey(OAIXML.FROM)) { |
|---|
| 385 | String from = (String)param_map.get(OAIXML.FROM); |
|---|
| 386 | from_date = OAIXML.getDate(from); |
|---|
| 387 | } |
|---|
| 388 | if(param_map.containsKey(OAIXML.UNTIL)) { |
|---|
| 389 | String until = (String)param_map.get(OAIXML.UNTIL); |
|---|
| 390 | until_date = OAIXML.getDate(until); |
|---|
| 391 | } |
|---|
| 392 | Element metadata_format = getMetadataFormatElement(prefix); |
|---|
| 393 | if(metadata_format == null) { |
|---|
| 394 | logger.error("metadata prefix is not supported."); |
|---|
| 395 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 396 | } |
|---|
| 397 | // Another way of doing the same job! |
|---|
| 398 | // HashMap prefix_map = OAIXML.getChildrenMapByTagName(coll_config_xml, OAIXML.METADATA_PREFIX); |
|---|
| 399 | // if(!prefix_map.contains(prefix)) { |
|---|
| 400 | // logger.error("metadata prefix is not supported."); |
|---|
| 401 | // return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.CANNOT_DISSEMINATE_FORMAT, "")); |
|---|
| 402 | // } |
|---|
| 403 | |
|---|
| 404 | //get a list of identifiers (it contains a list of strings) |
|---|
| 405 | ArrayList oid_list = getChildrenIds(OAIXML.BROWSELIST); |
|---|
| 406 | if (oid_list == null) { |
|---|
| 407 | logger.error("No matched records found in collection: browselist is empty"); |
|---|
| 408 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.NO_RECORDS_MATCH, "")); |
|---|
| 409 | } |
|---|
| 410 | // all validation is done |
|---|
| 411 | Element list_records = OAIXML.createElement(OAIXML.LIST_RECORDS); |
|---|
| 412 | for(int i=0; i<oid_list.size(); i++) { |
|---|
| 413 | String oid = (String)oid_list.get(i); |
|---|
| 414 | DBInfo info = this.coll_db.getInfo(oid); |
|---|
| 415 | if (info == null) { |
|---|
| 416 | logger.error("Database does not contains information about oid: " +oid); |
|---|
| 417 | continue; |
|---|
| 418 | } |
|---|
| 419 | ArrayList keys = new ArrayList(info.getKeys()); |
|---|
| 420 | String lastmodified = ""; |
|---|
| 421 | if(keys.contains(OAIXML.LASTMODIFIED)) { |
|---|
| 422 | lastmodified = info.getInfo(OAIXML.LASTMODIFIED); |
|---|
| 423 | } |
|---|
| 424 | lastmodified = OAIXML.getTime(Long.parseLong(lastmodified)); |
|---|
| 425 | |
|---|
| 426 | Date this_date = OAIXML.getDate(lastmodified); |
|---|
| 427 | if (from_date != null) { |
|---|
| 428 | if(this_date.before(from_date)) { |
|---|
| 429 | continue; |
|---|
| 430 | } |
|---|
| 431 | } |
|---|
| 432 | if (until_date != null) { |
|---|
| 433 | if (this_date.after(until_date)) { |
|---|
| 434 | continue; |
|---|
| 435 | } |
|---|
| 436 | } |
|---|
| 437 | |
|---|
| 438 | Element record = OAIXML.createElement(OAIXML.RECORD); |
|---|
| 439 | list_records.appendChild(record); |
|---|
| 440 | //compose the header element |
|---|
| 441 | record.appendChild(createHeaderElement(oid, lastmodified)); |
|---|
| 442 | //compose the metadata element |
|---|
| 443 | record.appendChild(createMetadataElement(prefix, info, metadata_format)); |
|---|
| 444 | |
|---|
| 445 | }//end of for(int i=0; i<oid_list.size(); i++) of doing thru each record |
|---|
| 446 | |
|---|
| 447 | return OAIXML.getResponse(list_records); |
|---|
| 448 | } |
|---|
| 449 | |
|---|
| 450 | /** get the metadataFormat element from the collectionConfig.xml containing the specified metadata prefix. |
|---|
| 451 | * return null if not found. |
|---|
| 452 | */ |
|---|
| 453 | private Element getMetadataFormatElement(String prefix) { |
|---|
| 454 | Element oai = (Element)GSXML.getChildByTagName(this.coll_config_xml, OAIXML.OAI); |
|---|
| 455 | Element list_meta_format = (Element)GSXML.getChildByTagName(oai, OAIXML.LIST_METADATA_FORMATS); |
|---|
| 456 | Element metadata_format = GSXML.getNamedElement(list_meta_format, OAIXML.METADATA_FORMAT, OAIXML.METADATA_PREFIX, prefix); |
|---|
| 457 | return metadata_format; |
|---|
| 458 | } |
|---|
| 459 | /** create the metadata element used when processing ListRecords/GetRecord requests |
|---|
| 460 | */ |
|---|
| 461 | private Element createMetadataElement(String prefix, DBInfo info, Element metadata_format) { |
|---|
| 462 | //the prefix string is in the form: oai_dc, for example. |
|---|
| 463 | String prfx_str = ""; |
|---|
| 464 | //the metadata namespace used to retrieve metadata in the repository |
|---|
| 465 | //For example, if the prefix is like 'oai_ex' then we used 'ex' to get the metadata |
|---|
| 466 | //Normally we would use 'dc' to find metadata. |
|---|
| 467 | String meta_ns = ""; |
|---|
| 468 | if(prefix.equals(OAIXML.OAI_DC)) { |
|---|
| 469 | if(OAIXML.oai_version.equals(OAIXML.OAI_VERSION2)) { |
|---|
| 470 | prfx_str = prefix + ":" + OAIXML.DC; |
|---|
| 471 | } else { |
|---|
| 472 | prfx_str = OAIXML.DC;//oai version 1 |
|---|
| 473 | } |
|---|
| 474 | meta_ns = OAIXML.DC; |
|---|
| 475 | } else { |
|---|
| 476 | prfx_str = prefix.substring(prefix.indexOf("_") + 1); |
|---|
| 477 | meta_ns = prfx_str; |
|---|
| 478 | } |
|---|
| 479 | //create the <metadata> element |
|---|
| 480 | //OAIXML.oai_version is read from OAIConfig.xml and its default value is "2.0" |
|---|
| 481 | Element prfx_str_elem = OAIXML.getMetadataPrefixElement(prfx_str, OAIXML.oai_version); |
|---|
| 482 | String[] metadata_names = getMetadataNames(metadata_format); |
|---|
| 483 | HashMap meta_map = getInfoByNames(info, metadata_names); |
|---|
| 484 | ArrayList meta_list = new ArrayList(meta_map.entrySet()); |
|---|
| 485 | for (int j=0; j<meta_list.size(); j++) { |
|---|
| 486 | Entry men = (Entry)meta_list.get(j); |
|---|
| 487 | String meta_name = (String)men.getKey(); |
|---|
| 488 | String meta_value = (String)men.getValue(); |
|---|
| 489 | Element e = OAIXML.createElement(meta_name); |
|---|
| 490 | GSXML.setNodeText(e, meta_value); |
|---|
| 491 | prfx_str_elem.appendChild(e); |
|---|
| 492 | } |
|---|
| 493 | Element metadata = OAIXML.createElement(OAIXML.METADATA); |
|---|
| 494 | metadata.appendChild(prfx_str_elem); |
|---|
| 495 | return metadata; |
|---|
| 496 | } |
|---|
| 497 | /** create a header element used when processing requests like ListRecords/GetRecord/ListIdentifiers |
|---|
| 498 | */ |
|---|
| 499 | private Element createHeaderElement(String oid, String lastmodified) { |
|---|
| 500 | Element header = OAIXML.createElement(OAIXML.HEADER); |
|---|
| 501 | Element identifier = OAIXML.createElement(OAIXML.IDENTIFIER); |
|---|
| 502 | GSXML.setNodeText(identifier, site_name + ":" + coll_name + ":" + oid); |
|---|
| 503 | header.appendChild(identifier); |
|---|
| 504 | Element set_spec = OAIXML.createElement(OAIXML.SET_SPEC); |
|---|
| 505 | GSXML.setNodeText(set_spec, site_name + ":" + coll_name); |
|---|
| 506 | header.appendChild(set_spec); |
|---|
| 507 | Element datestamp = OAIXML.createElement(OAIXML.DATESTAMP); |
|---|
| 508 | GSXML.setNodeText(datestamp, lastmodified); |
|---|
| 509 | header.appendChild(datestamp); |
|---|
| 510 | return header; |
|---|
| 511 | } |
|---|
| 512 | /** return the metadata information */ |
|---|
| 513 | protected Element processListMetadataFormats(Element req) { |
|---|
| 514 | // the request sent here must contain an OID. see doListMetadataFormats() in OAIReceptionist |
|---|
| 515 | Element param = GSXML.getNamedElement(req, OAIXML.PARAM, OAIXML.NAME, OAIXML.OID); |
|---|
| 516 | if (param == null) { |
|---|
| 517 | logger.error("An element containing the OID attribute not is present."); |
|---|
| 518 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, "")); |
|---|
| 519 | } |
|---|
| 520 | String oid = param.getAttribute(OAIXML.VALUE); |
|---|
| 521 | if (oid == null || oid.equals("")) { |
|---|
| 522 | logger.error("No OID is present in the request."); |
|---|
| 523 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, "")); |
|---|
| 524 | } |
|---|
| 525 | ArrayList oid_list = getChildrenIds(OAIXML.BROWSELIST); |
|---|
| 526 | if (oid_list == null || oid_list.contains(oid) == false) { |
|---|
| 527 | logger.error("OID: " + oid + " is not present in the database."); |
|---|
| 528 | Element e= OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.ID_DOES_NOT_EXIST, "")); |
|---|
| 529 | // logger.error((new XMLConverter()).getPrettyString (e)); |
|---|
| 530 | return e; |
|---|
| 531 | } |
|---|
| 532 | |
|---|
| 533 | DBInfo info = null; |
|---|
| 534 | info = this.coll_db.getInfo(oid); |
|---|
| 535 | if (info == null) { //just double check |
|---|
| 536 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.OAI_SERVICE_UNAVAILABLE, "")); |
|---|
| 537 | } |
|---|
| 538 | |
|---|
| 539 | NodeList meta_list = getMetadataFormatList(this.coll_config_xml); |
|---|
| 540 | if (meta_list == null || meta_list.getLength() == 0) { |
|---|
| 541 | logger.error("No metadata format is present in collectionConfig.xml"); |
|---|
| 542 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.NO_METADATA_FORMATS, "")); |
|---|
| 543 | } |
|---|
| 544 | |
|---|
| 545 | Element list_metadata_formats = OAIXML.createElement(OAIXML.LIST_METADATA_FORMATS); |
|---|
| 546 | boolean has_meta_format = false; |
|---|
| 547 | |
|---|
| 548 | for (int i=0; i<meta_list.getLength(); i++) { |
|---|
| 549 | Element metadata_format = (Element)meta_list.item(i); |
|---|
| 550 | String[] metadata_names = getMetadataNames(metadata_format); |
|---|
| 551 | if (containsMetadata(info, metadata_names) == true) { |
|---|
| 552 | has_meta_format = true; |
|---|
| 553 | Element meta_fmt = OAIXML.createElement(OAIXML.METADATA_FORMAT); |
|---|
| 554 | OAIXML.copyElement(meta_fmt, metadata_format, OAIXML.METADATA_PREFIX); |
|---|
| 555 | OAIXML.copyElement(meta_fmt, metadata_format, OAIXML.METADATA_NAMESPACE); |
|---|
| 556 | OAIXML.copyElement(meta_fmt, metadata_format, OAIXML.SCHEMA); |
|---|
| 557 | list_metadata_formats.appendChild(meta_fmt); |
|---|
| 558 | } |
|---|
| 559 | }//end of for loop |
|---|
| 560 | if (has_meta_format == false) { |
|---|
| 561 | logger.error("Specified metadata names are not contained in the database."); |
|---|
| 562 | return OAIXML.getResponse(OAIXML.createErrorElement(OAIXML.NO_METADATA_FORMATS, "")); |
|---|
| 563 | } else { |
|---|
| 564 | return OAIXML.getResponse(list_metadata_formats); |
|---|
| 565 | } |
|---|
| 566 | } |
|---|
| 567 | |
|---|
| 568 | /** return the ListMetadataFormats element in collectionConfig.xml |
|---|
| 569 | * Currently, it will only contain one metadata format: oai_dc |
|---|
| 570 | */ |
|---|
| 571 | protected NodeList getMetadataFormatList(Element coll_config_xml) { |
|---|
| 572 | Element oai_elem = (Element)GSXML.getChildByTagName(coll_config_xml, OAIXML.OAI); |
|---|
| 573 | Element list_meta_formats = (Element)GSXML.getChildByTagName(oai_elem, OAIXML.LIST_METADATA_FORMATS); |
|---|
| 574 | return GSXML.getChildrenByTagName(list_meta_formats, OAIXML.METADATA_FORMAT); |
|---|
| 575 | } |
|---|
| 576 | /** @param metadata_format - the metadataFormat element in collectionConfig.xml |
|---|
| 577 | */ |
|---|
| 578 | protected String[] getMetadataNames(Element metadata_format) { |
|---|
| 579 | String[] names = null; |
|---|
| 580 | |
|---|
| 581 | //read the mappingList element |
|---|
| 582 | Element mapping_list = (Element)GSXML.getChildByTagName(metadata_format, OAIXML.MAPPING_LIST); |
|---|
| 583 | if (mapping_list == null) { |
|---|
| 584 | logger.info("No metadata mappings are provided in collectionConfig.xml. Use the standard Dublin Core names."); |
|---|
| 585 | names = OAIXML.getGlobalMetadataMapping(metadata_format.getAttribute(OAIXML.METADATA_PREFIX)); |
|---|
| 586 | |
|---|
| 587 | return (names != null)? names : OAIXML.getDublinCoreNames(); |
|---|
| 588 | } |
|---|
| 589 | NodeList mappings = GSXML.getChildrenByTagName(mapping_list, OAIXML.MAPPING); |
|---|
| 590 | int size = mappings.getLength(); |
|---|
| 591 | if (size == 0) { |
|---|
| 592 | logger.info("No metadata mappings are provided in collectionConfig.xml. \n Return standard DC names."); |
|---|
| 593 | // read the standard Dublin Core metadata names |
|---|
| 594 | return OAIXML.getDublinCoreNames(); |
|---|
| 595 | } |
|---|
| 596 | names = new String[size]; |
|---|
| 597 | for (int i=0; i<size; i++) { |
|---|
| 598 | names[i] = GSXML.getNodeText((Element)mappings.item(i)).trim(); |
|---|
| 599 | } |
|---|
| 600 | return names; |
|---|
| 601 | } |
|---|
| 602 | |
|---|
| 603 | /** returns a list of the child ids in order, null if no children */ |
|---|
| 604 | protected ArrayList getChildrenIds(String node_id) { |
|---|
| 605 | DBInfo info = this.coll_db.getInfo(node_id); |
|---|
| 606 | if (info == null) { |
|---|
| 607 | return null; |
|---|
| 608 | } |
|---|
| 609 | |
|---|
| 610 | String contains = info.getInfo("contains"); |
|---|
| 611 | if (contains.equals("")) { |
|---|
| 612 | return null; |
|---|
| 613 | } |
|---|
| 614 | ArrayList children = new ArrayList(); |
|---|
| 615 | StringTokenizer st = new StringTokenizer(contains, ";"); |
|---|
| 616 | while (st.hasMoreTokens()) { |
|---|
| 617 | String child_id = st.nextToken().replaceAll("\"", node_id); |
|---|
| 618 | children.add(child_id); |
|---|
| 619 | } |
|---|
| 620 | return children; |
|---|
| 621 | } |
|---|
| 622 | /**method to check whether any of the 'metadata_names' is contained in the 'info'. |
|---|
| 623 | * The name may be in the form: <name>,<mapped name>, in which the mapped name is |
|---|
| 624 | * optional. The mapped name is looked up in the DBInfo; if not present, use the first |
|---|
| 625 | * name which is mendatory. |
|---|
| 626 | */ |
|---|
| 627 | protected boolean containsMetadata(DBInfo info, String[] metadata_names) { |
|---|
| 628 | if (metadata_names == null) return false; |
|---|
| 629 | logger.info("checking metadata names in db."); |
|---|
| 630 | for(int i=0; i<metadata_names.length; i++) { |
|---|
| 631 | int index = metadata_names[i].indexOf(","); |
|---|
| 632 | String meta_name = (index == -1) ? metadata_names[i] : |
|---|
| 633 | metadata_names[i].substring(index + 1); |
|---|
| 634 | |
|---|
| 635 | if(info.getInfo(meta_name).equals("") == false) { |
|---|
| 636 | return true; |
|---|
| 637 | } |
|---|
| 638 | } |
|---|
| 639 | return false; |
|---|
| 640 | } |
|---|
| 641 | /** @param keys - contains a list of keys in string format. |
|---|
| 642 | * Here is a typical record in the collection database, 'keys' contains the values in <...>: |
|---|
| 643 | *---------------------------------------------------------------------- |
|---|
| 644 | [HASH01a84acb0f1aad2380493b3a] |
|---|
| 645 | <doctype>doc |
|---|
| 646 | <hastxt>1 |
|---|
| 647 | <Language>en |
|---|
| 648 | <Encoding>windows_1252 |
|---|
| 649 | <Plugin>HTMLPlug |
|---|
| 650 | <FileSize>205093 |
|---|
| 651 | <Source>wb34te.htm |
|---|
| 652 | <hascover>1 |
|---|
| 653 | <dls.Organization>World Bank |
|---|
| 654 | <dls.Title>Development in practice: Toward Gender Equality (wb34te) |
|---|
| 655 | <dls.Language>English |
|---|
| 656 | <dls.AZList>A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z |
|---|
| 657 | <dls.Subject>Women, gender and development, women's organizations |
|---|
| 658 | <dls.Keyword>achieve gender equality |
|---|
| 659 | <URL>http://wb34te/wb34te.htm |
|---|
| 660 | <Title>Development in practice: Toward Gender Equality |
|---|
| 661 | <lastmodified>1178245194 |
|---|
| 662 | <assocfilepath>HASH01a8.dir |
|---|
| 663 | <memberof>CL3 |
|---|
| 664 | <archivedir>HASH01a8.dir |
|---|
| 665 | <thistype>VList |
|---|
| 666 | <childtype>VList |
|---|
| 667 | <contains>".1;".2;".3;".4;".5;".6;".7;".8;".9 |
|---|
| 668 | <docnum>349 |
|---|
| 669 | ---------------------------------------------------------------------- |
|---|
| 670 | */ |
|---|
| 671 | public String[] getMetadata(DBInfo info, String names) { |
|---|
| 672 | String[] name_value = new String[2]; |
|---|
| 673 | ArrayList keys = new ArrayList(info.getKeys()); |
|---|
| 674 | for (int i=0; i<keys.size(); i++) { |
|---|
| 675 | String key = (String)keys.get(i); |
|---|
| 676 | String first_name = ""; |
|---|
| 677 | String second_name = ""; |
|---|
| 678 | int index = names.indexOf(","); |
|---|
| 679 | if(index != -1) { |
|---|
| 680 | first_name = names.substring(0, index); |
|---|
| 681 | second_name = names.substring(index + 1); |
|---|
| 682 | } else { |
|---|
| 683 | first_name = second_name = names; |
|---|
| 684 | } |
|---|
| 685 | if(key.equals(second_name)) { |
|---|
| 686 | String meta_value = info.getInfo(key); |
|---|
| 687 | name_value[0] = first_name; |
|---|
| 688 | name_value[1] = meta_value; |
|---|
| 689 | return name_value; |
|---|
| 690 | } |
|---|
| 691 | } |
|---|
| 692 | return null; |
|---|
| 693 | } |
|---|
| 694 | protected HashMap getInfoByNames(DBInfo info, String[] metadata_names) { |
|---|
| 695 | HashMap map = new HashMap(); |
|---|
| 696 | boolean empty_map = true; |
|---|
| 697 | |
|---|
| 698 | for(int i=0; i<metadata_names.length; i++) { |
|---|
| 699 | String[] name_value = getMetadata(info, metadata_names[i]); |
|---|
| 700 | if(name_value != null) { |
|---|
| 701 | map.put(name_value[0], name_value[1]); |
|---|
| 702 | empty_map = false; |
|---|
| 703 | } |
|---|
| 704 | } |
|---|
| 705 | return (empty_map == true) ? null : map; |
|---|
| 706 | } |
|---|
| 707 | } |
|---|
| 708 | |
|---|
| 709 | |
|---|