/* * OAIXML.java * Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.greenstone.gsdl3.util; import org.greenstone.util.GlobalProperties; import org.w3c.dom.*; import java.io.*; import java.net.*; import java.util.*; import java.text.DateFormat; import java.text.SimpleDateFormat; import org.apache.xerces.parsers.*; import org.apache.xml.serialize.*; // SAX import org.xml.sax.XMLReader; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.InputSource; // JAXP import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; // import file Logger.java import org.apache.log4j.*; /** these constants are used for the OAI service */ public class OAIXML { static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GSXML.class.getName()); // the leading keyword of oai protocol public static final String VERB = "verb"; // six valid oai verbs public static final String GET_RECORD = "GetRecord"; public static final String LIST_RECORDS = "ListRecords"; public static final String LIST_IDENTIFIERS = "ListIdentifiers"; public static final String LIST_SETS = "ListSets"; public static final String LIST_METADATA_FORMATS = "ListMetadataFormats"; public static final String IDENTIFY = "Identify"; // other valid oai parameters public static final String OAI_METADATAFORMAT = "OAIMetadataFormat"; public static final String METADATA_NAMESPACE = "metadataNamespace"; public static final String OAI_DC = "oai_dc"; public static final String DC = "dc"; public static final String METADATA_PREFIX = "metadataPrefix"; public static final String FROM = "from"; public static final String UNTIL = "until"; public static final String SET = "set"; public static final String RESUMPTION_TOKEN = "resumptionToken"; public static final String RESUMPTION_TOKEN_EXPIRATION = "resumptionTokenExpiration"; public static final String IDENTIFIER = "identifier"; public static final String USE_STYLESHEET = "useOAIStylesheet"; public static final String STYLESHEET = "OAIStylesheet"; // words used to compose oai responses and read in OAIConfig.xml public static final String ADMIN_EMAIL = "adminEmail"; public static final String BAD_ARGUMENT = "badArgument"; public static final String BAD_RESUMPTION_TOKEN = "badResumptionToken"; public static final String BAD_VERB = "badVerb"; public static final String BASE_URL = "baseURL"; public static final String CANNOT_DISSEMINATE_FORMAT = "cannotDisseminateFormat"; public static final String CODE = "code"; public static final String COLLECTION = "collection"; public static final String COLLECTION_LIST = "collectionList"; public static final String COMPLETE_LIST_SIZE = "completeListSize"; public static final String COMPRESSION = "compression"; public static final String CURSOR = "cursor"; public static final String DATESTAMP = "datestamp"; public static final String DELETED_RECORD = "deletedRecord"; public static final String DESCRIPTION = "description"; public static final String EARLIEST_DATESTAMP = "earliestDatestamp"; public static final String ERROR = "error"; public static final String EXPIRATION_DATE = "expirationDate"; public static final String GRANULARITY = "granularity"; public static final String GS3OAI = "GS3OAI"; public static final String GS_OAI_RESOURCE_URL = "gs.OAIResourceURL"; public static final String HAS_OAI = "hasOAI"; public static final String HEADER = "header"; public static final String ILLEGAL_OAI_VERB = "Illegal OAI verb"; public static final String INDEX_STEM = "indexStem"; public static final String INFO_METADATA = "Metadata"; // this has capital M public static final String LASTMODIFIED = "lastmodified"; public static final String MAPPING = "mapping"; public static final String MAPPING_LIST = "mappingList"; public static final String MESSAGE = "message"; public static final String METADATA = "metadata"; public static final String METADATA_FORMAT = "metadataFormat"; public static final String NAME = "name"; public static final String NO_RECORDS_MATCH = "noRecordsMatch"; public static final String OAI = "OAI"; public static final String OAI_DASH_PMH = "OAI-PMH"; public static final String OAI_LASTMODIFIED = "oailastmodified"; public static final String OAIPMH = "OAIPMH"; public static final String OAI_RESUMPTION_TOKENS = "OAIResumptionTokens"; public static final String OAI_INFO = "oaiInfo"; public static final String OAI_SERVICE = "oaiService"; public static final String OAI_SET_LIST = "oaiSetList"; public static final String OAI_SERVICE_UNAVAILABLE = "OAI service unavailable"; public static final String OID = "OID"; public static final String PARAM = "param"; public static final String PARAM_LIST = "paramList"; public static final String PROTOCOL_VERSION = "protocolVersion"; public static final String RECORD = "record"; public static final String REQUEST = "request"; public static final String REPOSITORY_NAME = "repositoryName"; public static final String REPOSITORY_ID = "repositoryId"; public static final String RESPONSE = "response"; public static final String RESPONSE_DATE = "responseDate"; public static final String RESUME_AFTER = "resumeAfter"; public static final String SCHEMA = "schema"; public static final String SERVICE = "service"; public static final String SERVICE_UNAVAILABLE = "service unavailable"; public static final String SET_SPEC = "setSpec"; public static final String SET_NAME = "setName"; public static final String SET_DESCRIPTION = "setDescription"; public static final String SITE = "site"; public static final String TO = "to"; public static final String TYPE = "type"; public static final String VALUE = "value"; //Two error and exception conditions for the verb 'ListMetadataFormats' public static final String ID_DOES_NOT_EXIST = "idDoesNotExist"; public static final String NO_METADATA_FORMATS = "noMetadataFormats"; // The node id in the collection database, which contains all the OIDs in the database public static final String BROWSELIST = "browselist"; //system-dependent file separator, maybe '/' or '\' public static final String FILE_SEPARATOR = File.separator; public static final String OAI_VERSION1 = "1.0"; public static final String OAI_VERSION2 = "2.0"; /*************************above are final values****************************/ public static Element resumption_token_elem = null; //used when saving the token file public static File resumption_token_file = null; //public static ArrayList token_list = new ArrayList(); //initialized in getOAIConfigXML() public static Element oai_config_elem = null; //stores the date format "yyyy-MM-ddTHH:mm:ssZ" public static String granularity = ""; // http://www.openarchives.org/OAI/openarchivesprotocol.html#DatestampsRequests // specifies that all repositories must support YYYY-MM-DD (yyyy-MM-dd in Java) // this would be in addition to the other (optional) granularity of above that // a repository may additionally choose to support. public static final String default_granularity = "yyyy-MM-dd"; //this value is overriden in getOAIConfigXML() public static long token_expiration = 7200; /** which version of oai that this oaiserver supports; default is 2.0 * initialized in getOAIConfigXML() */ public static String oai_version = "2.0"; public static String baseURL = ""; /**response owner document */ public static Document response_doc = new XMLConverter().newDOM(); public static String[] special_char = {"/", "?", "#", "=", "&", ":", ";", " ", "%", "+"}; public static String[] escape_sequence = {"%2F", "%3F", "%23", "%3D", "%26", "%3A", "%3B", "%20", "%25", "%2B"}; // /** key=special character; value=escaped sequence */ // public static HashMap encode_map = new HashMap(); // /** key=escaped sequence; value=special character */ // public static HashMap decode_map = new HashMap(); public static void init() { resumption_token_elem = getOAIResumptionTokenXML(); } public static String getOAIVersion() { return oai_version; } public static String getBaseURL() { return baseURL; } public static Element createElement(String tag_name) { return response_doc.createElement(tag_name); } /**Compose a response element used when OAIPMH service sending responses thru * ServiceCluster and MessageRouter, as they automatically wrap a message element * on this response element */ public static Element getResponse(Element core_msg) { Element res = createElement(RESPONSE); res.appendChild(response_doc.importNode(core_msg, true)); return res; } /** Read in OAIResumptionToken.xml (residing web/WEB-INF/classes/) */ public static Element getOAIResumptionTokenXML() { // The system environment variable $GSDL3HOME(ends ../web) does not contain the file separator resumption_token_file = new File(GlobalProperties.getGSDL3Home() + FILE_SEPARATOR + "WEB-INF" + FILE_SEPARATOR + "classes" +FILE_SEPARATOR + "OAIResumptionToken.xml"); if (resumption_token_file.exists()) { Document token_doc = parseXMLFile(resumption_token_file); if (token_doc != null) { resumption_token_elem = token_doc.getDocumentElement(); } else { logger.error("Fail to parse resumption token file OAIReceptionToken.xml."); return null; } //remove all expired tokens clearExpiredTokens(); return resumption_token_elem; } //if resumption_token_file does not exist logger.info("resumption token file: "+ resumption_token_file.getPath()+" not found! create an empty one."); resumption_token_elem = createElement(OAI_RESUMPTION_TOKENS); saveOAIResumptionTokenXML(resumption_token_elem); return resumption_token_elem; } public static void saveOAIResumptionTokenXML(Element token_elem) { if(writeXMLFile(resumption_token_file, token_elem.getOwnerDocument()) == false) { logger.error("Fail to save the resumption token file"); } } public static void clearExpiredTokens() { boolean token_deleted = false; NodeList tokens = GSXML.getChildrenByTagName(resumption_token_elem, RESUMPTION_TOKEN); for (int i=0; i=0; i--) { int index = pairs[i].indexOf("="); if (index != -1) { String[] strs = pairs[i].split("="); if(strs != null && strs.length == 2) { request_elem.setAttribute(strs[0], oaiDecode(strs[1])); } } }//end of for() GSXML.setNodeText(request_elem, baseURL); Node resp_date = GSXML.getChildByTagName(response, RESPONSE_DATE); if (resp_date != null) { GSXML.setNodeText((Element)resp_date, getCurrentUTCTime()); } return response; } /** @param error_code the value of the code attribute * @param error_text the node text of the error element * @return an oai error element * Used by receptionist */ public static Element createErrorElement(String error_code, String error_text) { Element error = createElement(ERROR); error.setAttribute(CODE, error_code); GSXML.setNodeText(error, error_text); return error; } /** convert the escaped sequences (eg, '%3A') of those special characters back to their * original form (eg, ':'). */ public static String oaiDecode(String escaped_str) { logger.info("oaiDecode() " +escaped_str); for (int i=0; i getChildrenMapByTagName(Node n, String tag_name) { HashMap map= new HashMap(); Node child = n.getFirstChild(); while (child!=null) { String name = child.getNodeName(); if(name.equals(tag_name)) { map.put(name, child); } child = child.getNextSibling(); } return map; } /** Duplicates an element */ public static Element duplicateElement (Document owner, Element element, boolean with_attributes) { return duplicateElementNS (owner, element, null, with_attributes); } /** Duplicates an element */ public static Element duplicateElementNS (Document owner, Element element, String namespace_uri, boolean with_attributes) { Element duplicate; if (namespace_uri == null) { duplicate = owner.createElement (element.getTagName ()); } else { duplicate = owner.createElementNS (namespace_uri, element.getTagName ()); } // Copy element attributes if (with_attributes) { NamedNodeMap attributes = element.getAttributes (); for (int i = 0; i < attributes.getLength (); i++) { Node attribute = attributes.item (i); duplicate.setAttribute (attribute.getNodeName (), attribute.getNodeValue ()); } } // Copy element children NodeList children = element.getChildNodes (); for (int i = 0; i < children.getLength (); i++) { Node child = children.item (i); duplicate.appendChild (owner.importNode (child, true)); } return duplicate; } public static void copyElement(Element to, Element from, String elem_name) { Document to_doc = to.getOwnerDocument(); Node child = from.getFirstChild(); while (child != null) { if (child.getNodeName().equals(elem_name)) { to.appendChild(to_doc.importNode(child, true)); return; } child = child.getNextSibling(); } } public static HashMap getParamMap(NodeList params) { HashMap map = new HashMap(); for(int i=0; i\n oai\n" + repository_id + "\n:\noai:"+repository_id+":"+sample_collection+":"+sample_doc_id+"\n"; Document xml_doc = new XMLConverter().getDOM(xml); return (Element)response_doc.importNode(xml_doc.getDocumentElement(), true); } public static Element createGSDLElement() { String xml = ""; Document xml_doc = new XMLConverter().getDOM(xml); return (Element)response_doc.importNode(xml_doc.getDocumentElement(), true); } }