org.jzkit.z3950.util.ZEndpoint.java Source code

Java tutorial

Introduction

Here is the source code for org.jzkit.z3950.util.ZEndpoint.java

Source

// Title:       ZEndpoint
// @version:    $Id: ZEndpoint.java,v 1.11 2005/10/27 16:51:52 ibbo Exp $
// Copyright:   Copyright (C) 1999,2000 Knowledge Integration Ltd.
// @author:     Ian Ibbotson ( ibbo@k-int.com )
// Company:     Knowledge Integration Ltd.
// Description: Utility class representing the endpoint for a Z39.50 Association
//

//
// 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.1 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., 59 Temple Place - Suite
// 330, Boston, MA  02111-1307, USA.
//                                                                                                                                            

package org.jzkit.z3950.util;

import org.jzkit.a2j.codec.runtime.*;
import org.jzkit.a2j.codec.util.*;
import org.jzkit.z3950.gen.v3.Z39_50_APDU_1995.*;
import org.jzkit.z3950.gen.v3.NegotiationRecordDefinition_charSetandLanguageNegotiation_3.*;
import org.jzkit.search.util.RecordModel.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
  ZEndpoint : Manages the socket endpoint for a z39.50 Origin
    
  * @author Ian Ibbotson
  * @version $Id: ZEndpoint.java,v 1.11 2005/10/27 16:51:52 ibbo Exp $
  *
  * This class manages the comminication with a Z origin. It deals with accepting
  * incoming PDU's and announcing them to any observers.
  *
  * The class also has helpers to construct outgoing PDU's. It *shoud not*
  * be used to store information about the Z-Association, instead that should be
  * dealt with by users of this class.
  * 
  * Normally, we will configure an instance of this class using Beanutils.copyProperties to 
  * set all the bean style properties on this object from. IE Beanutils.copyProperties(new ZEndpoint(), config);
  *
  */

// Basic imports
import java.io.*;
import java.net.*;
import java.util.*;
import java.math.BigInteger;

import org.jzkit.a2j.gen.AsnUseful.*;
import org.jzkit.a2j.codec.util.*;

import org.jzkit.util.*;

public class ZEndpoint extends Thread {
    private Socket z_assoc = null;
    private InputStream incoming_data = null;
    private OutputStream outgoing_data = null;
    private PDU_codec codec = PDU_codec.getCodec();
    private OIDRegister reg = null;
    private boolean running = true;
    private boolean close_notified = false;

    // Properties
    private String target_hostname;
    private int target_port;
    private String charset_encoding = US_ASCII_ENCODING;
    private int auth_type;
    private int pref_message_size = 1048576;
    private int exceptional_record_size = 5242880;
    private String service_user_principal;
    private String service_user_group;
    private String service_user_credentials;

    private boolean use_refid = true;
    private int refid_counter = 0;
    private int assoc_status = ASSOC_STATUS_IDLE;

    public APDUObservable pdu_announcer = new APDUObservable();

    private boolean supports_concurrent_operations = true;

    private Object op_counter_lock = new Object();
    private int op_counter;
    private List outbound_apdu_queue = new ArrayList();

    private static final int ASSOC_STATUS_IDLE = 0;
    private static final int ASSOC_STATUS_CONNECTING = 1;
    private static final int ASSOC_STATUS_CONNECTED = 2;
    private static final int ASSOC_STATUS_PERM_FAILURE = 3;
    private static final String EMPTY_STRING = "";

    public static final String US_ASCII_ENCODING = "US-ASCII";
    public static final String UTF_8_ENCODING = "UTF-8";
    public static final String UTF_16_ENCODING = "UTF-16";

    private static Log log = LogFactory.getLog(ZEndpoint.class);

    private static int dbg_counter = 0;
    private static int active_thread_counter = 0;

    // private static Properties default_props = new Properties();

    /** The default buffer size for BER send and recieve streams */
    private static final int DEFAULT_BUFF_SIZE = 32768;

    private boolean do_charset_neg = true;

    /**
     * Z3950 Endpoint.
     */
    public ZEndpoint(OIDRegister reg) {
        super("Z3950 Search Thread");
        this.reg = reg;
        dbg_counter++;
        // this.setIsDaemon(false);
    }

    protected void finalize() {
        dbg_counter--;
        log.debug("ZEndpoint::finalize() (" + dbg_counter + " active)");
    }

    // Slightly modify the pattern... notify looks into the PDU and sends an appropriate 
    // notification rather than just an "IncomingAPDU". Seems more efficient than writing
    // 25 notify<<XXXPDU>>Event functions...
    //
    protected void notifyAPDUEvent(PDU_type pdu) {

        log.debug("notifyAPDUEvent : " + pdu.which);

        byte[] refid = null;
        // Extract the refid from the PDU
        switch (pdu.which) {
        case PDU_type.initresponse_CID:
            refid = ((InitializeResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.searchresponse_CID:
            refid = ((SearchResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.presentresponse_CID:
            refid = ((PresentResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.deleteresultsetresponse_CID:
            refid = ((DeleteResultSetResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.accesscontrolrequest_CID:
            refid = ((AccessControlRequest_type) pdu.o).referenceId;
            break;
        case PDU_type.resourcecontrolresponse_CID:
            refid = ((ResourceControlResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.resourcereportresponse_CID:
            refid = ((ResourceReportResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.scanresponse_CID:
            refid = ((ScanResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.sortresponse_CID:
            refid = ((SortResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.extendedservicesresponse_CID:
            refid = ((ExtendedServicesResponse_type) pdu.o).referenceId;
            break;
        case PDU_type.close_CID:
            refid = ((Close_type) pdu.o).referenceId;
            break;
        }

        log.debug("Incoming PDU refid: " + (refid != null ? new String(refid) : null));
        // Create the new event and add the PDU that was sent
        APDUEvent e = new APDUEvent(this, pdu, (refid != null ? new String(refid) : null));

        pdu_announcer.setChanged();
        pdu_announcer.notifyObservers(e);
    }

    /**
     *  Send an init request to the target. This is the original function that
     *  accepts no authentication information. Now just calls the new sendInitRequest
     *  with default parameters asking for no authentication 
     */
    private void sendInitRequest(AsnBitString versionInfo, AsnBitString ops, String refId, int auth_type,
            String principal, String group, String credentials) throws IOException {
        log.debug("sendInitRequest, pref=" + pref_message_size + " excep=" + exceptional_record_size);

        PDU_type pdu = new PDU_type();
        InitializeRequest_type initialize_request = new InitializeRequest_type();
        pdu.o = initialize_request;
        pdu.which = PDU_type.initrequest_CID;

        if (refId != null)
            initialize_request.referenceId = refId.getBytes();

        initialize_request.protocolVersion = versionInfo;
        initialize_request.options = ops;
        initialize_request.preferredMessageSize = BigInteger.valueOf(pref_message_size);
        initialize_request.exceptionalRecordSize = BigInteger.valueOf(exceptional_record_size);

        switch (auth_type) {
        case 1:
            log.debug("Using anonymous authentication");
            initialize_request.idAuthentication = new IdAuthentication_type();
            initialize_request.idAuthentication.which = IdAuthentication_type.anonymous_CID;
            break;
        case 2:
            log.debug("Using idopen authentication : \"" + principal + "\"");
            initialize_request.idAuthentication = new IdAuthentication_type();
            initialize_request.idAuthentication.which = IdAuthentication_type.open_CID;
            initialize_request.idAuthentication.o = principal;
            break;
        case 3:
            log.debug("Using idpass authentication : user:\"" + principal + "\"" + ", group:\"" + group + "\""
                    + ", pass:\"" + credentials + "\"");
            initialize_request.idAuthentication = new IdAuthentication_type();
            initialize_request.idAuthentication.which = IdAuthentication_type.idpass_CID;
            idPass_inline0_type idpass = new idPass_inline0_type();
            idpass.groupId = group;
            idpass.userId = principal;
            idpass.password = credentials;
            initialize_request.idAuthentication.o = idpass;
            break;
        case 0:
        default:
            log.debug("Not using z-authentication");
            initialize_request.idAuthentication = null;
            break;
        }
        initialize_request.implementationId = "174";
        initialize_request.implementationName = "JZKit2 Generic ZEndpoint";
        initialize_request.implementationVersion = "2.0";
        initialize_request.userInformationField = null;

        // Ask for utf-8 encoding of data.
        if (do_charset_neg) {
            log.debug("Sending character set negotiation in init request");
            if (initialize_request.otherInfo == null)
                initialize_request.otherInfo = new ArrayList();

            initialize_request.otherInfo.add(createCharsetNeg());
        }

        encodeAndSend(pdu);
    }

    /** createCharsetNeg : Create and populate the type asking for character set
     * negotiation.
     */
    private OtherInformationItem43_type createCharsetNeg() {
        OtherInformationItem43_type oit = new OtherInformationItem43_type();

        oit.information = new information_inline44_type();
        oit.information.which = information_inline44_type.externallydefinedinfo_CID;
        EXTERNAL_type et = new EXTERNAL_type();
        oit.information.o = et;

        et.direct_reference = reg.oidByName("z_charset_neg_3");

        if (et.direct_reference == null)
            log.warn("Unable to locate direct reference for oid");

        et.encoding = new encoding_inline0_type();
        et.encoding.which = encoding_inline0_type.single_asn1_type_CID;

        CharSetandLanguageNegotiation_type t = new CharSetandLanguageNegotiation_type();
        et.encoding.o = t;

        t.which = CharSetandLanguageNegotiation_type.proposal_CID;
        OriginProposal_type proposal = new OriginProposal_type();
        t.o = proposal;

        proposal.proposedCharSets = new ArrayList();

        // For now, we just ask for UTF-8 as a charcter set (No language neg.)
        proposedCharSets_inline0_choice1_type c1 = new proposedCharSets_inline0_choice1_type();
        c1.which = proposedCharSets_inline0_choice1_type.iso10646_CID;
        Iso10646_type utf_8_request = new Iso10646_type();
        c1.o = utf_8_request;
        utf_8_request.encodingLevel = reg.oidByName("charset_utf8");
        proposal.proposedCharSets.add(c1);

        return oit;
    }

    // Used to take a QueryModel, now accepts a Z39.50 Query_type.. Need to remove all
    // dependencies on JIRI.
    public void sendSearchRequest(ArrayList database_names, Query_type z3950_query_model, String reference_id,
            int ssub, int lslb, int mspn, boolean replace, String setname, String ssen, String msen,
            int[] prefRecSynOID) throws IOException {
        // In debug mode, dump the query we have been asked to send.
        log.debug("Sending search request with refid: " + reference_id);

        // Create a search request
        PDU_type pdu = new PDU_type();
        pdu.which = PDU_type.searchrequest_CID;
        SearchRequest_type srt = new SearchRequest_type();
        pdu.o = srt;

        if (reference_id != null)
            srt.referenceId = reference_id.getBytes();
        srt.smallSetUpperBound = BigInteger.valueOf(ssub);
        srt.largeSetLowerBound = BigInteger.valueOf(lslb);
        srt.mediumSetPresentNumber = BigInteger.valueOf(mspn);
        srt.replaceIndicator = Boolean.TRUE;

        srt.resultSetName = setname;

        srt.databaseNames = database_names;

        if (ssen != null) {
            srt.smallSetElementSetNames = new ElementSetNames_type();
            srt.smallSetElementSetNames.which = ElementSetNames_type.genericelementsetname_CID;
            srt.smallSetElementSetNames.o = ssen;
        }

        if (msen != null) {
            srt.mediumSetElementSetNames = new ElementSetNames_type();
            srt.mediumSetElementSetNames.which = ElementSetNames_type.genericelementsetname_CID;
            srt.mediumSetElementSetNames.o = msen;
        }

        srt.preferredRecordSyntax = prefRecSynOID;

        srt.query = z3950_query_model;
        // srt.query = z3950_query_model.toASNType();
        // Query_type query;
        // srt.query = new Query_type();
        // srt.query.which=Query_type.type_1_CID;
        // srt.query.o =  RPNHelper.RootNodeToZRPNStructure(query_tree,charset_encoding);

        encodeAndSend(pdu);
    }

    public void sendPresentRequest(String refid, String rsname, long first, long count,
            ExplicitRecordFormatSpecification spec) throws IOException {
        sendPresentRequest(refid, rsname, first, count, spec.getSetname().getRef(),
                FormatSpecOIDHelper.getOID(reg, spec));
    }

    public void sendPresentRequest(String refid, String rsname, long first, long count, String element_set_name,
            int[] preferred_record_syntax_oid) throws IOException {
        //if ( log.isDebugEnabled() )
        //{
        log.debug("sendPresentRequest, refid=" + refid);
        log.debug("setname: " + rsname);
        log.debug("first=" + first);
        log.debug("count=" + count);
        //}
        PDU_type pdu = new PDU_type();
        pdu.which = PDU_type.presentrequest_CID;
        PresentRequest_type pr = new PresentRequest_type();
        pdu.o = pr;

        // System.err.println("Send present request, element set name is "+element_set_name);

        if (refid != null)
            pr.referenceId = refid.getBytes();

        pr.resultSetId = rsname;
        pr.resultSetStartPoint = BigInteger.valueOf((long) first);
        pr.numberOfRecordsRequested = BigInteger.valueOf((long) count);

        // If element_set_name is non-null, set the recordComposition
        if (element_set_name != null) {
            pr.recordComposition = new recordComposition_inline9_type();
            pr.recordComposition.which = recordComposition_inline9_type.simple_CID;
            ElementSetNames_type esn = new ElementSetNames_type();
            pr.recordComposition.o = esn;
            esn.which = ElementSetNames_type.genericelementsetname_CID;
            esn.o = element_set_name;
        }

        // preferred_record_syntax
        // pr.preferredRecordSyntax = spec.getFormat().getValue();
        pr.preferredRecordSyntax = preferred_record_syntax_oid;

        encodeAndSend(pdu);
    }

    /**
     * sort_sequence is a list of type x
     */
    public void sendSortRequest(String refid, ArrayList input_result_set_names, String output_result_set_name,
            ArrayList sort_sequence) throws IOException {
        PDU_type pdu = new PDU_type();
        pdu.which = PDU_type.sortrequest_CID;
        SortRequest_type sr = new SortRequest_type();
        pdu.o = sr;

        if (refid != null)
            sr.referenceId = refid.getBytes();
        sr.inputResultSetNames = input_result_set_names;
        sr.sortedResultSetName = output_result_set_name;
        sr.sortSequence = sort_sequence;
        encodeAndSend(pdu);
    }

    /** sendScanRequest: Send a scan request to the remote server.
     *
     */
    public void sendScanRequest(String refid, ArrayList database_names, Query_type z3950_query_model, // QueryModel qm,
            long step_size, long number_of_terms_requested, long preferred_position_in_response)
            throws IOException {
        log.debug("Sending scan request....");

        // PDU_type pdu = new PDU_type();
        // pdu.which = PDU_type.scanrequest_CID;
        // ScanRequest_type sr = new ScanRequest_type();
        // pdu.o = sr;

        // if ( refid != null )
        //   sr.referenceId = refid.getBytes();

        // sr.databaseNames = database_names;

        // RootNode rn = qm.toRPN();

        // String attrset_name = rn.getAttrset();
        // sr.attributeSet = reg.oidByName(attrset_name);

        // if ( rn.getChild() instanceof AttrPlusTermNode)
        //   sr.termListAndStartPoint = RPNHelper.AttrPlusTermNode2apt_type((AttrPlusTermNode)(rn.getChild()),charset_encoding);
        // else
        //   log.warn("Unable to parse scan expression");

        // sr.stepSize = BigInteger.valueOf(step_size);
        // sr.numberOfTermsRequested = BigInteger.valueOf(number_of_terms_requested);
        // sr.preferredPositionInResponse = BigInteger.valueOf(preferred_position_in_response);
        // sr.otherInfo = null;

        // encodeAndSend(pdu);
    }

    public void sendCloseRequest(String refid, long reason, String diagnosticInfo) throws IOException {
        PDU_type pdu = new PDU_type();
        pdu.which = PDU_type.close_CID;
        Close_type cr = new Close_type();
        pdu.o = cr;

        if (refid != null)
            cr.referenceId = refid.getBytes();
        cr.closeReason = BigInteger.valueOf((long) reason);
        cr.diagnosticInformation = diagnosticInfo;

        encodeAndSend(pdu);
    }

    /** setSerialOps: Called to indicate that the assoc cannot handle concurrent operations.
     * All requests to send APDU's <i>With the exception of close</i> 
     * will block until the previous operation has completed.
     */
    public void setSerialOps() {
        this.supports_concurrent_operations = false;
    }

    public void encodeAndSend(PDU_type the_pdu) throws IOException {
        log.debug("encodeAndSend...");

        if ((!supports_concurrent_operations) && (the_pdu.which != PDU_type.close_CID)) {
            enqueueOutboundAPDU(the_pdu);
            sendPending();
        } else {
            internalEncodeAndSend(the_pdu);
        }
    }

    private void enqueueOutboundAPDU(PDU_type the_pdu) {
        log.debug("enqueue outbound apdu");

        synchronized (outbound_apdu_queue) {
            outbound_apdu_queue.add(the_pdu);
        }
    }

    private void sendPending() {
        log.debug("sendPending()");

        // Here we check that there is currently no outstanding operation and then
        // send the next apdu on the queue.
        PDU_type pdu = null;

        synchronized (op_counter_lock) {
            // If there are no outstanding operations
            if (op_counter == 0) {
                synchronized (outbound_apdu_queue) {
                    if (outbound_apdu_queue.size() > 0) {
                        pdu = (PDU_type) outbound_apdu_queue.remove(0);
                    }
                }
            }

            if (pdu != null) {
                try {
                    log.debug("Sending queued apdu");
                    internalEncodeAndSend(pdu);
                } catch (IOException ioe) {
                    log.warn("Problem sending enqueued apdu", ioe);
                }
            }
        }
    }

    private synchronized void internalEncodeAndSend(PDU_type the_pdu) throws IOException {
        incOpCount();
        BEROutputStream encoder = new BEROutputStream(DEFAULT_BUFF_SIZE, charset_encoding, reg);
        codec.serialize(encoder, the_pdu, false, "PDU");
        encoder.flush();
        encoder.writeTo(outgoing_data);
        outgoing_data.flush();
        yield();
    }

    private void incOpCount() {
        synchronized (op_counter_lock) {
            op_counter++;
            op_counter_lock.notifyAll();
        }
    }

    private void decOpCount() {
        synchronized (op_counter_lock) {
            op_counter--;
            op_counter_lock.notifyAll();
        }
    }

    public void notifyClose() {
        PDU_type pdu = new PDU_type();
        pdu.which = PDU_type.close_CID;
        Close_type close = new Close_type();
        pdu.o = close;
        close.closeReason = BigInteger.valueOf(100);
        close.diagnosticInformation = "Internal close notification";

        // This is a bit of a hack.. Notify ourselves that the assoc has closed.
        notifyAPDUEvent(pdu);
    }

    public void shutdown() throws IOException {
        log.debug("ZEndpoint::shutdown() - host=" + target_hostname + " status=" + assoc_status + " running="
                + running);

        if (z_assoc != null) {
            if (assoc_status == ASSOC_STATUS_CONNECTED)
                sendCloseRequest((String) null, 0, "User requested shutdown");

            this.interrupt();

            running = false;
            z_assoc.close();
            z_assoc = null;
            assoc_status = ASSOC_STATUS_IDLE;
        }
    }

    public void run() {
        log.debug("Bringing assoc up........Active Z Thread counter = " + (++active_thread_counter));
        log.debug("My thread priority : " + this.getPriority());
        log.debug("My isDaemon: " + this.isDaemon());

        try {
            assoc_status = ASSOC_STATUS_CONNECTING;
            connect();
            assoc_status = ASSOC_STATUS_CONNECTED;
            log.debug("Connect completed OK, Listening for incoming PDUs");
        } catch (ConnectException ce) {
            log.info(ce.toString());
            assoc_status = ASSOC_STATUS_PERM_FAILURE;
            sendDummyFailInitResponse(ce.toString());
            running = false;
        } catch (IOException ioe) {
            log.warn("ZEndpoint thread encountered an exception", ioe);
            assoc_status = ASSOC_STATUS_PERM_FAILURE;
            sendDummyFailInitResponse(ioe.toString());
            running = false;
        }

        while (running) {
            try {
                log.debug("Waiting for data on input stream.....");
                BERInputStream bds = new BERInputStream(incoming_data, charset_encoding, DEFAULT_BUFF_SIZE, reg);
                PDU_type pdu = null;
                pdu = (PDU_type) codec.serialize(bds, pdu, false, "PDU");
                log.debug("Notifiy observers");

                if (pdu.which == PDU_type.close_CID) {
                    log.debug("Just got a close APDU");
                    close_notified = true;
                }

                decOpCount();

                notifyAPDUEvent(pdu);

                // If the target does not support concurrent operations then it's possible that
                // outbound APDU's have stacked up whilst we wait for the response handled here.
                // Therefore, here we  check the outgoing apdu queue and send any messages that
                // have been queued
                if (!close_notified)
                    sendPending();

                log.debug("Yield to other threads....");
                yield();
            } catch (InterruptedIOException iioe) {
                log.debug("Processing java.io.InterruptedIOException, shut down association" + " - hostname="
                        + target_hostname);
                log.info(iioe.toString());
                running = false;
            } catch (SocketException se) {
                // Most likely socket closed.
                log.info("SocketException");
                log.info(se.toString() + " - hostname=" + target_hostname);
                running = false;
            } catch (Exception e) {
                log.warn("ZEndpoint Unknown error", e);
                log.info(e.toString() + " - hostname=" + target_hostname);
                running = false;
            } finally {
            }
        }

        synchronized (op_counter_lock) {
            op_counter_lock.notifyAll();
        }

        // We might need to notify any listening objects that the assoc has been
        // shut down if the target did not send a close before snapping the assoc
        // (Or in case there was a network problem etc)
        if (!close_notified) {
            notifyClose();
        }

        // End of association, clear out all listeners
        log.debug("End of ZEndpoint listening thread for host " + target_hostname + " active z thread counter="
                + (--active_thread_counter));
        pdu_announcer.deleteObservers();
        pdu_announcer = null;

        try {
            incoming_data = null;
            outgoing_data = null;
            if (z_assoc != null)
                z_assoc.close();
        } catch (Exception e) {
            // catches the socket close execption...
        }

        assoc_status = ASSOC_STATUS_IDLE;
        z_assoc = null;
    }

    protected void connect() throws ConnectException, IOException {
        // connect("INIT");  // Backward compat. but useless for Isite targets :-(
        connect(null);
    }

    protected void connect(String refid) throws ConnectException, IOException {
        if ((null != target_hostname) && (target_port > 0)) {
            log.debug("Attempting to connect to " + target_hostname + ":" + target_port);
            int timeout = 20000;
            z_assoc = new Socket(target_hostname, target_port);
            outgoing_data = z_assoc.getOutputStream();
            incoming_data = z_assoc.getInputStream();
        } else {
            throw new ConnectException("Properties do not contain ServiceHost and/or ServicePort");
        }

        log.debug("Connect completed OK, send init request (nodelay=" + z_assoc.getTcpNoDelay() + ", timeout="
                + z_assoc.getSoTimeout() + ", linger=" + z_assoc.getSoLinger() + ")");

        // Will allow user access to init parameters via props soon...
        // We might need to sleep here for a little while to give the remote target
        // a chance to warm up (testing against moray.stir.ac.uk indicates this to be
        // the case).

        AsnBitString version_info = new AsnBitString();
        version_info.setBit(0);
        version_info.setBit(1);
        version_info.setBit(2);

        AsnBitString options = new AsnBitString();
        options.setBit(0); // search
        options.setBit(1); // present
        options.setBit(2); // del Set
        // options.setBit(3);     // resourceReport
        // options.setBit(4);     // trigger Resource Control
        // options.setBit(5);     // ResourceControl
        // options.setBit(6);     // Access control
        options.setBit(7); // scan
        options.setBit(8); // Sort
                           // 9 is reserved
        options.setBit(10); // Extended Services
        // options.setBit(11);    // Level 1 Segmentations
        // options.setBit(12);    // Level 2 Segmentations
        options.setBit(13); // Concurent ops
        options.setBit(14); // Named result sets

        String s = null;

        // Init request, 8k pref. message size, 64k exceptional
        sendInitRequest(version_info, options, refid, auth_type, service_user_principal, service_user_group,
                service_user_credentials);

        log.debug("Sent init request");
    }

    private void sendDummyFailInitResponse(String reason) {
        PDU_type pdu = new PDU_type();
        InitializeResponse_type initialize_response = new InitializeResponse_type();
        pdu.o = initialize_response;
        pdu.which = PDU_type.initresponse_CID;

        initialize_response.referenceId = new byte[0];
        initialize_response.protocolVersion = new AsnBitString();
        initialize_response.options = new AsnBitString();
        initialize_response.result = Boolean.FALSE;

        notifyAPDUEvent(pdu);
    }

    public int getAssocStatus() {
        return assoc_status;
    }

    public Observable getPDUAnnouncer() {
        return pdu_announcer;
    }

    public String genRefid(String base) {
        if (use_refid)
            return base + ":" + (refid_counter++);

        return null;
    }

    public String getHost() {
        return target_hostname;
    }

    public void setHost(String host) {
        this.target_hostname = host;
    }

    public int getPort() {
        return target_port;
    }

    public void setPort(int port) {
        this.target_port = port;
    }

    public String getCharsetEncoding() {
        return charset_encoding;
    }

    public void setCharsetEncoding(String enc) {
        if (enc != null)
            charset_encoding = enc;
        else
            throw new RuntimeException("Null charset encoding is not allowed");
    }

    public int getAuthType() {
        return auth_type;
    }

    public void setAuthType(int auth_type) {
        this.auth_type = auth_type;
    }

    public int getPrefMessageSize() {
        return pref_message_size;
    }

    public void setPrefMessageSize(int pref_message_size) {
        this.pref_message_size = pref_message_size;
    }

    public int getExceptionalMessageSize() {
        return exceptional_record_size;
    }

    public void setExceptionalMessageSize(int exceptional_record_size) {
        this.exceptional_record_size = exceptional_record_size;
    }

    public String getServiceUserPrincipal() {
        return service_user_principal;
    }

    public void setServiceUserPrincipal(String service_user_principal) {
        this.service_user_principal = service_user_principal;
    }

    public String getServiceUserGroup() {
        return service_user_group;
    }

    public void setServiceUserGroup(String service_user_group) {
        this.service_user_group = service_user_group;
    }

    public String getServiceUserCredentials() {
        return service_user_credentials;
    }

    public void setServiceUserCredentials(String service_user_credentials) {
        this.service_user_credentials = service_user_credentials;
    }

    public void setDoCharsetNeg(boolean do_charset_neg) {
        this.do_charset_neg = do_charset_neg;
    }

    public boolean getDoCharsetNeg() {
        return do_charset_neg;
    }
}