Package ORG.oclc.os.SRW

Source Code of ORG.oclc.os.SRW.SRWDatabase

/*
   Copyright 2008 OCLC Online Computer Library Center, Inc.

    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.
*/
/*
* SRWDatabase.java
*
* Created on August 4, 2003, 1:49 PM
*/

package ORG.oclc.os.SRW;

import de.fuberlin.wiwiss.pubby.negotiation.ContentTypeNegotiator;
import de.fuberlin.wiwiss.pubby.negotiation.ContentTypeNegotiator.VariantSpec;
import gov.loc.www.zing.srw.RecordType;
import gov.loc.www.zing.srw.RecordsType;
import gov.loc.www.zing.srw.StringOrXmlFragment;
import gov.loc.www.zing.srw.TermsType;
import gov.loc.www.zing.srw.diagnostic.DiagnosticType;
import gov.loc.www.zing.srw.DiagnosticsType;
import gov.loc.www.zing.srw.ExplainResponseType;
import gov.loc.www.zing.srw.ExtraDataType;
import gov.loc.www.zing.srw.ScanRequestType;
import gov.loc.www.zing.srw.ScanResponseType;
import gov.loc.www.zing.srw.SearchRetrieveRequestType;
import gov.loc.www.zing.srw.SearchRetrieveResponseType;
import gov.loc.www.zing.srw.srw_bindings.SRWSoapBindingImpl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.ServletException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import org.apache.axis.MessageContext;
import org.apache.axis.message.MessageElement;
import org.apache.axis.message.Text;
import org.apache.axis.types.NonNegativeInteger;
import org.apache.axis.types.PositiveInteger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.z3950.zing.cql.CQLBooleanNode;
import org.z3950.zing.cql.CQLNode;
import org.z3950.zing.cql.CQLParseException;
import org.z3950.zing.cql.CQLParser;
import org.z3950.zing.cql.CQLTermNode;

/**
*
* @author  levan
*/
public abstract class SRWDatabase {
    private static Log log=LogFactory.getLog(SRWDatabase.class);
    private static SimpleDateFormat ISO8601FORMAT=new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");

    public static final String DEFAULT_SCHEMA = "default";

    public abstract String      getExtraResponseData(QueryResult result,
                                  SearchRetrieveRequestType request);

    public abstract String      getIndexInfo();

    public abstract QueryResult getQueryResult(String query,
                                  SearchRetrieveRequestType request)
                                  throws InstantiationException;

    public abstract TermList    getTermList(CQLTermNode term, int position,
                                  int maxTerms, ScanRequestType request);

    public abstract void        init(String dbname, String srwHome,
                                  String dbHome, String dbPropertiesFileName,
                                  Properties dbProperties,
                                  HttpServletRequest request) throws Exception;

    public abstract boolean     supportsSort();


    public static Hashtable<String, String> badDbs=new Hashtable<String, String>();
    public static Hashtable<String, LinkedList<SRWDatabase>> dbs=new Hashtable<String, LinkedList<SRWDatabase>>();
    public static final Hashtable<String, QueryResult> oldResultSets=new Hashtable<String, QueryResult>();
    public static final Hashtable<String, Long> timers=new Hashtable<String, Long>();

    public static Properties srwProperties;
    public static String servletContext, srwHome;
    private static final Timer timer=new Timer();

    static {
        timer.schedule(new HouseKeeping(timers, oldResultSets), 60000L, 60000L);
    }
    private boolean returnResultSetId=true;
    private Matcher extractRecordIdFromUriPatternMatcher;
    private Random rand=new Random();
    public ContentTypeNegotiator conneg=null;
    public HashMap<String, String> contentLocations=new HashMap<String, String>();
    public HashMap<String, String> mediaTypes=new HashMap<String, String>();
    public HashMap<String, String> recordSchemas=new HashMap<String, String>();
    public HttpHeaderSetter httpHeaderSetter=null;
    public SearchRetrieveRequestType searchRequest;
    public SearchRetrieveResponseType response;
    public String  baseURL=null, databaseTitle, dbname, explainStyleSheet=null,
                   scanStyleSheet=null, searchStyleSheet=null;

    protected boolean     letDefaultsBeDefault=false;
    protected CQLParser   parser = new CQLParser(CQLParser.V1POINT1);
    protected DocumentBuilder docb = null;
    protected Hashtable<String, String> nameSpaces=new Hashtable<String, String>(), schemas=new Hashtable<String, String>();
    protected Hashtable   sortTools = new Hashtable();
    protected Hashtable<String, Transformer> transformers=new Hashtable<String, Transformer>();
    protected int         defaultNumRecs=10, defaultResultSetTTL,
                          maximumRecords=20, maxTerms = 9, position = 5;
    protected Properties  dbProperties;
    protected SRWDatabase db;
    protected String      dbHome, dbPropertiesFileName, defaultMimeType="text/xml",
                          defaultSchema, defaultStylesheet=null,
                          explainRecord=null, schemaInfo;
   
   
    public String add(byte[] record) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    private void addSupports(String s, StringBuilder sb) {
        StringTokenizer st=new StringTokenizer(s, " =");
        if(st.countTokens()<2)
            return;
        sb.append("        <supports type=\"").append(st.nextToken()).append("\">");
        sb.append(s.substring(s.indexOf("=")+1).trim()).append("</supports>\n");
    }
   
    public void close() {
        timer.cancel();
    }

    public void addRenderer(String schemaName, String schemaID, Properties props)
      throws InstantiationException {
    }

    public Transformer addTransformer(String schemaName, String schemaID,
      String transformerFileName, ArrayList parameterNames, ArrayList parameterValues)
      throws FileNotFoundException, TransformerConfigurationException {
        if(schemaID!=null) {
            schemas.put(schemaName, schemaID);
            schemas.put(schemaID, schemaID);
        }
        if(transformerFileName==null) {
            log.info(schemaName+" transformer not specified");
            log.info(".props filename is " + dbPropertiesFileName);
            return null;
        }
        if(transformerFileName.startsWith("Renderer=")) // old notation, ignore
            return null;
//        StringTokenizer st=new StringTokenizer(transformerFileName, " \t=");
//        String token=st.nextToken();
        Source             xslSource;
        TransformerFactory tFactory=
            TransformerFactory.newInstance();
        File f=Utilities.findFile(transformerFileName, dbHome, srwHome);
        xslSource=new StreamSource(Utilities.openInputStream(
            transformerFileName, dbHome, srwHome));
        if(xslSource==null) {
            log.error("Unable to make StreamSource for: "+
                transformerFileName);
            log.error(".props filename is " + dbPropertiesFileName);
            return null;
        }
        try {
            xslSource.setSystemId(f.toURI().toURL().toString());
        }
        catch(MalformedURLException e) {
            log.error("trying to set the xslSource SystemID", e);
        }

        Transformer t=tFactory.newTransformer(xslSource);
        // set any parameters to be passed to the transformer
        if(parameterNames!=null)
            for(int i=0; i<parameterNames.size(); i++)
                t.setParameter((String)parameterNames.get(i), parameterValues.get(i));

        transformers.put(schemaName, t);
        if(schemaID!=null)
            transformers.put(schemaID, t);
        log.debug("added transformer for schemaName "+schemaName+", and schemaID "+schemaID);
        return t;
    }

    public static synchronized void createDB(final String dbname, Properties properties)
      throws InstantiationException {
        createDB(dbname, properties, null, null);
    }

    public static synchronized void createDB(final String dbname, Properties properties, String context, HttpServletRequest request)
      throws InstantiationException {
        log.debug("Enter: initDB, dbname="+dbname);
        String dbn="db."+dbname;

        srwProperties=properties;
        srwHome=properties.getProperty("SRW.Home");
        servletContext=context;
        if(srwHome!=null && !srwHome.endsWith("/"))
            srwHome=srwHome+"/";
        log.debug("SRW.Home="+srwHome);
        Properties dbProperties=new Properties();
        String dbHome=properties.getProperty(dbn+".home"),
               dbPropertiesFileName=null;
        if(dbHome!=null) {
            if(!dbHome.endsWith("/"))
                dbHome=dbHome+"/";
            log.debug("dbHome="+dbHome);
        }

        String className=properties.getProperty(dbn+".class");
        log.debug("className="+className);
        if(className==null) {
            // let's see if there's a fallback database to use
            className=properties.getProperty("db.default.class");
            if(className==null)
                throw new InstantiationException("No "+
                    dbn+".class entry in properties file");
            dbn="db.default";
        }
        if(className.equals("ORG.oclc.os.SRW.SRWPearsDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.SRWPearsDatabase has been replaced with ORG.oclc.os.SRW.Pears.SRWPearsDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.Pears.SRWPearsDatabase";
            log.debug("new className="+className);
        }
        else if(className.equals("ORG.oclc.os.SRW.SRWRemoteDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.SRWRemoteDatabase has been replaced with ORG.oclc.os.SRW.ParallelSearching.SRWRemoteDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.ParallelSearching.SRWRemoteDatabase";
            log.debug("new className="+className);
        }
        else if(className.equals("ORG.oclc.os.SRW.Pears.SRWRemoteDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.Pears.SRWRemoteDatabase has been replaced with ORG.oclc.os.SRW.ParallelSearching.SRWRemoteDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.ParallelSearching.SRWRemoteDatabase";
            log.debug("new className="+className);
        }
        else if(className.equals("ORG.oclc.os.SRW.SRWMergeDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.SRWMergeDatabase has been replaced with ORG.oclc.os.SRW.ParallelSearching.SRWMergeDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.ParallelSearching.SRWMergeDatabase";
            log.debug("new className="+className);
        }
        else if(className.equals("ORG.oclc.os.SRW.Pears.SRWMergeDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.Pears.SRWMergeDatabase has been replaced with ORG.oclc.os.SRW.ParallelSearching.SRWMergeDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.ParallelSearching.SRWMergeDatabase";
            log.debug("new className="+className);
        }
        else if(className.equals("ORG.oclc.os.SRW.SRWDLuceneDatabase")) {
            log.info("** Warning ** the class ORG.oclc.os.SRW.SRWLuceneDatabase has been replaced with ORG.oclc.os.SRW.DSpaceLucene.SRWLuceneDatabase");
            log.info("              Please correct the server's properties file");
            className="ORG.oclc.os.SRW.DSpaceLucene.SRWLuceneDatabase";
            log.debug("new className="+className);
        }
        SRWDatabase db=null;
        try {
            log.debug("creating class "+className);
            Class  dbClass=Class.forName(className);
            log.debug("creating instance of class "+dbClass);
            db=(SRWDatabase)dbClass.newInstance();
            log.debug("class created");
        }
        catch(Exception e) {
            log.error("Unable to create Database class "+className+
                " for database "+dbname);
            log.error(e, e);
            throw new InstantiationException(e.getMessage());
        }

        dbPropertiesFileName=properties.getProperty(dbn+".configuration");
        if(db.hasaConfigurationFile() || dbPropertiesFileName!=null) {
            if(dbPropertiesFileName==null) {
                throw new InstantiationException("No "+dbn+
                    ".configuration entry in properties file");
            }

            try {
                log.debug("Reading database configuration file: "+
                    dbPropertiesFileName);
                InputStream is=Utilities.openInputStream(dbPropertiesFileName, dbHome, srwHome);
                dbProperties.load(is);
                is.close();
            }
            catch(java.io.FileNotFoundException e) {
                log.error("Unable to open database configuration file!");
                log.error(e);
            }
            catch(Exception e) {
                log.error("Unable to load database configuration file!");
                log.error(e, e);
            }
            makeUnqualifiedIndexes(dbProperties);
            try {
                db.init(dbname, srwHome, dbHome, dbPropertiesFileName, dbProperties, request);
            }
            catch(InstantiationException e) {
                throw e;
            }
            catch(Exception e) {
                log.error("Unable to initialize database "+dbname);
                log.error(e, e);
                throw new InstantiationException(e.getMessage());
            }
            if(request!=null) {
                StringBuilder urlStr=new StringBuilder("http://").append(request.getServerName());
                if(request.getServerPort()!=80)
                    urlStr.append(":").append(request.getServerPort());
                urlStr.append('/');
                String contextPath=request.getContextPath();
                if(contextPath!=null && contextPath.length()>1) {
                    urlStr.append(contextPath.substring(1));
                }
                int pathInfoIndex=Integer.parseInt(
                    properties.getProperty("pathInfoIndex", "1"));
                String path=request.getPathInfo();
                StringBuilder newPath=new StringBuilder();
                if(path!=null) {
                    StringTokenizer st=new StringTokenizer(path, "/");
                    for(int i=0; st.hasMoreTokens(); i++)
                        if(i==pathInfoIndex-1) {
                            newPath.append('/').append(dbname);
                            st.nextToken();
                        }
                        else
                            newPath.append('/').append(st.nextToken());
                }
                urlStr.append(request.getServletPath()).append(newPath.toString());
                db.baseURL=urlStr.toString();
            }
            String temp=dbProperties.getProperty("maximumRecords");
            if(temp==null)
                temp=dbProperties.getProperty("configInfo.maximumRecords");
            if(temp!=null) {
                try {
                    db.setMaximumRecords(Integer.parseInt(temp));
                }
                catch(NumberFormatException e) {
                    log.error("bad value for maximumRecords: \""+temp+"\"");
                    log.error("maximumRecords parameter ignored");
                }
            }
            temp=dbProperties.getProperty("numberOfRecords");
            if(temp==null)
                temp=dbProperties.getProperty("configInfo.numberOfRecords");
            if(temp!=null) {
                try {
                    db.setNumberOfRecords(Integer.parseInt(temp));
                }
                catch(NumberFormatException e) {
                    log.error("bad value for numberOfRecords: \""+temp+"\"");
                    log.error("numberOfRecords parameter ignored");
                }
            }
            temp=dbProperties.getProperty("defaultResultSetTTL");
            if(temp==null)
                temp=dbProperties.getProperty("configInfo.resultSetTTL");
            if(temp!=null) {
                try {
                    db.setDefaultResultSetTTL(Integer.parseInt(temp));
                }
                catch(NumberFormatException e) {
                    log.error("bad value for defaultResultSetTTL: \""+temp+"\"");
                    log.error("defaultResultSetTTL parameter ignored");
                }
            }
            else
                db.setDefaultResultSetTTL(300);

            temp=dbProperties.getProperty("letDefaultsBeDefault");
            if(temp!=null && temp.equals("true"))
                db.letDefaultsBeDefault=true;
        }
        else { // default settings
            try {
                db.init(dbname, srwHome, dbHome, "(no database configuration file specified)", dbProperties, request);
            }
            catch(Exception e) {
                log.error("Unable to create Database class "+className+
                    " for database "+dbname);
                log.error(e, e);
                throw new InstantiationException(e.getMessage());
            }
            db.setDefaultResultSetTTL(300);
            log.info("no configuration file needed or specified");
        }

        if(!(db instanceof SRWDatabasePool)) {
            LinkedList<SRWDatabase> queue=dbs.get(dbname);
            if(queue==null)
                queue=new LinkedList<SRWDatabase>();
            queue.add(db);
            if(log.isDebugEnabled())
                log.debug(dbname+" has "+queue.size()+" copies");
            dbs.put(dbname, queue);
        }
        log.debug("Exit: initDB");
        return;
    }


    public boolean delete(String recordID) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public ExplainResponseType diagnostic(final int code,
      final String details, final ExplainResponseType response) {
        boolean         addDiagnostics=false;
        DiagnosticsType diagnostics=response.getDiagnostics();
        if(diagnostics==null)
            addDiagnostics=true;
        diagnostics=newDiagnostic(code, details, diagnostics);
        if(addDiagnostics)
            response.setDiagnostics(diagnostics);
        return response;
    }


    public ScanResponseType diagnostic(final int code,
      final String details, final ScanResponseType response) {
        boolean         addDiagnostics=false;
        DiagnosticsType diagnostics=response.getDiagnostics();
        if(diagnostics==null)
            addDiagnostics=true;
        diagnostics=newDiagnostic(code, details, diagnostics);
        if(addDiagnostics)
            response.setDiagnostics(diagnostics);
        return response;
    }


    public SearchRetrieveResponseType diagnostic(final int code,
      final String details, final SearchRetrieveResponseType response) {
        boolean         addDiagnostics=false;
        DiagnosticsType diagnostics=response.getDiagnostics();
        if(diagnostics==null)
            addDiagnostics=true;
        diagnostics=newDiagnostic(code, details, diagnostics);
        if(addDiagnostics)
            response.setDiagnostics(diagnostics);
        return response;
    }


    public ScanResponseType doRequest(ScanRequestType request) throws ServletException {
        searchRequest=null;
        response=null;
        ScanResponseType scanResponse = new ScanResponseType();
        String version=request.getVersion();
        if(version!=null && !version.equals("1.1"))
            return diagnostic(SRWDiagnostic.UnsupportedVersion, version, scanResponse);

        CQLTermNode root = null;
        int max = maxTerms,  pos = position;
        long startTime = System.currentTimeMillis();
        PositiveInteger pi = request.getMaximumTerms();
        if(pi!=null) {
            max=pi.intValue();
            pos=max/2+1;
        }
        NonNegativeInteger nni = request.getResponsePosition();
        if(nni!=null)
            pos=nni.intValue();
        String scanTerm = request.getScanClause();
        try{
            if (scanTerm!=null)
                log.info("scanTerm:\n" + Utilities.byteArrayToString(scanTerm.getBytes("UTF-8")));
        } catch (Exception e){}
        log.info("maxTerms="+max+", position="+pos);
        try {
            root = Utilities.getFirstTerm(parser.parse(scanTerm));
        } catch (CQLParseException e) {
            log.error(e);
            return diagnostic(SRWDiagnostic.QuerySyntaxError, e.getMessage(),
                scanResponse);
        } catch (IOException e) {
            log.error(e);
            return diagnostic(SRWDiagnostic.QuerySyntaxError, e.getMessage(),
                scanResponse);
        }
        if (root.getTerm().length()==0)
            // The method getQualifier() was replaced by getIndex() in version
            // 1.0 of the parser. This code ensures that either one works.
            //root = new CQLTermNode(root.getQualifier(), root.getRelation(), "$");
            root = new CQLTermNode(SRWSoapBindingImpl.getQualifier(root), root.getRelation(), "$");
        String resultSetID = root.getResultSetName();
        if (resultSetID!=null) { // you can't scan on resultSetId!
            return diagnostic(SRWDiagnostic.UnsupportedIndex,
                    "cql.resultSetId", scanResponse);
        }
        TermsType terms = new TermsType();
        TermList tl=getTermList(root, pos, max, request);
        terms.setTerm(tl.getTerms());
        scanResponse.setTerms(terms);

        Vector<DiagnosticType> diagnostics = tl.getDiagnostics();
        if (diagnostics!=null && !diagnostics.isEmpty()) {
            DiagnosticType diagArray[] = new DiagnosticType[diagnostics.size()];
            diagnostics.toArray(diagArray);
            scanResponse.setDiagnostics(new DiagnosticsType(diagArray));
        }

        log.info("scan "+scanTerm+": (" + (System.currentTimeMillis() - startTime) + "ms)");
        return scanResponse;
    }



    public SearchRetrieveResponseType doRequest(SearchRetrieveRequestType request) throws ServletException {
        boolean cachedResultSet=false;
        QueryResult result;
        searchRequest=request;
        response = new SearchRetrieveResponseType();

        response.setNumberOfRecords(new NonNegativeInteger("0"));       
        //        try {
                    MessageContext msgContext = MessageContext.getCurrentContext();

        String version=request.getVersion();
        if(version!=null && !version.equals("1.1"))
            return diagnostic(SRWDiagnostic.UnsupportedVersion, version, response);

        String query = request.getQuery();
        if(query==null || query.length()==0)
            return diagnostic(SRWDiagnostic.MandatoryParameterNotSupplied, "query", response);

        if(log.isDebugEnabled())
            try{
                log.debug("query:\n" + Utilities.byteArrayToString(query.getBytes("UTF-8")));
            } catch (Exception e){}

        String recordPacking = request.getRecordPacking();
        if(recordPacking==null) {
            if(msgContext!=null && msgContext.getProperty("sru")!=null)
                recordPacking="xml"; // default for sru
            else
                recordPacking="string"; // default for srw
        }

        String resultSetID = getResultSetId(query);
        if (resultSetID!=null) { // got a cached result
            log.info("resultSetID="+resultSetID);
            result = oldResultSets.get(resultSetID);
            if (result==null)
                return diagnostic(SRWDiagnostic.ResultSetDoesNotExist,
                        resultSetID, response);
            cachedResultSet=true;
        }
        else { // Evaluate the query.
            try {
                result = getQueryResult(query, request);
            } catch (InstantiationException e) {
                log.error(e, e);
                return diagnostic(SRWDiagnostic.GeneralSystemError,
                        e.getMessage(), response);
            }
        }

        long postingsCount = result.getNumberOfRecords();
        log.info("'" + query + "'==> " + postingsCount);
        response.setNumberOfRecords(new NonNegativeInteger(Long.toString(postingsCount)));

        int resultSetTTL = defaultResultSetTTL;
        NonNegativeInteger nni = request.getResultSetTTL();
        if(nni!=null)
            resultSetTTL=nni.intValue();
        result.setResultSetIdleTime(resultSetTTL);
        if (postingsCount>0) {  // we don't mess with records otherwise
            if (resultSetTTL>0 && returnResultSetId) {
                // cache the resultSet and set (or reset) its timer
                if(resultSetID==null)
                    resultSetID=makeResultSetID();
                log.debug("keeping resultSet '"+resultSetID+"' for "+resultSetTTL+
                    " seconds");
                oldResultSets.put(resultSetID, result);
                resetTimer(resultSetID);
                response.setResultSetId(resultSetID);
                response.setResultSetIdleTime(
                    new PositiveInteger(Integer.toString(resultSetTTL)));
                cachedResultSet=true;
            }

            int numRecs = defaultNumRecs;
            NonNegativeInteger maxRecs = request.getMaximumRecords();
            if (maxRecs!=null)
                numRecs = (int) Math.min(maxRecs.longValue(), maximumRecords);

            long startPoint = 1;
            PositiveInteger startRec = request.getStartRecord();
            if(startRec!=null)
                startPoint=startRec.longValue();
            if (startPoint>postingsCount)
                diagnostic(SRWDiagnostic.FirstRecordPositionOutOfRange,
                        null, response);

            if ((startPoint-1+numRecs)>postingsCount)
                numRecs = (int) (postingsCount-(startPoint-1));

            if (!recordPacking.equals("xml") &&
              !recordPacking.equals("string")) {
                diagnostic(SRWDiagnostic.UnsupportedRecordPacking, recordPacking, response);
                numRecs=0;
            }

            String schemaName = request.getRecordSchema();
            if(schemaName==null)
                schemaName="default";
            String schemaID=null;
            if(!letDefaultsBeDefault || !schemaName.equals("default")) {
                schemaID = getSchemaID(schemaName);
                if(schemaID==null) {
                    log.debug("unknown schema: "+schemaName);
                    diagnostic(SRWDiagnostic.UnknownSchemaForRetrieval, schemaName, response);
                    numRecs=0;
                }
            }
           
            if (numRecs==0)
                response.setNextRecordPosition(new PositiveInteger("1"));
            else
                if (numRecs>0) { // render some records into SGML
                    String sortKeys = request.getSortKeys();
                    log.debug("schemaName="+schemaName+", schemaID="+schemaID+
                        ", sortKeys="+sortKeys);                   
                    if(sortKeys!=null && sortKeys.length()>0) { // do we need to sort them first?
                        QueryResult sortedResult=result.getSortedResult(sortKeys);
                        if(sortedResult==null) { // sigh, we've got some sorting to do
//                            log.info("sorting resultSet");
//                            boolean       ascending=true;
//                            SortTool sortTool=null;
//                            String   sortKey;
//                            if(schemaName==null)
//                                schemaName="default";
//                            log.info("recordSchema="+schemaName);
//                            Object handler=transformers.get(schemaName);
//                            if(handler==null) {
//                                log.error("no handler for schema "+schemaName);
//                                if(log.isInfoEnabled()) {
//                                    for(Enumeration enum2=transformers.keys();
//                                      enum2.hasMoreElements();)
//                                        log.info("handler name="+(String)enum2.nextElement());
//                                }
//                                return diagnostic(SRWDiagnostic.UnknownSchemaForRetrieval,
//                                    schemaName, response);
//                            }
//                            StringTokenizer keysTokenizer=new StringTokenizer(sortKeys);
//                            //while(keysTokenizer.hasMoreTokens()) {
//                            // just one key for now
//                                sortKey=keysTokenizer.nextToken();
//                                sortTool=new PearsSortTool(sortKey, transformers);
//                            //}
//                            String sortSchema=(String)nameSpaces.get(sortTool.prefix);
//                            if(sortSchema==null)
//                                sortSchema="";
//                            sortTool.setSchema(sortSchema);
//                            sortTool.makeSortElementExtractor();
//                            BerString        doc;
//                            DataDir          recDir;
//                            DocumentIterator list=(DocumentIterator)result.getDocumentIdList();
//                            int              listEntry;
//                            String           stringRecord;
//                            entries=new SortEntry[postings];
//                            for(int i=0; i<postings; i++) {
//                                listEntry=list.nextInt();
//                                log.debug("listEntry="+listEntry);
//                                doc=(BerString)pdb.getDocument(listEntry);
//                                recDir=new DataDir(doc);
//                                if(sortTool.dataType.equals("text"))
//                                    entries[i]=new SortEntry(sortTool.extract(recDir), listEntry);
//                                else {
//                                    try {
//                                        entries[i]=new SortEntry(Integer.parseInt(sortTool.extract(recDir)), listEntry);
//                                    }
//                                    catch(java.lang.NumberFormatException e) {
//                                        entries[i]=new SortEntry(0, listEntry);
//                                    }
//                                }
//                                if(entries[i].getKey()==null) { // missing value code
//                                    if(sortTool.missingValue.equals("abort"))
//                                        return diagnostic(SRWDiagnostics.SortEndedDueToMissingValue,
//                                            null, response);
//                                    else if(sortTool.missingValue.equals("highValue"))
//                                        entries[i]=new SortEntry("\ufffffe\ufffffe\ufffffe\ufffffe\ufffffe", listEntry);
//                                    else if(sortTool.missingValue.equals("lowValue"))
//                                        entries[i]=new SortEntry("\u000000", listEntry);
//                                    else { // omit
//                                        i--;
//                                        postings--;
//                                    }
//                                }
//                                if(log.isDebugEnabled())
//                                    log.debug("entries["+i+"]="+entries[i]);
//                            }
//                            Arrays.sort(entries);
//                            sortedResultSets.put(resultSetID+"/"+sortKeys, entries);
//                            sortTools.put(sortKeys, sortTool);
                        }
                        else {
                            log.debug("reusing old sorted resultSet");
                        }
                        if(sortedResult==null)
                            diagnostic(SRWDiagnostic.SortNotSupported,
                                null, response);
                        else
                            result=sortedResult;
                    // if(sortKeys!=null && sortKeys.length()>0)
                   
                                        // render some records
                                        RecordIterator list = null;
                    try {
                        log.debug("making RecordIterator, startPoint="+startPoint+", schemaID="+schemaID);
                        list=result.recordIterator(startPoint, numRecs, schemaID, request.getExtraRequestData());
                    } catch (InstantiationException e) {
                        log.error(e, e);
                        diagnostic(SRWDiagnostic.GeneralSystemError,
                            e.getMessage(), response);
                    }
                    RecordsType records = new RecordsType();

                    records.setRecord(new RecordType[numRecs]);
                    int                    i;
                    MessageElement         elems[];
                    Record                 rec;
                    RecordType             rt;
                    String                 recStr = "";
                    StringOrXmlFragment    frag;

                    /**
                     * One at a time, retrieve and display the requested documents.
                     */
                    log.debug("trying to get "+numRecs+
                        " records starting with record "+startPoint+
                        " from a set of "+postingsCount+" records");
                    for (i=0; list!=null && i<numRecs && list.hasNext(); i++) {
                        rt = new RecordType();
                        rt.setRecordPacking(recordPacking);
                        frag = new StringOrXmlFragment();
                        elems = new MessageElement[1];
                        frag.set_any(elems);
                        try {
                            rec=list.nextRecord();
                            log.debug("rec="+rec);
                            recStr=transform(rec, schemaID).getRecord();
                            if (log.isDebugEnabled())
                                try {
                                    log.debug("Transformed XML:\n" + Utilities.byteArrayToString(
                                        recStr.getBytes("UTF8")));
                                } catch (UnsupportedEncodingException e) {} // can't happen
                            makeElem(recStr, rt, schemaID, schemaName, recordPacking, docb, elems);
                            if(rec.hasExtraRecordInfo())
                                setExtraRecordData(rt, rec.getExtraRecordInfo());
                        } catch (IOException e) {
                            log.error("error getting document "+(i+1)+", postings="+postingsCount);
                            log.error(e, e);
                            try {
                                makeElem(SRWDiagnostic.newSurrogateDiagnostic(
                                    "info:srw/diagnostic/1/",
                                    SRWDiagnostic.RecordTemporarilyUnavailable,
                                    null), rt, null,
                                    "info:srw/schema/1/diagnostics-v1.1",
                                    recordPacking, docb, elems);
                            } catch (IOException e2) {
                                log.error(e, e);
                                break;
                            } catch (SAXException e2) {
                                log.error(e, e);
                                break;
                            }
                        } catch (NoSuchElementException e) {
                            log.error("error getting document "+(i+1)+", postings="+postingsCount);
                            log.error(e, e);
                            try {
                                makeElem(SRWDiagnostic.newSurrogateDiagnostic(
                                    "info:srw/diagnostic/1/",
                                    SRWDiagnostic.RecordTemporarilyUnavailable,
                                    null), rt, null,
                                    "info:srw/schema/1/diagnostics-v1.1",
                                    recordPacking, docb, elems);
                            } catch (IOException e2) {
                                log.error(e, e);
                                break;
                            } catch (SAXException e2) {
                                log.error(e, e);
                                break;
                            }
                        } catch (SAXException e) {
                            try {
                                log.error(e, e);
                                makeElem(SRWDiagnostic.newSurrogateDiagnostic(
                                    "info:srw/diagnostic/1/",
                                    SRWDiagnostic.RecordTemporarilyUnavailable,
                                    null), rt, null,
                                    "info:srw/schema/1/diagnostics-v1.1",
                                    recordPacking, docb, elems);
                            } catch (IOException e2) {
                                log.error(e, e);
                                break;
                            } catch (SAXException e2) {
                                log.error(e, e);
                                break;
                            }
                            log.error("error getting document "+(i+1)+", postings="+postingsCount);
                            log.error(e, e);
                            try {
                                log.error("Bad record:\n" + Utilities.byteArrayToString(
                                        recStr.getBytes("UTF8")));
                            } catch (UnsupportedEncodingException e2) {} // can't happen
                        } catch (SRWDiagnostic e) {
                            try {
                                makeElem(SRWDiagnostic.newSurrogateDiagnostic(
                                    "info:srw/diagnostic/1/", e.getCode(),
                                    e.getAddInfo()), rt, null,
                                    "info:srw/schema/1/diagnostics-v1.1",
                                    recordPacking, docb, elems);
                            } catch (IOException e2) {
                                log.error(e, e);
                                break;
                            } catch (SAXException e2) {
                                log.error(e, e);
                                break;
                            }
                            log.error("error getting document "+(i+1)+", postings="+postingsCount);
                            log.error(e, e);
                            try {
                                log.error("Bad record:\n" + Utilities.byteArrayToString(
                                        recStr.getBytes("UTF8")));
                            } catch (UnsupportedEncodingException e2) {} // can't happen
                        }

                        rt.setRecordData(frag);

                        rt.setRecordPosition(new PositiveInteger(Long.toString(startPoint+i)));

                        records.setRecord(i, rt);
                    }
                    response.setRecords(records);
                    if (startPoint+i<=postingsCount)
                        response.setNextRecordPosition(new PositiveInteger(Long.toString(startPoint+i)));
                } // else if(numRecs>0)
        } // if(postingsCount>0)

        String extraResponseData = getExtraResponseData(result, request);
        if(extraResponseData!=null)
            setExtraResponseData(response, extraResponseData);

        Vector<DiagnosticType> diagnostics = result.getDiagnostics();
        if (diagnostics!=null && !diagnostics.isEmpty()) {
            DiagnosticType diagArray[] = new DiagnosticType[diagnostics.size()];
            diagnostics.toArray(diagArray);
            response.setDiagnostics(new DiagnosticsType(diagArray));
        }
       
        if(!cachedResultSet)
            result.close();

        log.debug("exit doRequest");
        return response;
//        }
//        catch(Exception e) {
//            //log.error(e);
//            log.error(e, e);
//            throw new ServletException(e.getMessage());
//        }
    }


    public String extractRecordIdFromUri(String uri) {
        log.info("uri="+uri);
        extractRecordIdFromUriPatternMatcher.reset(uri);
        if(extractRecordIdFromUriPatternMatcher.find()) {
            return extractRecordIdFromUriPatternMatcher.group(1);
        }
        log.info("recordID not found.  Pattern="+extractRecordIdFromUriPatternMatcher.pattern());
        return null;
    }

    public String extractSortField(Object record) {
        return null;
    }

    public String getConfigInfo() {
        StringBuilder sb=new StringBuilder();
        sb.append("        <configInfo>\n");
        sb.append("          <default type=\"maximumRecords\">").append(getMaximumRecords()).append("</default>\n");
        sb.append("          <default type=\"numberOfRecords\">").append(getNumberOfRecords()).append("</default>\n");
        sb.append("          <default type=\"retrieveSchema\">").append(schemas.get("default")).append("</default>\n");
        if(dbProperties!=null) {
            String s=dbProperties.getProperty("supports");
            if(s!=null)
                addSupports(s, sb);
            else
                for(int i=1; ; i++) {
                    s=dbProperties.getProperty("supports"+i);
                    if(s!=null)
                        addSupports(s, sb);
                    else
                        break;
                }
        }
        sb.append("          </configInfo>\n");
        return sb.toString();
    }


    public String getDatabaseInfo() {
        StringBuilder sb=new StringBuilder();
        sb.append("        <databaseInfo>\n");
        if(dbProperties!=null) {
            String t=dbProperties.getProperty("databaseInfo.title");
            if(t!=null) {
                databaseTitle=t;
                sb.append("          <title>").append(t).append("</title>\n");
            }
            t=dbProperties.getProperty("databaseInfo.description");
            if(t!=null)
                sb.append("          <description>").append(t).append("</description>\n");
            t=dbProperties.getProperty("databaseInfo.author");
            if(t!=null)
                sb.append("          <author>").append(t).append("</author>\n");
            t=dbProperties.getProperty("databaseInfo.contact");
            if(t!=null)
                sb.append("          <contact>").append(t).append("</contact>\n");
            t=dbProperties.getProperty("databaseInfo.restrictions");
            if(t!=null)
                sb.append("          <restrictions>").append(t).append("</restrictions>\n");
        }
        sb.append("          <implementation version='1.1' indentifier='http://www.oclc.org/research/software/srw'>\n");
        sb.append("            <title>OCLC Research SRW Server version 1.1</title>\n");
        sb.append("            </implementation>\n");
        sb.append("          </databaseInfo>\n");
        return sb.toString();
    }


    public static SRWDatabase getDB(String dbname, Properties properties) {
        return getDB(dbname, properties, null, null);
    }
    public static SRWDatabase getDB(String dbname, Properties properties, String servletContext, HttpServletRequest request) {
        log.debug("enter SRWDatabase.getDB");
        if(badDbs.get(dbname)!=null) // we've seen this one before
            return null;

        LinkedList<SRWDatabase> queue=dbs.get(dbname);
        SRWDatabase db=null;
        try {
            if(queue==null)
                log.info("No SRW databases opened yet for database "+dbname);
            else {
                log.debug("about to synchronize #1 on queue");
                synchronized(queue) {
                    if(queue.isEmpty())
                        log.info("No SRW databases left in queue for database "+dbname);
                    else {
                        db=queue.removeFirst();
                        if(db==null)
                            log.debug("popped a null database off the queue for database "+dbname);
                    }
                }
                log.debug("done synchronize #1 on queue");
            }
            if(db==null) {
                log.info("Opening an SRW database for "+dbname);
                try{
                    while(db==null) {
                        createDB(dbname, properties, servletContext, request);
                        queue=dbs.get(dbname);
                        log.debug("about to synchronize #2 on queue");
                        synchronized(queue) {
                            if(!queue.isEmpty()) // crap, someone got to it before us
                                db=queue.removeFirst();
                        }
                    }
                log.debug("done synchronize #2 on queue");
                }
                catch(Exception e) { // database not available
                    badDbs.put(dbname, dbname);
                    log.error(e, e);
                    return null;
                }
            }
        }
        catch(Exception e) {
            log.error(e,e);
            log.error("shoot!");
        }
        if(log.isDebugEnabled())
            log.debug("getDB: db="+db);
        log.debug("exit SRWDatabase.getDB");
        return db;
    }

   
    public Properties getDbProperties() {
        return dbProperties;
    }

   
    public int getDefaultResultSetTTL() {
        return defaultResultSetTTL;
    }


    public String getDefaultSchema() {
        return defaultSchema;
    }


//    public String getExplainRecord() {
//        if(explainRecord==null)
//            makeExplainRecord(null);
//        return explainRecord;
//    }


    public String getExplainRecord(HttpServletRequest request) {
        if(explainRecord==null)
            makeExplainRecord(request);
        return explainRecord;
    }


    /**
     *  overridable method to return the update date for a database
     * @return long
     */
    public long getLastUpdated() {
        return 0;
    }


    public int getMaximumRecords() {
        return maximumRecords;
    }
   
   
    public String getMetaInfo() {
        StringBuilder sb=new StringBuilder();
        sb.append("        <metaInfo>\n");
        if(dbProperties!=null) {
            String t=dbProperties.getProperty("metaInfo.dateModified");
            if(t!=null)
                sb.append("          <dateModified>").append(t).append("</dateModified>\n");
            t=dbProperties.getProperty("metaInfo.aggregatedFrom");
            if(t!=null)
                sb.append("          <aggregatedFrom>").append(t).append("</aggregatedFrom>\n");
            t=dbProperties.getProperty("metaInfo.dateAggregated");
            if(t!=null)
                sb.append("          <dateAggregated>").append(t).append("</dateAggregated>\n");
        }
        sb.append("          </metaInfo>\n");
        return sb.toString();
    }


    /**
     *  How many records does this database have in it?
     * @return int
     */
    public int getNumberOfDatabaseRecords() {
        return 0;
    }


    public int getNumberOfRecords() {
        return defaultNumRecs;
    }


    public static String getResultSetId(String query) {
        StringTokenizer st = new StringTokenizer(query, " =\"");
        int num = st.countTokens();
        if(num<2 || num>3)
            return null;
        String index = st.nextToken();
        if(!index.equals("cql.resultSetId"))
            return null;
        String relationOrResultSetId = st.nextToken();
        if(relationOrResultSetId.equals("exact")) {
            if(num<3)
                return null;
            return st.nextToken();
        }
        if(num==2)
            return relationOrResultSetId;
        return null;
    }

    public static ArrayList getResultSetIds(CQLNode root) throws SRWDiagnostic {
        ArrayList<String> resultSetIds=new ArrayList<String>();
        getResultSetIds(root, resultSetIds);
        return resultSetIds;
    }
   
    public static void getResultSetIds(CQLNode root, ArrayList<String> resultSetIds) throws SRWDiagnostic {
        if(root instanceof CQLBooleanNode) {
            CQLBooleanNode cbn=(CQLBooleanNode)root;
            getResultSetIds(cbn.left, resultSetIds);
            getResultSetIds(cbn.right, resultSetIds);
        }
        else {
            CQLTermNode ctn=(CQLTermNode)root;
            //if(ctn.getQualifier().equals("cql.resultSetId")) {
            if(SRWSoapBindingImpl.getQualifier(ctn).equals("cql.resultSetId")) {
                String resultSetId=ctn.getTerm();
                if(oldResultSets.get(resultSetId)==null)
                    throw new SRWDiagnostic(SRWDiagnostic.ResultSetDoesNotExist, resultSetId);
                resultSetIds.add(resultSetId);
                resetTimer(resultSetId);
                log.info("added resultSetId "+ctn.getTerm());
            }
        }
    }



    /**
     *  This class assumes that schema information was provided in the .props
     *  file for this database.  This method provides a way for extending
     *  classes to provide the schemaName to schemaID mapping themselves.
     */
    public String getSchemaID(String schemaName) {
        return schemas.get(schemaName);
    }


    public String getSchemaInfo() {
        return schemaInfo;
    }


    public boolean hasaConfigurationFile() {
        return true//  expect a configuration file unless overridden
    }


    protected void initDB(final String dbname, String srwHomeVal, String dbHome, String dbPropertiesFileName, Properties dbProperties) {
        log.debug("Enter: private initDB, dbname="+dbname);
        this.dbname=dbname;
        srwHome=srwHomeVal;
        this.dbHome=dbHome;
        this.dbPropertiesFileName=dbPropertiesFileName;
        this.dbProperties=dbProperties;

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        try {
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            docb=dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            log.error(e, e);
        }

        if(dbProperties!=null) {
            String httpHeaderSetterClass=dbProperties.getProperty("HttpHeaderSetter");
            if(httpHeaderSetterClass!=null) {
                try {
                    httpHeaderSetter = (HttpHeaderSetter) Class.forName(httpHeaderSetterClass).newInstance();
                    httpHeaderSetter.init(dbProperties);
                }
                catch (InstantiationException ex) {
                    log.error("Unable to create HttpHeaderSetter: "+httpHeaderSetterClass, ex);
                }
                catch (IllegalAccessException ex) {
                    log.error("Unable to create HttpHeaderSetter: "+httpHeaderSetterClass, ex);
                }
                catch (ClassNotFoundException ex) {
                    log.error("Unable to create HttpHeaderSetter: "+httpHeaderSetterClass, ex);
                }
            }

            // if the client doesn't say what they want, what should they get?
            // typically, this only happens with applications that don't set
            // the "accept" header, i.e., non-browser applications.  The
            // default value is set to text/xml, assuming that non-browser apps
            // want to see the full xml, not some rendered html
            defaultMimeType=dbProperties.getProperty("defaultMimeType", defaultMimeType);

            // get schema transformers
            String          firstSchema=null, xmlSchemaList=dbProperties.getProperty("xmlSchemas");
            StringBuilder    schemaInfoBuf=new StringBuilder("        <schemaInfo>\n");
            StringTokenizer st;
            if(xmlSchemaList!=null) {
                Enumeration propertyNames;
                String      name, schemaIdentifier, schemaName, transformerName,
                            value;
                ArrayList<String> parms, values;
                st=new StringTokenizer(xmlSchemaList, ", \t");
                log.info("xmlSchemaList="+xmlSchemaList);
                while(st.hasMoreTokens()) {
                    schemaName=st.nextToken();
                    log.debug("looking for schema "+schemaName);
                    if(firstSchema==null)
                        firstSchema=schemaName;
                    schemaIdentifier=dbProperties.getProperty(schemaName+".identifier");
                    transformerName=dbProperties.getProperty(schemaName+".transformer");
                    if(transformerName==null) {
                        // maybe this is an old .props file and the transformer name
                        // is associated with the bare schemaName
                        transformerName=dbProperties.getProperty(schemaName);
                    }
                    parms=new ArrayList<String>();
                    values=new ArrayList<String>();
                    propertyNames=dbProperties.propertyNames();
                    while(propertyNames.hasMoreElements()) {
                        name=(String)propertyNames.nextElement();
                        if(name.startsWith(schemaName+".parameter.")) {
                            value=dbProperties.getProperty(name);
                            values.add(value);
                            name=name.substring(schemaName.length()+11);
                            parms.add(name);
                            if(log.isDebugEnabled())
                                log.debug("transformer parm: "+name+"="+value);
                        }
                    }

                    try {
                        addTransformer(schemaName, schemaIdentifier, transformerName, parms, values);
                        addRenderer(schemaName, schemaIdentifier, dbProperties);
                        String schemaLocation=dbProperties.getProperty(schemaName+".location");
                        String schemaTitle=dbProperties.getProperty(schemaName+".title");
                        String schemaNamespace=dbProperties.getProperty(schemaName+".namespace");
                        if(schemaNamespace!=null)
                            nameSpaces.put(schemaName, schemaNamespace);
                        else
                            nameSpaces.put(schemaName, "NoNamespaceProvided");
                        schemaInfoBuf.append("          <schema sort=\"false\" retrieve=\"true\"")
                                     .append(" name=\"").append(schemaName)
                                     .append("\"\n              identifier=\"").append(schemaIdentifier)
                                     .append("\"\n              location=\"").append(schemaLocation).append("\">\n")
                                     .append("            <title>").append(schemaTitle).append("</title>\n")
                                     .append("            </schema>\n");
                    }
                    catch(Exception e) {
                        log.error("Unable to load schema "+schemaName);
                        log.error(e, e);
                    }
                }

                defaultSchema=dbProperties.getProperty("defaultSchema");
                if(defaultSchema==null)
                    defaultSchema=firstSchema;
                log.info("defaultSchema="+defaultSchema);
                schemaIdentifier=schemas.get(defaultSchema);
                log.info("default schemaID="+schemaIdentifier);
                if(schemaIdentifier==null)
                    log.error("Default schema "+defaultSchema+" not loaded");
                else {
                    schemas.put("default", schemaIdentifier);
                    Transformer t=transformers.get(defaultSchema);
                    if(t!=null) {
                        transformers.put("default", t);
                    }
                }
            }
            schemaInfoBuf.append("          </schemaInfo>\n");
            schemaInfo=schemaInfoBuf.toString();

            String t=srwProperties.getProperty("SRW.Context");
            if(t!=null)
                servletContext=t;
            explainStyleSheet=dbProperties.getProperty("explainStyleSheet");
            if(explainStyleSheet==null)
                explainStyleSheet="/$context/explainResponse.xsl";
            searchStyleSheet=dbProperties.getProperty("searchStyleSheet");
            if(searchStyleSheet==null)
                searchStyleSheet="/$context/searchRetrieveResponse.xsl";
            scanStyleSheet=dbProperties.getProperty("scanStyleSheet");
            if(scanStyleSheet==null)
                scanStyleSheet="/$context/scanResponse.xsl";
            if(servletContext!=null && servletContext.startsWith("/"))
                servletContext=servletContext.substring(1);
            if(servletContext!=null && servletContext.length()>0) {
                explainStyleSheet=explainStyleSheet.replace("$context", servletContext);
                searchStyleSheet=searchStyleSheet.replace("$context", servletContext);
                scanStyleSheet=scanStyleSheet.replace("$context", servletContext);
            }
            else {
                explainStyleSheet=explainStyleSheet.replace("/$context", "");
                searchStyleSheet=searchStyleSheet.replace("/$context", "");
                scanStyleSheet=scanStyleSheet.replace("/$context", "");
            }

            conneg=new ContentTypeNegotiator();
            Enumeration enumer = dbProperties.keys();
            int offset;
            String contentLocation, key, mediaType, mimeType, name, recordSchema, value;
            VariantSpec vs;
            while(enumer.hasMoreElements()) {
                key=(String)enumer.nextElement();
                offset=key.indexOf("mimeTypes");
                if(offset>0) {
                    name=key.substring(0, offset-1);
                    value=dbProperties.getProperty(key);
                    log.info(key+"="+value);
                    st=new StringTokenizer(value, ", ");
                    mimeType=st.nextToken();
                    vs=conneg.addVariant(mimeType);
                    mediaType=vs.getMediaType().getMediaType();
                    try {
                        addTransformer(mediaType, null, dbProperties.getProperty(name+".styleSheet"), null, null);
                        while(st.hasMoreTokens())
                            vs.addAliasMediaType(st.nextToken());
                    } catch (Exception e) {
                        log.error("Unable to stylesheet "+dbProperties.getProperty(name+".styleSheet"));
                        log.error(e, e);
                    }
                    // we should only need this when the transformer
                    // can't run from the default schema
                    recordSchema=dbProperties.getProperty(name+".recordSchema");
                    if(recordSchema!=null && transformers.get(recordSchema)==null) {
                        recordSchemas.put(mediaType, recordSchema);
                        log.info("mediaType: "+mediaType+" requires recordSchema: "+dbProperties.getProperty(name+".recordSchema"));
                    }
                    contentLocation=dbProperties.getProperty(name+".ContentLocation");
                    if(contentLocation!=null) {
                        contentLocations.put(mediaType, contentLocation);
                        log.info("mediaType: "+mediaType+" ContentLocation: "+contentLocation);
                    }
                }
            }

            String patternStr=dbProperties.getProperty("extractRecordIdFromUriPattern");
            if(patternStr!=null)
                extractRecordIdFromUriPatternMatcher=Pattern.compile(patternStr).matcher("");
        }
        log.debug("Exit: private initDB");
    }


    public void makeElem(String recStr, RecordType rt, String schemaID, String schemaName, String recordPacking, DocumentBuilder db, Element[] elems) throws IOException, SAXException {
        if (recordPacking.equals("xml")) {
            Document domDoc;
            try {
                domDoc = db.parse(new InputSource(new StringReader(recStr)));
            }
            catch(SAXParseException e) {
                log.error("bad XML!");
                log.error(recStr);
                throw e;
            }
            Element el = domDoc.getDocumentElement();
            log.debug("got the DocumentElement");
            elems[0] = new MessageElement(el);
            log.debug("put the domDoc into elems[0]");
//            if(log.isDebugEnabled())
//                log.debug("elems[0]\n"+elems[0].toString());
        }
        else { // string
            Text t = new Text(recStr);
            elems[0] = new MessageElement(t);
        }
        if(schemaID!=null)
            rt.setRecordSchema(schemaID);
        else
            rt.setRecordSchema(schemaName);
    }


    public void makeExplainRecord(HttpServletRequest request) {
//        log.error("makeExplainRecord being called from:");
//        Thread.dumpStack();
        log.debug("Making an explain record for database "+dbname);
        StringBuilder sb=new StringBuilder(), urlStr=new StringBuilder();
        sb.append("      <explain authoritative=\"true\" xmlns=\"http://explain.z3950.org/dtd/2.0/\">\n");
        sb.append("        <serverInfo protocol=\"SRW/U\">\n");
        if(request!=null) {
            sb.append("          <host>").append(request.getServerName()).append("</host>\n");
            urlStr.append("http://").append(request.getServerName());
            sb.append("          <port>").append(request.getServerPort()).append("</port>\n");
            if(request.getServerPort()!=80)
                urlStr.append(":").append(request.getServerPort());
            long lastUpdated=getLastUpdated();
            if(lastUpdated==0)
                sb.append("          <database>");
            else {
                sb.append("          <database lastUpdate=\"")
                  .append(ISO8601FORMAT.format(new Date(lastUpdated)))
                  .append("\" numRecs=\"")
                  .append(getNumberOfDatabaseRecords())
                  .append("\">");
            }
            urlStr.append('/');
            String contextPath=request.getContextPath();
            if(contextPath!=null && contextPath.length()>1) {
                sb.append(contextPath.substring(1));
                urlStr.append(contextPath.substring(1));
            }
            sb.append(request.getServletPath());
            urlStr.append(request.getServletPath());
            String pathInfo=request.getPathInfo();
            if(pathInfo!=null) {
                sb.append(request.getPathInfo());
                urlStr.append(request.getPathInfo());
            }
            sb.append("</database>\n");
            baseURL=urlStr.toString();
            log.debug("baseURL="+baseURL);
        }
        sb.append("          </serverInfo>\n");
        sb.append(getDatabaseInfo());
        sb.append(getMetaInfo());
        sb.append(getIndexInfo());
        sb.append(getSchemaInfo());
        sb.append(getConfigInfo());
        sb.append("        </explain>\n");
        setExplainRecord(sb.toString());
    }


    private static ExtraDataType makeExtraDataType(String extraData) {
        ExtraDataType edt = null;
        // extraData is always encoded as "xml"
        Document domDoc;
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        dbf.setNamespaceAware(true);
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader("<bogus>"+extraData+"</bogus>");
            domDoc = db.parse(new InputSource(sr));
            sr.close();
            Element el = domDoc.getDocumentElement();
            NodeList nodes=el.getChildNodes();
            MessageElement elems[] = new MessageElement[nodes.getLength()];
            for(int i=0; i<elems.length; i++)
                elems[i]=new MessageElement((Element)nodes.item(i));
            edt = new ExtraDataType();
            edt.set_any(elems);
            domDoc=null;
        } catch (IOException e) {
            log.error(e, e);
        } catch (ParserConfigurationException e) {
            log.error(e, e);
        } catch (SAXException e) {
            log.error(e, e);
            try {
                log.error("Bad ExtraResponseData:\n" + Utilities.byteArrayToString(
                    extraData.getBytes("UTF8")));
            } catch (UnsupportedEncodingException e2) {} // can't happen
        }
        return edt;
    }

    public static ExtraDataType makeExtraRequestDataType(String extraData) {
        return makeExtraDataType(extraData);
    }

    protected String makeResultSetID() {
        int          i, j;
        StringBuilder sb=new StringBuilder();
        for(i=0; i<6; i++) {
            j=rand.nextInt(35);
            if(j<26)
                sb.append((char)('a'+j));
            else
                sb.append((char)('0'+j-26));
        }
        return sb.toString();
    }

    /**
     * Look for indexes with context sets and construct a new index entry
     * without the context set.  If the new index is unique, keep it.
     */
    static protected void makeUnqualifiedIndexes(Properties props) {
        Enumeration enumer=props.propertyNames();
        HashMap<String, String> newIndexes=new HashMap<String, String>();
        int         start;
        String      name, newName, value;
        while(enumer.hasMoreElements()) {
            name=(String)enumer.nextElement();
            if(name.startsWith("qualifier.")) {
                if((start=name.indexOf('.', 11))>0) {
                    newName="hiddenQualifier."+name.substring(start+1);
                    log.debug("checking for "+newName);
                    if(newIndexes.get(newName)!=null) { // already got one
                        log.debug("dropping "+newName);
                        newIndexes.remove(newName); // so throw it away
                    }
                    else {
                        log.debug("keeping "+newName);
                        newIndexes.put(newName, (String)props.get(name));
                    }
                }
            }
        }
        Iterator<String> iter = newIndexes.keySet().iterator();
        while(iter.hasNext()) {
            name=iter.next();
            value=newIndexes.get(name);
            if(value!=null) {
                log.debug("adding: "+name+"="+value);
                props.put(name, value);
            }
        }
    }

    public static DiagnosticsType newDiagnostic(final int code,
      final String details, final DiagnosticsType diagnostics) {
        DiagnosticType  diags[];
        DiagnosticsType newDiagnostics=diagnostics;
        int numExistingDiagnostics=0;
        if(diagnostics!=null) {
            diags=diagnostics.getDiagnostic();
            numExistingDiagnostics=diags.length;
            DiagnosticType[] newDiags=
                new DiagnosticType[numExistingDiagnostics+1];
            System.arraycopy(diags, 0, newDiags, 0, numExistingDiagnostics);
            diags=newDiags;
            diagnostics.setDiagnostic(diags);
        }
        else {
            diags=new DiagnosticType[1];
            newDiagnostics=new DiagnosticsType();
            newDiagnostics.setDiagnostic(diags);
        }
        diags[numExistingDiagnostics]=SRWDiagnostic.newDiagnosticType(code, details);
        return newDiagnostics;
    }


    public static HashMap<String, String> parseElements(ExtraDataType extraData) {
        HashMap<String, String> extraDataTable = new HashMap<String, String>();
        if (extraData!=null) {
            MessageElement[] elems = extraData.get_any();
            NameValuePair    nvp;
            String extraRequestData = elems[0].toString();
            ElementParser ep = new ElementParser(extraRequestData);
            log.debug("extraRequestData="+extraRequestData);
            while (ep.hasMoreElements()) {
                nvp = (NameValuePair) ep.nextElement();
                extraDataTable.put(nvp.getName(), nvp.getValue());
                log.debug(nvp);
            }
        }
        return extraDataTable;
    }

   
    public static void putDb(String dbname, SRWDatabase db) {
        LinkedList<SRWDatabase> queue=dbs.get(dbname);
        log.debug("about to synchronize #3 on queue");
        synchronized(queue) {
            queue.add(db);
            if(log.isDebugEnabled())
                log.debug("returning "+dbname+" database to the queue; "+queue.size()+" available");
        }
        log.debug("done synchronize #3 on queue");
    }

    public boolean replace(String recordID, byte[] record) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    static public void resetTimer(String resultSetID) {
        QueryResult qr=oldResultSets.get(resultSetID);
        timers.put(resultSetID, new Long(System.currentTimeMillis() + (qr.getResultSetIdleTime()*1000)));
    }

    public void setDefaultResultSetTTL(int defaultResultSetTTL) {
        this.defaultResultSetTTL=defaultResultSetTTL;
    }


    public void setExplainRecord(String explainRecord) {
        this.explainRecord=explainRecord;
    }


    static public void setExtraRecordData(RecordType rt, String extraData) {
        ExtraDataType edt=rt.getExtraRecordData();
        StringBuilder extraResponseData = new StringBuilder("<extraData xmlns=\"http://oclc.org/srw/extraData\">");
        if(edt!=null) {
            MessageElement[] elems = edt.get_any();
            String currentExtraData=elems[0].toString();
            int end=currentExtraData.lastIndexOf('<'), start=currentExtraData.indexOf('<', 1);
            extraResponseData.append(currentExtraData.substring(start, end-1));
        }
        extraResponseData.append(extraData).append("</extraData>");
        rt.setExtraRecordData(makeExtraDataType(extraResponseData.toString()));
    }


    static public void setExtraResponseData(ScanResponseType response, String extraData) {
        ExtraDataType edt=response.getExtraResponseData();
        StringBuilder extraResponseData = new StringBuilder("<extraData xmlns=\"http://oclc.org/srw/extraData\">");
        if(edt!=null) {
            MessageElement[] elems = edt.get_any();
            String currentExtraData=elems[0].toString();
            int end=currentExtraData.lastIndexOf('<'), start=currentExtraData.indexOf('<', 1);
            extraResponseData.append(currentExtraData.substring(start, end-1));
        }
        extraResponseData.append(extraData).append("</extraData>");
        response.setExtraResponseData(makeExtraDataType(extraResponseData.toString()));
    }


    static public void setExtraResponseData(SearchRetrieveResponseType response, String extraData) {
        ExtraDataType edt=response.getExtraResponseData();
        StringBuilder extraResponseData = new StringBuilder("<extraData xmlns=\"http://oclc.org/srw/extraData\">");
        if(edt!=null) {
            MessageElement[] elems = edt.get_any();
            String currentExtraData=elems[0].toString();
            int end=currentExtraData.lastIndexOf('<'), start=currentExtraData.indexOf('<', 1);
            extraResponseData.append(currentExtraData.substring(start, end));
        }
        extraResponseData.append(extraData).append("</extraData>");
        response.setExtraResponseData(makeExtraDataType(extraResponseData.toString()));
    }


    public void setMaximumRecords(int maximumRecords) {
        this.maximumRecords=maximumRecords;
    }


    public void setReturnResultSetId(boolean value) {
        returnResultSetId=value;
    }


    public void setNumberOfRecords(int numberOfRecords) {
        defaultNumRecs=numberOfRecords;
    }
   

    @Override
    public String toString() {
        StringBuilder sb=new StringBuilder();
        sb.append("Database ").append(dbname).append(" of type ")
          .append(this.getClass().getName());
        return sb.toString();
    }


    public Record transform(Record rec, String schemaID) throws SRWDiagnostic {
        if (schemaID!=null && !rec.getRecordSchemaID().equals(schemaID)) {
            log.debug("transforming to "+schemaID);
            // They must have specified a transformer
            Transformer t = transformers.get(schemaID);
            if (t==null) {
                log.error("can't transform record in schema "+rec.getRecordSchemaID());
                log.error("record not available in schema "+schemaID);
                log.error("available schemas are:");
                Enumeration enumer = transformers.keys();
                while (enumer.hasMoreElements()) {
                    log.error("    " + (String) enumer.nextElement());
                }
                throw new SRWDiagnostic(SRWDiagnostic.RecordNotAvailableInThisSchema, schemaID);
            }
            String recStr = Utilities.hex07Encode(rec.getRecord());
            StringWriter toRec = new StringWriter();
            StreamSource fromRec = new StreamSource(new StringReader(recStr));
            try {
                t.transform(fromRec, new StreamResult(toRec));
            } catch (TransformerException e) {
                log.error(e, e);
                throw new SRWDiagnostic(SRWDiagnostic.RecordNotAvailableInThisSchema, schemaID);
            }
            recStr=toRec.toString();
            return new Record(recStr, schemaID);
        }
        return rec;
    }


    public void useConfigInfo(String configInfo) {
        if(log.isDebugEnabled()) log.debug("configInfo="+configInfo);
        ElementParser ep = new ElementParser(configInfo);
        NameValuePair nvp,  configInfoPair = (NameValuePair)ep.nextElement();
        String attribute,  attributes,  type;
        StringTokenizer st;
        ep = new ElementParser(configInfoPair.getValue());
        while (ep.hasMoreElements()) {
            nvp = (NameValuePair) ep.nextElement();
            if (nvp.getName().equals("default")) {
                attributes=ep.getAttributes();
                st = new StringTokenizer(attributes, " =\"");
                type=null;
                while(st.hasMoreTokens()) {
                    attribute=st.nextToken();
                    if(attribute.equals("type")) {
                        type=st.nextToken();
                    }
                }
                if (type!=null) {
                    if (type.equals("retrieveSchema"))
                        schemas.put("default", nvp.getValue());
                    else
                        if (type.equals("maximumRecords"))
                            maximumRecords = Integer.parseInt(nvp.getValue());
                        else
                            if (type.equals("numberOfRecords"))
                                defaultNumRecs = Integer.parseInt(nvp.getValue());
                }
            }
        }
    }


    public void useSchemaInfo(String schemaInfo) {
        ElementParser ep = new ElementParser(schemaInfo);
        NameValuePair nvp,  schemaInfoPair = (NameValuePair)ep.nextElement();
        String attribute,  attributes,  schemaID,  schemaName;
        StringTokenizer st;
        ep = new ElementParser(schemaInfoPair.getValue());
        while (ep.hasMoreElements()) {
            nvp = (NameValuePair) ep.nextElement();
            if (nvp.getName().equals("schema")) {
                attributes=ep.getAttributes();
                log.debug("in useSchemaInfo: attributes="+attributes);
                st = new StringTokenizer(attributes, " =\"");
                schemaID=schemaName=null;
                while(st.hasMoreTokens()) {
                    attribute=st.nextToken();
                    if(attribute.equals("name")) {
                        schemaName=st.nextToken();
                    }
                    else if(attribute.equals("identifier")) {
                        schemaID=st.nextToken();
                    }
                }
                if(schemaID!=null && schemaName!=null) {
                    schemas.put(schemaName, schemaID);
                    log.info("adding schema: "+schemaName);
                    schemas.put(schemaID, schemaID);
                    log.info("with schemaID: "+schemaID);
                }
            }
        }
    }
}
TOP

Related Classes of ORG.oclc.os.SRW.SRWDatabase

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.