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

src.org.jafer.srwserver.SRWServer Maven / Gradle / Ivy

/**
 * JAFER Toolkit Project. Copyright (C) 2002, JAFER Toolkit Project, Oxford
 * University. This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of the License,
 * or (at your option) any later version. This library is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU Lesser General Public License for more details. You should have
 * received a copy of the GNU Lesser General Public License along with this
 * library; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.jafer.srwserver;

import gov.loc.www.zing.cql.xcql.OperandType;
import gov.loc.www.zing.srw.EchoedSearchRetrieveRequestType;
import gov.loc.www.zing.srw.RecordType;
import gov.loc.www.zing.srw.SearchRetrieveRequestType;
import gov.loc.www.zing.srw.SearchRetrieveResponseType;
import gov.loc.www.zing.srw.StringOrXmlFragment;
import gov.loc.www.zing.srw.diagnostic.DiagnosticType;

import java.io.StringWriter;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.logging.Logger;

import org.apache.axis.message.MessageElement;
import org.apache.axis.types.NonNegativeInteger;
import org.apache.axis.types.PositiveInteger;
import org.apache.axis.types.URI;
import org.apache.axis.types.URI.MalformedURIException;
import org.apache.xerces.dom.TextImpl;
import org.jafer.databeans.DatabeanManager;
import org.jafer.databeans.DatabeanManagerFactory;
import org.jafer.databeans.DatabeanManagerFactoryConfig;
import org.jafer.exception.JaferException;
import org.jafer.query.CQLQuery;
import org.jafer.query.QueryException;
import org.jafer.record.Field;
import org.jafer.util.xml.XMLSerializer;
import org.w3c.dom.Element;

/**
 * This class services SRW requests
 */
public class SRWServer implements gov.loc.www.zing.srw.interfaces.SRWPort
{

    /**
     * Stores a reference to the logger
     */
    protected static Logger logger = Logger.getLogger("org.jafer.srwserver");

    /**
     * Stores a reference to the data bean manager factory to use
     */
    private DatabeanManagerFactory databeanManagerFactory = null;

    /**
     * Stores a reference to server configuration information
     */
    private SRWServerConfig serverConfig = null;

    /**
     * The SRWServer is constructed with the databean manager factory that
     * creates a databean manager to process all the search requests. The
     * databean manager will be configured using the config file
     * 
     * @param srwserverConfigLocation The location of the resource in the
     *        distribution that will be retrieved as a stream using
     *        class.getResourceAsStream() to load the srwserver config details
     * @param databeanManagerConfigLocation The location of the resource in the
     *        distribution that will be retrieved as a stream using
     *        class.getResourceAsStream() to load the databeanmanager config
     *        details
     * @throws JaferException
     */
    public SRWServer(String srwserverConfigLocation, String databeanManagerConfigLocation) throws JaferException
    {
        try
        {
            serverConfig = new SRWServerConfig();
            serverConfig.initialiseFromResourceStream(srwserverConfigLocation);
            DatabeanManagerFactoryConfig config = new DatabeanManagerFactoryConfig();
            config.initialiseFromResourceStream(databeanManagerConfigLocation);
            databeanManagerFactory = config.getDatabeanManagerFactory();
        }
        catch (JaferException exc)
        {
            String msg = "Unable to configure SRWServer: ";
            logger.severe(msg + exc);
            throw new JaferException(msg, exc);
        }
    }

    /**
     * The SRWServer is constructed with the databean manager factory that
     * creates a databean manager to process all the search requests. The
     * databean manager will be configured using the supplied factory rather
     * than the config file
     * 
     * @param srwserverConfigLocation The location of the resource in the
     *        distribution that will be retrieved as a stream using
     *        class.getResourceAsStream()
     * @param factory The databean manager factory to use
     * @throws JaferException
     */
    public SRWServer(String srwserverConfigLocation, DatabeanManagerFactory factory) throws JaferException
    {
        try
        {
            serverConfig = new SRWServerConfig();
            serverConfig.initialiseFromResourceStream(srwserverConfigLocation);
            // make sure we have a factory to use
            if (factory == null)
            {
                throw new JaferException("You must supply a DatabeanManagerFactory to configure the SRWSever with");
            }
            databeanManagerFactory = factory;
        }
        catch (JaferException exc)
        {
            String msg = "Unable to configure SRWServer: ";
            logger.severe(msg + exc);
            throw new JaferException(msg, exc);
        }
    }

    /**
     * The SRWServer is constructed with the databean manager factory that
     * creates a databean manager to process all the search requests. The
     * databean manager will be configured using the supplied factory rather
     * than the config file
     * 
     * @param srwserverConfig The srw server config class
     * @param factoryConfig The databean factory config class
     * @throws JaferException
     */
    public SRWServer(SRWServerConfig srwserverConfig, DatabeanManagerFactoryConfig factoryConfig) throws JaferException
    {
        try
        {
            serverConfig = srwserverConfig;
            databeanManagerFactory = factoryConfig.getDatabeanManagerFactory();
            // make sure we have a factory to use
            if (databeanManagerFactory == null)
            {
                throw new JaferException("You must supply a valid DatabeanManagerFactoryConfig to configure the SRWSever with");
            }
        }
        catch (JaferException exc)
        {
            String msg = "Unable to configure SRWServer: ";
            logger.severe(msg + exc);
            throw new JaferException(msg, exc);
        }
    }
    
    /**
     * Allows the databean factory to be updated on the server
     * @param factory the databean factory to use
     * @throws JaferException
     */
    public void setDatabeanManagerFactory(DatabeanManagerFactory factory) throws JaferException
    {
        // make sure we have a factory to use
        if (databeanManagerFactory == null)
        {
            throw new JaferException("You must supply a valid DatabeanManagerFactoryConfig to configure the SRWSever with");
        }
        databeanManagerFactory = factory;        
    }

    /**
     * This method creates a DiagnosticType for the information provided
     * 
     * @param code the diagnostic lookup code
     * @param details The diagnotic details
     * @return The created DiagnosticType
     * @throws MalformedURIException
     */
    private DiagnosticType createDiagnostic(String code, String details) throws MalformedURIException
    {
        // create the diagnostic URI using the code
        URI uri = new URI("info:srw/diagnostic/1/" + code);
        String message;
        try
        {
            message = serverConfig.getDiagnosticMessaage(code);
        }
        catch (JaferException exc)
        {
            logger.warning("Unable to find diagnostic message for code: " + code);
            message = "UNABLE TO FIND MESSAGE";
        }
        return new DiagnosticType(uri,details, message);
    }

    /**
     * This method makes sure that the request contains all the required
     * parameters. If it fails the response will be populated with the
     * appropriate diagnostic
     * 
     * @param request The SearchRetrieveRequestType request message
     * @param response The SearchRetrieveResponseType response message
     * @return true if the request is valid otherwise false and diagnostics
     *         added to response
     * @throws MalformedURIException
     * @throws JaferException
     */
    private boolean validateSearchRetrieveRequest(SearchRetrieveRequestType request, SearchRetrieveResponseType response)
            throws MalformedURIException, JaferException
    {
        logger.fine("Validating request");
        ArrayList diagnostics = new ArrayList();

        // check mandatory parameters and that version is valid
        if (request.getVersion() == null || request.getVersion().length() == 0
                || Double.parseDouble(request.getVersion()) > serverConfig.getHighestSupportedSearchVersion())
        {
            diagnostics.add(createDiagnostic("5", Double.toString(serverConfig.getHighestSupportedSearchVersion())));
            // set version to empty string so response will serialise only if
            // not set in the request
            if (request.getVersion() == null)
            {
                response.setVersion("");
                response.getEchoedSearchRetrieveRequest().setVersion("");
            }
        }
        if (request.getQuery() == null || request.getQuery().length() == 0)
        {
            diagnostics.add(createDiagnostic("7", "Query"));
            // set query to empty string so response will serialise
            response.getEchoedSearchRetrieveRequest().setQuery("");
        }

        // add the diagnostics to the response
        response.setDiagnostics(
                (DiagnosticType[]) diagnostics.toArray(new DiagnosticType[diagnostics.size()]));

        logger.fine("Assigning any default values to request when not set");

        if (request.getStartRecord() == null)
        {
            // standards state this defaults to 1 when not set
            request.setStartRecord(new PositiveInteger("1"));
        }
        if (request.getMaximumRecords() == null)
        {
            // standards state this defaults to server config when not set
            request.setMaximumRecords(new NonNegativeInteger(serverConfig.getDefaultMaxRecords()));
        }

        logger.fine("Request Valid: " + diagnostics.isEmpty());
        // validated succesfully if diagnostics array is empty
        return diagnostics.isEmpty();
    }

    /**
     * This method creates a basic response object from the request to be
     * populated with the results of the search and retrieve operaration
     * 
     * @param request The request object that must be replecated in the response
     * @return A basic SearchRetrieveResponseType object that contains the
     *         replecated request information
     */
    private gov.loc.www.zing.srw.SearchRetrieveResponseType createBasicSearchRetrieveResponse(
            gov.loc.www.zing.srw.SearchRetrieveRequestType request)
    {
        logger.fine("Creating basic search and retrieve response from request");

        SearchRetrieveResponseType response = new SearchRetrieveResponseType();

        // create the echoed request object
        EchoedSearchRetrieveRequestType echoedRequest = new EchoedSearchRetrieveRequestType();

        // copy the basic information over
        echoedRequest.setVersion(request.getVersion());
        echoedRequest.setStylesheet(request.getStylesheet());
        echoedRequest.setStartRecord(request.getStartRecord());
        echoedRequest.setSortKeys(request.getSortKeys());
        echoedRequest.setResultSetTTL(request.getResultSetTTL());
        echoedRequest.setRecordXPath(request.getRecordXPath());
        echoedRequest.setRecordSchema(request.getRecordSchema());
        echoedRequest.setRecordPacking(request.getRecordPacking());
        echoedRequest.setQuery(request.getQuery());
        echoedRequest.setMaximumRecords(request.getMaximumRecords());
        echoedRequest.setExtraRequestData(request.getExtraRequestData());
        echoedRequest.setXQuery(new OperandType());

        // For now the setting of the XQuery (XCQL) information will not be set
        response.setEchoedSearchRetrieveRequest(echoedRequest);
        response.setVersion(request.getVersion());
        response.setNumberOfRecords(new NonNegativeInteger("0"));

        logger.fine("Built search and retrieve response");
        return response;
    }

    /**
     * This method adds the requested results to the response according to the
     * request information for startrecord, maxrecords, recordpacking values
     * 
     * @param request The SearchRetrieveRequestType request message
     * @param response The SearchRetrieveResponseType response message
     * @param beanManager The bean manager to retrieve results from
     * @throws JaferException
     */
    private void addReplyRecordsToResponse(SearchRetrieveRequestType request, SearchRetrieveResponseType response,
            DatabeanManager beanManager) throws JaferException
    {
        logger.fine("Processing search and retrieve results into response ");
        try
        {
            // extract condition values required for loop
            int startRecord = request.getStartRecord().intValue();
            int maxRecords = request.getMaximumRecords().intValue();
            int numberOfResults = beanManager.getNumberOfResults();

            // holds all the records to be added
            RecordType[] records = new RecordType[maxRecords];

            // loop round retrieving and adding all the records from the start
            // record through to either the last result or until the maximum
            // number
            // of requested records has been reached
            for (int index = startRecord; index <= numberOfResults && index < startRecord + maxRecords; index++)
            {
                logger.fine("Processing record " + index);

                // create basic record Type
                RecordType record = new RecordType();
                record.setRecordSchema(request.getRecordSchema());
                record.setRecordPacking(request.getRecordPacking());
                record.setRecordPosition(new PositiveInteger(Integer.toString(index)));

                beanManager.setRecordCursor(index);
                Field field = beanManager.getCurrentRecord();
                // make sure the record was retrieved
                if (field == null)
                {
                    throw new JaferException("Unable to read record " + (startRecord + index));
                }

                MessageElement element = null;

                // are we adding the record as a string representation or as XML
                if (request.getRecordPacking().equalsIgnoreCase("string"))
                {
                    // convert the xml to a string ommiting XML header
                    StringWriter writer = new StringWriter();
                    XMLSerializer.out(field.getXML(), true, writer);
                    writer.flush();

                    // MAY NEED TO ENCODE THE XML STRING HERE BEFORE ADDING
                    // TO MESSAGE ELEMENT. TESTING SO FAR HAS NOT PROVED THIS TO
                    // BE REQUIRED

                    // add XML as the string node value for the message element
                    TextImpl data = new TextImpl();
                    data.replaceData(writer.toString());
                    element = new MessageElement(data);
                }
                else
                {
                    element = new MessageElement((Element) field.getXML());
                    // add the XML in as a NODE to the message element
                    // element.appendChild(field.getXML());
                }

                // add the message Element into response
                record.setRecordData(new StringOrXmlFragment(new MessageElement[] { element }));

                // add the record to the array
                records[index - startRecord] = record;
            }

            // add the records to the response
            response.setRecords(records);
        }
        finally
        {
            logger.fine("Search and retrieve results added to response");
        }
    }

    /**
     * This method processes a search and retrieve operation
     * 
     * @param request The search and retrieve request message
     * @return The search and retrieve response message object
     * @throws RemoteException
     */
    public gov.loc.www.zing.srw.SearchRetrieveResponseType searchRetrieveOperation(
            gov.loc.www.zing.srw.SearchRetrieveRequestType request) throws java.rmi.RemoteException
    {
        // QUESTION - WILL THIS BE CALLED SYCHRONOUSLY BY AXIS OR DO WE NEED TO
        // SYNCHRONISE THE CALL AS WE REFERENCE A PRIVATE MEMEBER

        logger.fine("Processing search and retrieve request");
        DatabeanManager beanManager = null;

        SearchRetrieveResponseType response = createBasicSearchRetrieveResponse(request);
        try
        {
            try
            {
                // make sure the request is valid
                if (validateSearchRetrieveRequest(request, response))
                {
                    // make sure we have a record schema set otherwise use the
                    // default schema of this SRWServer instance
                    if (request.getRecordSchema() == null || request.getRecordSchema().length() == 0)
                    {
                        request.setRecordSchema(serverConfig.getDefaultSchema());
                    }

                    // create the databean to perform the search
                    beanManager = (DatabeanManager) databeanManagerFactory.getDatabean();
                    beanManager.setRecordSchema(request.getRecordSchema());

                    logger.fine("Converting CQL query to CQLQuery");
                    // take the request query and convert it to a CQLQuery
                    CQLQuery query = new CQLQuery(request.getQuery());

                    logger.fine("Submitting cql query to databeanManager");
                    int numberOfResults = beanManager.submitQuery(query);
                    logger.fine("Found " + numberOfResults + " of results");

                    // populate the response message from results
                    response.setNumberOfRecords(new NonNegativeInteger(Integer.toString(numberOfResults)));

                    // did we get any search exceptions for the databases
                    String[] databases = beanManager.getAllDatabases();
                    JaferException[] exceptions = beanManager.getSearchException(databases);

                    logger.fine("Adding any search diagnostics");
                    // add each exception as a diagnostic message
                    ArrayList diagnostics = new ArrayList();
                    for (int index = 0; index < exceptions.length; index++)
                    {
                        // only add the diagnotic if the search exception is not
                        // null
                        if (exceptions[index] != null)
                        {
                            diagnostics.add(createDiagnostic("1", databases[index] + ":" + exceptions[index].getMessage()));
                        }
                    }

                    // add the diagnostics to the response
                    response.setDiagnostics(
                            (DiagnosticType[]) diagnostics.toArray(new DiagnosticType[diagnostics.size()]));

                    // calculate the next position
                    int nextPosition = request.getStartRecord().intValue() + request.getMaximumRecords().intValue();
                    // only set the next postion if it's going to be in the
                    // result set
                    if (nextPosition <= numberOfResults)
                    {
                        response.setNextRecordPosition(new PositiveInteger(Integer.toString(nextPosition)));
                    }

                    if (numberOfResults > 0)
                    {
                        // add the reply records to the response
                        addReplyRecordsToResponse(request, response, beanManager);
                    }
                }
            }
            catch (QueryException exc)
            {
                // make sure we return no records now
                response.setNumberOfRecords(new NonNegativeInteger("0"));
                logger.severe("QueryException performing search: " + exc);
                DiagnosticType diagnostic = createDiagnostic("1", exc.getMessage());
                // add the diagnostics to the response
                response.setDiagnostics(new DiagnosticType[] { diagnostic });
            }
            catch (JaferException exc)
            {
                // make sure we return no records now
                response.setNumberOfRecords(new NonNegativeInteger("0"));
                logger.severe("JaferException performing search: " + exc);
                DiagnosticType diagnostic = createDiagnostic("1", exc.getMessage());
                // add the diagnostics to the response
                response.setDiagnostics(new DiagnosticType[] { diagnostic });
            }
        }
        catch (Exception exc)
        {
            // this should not ever occur so if it does throw a remote exception
            logger.severe("Exception performing search: " + exc);
            throw new RemoteException(exc.getMessage());
        }
        finally
        {
            if (beanManager != null)
            {
                // stop any auto populating of cache as we have completed and
                // will no longer need any more records from the cache
                beanManager.stopAutoPopulateCache();
            }
            logger.fine("Processed search and retrieve request");
        }
        return response;
    }

    /**
     * This method processes a scan operation. Currently not supported by this
     * Server class
     * 
     * @param body The scan request message
     * @return The scan response message object
     * @throws RemoteException
     */
    public gov.loc.www.zing.srw.ScanResponseType scanOperation(gov.loc.www.zing.srw.ScanRequestType body)
            throws java.rmi.RemoteException
    {
        throw new UnsupportedOperationException();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy