Package net.sf.jz3950

Source Code of net.sf.jz3950.Association

/*
* $Id: Association.java 5 2007-03-09 23:22:30Z gremid $
*
* Copyright (c) 2007 the respective author(s) -- see javadoc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package net.sf.jz3950;

import net.sf.jz3950.auth.AuthenticationMethod;
import net.sf.jz3950.auth.SimpleAuthenticationMethod;
import net.sf.jz3950.protocol.io.ProtocolInputThread;
import net.sf.jz3950.protocol.io.ProtocolOutputThread;
import net.sf.jz3950.protocol.operation.CloseOperation;
import net.sf.jz3950.protocol.operation.InitOperation;
import net.sf.jz3950.protocol.operation.Operation;
import net.sf.jz3950.protocol.operation.PresentOperation;
import net.sf.jz3950.protocol.operation.SearchOperation;
import net.sf.jz3950.query.Query;
import net.sf.jz3950.record.Record;
import net.sf.jz3950.record.decoder.MarcRecordDecoder;
import net.sf.jz3950.record.decoder.RecordDecoder;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.IOException;

import java.net.Socket;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


public class Association
{
    private List<RecordDecoder> recordDecoders;
    private Lock concurrentOperationsLock = new ReentrantLock();
    private Log logger;
    private ProtocolInputThread inputThread;
    private ProtocolOutputThread outputThread;
    private Socket connection;
    private String associationReferenceId;
    private String host;
    private TargetInformation targetInformation = new TargetInformation();
    private int clientTimeout = 30;
    private int port;
    private int referenceIdCount;
    private int serverTimeout = 3;

    public Association()
    {
        registerRecordDecoders();
    }

    private void registerRecordDecoders()
    {
        this.recordDecoders = new ArrayList<RecordDecoder>();
        this.recordDecoders.add(new MarcRecordDecoder());
    }

    public void connect(String host, int port) throws IOException, ProtocolException
    {
        connect(host, port, SimpleAuthenticationMethod.NONE);
    }

    public void connect(String host, int port, String user, String password) throws IOException, ProtocolException
    {
        AuthenticationMethod auth = new SimpleAuthenticationMethod(AuthenticationMethod.AUTH_TYPE_ID_PASS, user, null, password);
        connect(host, port, auth);
    }

    @SuppressWarnings("unchecked")
    public void connect(String host, int port, AuthenticationMethod authenticationMethod) throws IOException, ProtocolException
    {
        if (isRunning())
        {
            throw new ProtocolException("Already connected and running");
        }

        this.referenceIdCount = 0;
        this.host = host;
        this.port = port;
        this.logger = LogFactory.getLog(toString() + "[" + getTarget() + "]");

        /*
         * Connecting to remote host
         */
        logger.debug("Connecting to " + getTarget());
        this.connection = new Socket(this.host, this.port);
        this.connection.setSoTimeout(this.serverTimeout * 1000);
        this.inputThread = new ProtocolInputThread("<--" + getTarget(), this.connection.getInputStream());
        this.outputThread = new ProtocolOutputThread("-->" + getTarget(), this.connection.getOutputStream());

        /*
         * INIT-Handshake
         */
        try
        {
            logger.debug("Initializing association");

            InitOperation initHandler = new InitOperation(this, authenticationMethod);
            requestResponse(initHandler);

            this.targetInformation = initHandler.getTargetInformation();
            this.associationReferenceId = initHandler.getReferenceId();
            logger.debug("Association " + ((this.associationReferenceId == null) ? "" : ("(" + this.associationReferenceId + ") ")) + "initialized (" + this.targetInformation + ")");
        }
        catch (ProtocolException e)
        {
            disconnect("INIT error");
            throw e;
        }
    }

    public void disconnect(String message) throws ProtocolException
    {
        checkConnection();

        logger.debug("Disconnecting association with " + getTarget());

        try
        {
            request(new CloseOperation(this, this.associationReferenceId, message));
        }
        catch (ProtocolException e)
        {
            logger.debug("Ignoring protocol error while closing association", e);
        }

        this.outputThread.shutdown();
        this.inputThread.shutdown();

        try
        {
            if (!this.connection.isClosed())
            {
                this.connection.close();
            }
        }
        catch (IOException e)
        {
            logger.warn("I/O error while closing connection to " + getTarget(), e);
        }

        this.outputThread = null;
        this.inputThread = null;
        this.connection = null;
        this.port = 0;
        this.host = null;
    }

    public void disconnect() throws ProtocolException
    {
        disconnect("Normal association shutdown");
    }

    public RecordResultSet search(Query query) throws ProtocolException
    {
        logger.debug("Search on target with " + query);

        checkConnection();

        SearchOperation searchProtocolHandler = new SearchOperation(this, query);
        requestResponse(searchProtocolHandler);

        return new RecordResultSet(this, generateResultSetName(), searchProtocolHandler.getNumOfResults(), searchProtocolHandler.getNextResult());
    }

    protected List<Record> fetch(String resultSetName, int nextResultNum, int fetchSize) throws ProtocolException
    {
        logger.debug("Fetching " + fetchSize + " record(s) for result set \"" + resultSetName + "\"; offset: " + (nextResultNum - 1));

        checkConnection();

        PresentOperation presentProtocolHandler = new PresentOperation(this, resultSetName, nextResultNum, fetchSize);
        requestResponse(presentProtocolHandler);

        return presentProtocolHandler.getRecords();
    }

    public String getTarget()
    {
        return this.host + ":" + this.port;
    }

    public boolean isRunning()
    {
        return ((this.inputThread != null) && inputThread.isAlive()) && ((this.outputThread != null) && outputThread.isAlive());
    }

    private void request(Operation protocolHandler) throws ProtocolException
    {
        synchronized (protocolHandler)
        {
            try
            {
                this.outputThread.addOutgoingOperation(protocolHandler);
                protocolHandler.wait(this.clientTimeout * 1000);
            }
            catch (InterruptedException e)
            {
            }
            finally
            {
                if (protocolHandler.hasError())
                {
                    throw protocolHandler.getError();
                }

                if (!protocolHandler.initializingRequestHasBeenSent())
                {
                    throw new ProtocolException("Client timeout while sending request");
                }
            }
        }
    }

    private void requestResponse(Operation protocolHandler) throws ProtocolException
    {
        if (!this.targetInformation.isSupportingConcurrentOperations())
        {
            concurrentOperationsLock.lock();
        }

        try
        {
            synchronized (protocolHandler)
            {
                request(protocolHandler);

                try
                {
                    this.inputThread.addPendingOperation(protocolHandler);
                    protocolHandler.wait(this.clientTimeout * 1000);
                }
                catch (InterruptedException e)
                {
                }
                finally
                {
                    if (protocolHandler.hasError())
                    {
                        throw protocolHandler.getError();
                    }

                    if (!protocolHandler.terminatingResponseReceived())
                    {
                        throw new ProtocolException("Client timeout while waiting for response");
                    }
                }
            }
        }
        finally
        {
            if (!this.targetInformation.isSupportingConcurrentOperations())
            {
                concurrentOperationsLock.unlock();
            }
        }
    }

    private void checkConnection() throws ProtocolException
    {
        if (!isRunning())
        {
            throw new ProtocolException("Not connected");
        }
    }

    public String generateReferenceId()
    {
        synchronized (this)
        {
            return "REF_ID_" + this.referenceIdCount++;
        }
    }

    public String generateResultSetName()
    {
        // TODO: support multiple result sets
        return "default";
    }

    public List<RecordDecoder> getRecordDecoders()
    {
        return recordDecoders;
    }
}
TOP

Related Classes of net.sf.jz3950.Association

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.