source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/OAIPMH.java@ 30553

Last change on this file since 30553 was 30553, checked in by kjdon, 8 years ago

added ability for teh collectionConfig.xml file to carry additional stuff. Can have extraInfo element at the top level (inside collectionConfig). For now this is used to add extra items to the navigation bar (<navigationTab type=external-link

File size: 30.2 KB
Line 
1/*
2 * OAIPMH.java
3 * Copyright (C) 2010 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 */
19package org.greenstone.gsdl3.service;
20
21// Greenstone classes
22import org.greenstone.gsdl3.core.GSException;
23import org.greenstone.gsdl3.util.GSXML;
24import org.greenstone.gsdl3.util.OAIXML;
25import org.greenstone.gsdl3.util.OID;
26import org.greenstone.gsdl3.util.GSFile;
27import org.greenstone.gsdl3.util.XMLConverter;
28
29import org.greenstone.gsdl3.util.SimpleCollectionDatabase;
30import org.greenstone.gsdl3.util.DBInfo;
31// XML classes
32import org.w3c.dom.Document;
33import org.w3c.dom.Element;
34import org.w3c.dom.NodeList;
35
36// General Java classes
37import java.io.File;
38import java.util.StringTokenizer;
39import java.util.Vector;
40import java.util.Set;
41import java.util.Iterator;
42import java.util.ArrayList;
43import java.util.Date;
44import java.util.HashMap;
45import java.util.HashSet;
46import java.util.Map.Entry;
47
48import org.apache.log4j.Logger;
49
50/** Implements the oai metadata retrieval service for GS3 collections.
51 * Dig into each collection's database and retrieve the metadata
52 *
53 */
54
55public 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
64 // set this up during configure
65 protected Element list_sets_response = null;
66
67 protected Element meta_formats_definition = null;
68 protected HashMap<String, HashSet<String>> format_elements_map = null;
69 protected HashMap<String, Element> format_response_map = null;
70 /** constructor */
71 public OAIPMH() {
72
73 }
74
75 public void cleanUp() {
76 super.cleanUp();//??
77 this.coll_db.closeDatabase();
78 }
79 /** configure this service
80 info is the OAIPMH service rack from collectionConfig.xml, and
81 extra_info is buildConfig.xml */
82 public boolean configure(Element info, Element extra_info) {
83 if (!super.configure(info, extra_info)){
84 logger.info("Configuring ServiceRack.java returns false.");
85 return false;
86 }
87
88 //get the names from ServiceRack.java
89 this.site_name = this.router.getSiteName();
90 this.coll_name = this.cluster_name;
91
92 logger.info("Configuring OAIPMH...");
93
94 this.config_info = info;
95
96 // the index stem is either specified in the buildConfig.xml file (extra_info) or uses the collection name
97 Element metadata_list = (Element) GSXML.getChildByTagName(extra_info, GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
98 String index_stem = "";
99 String infodb_type = "";
100 if (metadata_list != null) {
101
102 Element index_stem_elem = (Element) GSXML.getNamedElement(metadata_list, GSXML.METADATA_ELEM, GSXML.NAME_ATT, "indexStem");
103
104 if (index_stem_elem != null) {
105 index_stem = GSXML.getNodeText(index_stem_elem);
106 }
107
108 Element infodb_type_elem = (Element) GSXML.getNamedElement(metadata_list, GSXML.METADATA_ELEM, GSXML.NAME_ATT, "infodbType");
109 if (infodb_type_elem != null) {
110 infodb_type = GSXML.getNodeText(infodb_type_elem);
111 }
112
113 }
114
115 if (index_stem == null || index_stem.equals("")) {
116 index_stem = this.cluster_name;
117 }
118 if (infodb_type == null || infodb_type.equals("")) {
119 infodb_type = "gdbm"; // the default
120 }
121
122 coll_db = new SimpleCollectionDatabase(infodb_type);
123 if (!coll_db.databaseOK()) {
124 logger.error("Couldn't create the collection database of type "+infodb_type);
125 return false;
126 }
127
128 // Open database for querying
129 String coll_db_file = GSFile.collectionDatabaseFile(this.site_home, this.cluster_name, index_stem, infodb_type);
130 if (!this.coll_db.openDatabase(coll_db_file, SimpleCollectionDatabase.READ)) {
131 logger.error("Could not open collection database!");
132 return false;
133 }
134
135 // work out what sets this collection has. Will usually contain the collection itself, optional super collection, and maybe subcolls if appropriate classifiers are present.
136 configureSetInfo();
137 // the short_service_info is used by the message router to find the method names,
138
139 Element list_records = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
140 list_records.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_RECORDS);
141 list_records.setAttribute(GSXML.TYPE_ATT, "oai");
142 this.short_service_info.appendChild(list_records);
143
144 Element list_identifiers = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
145 list_identifiers.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_IDENTIFIERS);
146 list_identifiers.setAttribute(GSXML.TYPE_ATT, "oai");
147 this.short_service_info.appendChild(list_identifiers);
148
149 Element list_sets = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
150 list_sets.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_SETS);
151 list_sets.setAttribute(GSXML.TYPE_ATT, "oai");
152 this.short_service_info.appendChild(list_sets);
153
154 Element list_metadata_formats = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
155 list_metadata_formats.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_METADATA_FORMATS);
156 list_metadata_formats.setAttribute(GSXML.TYPE_ATT, "oai");
157 this.short_service_info.appendChild(list_metadata_formats);
158
159 Element get_record = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
160 get_record.setAttribute(GSXML.NAME_ATT, OAIXML.GET_RECORD);
161 get_record.setAttribute(GSXML.TYPE_ATT, "oai");
162 this.short_service_info.appendChild(get_record);
163
164 return true;
165 }
166
167 public boolean configureOAI(Element oai_config_elem) {
168 this.meta_formats_definition = this.desc_doc.createElement(OAIXML.LIST_METADATA_FORMATS);
169 this.format_response_map = new HashMap<String, Element>();
170 this.format_elements_map = new HashMap<String, HashSet<String>>();
171
172 // for now, all we want is the metadata prefix description and the mapping list
173 Element main_lmf_elem = (Element) GSXML.getChildByTagName(oai_config_elem, OAIXML.LIST_METADATA_FORMATS);
174 if (main_lmf_elem == null) {
175 logger.error("No listMetadataFormats element found in OAIConfig.xml");
176 return false;
177 }
178 NodeList meta_formats_list = this.config_info.getElementsByTagName(OAIXML.METADATA_FORMAT);
179 if (meta_formats_list.getLength() == 0) {
180 logger.error("no metadataFormat elements found in OAIPMH serviceRack element");
181 return false;
182 }
183 boolean found_meta_format = false;
184 for(int i=0; i<meta_formats_list.getLength(); i++) {
185 Element mf = (Element) meta_formats_list.item(i);
186 String prefix = mf.getAttribute(OAIXML.METADATA_PREFIX);
187 if (prefix.equals("")) {
188 logger.error("metadataFormat element had no metadataPrefix attribute");
189 continue;
190 }
191 // get the right format from OAICOnfig
192 Element meta_format = findNamedMetadataFormat(main_lmf_elem, prefix);
193 if (meta_format == null) {
194 logger.error("Couldn't find metadataFormat named "+prefix+" in OAIConfig.xml");
195 continue;
196 }
197 // copy the format definition into our stored Element
198 Element collection_version_format = (Element) this.desc_doc.importNode(meta_format, true);
199 collection_version_format.setAttribute(GSXML.NAME_ATT, prefix); // for convenience
200 this.meta_formats_definition.appendChild(collection_version_format);
201 // set up the response element for this format
202 format_response_map.put(prefix, OAIXML.getMetadataFormatShort(this.desc_doc, collection_version_format));
203 // add in collection specific mappings
204 addCollectionMappings(collection_version_format, mf);
205 // now set up a list of all collection elements for reverse lookup of the mapping
206 format_elements_map.put(prefix, getAllCollectionElements(collection_version_format));
207
208 }
209 return true;
210 }
211
212 protected Element findNamedMetadataFormat(Element list_meta_formats, String prefix) {
213 NodeList formats = list_meta_formats.getElementsByTagName(OAIXML.METADATA_FORMAT);
214 for (int i=0; i<formats.getLength(); i++) {
215 Element format = (Element)formats.item(i);
216 String meta_name = GSXML.getNodeText((Element)GSXML.getChildByTagName(format, OAIXML.METADATA_PREFIX));
217 if (prefix.equals(meta_name)) {
218 return format;
219 }
220 }
221 return null;
222 }
223
224 /** goes through the mappings from the collection one, and replaces existing ones in the main one */
225 protected void addCollectionMappings(Element main_meta_format, Element coll_meta_format) {
226
227 Element element_list = (Element)GSXML.getChildByTagName(main_meta_format, OAIXML.ELEMENT+GSXML.LIST_MODIFIER);
228 Document doc = element_list.getOwnerDocument();
229 NodeList coll_elements = coll_meta_format.getElementsByTagName(OAIXML.ELEMENT);
230 if (coll_elements.getLength()==0) {
231 // no mappings to include
232 return;
233 }
234 for (int i=0; i<coll_elements.getLength(); i++) {
235 Element e = (Element)coll_elements.item(i);
236 String elem_name = e.getAttribute(GSXML.NAME_ATT);
237 Element main_elem = GSXML.getNamedElement(element_list, OAIXML.ELEMENT, GSXML.NAME_ATT, elem_name);
238 if (main_elem == null) {
239 logger.error(elem_name+" not found in meta format, not using it");
240 } else {
241 element_list.replaceChild(doc.importNode(e, true),main_elem );
242 }
243 }
244 }
245
246 /** goes through all the mappings and makes a set of all collection
247 metadata names that could become an oai meta element - acts as
248 a reverse lookup for the mappings */
249 protected HashSet<String> getAllCollectionElements(Element meta_format) {
250 HashSet<String> meta_name_set = new HashSet<String>();
251 NodeList elements = meta_format.getElementsByTagName(OAIXML.ELEMENT);
252 for (int i=0; i<elements.getLength(); i++) {
253 Element e = (Element)elements.item(i);
254 Element map = (Element)GSXML.getChildByTagName(e, OAIXML.MAPPING);
255 if (map == null) {
256 // there is no mapping, just use the element name
257 meta_name_set.add(e.getAttribute(GSXML.NAME_ATT));
258 } else {
259 String list_of_names = map.getAttribute(OAIXML.ELEMENTS);
260 String[] name_array = list_of_names.split(",");
261 for (int j=0; j<name_array.length; j++) {
262 meta_name_set.add(name_array[j]);
263 }
264 }
265 }
266 return meta_name_set;
267 }
268
269 /** returns a specific service description */
270 public Element getServiceDescription(Document doc, String service_id, String lang, String subset) {
271
272 if (service_id.equals(OAIXML.LIST_RECORDS)) {
273 Element list_records = doc.createElement(GSXML.SERVICE_ELEM);
274 list_records.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_RECORDS);
275 list_records.setAttribute(GSXML.TYPE_ATT, "oai");
276 return list_records;
277 }
278
279 if (service_id.equals(OAIXML.LIST_IDENTIFIERS)) {
280 Element list_identifiers = doc.createElement(GSXML.SERVICE_ELEM);
281 list_identifiers.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_IDENTIFIERS);
282 list_identifiers.setAttribute(GSXML.TYPE_ATT, "oai");
283 return list_identifiers;
284 }
285 if (service_id.equals(OAIXML.LIST_SETS)) {
286 Element list_sets = doc.createElement(GSXML.SERVICE_ELEM);
287 list_sets.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_SETS);
288 list_sets.setAttribute(GSXML.TYPE_ATT, "oai");
289 return list_sets;
290 }
291 if (service_id.equals(OAIXML.LIST_METADATA_FORMATS)) {
292 Element list_metadata_formats = doc.createElement(GSXML.SERVICE_ELEM);
293 list_metadata_formats.setAttribute(GSXML.NAME_ATT, OAIXML.LIST_METADATA_FORMATS);
294 list_metadata_formats.setAttribute(GSXML.TYPE_ATT, "oai");
295 return list_metadata_formats;
296 }
297
298 if (service_id.equals(OAIXML.GET_RECORD)) {
299 Element get_record = doc.createElement(GSXML.SERVICE_ELEM);
300 get_record.setAttribute(GSXML.NAME_ATT, OAIXML.GET_RECORD);
301 get_record.setAttribute(GSXML.TYPE_ATT, "oai");
302 return get_record;
303 }
304
305 return null;
306 }
307
308 /** The list sets service returns all the sets that this collection is/is part of/contains. This is gathered by Receptionist from all collections to answer the OAI ListSets request. */
309 protected Element processListSets(Element req) {
310 return list_sets_response;
311 }
312 /** returns the actual record element used in the OAI GetRecord response */
313 protected Element processGetRecord(Element req) {
314 /** arguments:
315 identifier: required
316 metadataPrefix: required
317 * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
318 */
319 NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
320 HashMap<String, String> param_map = GSXML.getParamMap(params);
321
322 String prefix = param_map.get(OAIXML.METADATA_PREFIX);
323 if (prefix == null || prefix.equals("")) {
324 //Just a double-check
325 logger.error("the value of metadataPrefix att is not present in the request.");
326 return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
327 }
328
329 // check that we support this format
330 if (!format_response_map.containsKey(prefix)) {
331 logger.error("metadata prefix is not supported for collection "+this.coll_name);
332 return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
333 }
334
335 String oid = param_map.get(OAIXML.OID); // TODO should this be identifier???
336
337 //get a DBInfo object of the identifier; if this identifier is not present in the database,
338 // null is returned.
339 DBInfo info = this.coll_db.getInfo(oid);
340 if (info == null) {
341 logger.error("OID: " + oid + " is not present in the database.");
342 return OAIXML.createErrorResponse(OAIXML.ID_DOES_NOT_EXIST, "");
343 }
344
345 Document doc = XMLConverter.newDOM();
346 ArrayList<String> keys = new ArrayList<String>(info.getKeys());
347 long millis = getDateStampMillis(info);
348 String oailastmodified = "";
349 if (millis != -1) {
350 oailastmodified = OAIXML.getTime(millis);
351 }
352
353 Element get_record_response = doc.createElement(GSXML.RESPONSE_ELEM);
354 Element get_record = doc.createElement(OAIXML.GET_RECORD);
355 get_record_response.appendChild(get_record);
356 Element record = doc.createElement(OAIXML.RECORD);
357 //compose the header element
358 record.appendChild(createHeaderElement(doc, oid, oailastmodified));
359 //compose the metadata element
360 record.appendChild(createMetadataElement(doc, prefix, info));
361 get_record.appendChild(record);
362 return get_record_response;
363 }
364
365 /** return a list of records in specified set, containing metadata from specified prefix*/
366 protected Element processListRecords(Element req) {
367 return processListIdentifiersOrRecords(req, OAIXML.LIST_RECORDS, true);
368 }
369
370 /** return a list of identifiers in specified set that contain metadata belonging to specified prefix. */
371 protected Element processListIdentifiers(Element req) {
372 return processListIdentifiersOrRecords(req, OAIXML.LIST_IDENTIFIERS, false);
373 }
374
375 // Get a list of records/identifiers that match the parameters.
376 protected Element processListIdentifiersOrRecords(Element req, String response_name, boolean include_metadata) {
377 /** arguments:
378 metadataPrefix: required
379 * from: optional
380 * until: optional
381 * set: optional
382 * resumptionToken: exclusive and optional (ignored as it has been handled by OAIReceptionist)
383 * Exceptions: badArgument; cannotDisseminateFormat; idDoesNotExist
384 */
385 NodeList params = GSXML.getChildrenByTagName(req, GSXML.PARAM_ELEM);
386
387 if(params.getLength() == 0) {
388 logger.error("must at least have the metadataPrefix parameter, can't be none");
389 return OAIXML.createErrorResponse(OAIXML.BAD_ARGUMENT, "");
390 }
391
392 HashMap<String, String> param_map = GSXML.getParamMap(params);
393
394 String prefix = "";
395 Date from_date = null;
396 Date until_date = null;
397
398 if(param_map.containsKey(OAIXML.METADATA_PREFIX) == false) {
399 //Just a double-check
400 logger.error("A param element containing the metadataPrefix is not present.");
401 return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
402 }
403 prefix = param_map.get(OAIXML.METADATA_PREFIX);
404 if (prefix == null || prefix.equals("")) {
405 //Just a double-check
406 logger.error("the value of metadataPrefix att is not present in the request.");
407 return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
408 }
409
410 if(param_map.containsKey(OAIXML.FROM)) {
411 String from = param_map.get(OAIXML.FROM);
412 from_date = OAIXML.getDate(from);
413 }
414 if(param_map.containsKey(OAIXML.UNTIL)) {
415 String until = param_map.get(OAIXML.UNTIL);
416 until_date = OAIXML.getDate(until);
417 }
418
419 if (!format_response_map.containsKey(prefix)) {
420 logger.error(prefix + " metadata prefix is not supported for collection "+this.coll_name);
421 return OAIXML.createErrorResponse(OAIXML.CANNOT_DISSEMINATE_FORMAT, "");
422 }
423 ArrayList<String> oid_list = getChildrenIds(OAIXML.BROWSELIST);
424 if (oid_list == null) {
425 logger.error("No matched records found in collection: browselist is empty");
426 return OAIXML.createErrorResponse(OAIXML.NO_RECORDS_MATCH, "");
427 }
428 // all validation is done
429
430 // get the list of elements that are in this metadata prefix
431 HashSet<String> set_of_elems = format_elements_map.get(prefix);
432
433 Document doc = XMLConverter.newDOM();
434 Element list_items_response = doc.createElement(GSXML.RESPONSE_ELEM);
435 Element list_items = doc.createElement(response_name);
436 list_items_response.appendChild(list_items);
437
438 for(int i=0; i<oid_list.size(); i++) {
439 String oid = oid_list.get(i);
440 DBInfo info = this.coll_db.getInfo(oid);
441 if (info == null) {
442 logger.error("Database does not contains information about oid: " +oid);
443 continue;
444 }
445
446 long millis = getDateStampMillis(info);
447 Date this_date = null;
448 if (millis == -1) {
449 if (from_date != null || until_date !=null) {
450 continue; // if this doc doesn't have a date for some reason, and
451 // we are doing a date range, then don't include it.
452 }
453 } else {
454 this_date = new Date(millis);
455 if (from_date != null) {
456 if(this_date.before(from_date)) {
457 continue;
458 }
459 }
460 if (until_date != null) {
461 if (this_date.after(until_date)) {
462 continue;
463 }
464 }
465 }
466
467 //Now check that this id has metadata for the required prefix.
468 if (documentContainsMetadata(info, set_of_elems)) {
469 // YES, it does have some metadata for this prefix
470 if (include_metadata) {
471 // compose a record and add header and metadata
472 Element record = doc.createElement(OAIXML.RECORD);
473 list_items.appendChild(record);
474 //compose the header element
475 record.appendChild(createHeaderElement(doc, oid, OAIXML.getTime(millis)));
476 //compose the metadata element
477 record.appendChild(createMetadataElement(doc, prefix, info));
478 } else {
479 //compose the header element and append it
480 list_items.appendChild(createHeaderElement(doc, oid, OAIXML.getTime(millis)));
481 }
482 } // otherwise we won't include this oid.
483 }//end of for(int i=0; i<oid_list.size(); i++) of doing thru each record
484
485 return list_items_response;
486
487 }
488
489
490 // have implemented setDescription as an element, instead of a container containing metadata
491 private boolean configureSetInfo() {
492
493 Document doc = XMLConverter.newDOM();
494 this.list_sets_response = doc.createElement(GSXML.RESPONSE_ELEM);
495 Element list_sets_elem = doc.createElement(OAIXML.LIST_SETS);
496 this.list_sets_response.appendChild(list_sets_elem);
497 String set_name = this.coll_name;
498 String set_description = null;
499 Element name_elem = (Element)GSXML.getChildByTagName(this.config_info, OAIXML.SET_NAME);
500 if (name_elem!=null) {
501 set_name = GSXML.getNodeText(name_elem);
502 if (set_name.equals("")) {
503 set_name = this.coll_name; // default to coll name if can't find one
504 }
505 }
506 Element description_elem = (Element)GSXML.getChildByTagName(this.config_info, OAIXML.SET_DESCRIPTION);
507 if (description_elem!=null) {
508 set_description = GSXML.getNodeText(description_elem);
509 if (set_description.equals("")) {
510 set_description = null;
511 }
512 }
513 Element coll_set = OAIXML.createSet(doc, this.coll_name, set_name, set_description);
514 list_sets_elem.appendChild(coll_set);
515
516 // are we part of any super sets?
517 NodeList super_set_list = GSXML.getChildrenByTagName(this.config_info, OAIXML.OAI_SUPER_SET);
518 for (int i=0; i<super_set_list.getLength(); i++) {
519 String super_name = ((Element)super_set_list.item(i)).getAttribute(GSXML.NAME_ATT);
520 if (super_name != null && !super_name.equals("")) {
521 list_sets_elem.appendChild(OAIXML.createSet(doc, super_name, super_name, null));
522 }
523 }
524 return true;
525 }
526
527 /** create the metadata element used when processing ListRecords/GetRecord requests
528 */
529 protected Element createMetadataElement(Document doc, String prefix, DBInfo info) {
530 // the <metadata> element
531 Element metadata = doc.createElement(OAIXML.METADATA);
532 // the <oai:dc namespace...> element
533 Element prfx_str_elem = OAIXML.getMetadataPrefixElement(doc, prefix, OAIXML.oai_version);
534 metadata.appendChild(prfx_str_elem);
535
536 Element meta_format_element = GSXML.getNamedElement(this.meta_formats_definition, OAIXML.METADATA_FORMAT, GSXML.NAME_ATT, prefix);
537 NodeList elements = meta_format_element.getElementsByTagName(OAIXML.ELEMENT);
538 // for each element in the definition
539 for (int i=0; i<elements.getLength(); i++) {
540 Element e = (Element)elements.item(i);
541 Element map = (Element)GSXML.getChildByTagName(e, OAIXML.MAPPING);
542 if (map == null) {
543 // look up the element name
544 addMetadata(prfx_str_elem, e.getAttribute(GSXML.NAME_ATT), info);
545 } else {
546 // we go though the list of names in the mapping
547 addMetadata(prfx_str_elem, e.getAttribute(GSXML.NAME_ATT), map.getAttribute(OAIXML.SELECT), map.getAttribute(OAIXML.ELEMENTS), info);
548 }
549 }
550 // output any metadata that is not just a simple mapping
551 addCustomMetadata(prfx_str_elem, prefix, info);
552 return metadata;
553 }
554
555 /** a simple addMetadata where we look for meta_name metadata, and add as that name*/
556 protected void addMetadata(Element meta_list_elem, String meta_name, DBInfo info) {
557 Vector<String> values = info.getMultiInfo(meta_name);
558 if (values != null && values.size()!=0) {
559 for (int i=0; i<values.size(); i++) {
560 addMetadataElement(meta_list_elem, meta_name, values.get(i));
561 }
562 }
563 }
564
565 /** more complicated addMetadata - can add multiple items. */
566 protected void addMetadata(Element meta_list_elem, String new_meta_name, String select_type, String name_list, DBInfo info) {
567 String[] names = name_list.split(",");
568 for (int i=0; i<names.length; i++) {
569 Vector<String> values = info.getMultiInfo(names[i]);
570 if (values == null || values.size()==0) {
571 continue;
572 }
573 for (int j=0; j<values.size(); j++) {
574 addMetadataElement(meta_list_elem, new_meta_name, values.get(j));
575 if (select_type.equals(OAIXML.SELECT_SINGLE_VALUE)) {
576 return; // only want to add one value
577 }
578 }
579 if (select_type.equals(OAIXML.SELECT_FIRST_VALID_META)) {
580 return; // we have added all values of this meta elem
581 }
582 // otherwise, we will keep going through the list and add them all.
583 }
584 }
585
586 // specific metadata formats might need to do some custom metadata that is not
587 //just a standard mapping. eg oai_dc outputting an identifier that is a link
588 protected void addCustomMetadata(Element meta_list_elem, String prefix, DBInfo info) {
589
590 if (prefix.equals(OAIXML.META_FORMAT_DC)) {
591 // we want to add in another dc:identifier element with a link to the resource if possible
592 // try gs.OAIResourceURL first, then srclinkFile, then GS version of documnet
593 String gsURL = info.getInfo(OAIXML.GS_OAI_RESOURCE_URL);
594 if (gsURL.equals("")) {
595 String base_url = OAIXML.getBaseURL(); // e.g. e.g. http://host:port/greenstone3/oaiserver
596 // try srclinkFile
597 gsURL = info.getInfo("srclinkFile");
598 if (!gsURL.equals("")) {
599 // make up the link to the file
600 gsURL = base_url.replace("oaiserver", "") + "sites/" + this.site_name
601 + "/collect/" + this.coll_name + "/index/assoc/"
602 + info.getInfo("assocfilepath") + "/" + gsURL;
603 } else {
604 // no srclink file, lets provide a link to the greenstone doc
605 gsURL = base_url.replace("oaiserver", "library") + "/collection/" + this.coll_name + "/document/" + info.getInfo("Identifier");
606 }
607 }
608 // now we have the url link, add as metadata
609 addMetadataElement(meta_list_elem, "dc:identifier", gsURL);
610 }
611 }
612
613 /** create the actual metadata element for the list */
614 protected void addMetadataElement(Element meta_list_elem, String name, String value) {
615
616 Element meta = GSXML.createTextElement(meta_list_elem.getOwnerDocument(), name, value);
617 meta_list_elem.appendChild(meta);
618 }
619
620
621 /** create a header element used when processing requests like ListRecords/GetRecord/ListIdentifiers
622 */
623 protected Element createHeaderElement(Document doc, String oid, String oailastmodified) {
624 Element header = doc.createElement(OAIXML.HEADER);
625 Element identifier = doc.createElement(OAIXML.IDENTIFIER);
626 GSXML.setNodeText(identifier, coll_name + ":" + oid);
627 header.appendChild(identifier);
628 Element set_spec = doc.createElement(OAIXML.SET_SPEC);
629 GSXML.setNodeText(set_spec, coll_name);
630 header.appendChild(set_spec);
631 Element datestamp = doc.createElement(OAIXML.DATESTAMP);
632 GSXML.setNodeText(datestamp, oailastmodified);
633 header.appendChild(datestamp);
634 return header;
635 }
636
637 /** return the metadata information */
638 protected Element processListMetadataFormats(Element req) {
639 // the request sent here must contain an OID. see doListMetadataFormats() in OAIReceptionist
640 Element param = GSXML.getNamedElement(req, GSXML.PARAM_ELEM, GSXML.NAME_ATT, OAIXML.OID);
641 if (param == null) {
642 logger.error("An element containing the OID attribute not is present.");
643 return OAIXML.createErrorResponse(OAIXML.ID_DOES_NOT_EXIST, "");
644 }
645 String oid = param.getAttribute(GSXML.VALUE_ATT);
646 if (oid == null || oid.equals("")) {
647 logger.error("No OID is present in the request.");
648 return OAIXML.createErrorResponse(OAIXML.ID_DOES_NOT_EXIST, "");
649 }
650 ArrayList<String> oid_list = getChildrenIds(OAIXML.BROWSELIST);
651 if (oid_list == null || oid_list.contains(oid) == false) {
652 logger.error("OID: " + oid + " is not present in the database.");
653 Element e= OAIXML.createErrorResponse(OAIXML.ID_DOES_NOT_EXIST, "");
654// logger.error((new XMLConverter()).getPrettyString (e));
655 return e;
656 }
657
658 DBInfo info = null;
659 info = this.coll_db.getInfo(oid);
660 if (info == null) { //just double check
661 return OAIXML.createErrorResponse(OAIXML.OAI_SERVICE_UNAVAILABLE, "");
662 }
663
664 Document doc = XMLConverter.newDOM();
665 Element list_metadata_formats_response = doc.createElement(GSXML.RESPONSE_ELEM);
666
667 Element list_metadata_formats = doc.createElement(OAIXML.LIST_METADATA_FORMATS);
668 list_metadata_formats_response.appendChild(list_metadata_formats);
669 boolean has_meta_format = false;
670
671 // for each format in format_elements_map
672 Iterator<String> it = format_elements_map.keySet().iterator();
673 while (it.hasNext()) {
674 String format = it.next();
675 HashSet<String> set_of_elems = format_elements_map.get(format);
676 if (documentContainsMetadata(info, set_of_elems)) {
677 // add this format into the response
678 has_meta_format = true;
679 list_metadata_formats.appendChild(doc.importNode(format_response_map.get(format), true));
680 }
681 }
682
683 if (has_meta_format == false) {
684 logger.error("Specified metadata names are not contained in the database.");
685 return OAIXML.createErrorResponse(OAIXML.NO_METADATA_FORMATS, "");
686 } else {
687 return list_metadata_formats_response;
688 }
689 }
690
691 protected boolean documentContainsMetadata(DBInfo info, HashSet<String> set_of_elems) {
692 if (set_of_elems.size() == 0) {
693 return false;
694 }
695 Iterator<String> i = set_of_elems.iterator();
696 while (i.hasNext()) {
697 if (!info.getInfo(i.next()).equals("")) {
698 return true;
699 }
700 }
701 return false;
702 }
703
704 /** returns a list of the child ids in order, null if no children */
705 protected ArrayList<String> getChildrenIds(String node_id) {
706 DBInfo info = this.coll_db.getInfo(node_id);
707 if (info == null) {
708 return null;
709 }
710
711 String contains = info.getInfo("contains");
712 if (contains.equals("")) {
713 return null;
714 }
715 ArrayList<String> children = new ArrayList<String>();
716 StringTokenizer st = new StringTokenizer(contains, ";");
717 while (st.hasMoreTokens()) {
718 String child_id = st.nextToken().replaceAll("\"", node_id);
719 children.add(child_id);
720 }
721 return children;
722 }
723 /**method to check whether any of the 'metadata_names' is contained in the 'info'.
724 * The name may be in the form: <name>,<mapped name>, in which the mapped name is
725 * optional. The mapped name is looked up in the DBInfo; if not present, use the first
726 * name which is mendatory.
727 */
728 protected boolean containsMetadata(DBInfo info, String[] metadata_names) {
729 if (metadata_names == null) return false;
730 logger.info("checking metadata names in db.");
731 for(int i=0; i<metadata_names.length; i++) {
732 int index = metadata_names[i].indexOf(",");
733 String meta_name = (index == -1) ? metadata_names[i] :
734 metadata_names[i].substring(index + 1);
735
736 if(info.getInfo(meta_name).equals("") == false) {
737 return true;
738 }
739 }
740 return false;
741 }
742
743 protected long getDateStampMillis(DBInfo info) {
744 // gs.OAIDateStamp is in YYYY-MM-DD
745 String time_stamp = info.getInfo(OAIXML.GS_OAI_DATE_STAMP);
746 long millis = -1;
747 if (!time_stamp.equals("")) {
748 millis = OAIXML.getTime(time_stamp);
749 }
750 if (millis == -1) {
751 // oailastmodified is in seconds
752 time_stamp = info.getInfo(OAIXML.OAI_LASTMODIFIED);
753 if (!time_stamp.equals("")) {
754 millis = Long.parseLong(time_stamp)*1000;
755 }
756 }
757 return millis;
758
759
760 }
761}
762
763
Note: See TracBrowser for help on using the repository browser.