All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ORG.oclc.oai.server.catalog.FolderOAICatalog Maven / Gradle / Ivy

/**
 * Copyright 2006 OCLC Online Computer Library Center Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or
 * agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ORG.oclc.oai.server.catalog;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Vector;

import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.xpath.XPathAPI;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import ORG.oclc.oai.server.verb.BadResumptionTokenException;
import ORG.oclc.oai.server.verb.CannotDisseminateFormatException;
import ORG.oclc.oai.server.verb.IdDoesNotExistException;
import ORG.oclc.oai.server.verb.NoItemsMatchException;
import ORG.oclc.oai.server.verb.NoMetadataFormatsException;
import ORG.oclc.oai.server.verb.NoSetHierarchyException;
import ORG.oclc.oai.util.OAIUtil;


/**
 * NewFileSystemOAICatalog is an implementation of AbstractCatalog interface
 * with the data sitting in a directory on a filesystem.
 *
 * @author Jeff Young, OCLC Online Computer Library Center
 */

public class FolderOAICatalog extends AbstractCatalog {
    static final boolean debug=false;
    
    private SimpleDateFormat dateFormatter = new SimpleDateFormat();
    protected String           homeDir;
    private HashMap          datestampMap = new HashMap();
    private HashMap identifierMap = new HashMap();
    private HashMap setMap = new HashMap();
    private HashMap          resumptionResults=new HashMap();
    private int              maxListSize;
    private ArrayList sets = null;
    private ServletContext context = null;
    
    public FolderOAICatalog(Properties properties, ServletContext context)
    throws IOException {
        this.context = context;
        String          temp;
        
        dateFormatter.applyPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
        temp=properties.getProperty("NewFileSystemOAICatalog.maxListSize");
        if (temp==null)
            throw new IllegalArgumentException("NewFileSystemOAICatalog."+
            "maxListSize is missing from the properties file");
        maxListSize = Integer.parseInt(temp);
        if(debug)
            System.out.println("in NewFileSystemOAICatalog(): maxListSize="+
                    maxListSize);
        
        homeDir=properties.getProperty("NewFileSystemOAICatalog.homeDir");
        if (homeDir!=null) {
            if(debug)
                System.out.println("in NewFileSystemOAICatalog(): homeDir="+
                        homeDir);
            
            
            File homeFile = new File(homeDir);
            int homeDirLen = homeFile.getPath().length()+1;
            loadFileMap(homeDirLen, homeFile);
        } else {
            /* Try looking in a known location */
            Set resourcePaths = context.getResourcePaths("/WEB-INF/DATA/");
            if (resourcePaths == null || resourcePaths.size() == 0) {
                throw new IllegalArgumentException("NewFileSystemOAICatalog."+
                "homeDir is missing from the properties file");
            }
            String earliestDatestamp = loadFileMap(context, resourcePaths);
            properties.setProperty("Identify.earliestDatestamp", earliestDatestamp);
        }
//      Iterator iterator = datestampMap.entrySet().iterator();
//      while (iterator.hasNext()) {
//      Map.Entry entry = (Map.Entry)iterator.next();
//      System.out.println(entry.getKey() + ":" + entry.getValue());
//      }
        sets = getSets(properties);
    }
    
    private static ArrayList getSets(Properties properties) {
        InputStream is = Thread.currentThread().getContextClassLoader()
        .getResourceAsStream("sets.properties");
        Properties setProps = new Properties();
        try {
            setProps.load(is);
        } catch (Exception e) {
            e.printStackTrace();
        }
        TreeMap treeMap = new TreeMap();
        Enumeration propNames = setProps.propertyNames();
        while (propNames.hasMoreElements()) {
            String propertyName = (String)propNames.nextElement();
            String s = new StringBuffer("")
            .append(OAIUtil.xmlEncode(propertyName))
            .append("")
            .append(OAIUtil.xmlEncode((String) setProps.get(propertyName)))
            .append("")
            .toString();
            treeMap.put(propertyName, s);
        }
        return new ArrayList(treeMap.values());
    }
    
    private String loadFileMap(ServletContext context, Set resourcePaths)
    throws IOException {
        String earliestDatestamp = "9999-12-31";
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Iterator iter = resourcePaths.iterator();
            while (iter.hasNext()) {
                String resourcePath = (String)iter.next();
                if (!resourcePath.endsWith("/")) {
                    InputStream is = context.getResourceAsStream(resourcePath);
                    InputSource data = new InputSource(is);
                    Node doc = builder.parse(data);
                    is.close();
                    
//                    Node datestampNode = XPathAPI.selectSingleNode(doc, "/record/header/datestamp");
                    String datestamp = XPathAPI.eval(doc, "/record/header/datestamp").str();
                    if (datestamp.compareTo(earliestDatestamp) < 0) {
                        earliestDatestamp = datestamp;
                    }
                    String identifier = XPathAPI.eval(doc, "/record/header/identifier").str();
                    datestampMap.put(resourcePath,datestamp);
                    identifierMap.put(identifier, resourcePath);
                    NodeList setNodes = XPathAPI.selectNodeList(doc, "/record/header/setSpec");
                    for (int j=0; j= 0
                    && fileDate.compareTo(until) <= 0
                    && (setIdentifiers == null || setIdentifiers.contains(path))) {
                Document nativeRecord = getNativeRecord((String)entryDateMap.getKey());
                String[] header = getRecordFactory().createHeader(nativeRecord);
                headers.add(header[0]);
                identifiers.add(header[1]);
                count++;
            }
        }
        
        if (count == 0)
            throw new NoItemsMatchException();
        
        /* decide if you're done */
        if (iterator.hasNext()) {
            String resumptionId = getRSName();
            resumptionResults.put(resumptionId, iterator);
            
            /*****************************************************************
             * Construct the resumptionToken String however you see fit.
             *****************************************************************/
            StringBuffer resumptionTokenSb = new StringBuffer();
            resumptionTokenSb.append(resumptionId);
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(Integer.toString(count));
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(Integer.toString(numRows));
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(metadataPrefix);
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(set);
            
            /*****************************************************************
             * Use the following line if you wish to include the optional
             * resumptionToken attributes in the response. Otherwise, use the
             * line after it that I've commented out.
             *****************************************************************/
            listIdentifiersMap.put("resumptionMap",
                    getResumptionMap(resumptionTokenSb.toString(),
                            numRows,
                            0));
//          listIdentifiersMap.put("resumptionMap",
//          getResumptionMap(resumptionTokenSb.toString()));
        }
        listIdentifiersMap.put("headers", headers.iterator());
        listIdentifiersMap.put("identifiers", identifiers.iterator());
        return listIdentifiersMap;
    }
    
    /**
     * Retrieve the next set of Identifiers associated with the resumptionToken
     *
     * @param resumptionToken implementation-dependent format taken from the
     * previous listIdentifiers() Map result.
     * @return a Map object containing an optional "resumptionToken" key/value
     * pair and an "identifiers" Map object. The "identifiers" Map contains OAI
     * identifier keys with corresponding values of "true" or null depending on
     * whether the identifier is deleted or not.
     * @exception OAIBadRequestException signals an http status code 400
     *            problem
     */
    public Map listIdentifiers(String resumptionToken)
    throws BadResumptionTokenException {
        purge(); // clean out old resumptionTokens
        Map listIdentifiersMap = new HashMap();
        ArrayList headers = new ArrayList();
        ArrayList identifiers = new ArrayList();
        
        /**********************************************************************
         * parse your resumptionToken and look it up in the resumptionResults,
         * if necessary
         **********************************************************************/
        StringTokenizer tokenizer = new StringTokenizer(resumptionToken, ":");
        String resumptionId;
        int oldCount;
        String metadataPrefix;
        int numRows;
        String set;
        try {
            resumptionId = tokenizer.nextToken();
            oldCount = Integer.parseInt(tokenizer.nextToken());
            numRows = Integer.parseInt(tokenizer.nextToken());
            metadataPrefix = tokenizer.nextToken();
            set = tokenizer.nextToken();
        } catch (NoSuchElementException e) {
            throw new BadResumptionTokenException();
        }
        
        /* Get some more records from your database */
        Iterator iterator = (Iterator)resumptionResults.remove(resumptionId);
        if (iterator == null) {
            System.out.println("NewFileSystemOAICatalog.listIdentifiers: reuse of old resumptionToken?");
            iterator = datestampMap.entrySet().iterator();
            for (int i = 0; i String
     * @exception CannotDisseminateFormatException the record is not available
     * for the specified metadataPrefix.
     */
    private String constructRecord(Document nativeItem, String metadataPrefix)
    throws CannotDisseminateFormatException {
        String schemaURL = null;
        Iterator setSpecs = getSetSpecs(nativeItem);
        Iterator abouts = getAbouts(nativeItem);
        
        if (metadataPrefix != null) {
            if ((schemaURL = getCrosswalks().getSchemaURL(metadataPrefix)) == null)
                throw new CannotDisseminateFormatException(metadataPrefix);
        }
        return getRecordFactory().create(nativeItem, schemaURL, metadataPrefix, setSpecs, abouts);
    }
    
    /**
     * get an Iterator containing the setSpecs for the nativeItem
     *
     * @param rs ResultSet containing the nativeItem
     * @return an Iterator containing the list of setSpec values for this nativeItem
     */
    private Iterator getSetSpecs(Document nativeItem) {
        return null;
    }
    
    /**
     * get an Iterator containing the abouts for the nativeItem
     *
     * @param rs ResultSet containing the nativeItem
     * @return an Iterator containing the list of about values for this nativeItem
     */
    private Iterator getAbouts(Document nativeItem) {
        return null;
    }
    
    /**
     * Retrieve a list of records that satisfy the specified criteria
     *
     * @param from beginning date in the form of YYYY-MM-DD or null if earliest
     * date is desired
     * @param until ending date in the form of YYYY-MM-DD or null if latest
     * date is desired
     * @param set set name or null if no set is desired
     * @param metadataPrefix the OAI metadataPrefix
     * @return a Map object containing an optional "resumptionToken" key/value
     * pair and a "records" Iterator object. The "records" Iterator contains a
     * set of Records objects.
     * @exception OAIBadRequestException signals an http status code 400
     *            problem
     * @exception OAIInternalServerError signals an http status code 500
     *            problem
     */
    public Map listRecords(String from, String until, String set,
            String metadataPrefix)
    throws CannotDisseminateFormatException,
    NoItemsMatchException {
        purge(); // clean out old resumptionTokens
        Map listRecordsMap = new HashMap();
        ArrayList records = new ArrayList();
        Iterator iterator = datestampMap.entrySet().iterator();
        int numRows = datestampMap.entrySet().size();
        int count = 0;
        ArrayList setIdentifiers = (ArrayList)setMap.get(set);
        while (count < maxListSize && iterator.hasNext()) {
            Map.Entry entryDateMap = (Map.Entry)iterator.next();
            String fileDate = (String)entryDateMap.getValue();
            String path = (String)entryDateMap.getKey();
//            String extension = path.substring(path.lastIndexOf(".")+1);
            if (fileDate.compareTo(from) >= 0
                    && fileDate.compareTo(until) <= 0
//                    && extension.equals(metadataPrefix)
                    && (setIdentifiers == null || setIdentifiers.contains(path))) {
//                try {
                    Document nativeItem = getNativeRecord((String)entryDateMap.getKey());
                    String record = constructRecord(nativeItem, metadataPrefix);
                    records.add(record);
                    count++;
//                } catch (IOException e) {
//                    e.printStackTrace();
//                    throw new OAIInternalServerError(e.getMessage());
//                }
            }
        }
        
        if (count == 0)
            throw new NoItemsMatchException();
        
        /* decide if you're done */
        if (iterator.hasNext()) {
            String resumptionId = getRSName();
            resumptionResults.put(resumptionId, iterator);
            
            /*****************************************************************
             * Construct the resumptionToken String however you see fit.
             *****************************************************************/
            StringBuffer resumptionTokenSb = new StringBuffer();
            resumptionTokenSb.append(resumptionId);
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(Integer.toString(count));
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(Integer.toString(numRows));
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(metadataPrefix);
            resumptionTokenSb.append(":");
            resumptionTokenSb.append(set);
            
            /*****************************************************************
             * Use the following line if you wish to include the optional
             * resumptionToken attributes in the response. Otherwise, use the
             * line after it that I've commented out.
             *****************************************************************/
            listRecordsMap.put("resumptionMap",
                    getResumptionMap(resumptionTokenSb.toString(),
                            numRows,
                            0));
//          listRecordsMap.put("resumptionMap",
//          getResumptionMap(resumptionTokenSb.toString()));
        }
        listRecordsMap.put("records", records.iterator());
        return listRecordsMap;
    }
    
    
    /**
     * Retrieve the next set of records associated with the resumptionToken
     *
     * @param resumptionToken implementation-dependent format taken from the
     * previous listRecords() Map result.
     * @return a Map object containing an optional "resumptionToken" key/value
     * pair and a "records" Iterator object. The "records" Iterator contains a
     * set of Records objects.
     * @exception OAIBadRequestException signals an http status code 400
     *            problem
     */
    public Map listRecords(String resumptionToken)
    throws BadResumptionTokenException {
        purge(); // clean out old resumptionTokens
        Map listRecordsMap = new HashMap();
        ArrayList records = new ArrayList();
        
        /**********************************************************************
         * parse your resumptionToken and look it up in the resumptionResults,
         * if necessary
         **********************************************************************/
        StringTokenizer tokenizer = new StringTokenizer(resumptionToken, ":");
        String resumptionId;
        int oldCount;
        String metadataPrefix;
        int numRows;
        String set;
        try {
            resumptionId = tokenizer.nextToken();
            oldCount = Integer.parseInt(tokenizer.nextToken());
            numRows = Integer.parseInt(tokenizer.nextToken());
            metadataPrefix = tokenizer.nextToken();
            set = tokenizer.nextToken();
        } catch (NoSuchElementException e) {
            throw new BadResumptionTokenException();
        }
        
        /* Get some more records from your database */
        Iterator iterator = (Iterator)resumptionResults.remove(resumptionId);
        if (iterator == null) {
            System.out.println("NewFileSystemOAICatalog.listRecords: reuse of old resumptionToken?");
            iterator = datestampMap.entrySet().iterator();
            for (int i = 0; i




© 2015 - 2025 Weber Informatics LLC | Privacy Policy