/*
 *  ClientPool.java v0.10 28-JAN-2000
 *  Copyright (c) TKK/TLM/Calypso
 *  Author: Alexey Mednonogov
 */

package codec.client;

import java.io.*;
import java.util.*;

import codec.*;
import codec.adapt.*;
import codec.convert.*;
import codec.debug.*;
import codec.dyntree.*;
import codec.export.*;
import codec.orb.*;
import codec.pco.*;
import codec.server.*;
import codec.client.*;
import codec.visit.*;
import codec.build.*;

/** Class for handling Universal Servants exploited by Client objects.
 *  It registers and deregisters Universal Servants located in SUT and
 *  handles Client operations. */
final public class ClientPool {

	/** Defines how regularly sleeping thread will check for changes
     *  in DynObject status. */
	private static final long WAIT_TIMEOUT_MILLIS = 1000;

	/** Create an array of identifiers which could be used as a return
     *  value of "_ids()" method of DynObject.
     *  @return In case of error, returns <code>null</code>. */
	private static String[] extractIds(String pcoName) {

		String[] nameScope = AspConverter.extractNameScope(pcoName);
		if (nameScope == null) return null;

		StringBuffer buffer = new StringBuffer();
		buffer.append("IDL:");

		for (int i = 0; i < nameScope.length - 1; i++) {

			buffer.append(nameScope[i]);
			buffer.append("/");
		}
		buffer.append(nameScope[nameScope.length - 1]);
		buffer.append(":1.0");

		String[] ids = new String[1];
		ids[0] = buffer.toString();
		return ids;
	}

    /** Associate PCO and Universal Servant identified by IOR file location. 
     *  @param <code>asp</code> contains pCREG_RFILE PDU. */
    public static void registerRefFile(String pcoName, com.t3.ot.pco.ASP asp)
    {
        // Create Universal Servant:
		String[] ids = extractIds(pcoName);

		if (ids == null) {

			System.err.println("ClientPool::registerRefFile(): " +
				"PCO name is constructed incorrectly.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
        DynObject object = new DynObject(ids);

        // Extract refFile from ASP:
        String refFile = AspConverter.extractRefFile(asp);

		if (refFile == null) {

			System.err.println("ClientPool::registerRefFile(): " +
				"Can't extract IOR file location from PDU.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
        CorbaServer.getCodecObject().getCorbaObjectPool().deregister(pcoName);
        try {
			String ior = CorbaServer.getCodecObject().getOrbGeneric().
				getORB().object_to_string(object);

			PrintWriter out = new PrintWriter(new FileOutputStream(refFile));

			out.println(ior);
			out.flush();
		}
		catch (IOException ex) {

			System.err.println("ClientPool::registerRefFile(): " +
                "Can't write IOR to file: " + ex.getMessage());
            AspConverter.throwFatalException(pcoName);
            return;
		}

        object.refType = DynObject.REF_TYPE_RFILE;
        object.refFile = refFile;
        object.nsLocation = null;

        CorbaServer.getCodecObject().getCorbaObjectPool().
            register(object, pcoName, CorbaObject.PCO_TYPE_CLIENT);

        AspConverter.throwRecoverableException(pcoName);
        object.isInitialLockReleased = new Boolean(true);
    }

    /** Associate PCO and Universal Servant identified by location in
     *  the Naming Service. 
     *  @param <code>asp</code> contains pCREG_NSERV PDU. */
    public static void registerNaming(String pcoName, com.t3.ot.pco.ASP asp) {

        int i, j;
		String[] ids = extractIds(pcoName);

		if (ids == null) {

			System.err.println("ClientPool::registerNaming(): " +
				"PCO name is constructed incorrectly.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
        DynObject object = new DynObject(ids);

		org.omg.CosNaming.NamingContext nc = CorbaServer.
			getCodecObject().getOrbGeneric().getNamingContext();

        // Extract location of object in Naming Service from ASP:
        String[] nsLocation = AspConverter.extractNamingContext(pcoName, asp);

		// Check whether location has been extracted successfully. If not,
		// then Tester side is already notified of that by Exception PDU:
		if (nsLocation == null) return;

        // Put Universal Servant into appropriate location in naming
        // hierarchy in the Naming Service:

        org.omg.CosNaming.NameComponent[][] components =
            new org.omg.CosNaming.NameComponent[nsLocation.length][];

		for (i = 0; i < nsLocation.length; i++) {

			components[i] = new org.omg.CosNaming.NameComponent[i + 1];

			for (j = 0; j < i + 1; j++) {

				components[i][j] = new org.omg.CosNaming.NameComponent();
				components[i][j].id = nsLocation[j];
				components[i][j].kind = "";
			}
		}
        try {

        for (i = 0; i < nsLocation.length - 1; i++) {

			try { nc.bind_new_context(components[i]); }
			catch (org.omg.CosNaming.NamingContextPackage.AlreadyBound ex) { }
		}
        CorbaServer.getCodecObject().getCorbaObjectPool().deregister(pcoName);
        nc.rebind(components[nsLocation.length - 1], object);

        }
        catch (org.omg.CosNaming.NamingContextPackage.NotFound ex)
        {
            System.err.println("ClientPool::registerNaming(): " +
                "CORBA object was not found in Naming Service.");
            AspConverter.throwFatalException(pcoName);
            return;
        }
        catch (org.omg.CosNaming.NamingContextPackage.CannotProceed ex)
        {
            System.err.println("ClientPool::registerNaming(): Could " + 
                "not proceed with registration through Naming Service.");
            AspConverter.throwFatalException(pcoName);
            return;

        }
        catch (org.omg.CosNaming.NamingContextPackage.InvalidName ex)
        {
            System.err.println("ClientPool::registerNaming(): Naming " + 
                "context extracted from PDU points to invalid name.");
            AspConverter.throwFatalException(pcoName);
            return;
        }

        object.refType = DynObject.REF_TYPE_NSERV;
        object.nsLocation = nsLocation;
        object.refFile = null;

        CorbaServer.getCodecObject().getCorbaObjectPool().
            register(object, pcoName, CorbaObject.PCO_TYPE_CLIENT);

        AspConverter.throwRecoverableException(pcoName);
        object.isInitialLockReleased = new Boolean(true);
    }

    /** Invoke operation coming from Client object. */
    public static void callOperation(DynObject object,
        org.omg.CORBA.ServerRequest request) {

        int i;

        while (true) if (object.isInitialLockReleased.
          booleanValue() == true) break;

        CorbaObject corbaObject = CorbaServer.getCodecObject().
			getCorbaObjectPool().getObject(object);

        if (corbaObject == null) {

			System.err.println("ClientPool::callOperation(): An attempt " + 
                "is made to invoke operation from non-registered servant.");
            throw new org.omg.CORBA.UNKNOWN();
		}
        if (corbaObject.getPcoType() != CorbaObject.PCO_TYPE_CLIENT) {

			System.err.println("ClientPool::callOperation(): Error: " + 
                "Registered DynObject is not of type PCO_TYPE_CLIENT.");
            throw new org.omg.CORBA.UNKNOWN();
		}
        String pcoName = corbaObject.getPcoName();
        long callID = AspConverter.getUniqueID();

        String[] nameScope = AspConverter.extractNameScope(pcoName);

		if (nameScope == null) {

			System.err.println("ClientPool::callOperation(): Error: " + 
                "Failed to extract name scope from the PCO name.");
            throw new org.omg.CORBA.UNKNOWN();
		}
		String[] opNameScope = new String[nameScope.length + 1];

		for (i = 0; i < nameScope.length; i++)
		{ opNameScope[i] = nameScope[i]; }

		opNameScope[nameScope.length] = request.op_name();

        StringBuffer buffer = new StringBuffer();
		buffer.append("pCALL_i");
		buffer.append(AspConverter.createFromNameScope(opNameScope));

        String aspName = buffer.toString();
        ClientRequest clientRequest = new ClientRequest(
			pcoName, aspName, callID, request, object);

        CorbaServer.getCodecObject().getClientRequestPool().
            register(clientRequest);		

		ClientCallBuilder builder = new ClientCallBuilder();
		builder.construct(clientRequest);
	    AspConverter.sendASP(clientRequest.getPcoName(), builder.getAsp());

        // Wait until operation response arrives from Tester if
        // operation is not declared as oneway:

        if (!builder.getIsOneway()) {
            synchronized (object) {
		        while (true) {

				    if ((clientRequest.isProcessed.booleanValue() == true) || 
                        (clientRequest.isException.booleanValue() == true) ||
                        (object.isException.booleanValue() == true)) break;

  			        try { object.wait(WAIT_TIMEOUT_MILLIS); }
                    catch (InterruptedException ex) { }
				}
			}
		}

        CorbaServer.getCodecObject().getClientRequestPool().
            deregister(clientRequest.getCallID());

        if ((clientRequest.isException.booleanValue() == true) ||
            (object.isException.booleanValue() == true))
        { throw new org.omg.CORBA.UNKNOWN(); }

    }

    /** Handle the operation response coming from Tester. */
    public static void operResponse(String pcoName, com.t3.ot.pco.ASP asp) {

        ClientRequest clientRequest = null;

        try {
            if (asp.getName().equals("pRAISE") == false) {

                // Normal operation response:
				ClientReplyVisitor visitor =
					new ClientReplyVisitor(pcoName, asp);
				asp.accept(visitor);
			    clientRequest = visitor.getClientRequest();
			}
			else {

				// Exception has been thrown:
				ClientExceptionVisitor visitor =
					new ClientExceptionVisitor(pcoName, asp);
				visitor.accept(asp);
			    clientRequest = visitor.getClientRequest();
			}
		}
		catch (VisitorInvalidFormat vif) {
			System.err.println("ClientPool::operResponse(): " +
                "PDU has invalid format. Reason: " + 
                vif.getMessage());
            AspConverter.throwFatalException(pcoName);
            return;
		}
		catch (VisitorPcoUnreg vpu) {
			System.err.println("ClientPool::operResponse(): " +
                "PDU is sent through unregistered PCO which " +
                "does not refer to any CORBA object.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
		catch (VisitorNotClient vns) {
			System.err.println("ClientPool::operResponse(): " +
                "PDU is sent through registered PCO which " +
                "does not refer to Universal Servant object.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
		catch (VisitorOperationUndef vou) {
			System.err.println("ClientPool::operResponse(): " +
                "PDU name refers to operation not found in " +
                "Interface Repository.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
		catch (VisitorAttributeUndef vau) {
			System.err.println("ClientPool::operResponse(): " +
                "PDU name refers to attribute not found in " +
                "Interface Repository.");
            AspConverter.throwFatalException(pcoName);
            return;
		}
		catch (com.t3.ot.misc.OtException oe) {
			System.err.println("ClientPool::operResponse(): " +
                "OtException caught.");
            AspConverter.throwFatalException(pcoName);
            return;
		}

        if (clientRequest == null) {

			System.err.println("ClientPool::operResponse(): " +
                "PDU is sent as a response to operation " +
                "for which corresponding internal binding was " +
                "not found.");
            AspConverter.throwFatalException(pcoName);
			return;
		}

		DynObject object = clientRequest.getObject();

		synchronized (clientRequest.isProcessed) {
			synchronized (object) {

                clientRequest.isProcessed = new Boolean(true);
				object.notifyAll();
			}
		}
    }

	/** Perform clean-up actions necessary for accurate removal of a
     *  Universal Servant from CORBA Universe. */
	public static void removeServant(CorbaObject corbaObject) {

		int i;

        DynObject object = (DynObject) corbaObject.getObject();

        // Notify threads waiting for response from invocations initiated
        // through this object, so that they would terminate immediately:

        synchronized (object.isException) {
			synchronized (object) {

                object.isException = new Boolean(true);
				object.notifyAll();
			}
		}

        // Remove all kind of advertisements made for this object in
        // the CORBA Universe:

        if (object.refType == DynObject.REF_TYPE_RFILE) {

            // Object has been advertised by IOR file, remove this file:

			String refFile = object.refFile;
            File file = new File(refFile);
            try { file.delete(); }
            catch (java.lang.Exception ex) { }
		}
		else {

			// Object has been advertised by location in Naming Service,
            // remove existing binding from Naming Service:

            String[] nsLocation = object.nsLocation;

            org.omg.CosNaming.NamingContext nc = CorbaServer.
  			    getCodecObject().getOrbGeneric().getNamingContext();

            org.omg.CosNaming.NameComponent[] components =
				new org.omg.CosNaming.NameComponent[nsLocation.length];

			for (i = 0; i < components.length; i++) {

				components[i] = new org.omg.CosNaming.NameComponent();
				components[i].id = nsLocation[i];
				components[i].kind = "";
			}

            try { nc.unbind(components); }
            catch (java.lang.Exception exc) { }

		}

		org.omg.CORBA.ORB orb =
		    CorbaServer.getCodecObject().getOrbGeneric().getORB();

        // Remove reference to this object internally held by ORB. Unless
        // we do that, garbage collector will be unable to release it:

        orb.disconnect(object);
	}
}

