/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 * 
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

/*
 * Copyright 2004-2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
//----------------------------------------------------------------------------
//
// Module:      DefaultTransactionService.java
//
// Description: The JTS TransactionService class.
//
// Product:     com.sun.jts.CosTransactions
//
// Author:      Simon Holdsworth
//
// Date:        June, 1997
//
// Copyright (c):   1995-1997 IBM Corp.
//
//   The source code for this program is not published or otherwise divested
//   of its trade secrets, irrespective of what has been deposited with the
//   U.S. Copyright Office.
//
//   This software contains confidential and proprietary information of
//   IBM Corp.
//----------------------------------------------------------------------------

package com.sun.jts.CosTransactions;

// Import required classes.

import java.util.*;
import java.net.InetAddress;
import java.net.UnknownHostException;

import org.omg.CORBA.*;
import org.omg.CORBA.ORBPackage.InvalidName;
import org.omg.CosTransactions.*;
import org.omg.CORBA.Policy.*;
import org.omg.CosNaming.*;
import org.omg.CosNaming.NamingContextPackage.*;
import org.omg.PortableServer.*;
import org.omg.CORBA.LocalObject;

import com.sun.corba.ee.spi.costransactions.TransactionService;
import com.sun.corba.ee.spi.presentation.rmi.StubAdapter;

import com.sun.jts.otsidl.*;

import java.util.logging.Logger;
import java.util.logging.Level;
import com.sun.logging.LogDomains;
import com.sun.jts.utils.LogFormatter;

/**The DefaultTransactionService is our implemention of the
 * com.sun.CosTransactions.TransactionService class.
 * <p>
 * The TransactionService abstract class describes the packaging of a
 * Java Transaction Service implementation. Each implementation should be
 * packaged as a subclass of the TransactionService class.
 *
 * @version 0.01
 *
 * @author Simon Holdsworth, IBM Corporation
 *
 * @see
*/
//----------------------------------------------------------------------------// CHANGE HISTORY
//
// Version By     Change Description
//   0.01  SAJH   Initial implementation.
//-----------------------------------------------------------------------------

public class DefaultTransactionService implements TransactionService,
    ProxyChecker {
    private static CurrentImpl currentInstance = null;
    private static TransactionFactoryImpl factoryInstance = null;
    //private static AdministrationImpl adminInstance = null;
    private static NamingContext namingContext = null;
    private static ORB orb = null;
    private static boolean recoverable = false;
    private static boolean poasCreated = false;
    private static boolean active = false;
	/*
		Logger to log transaction messages
	*/ 
	static Logger _logger = LogDomains.getLogger(LogDomains.TRANSACTION_LOGGER);
    public static final String JTS_SERVER_ID = "com.sun.jts.persistentServerId"; /* FROZEN */

   
	/**Default constructor.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    public DefaultTransactionService() {
        // We do not set up the Current instance until we know the ORB.
        // This method is not traced as trace is not configured until the init method
        // is called.

    }

    /**
     * @return true, if transaction manager is available.
     */
    public static boolean isActive() {
        return active;
    }
    
    /**Obtain the implementation of the Current interface provided
     * by the transaction service implementation.
     *
     * @param
     *
     * @return An instance of the Current class
     *
     * @see
     */
    public org.omg.CosTransactions.Current get_current() {
        org.omg.CosTransactions.Current result = null;

        result = (org.omg.CosTransactions.Current)currentInstance;

        return result;
    }

    /**Request the transaction service to identify itself with a communication
     * manager.
     * <p>
     * Multiple communication managers may request a transaction
     * service to identify itself.
     *
     * @param orb         The ORB to be used for communication.
     * @param ident       The TSIdentification object with which the Sender and
     *                    Receiver must be registered.
     * @param properties  The Properties with which the ORB was initialised.
     *
     * @return
     *
     * @see
     */
    public void identify_ORB(ORB orb,
                             TSIdentification ident,
                             Properties properties) {
        if( this.orb == null ) {
            this.orb = orb;
            Configuration.setORB(orb);
            Configuration.setProperties(properties);
            Configuration.setProxyChecker(this);
        }

        // We have to wait until this point to trace entry into this method as trace
        // is only started by setting the properties in the Configuration class.

        // Get the persistent server id of the server.  If it does not represent a
        // transient server, then the server is recoverable.

        if( !poasCreated ) {
            //String serverId = properties.getProperty("com.sun.corba.ee.internal.POA.ORBServerId"/*#Frozen*/);
            String serverId = properties.getProperty(JTS_SERVER_ID);
            if (serverId == null) {
                serverId = 
                    properties.getProperty("com.sun.CORBA.POA.ORBServerId"/*#Frozen*/);
            }
            if (serverId != null) {
					_logger.log(Level.INFO,"jts.startup_msg",serverId);
            }
            String serverName = "UnknownHost"/*#Frozen*/;
            try {
                serverName = InetAddress.getLocalHost().getHostName();
            } catch (UnknownHostException ex) {
            }
            if( serverId != null ) {
                Configuration.setServerName(serverName + "," + 
		    Configuration.getPropertyValue(Configuration.INSTANCE_NAME)
		    +  ",P" + serverId/*#Frozen*/, true);
                recoverable = true;
            } else {
                long timestamp = System.currentTimeMillis();
                Configuration.setServerName(serverName + ",T" +
                                            String.valueOf(timestamp)/*#Frozen*/, false);
            }

            // Set up the POA objects for transient and persistent references.

            try {
                createPOAs();
            } catch( Exception exc ) {
				_logger.log(Level.WARNING,"jts.unexpected_error_when_creating_poa",exc);
                throw new INTERNAL(MinorCode.TSCreateFailed,CompletionStatus.COMPLETED_NO);
            }
        }

        // Set up the instance of the Current object now that we know the ORB.

        if( currentInstance == null )
           try {
                currentInstance = new CurrentImpl();
            } catch( Exception exc ) {
				_logger.log(Level.WARNING,"jts.unexpected_error_when_creating_current",exc);
                throw new INTERNAL(MinorCode.TSCreateFailed,CompletionStatus.COMPLETED_NO);
            }

        // Identify Sender and Receiver objects to the Comm Manager.

        SenderReceiver.identify(ident);

        // If the server is recoverable, create a NamingContext with which to
        // register the factory and admin objects.

        if( recoverable && namingContext == null )
            try {
                namingContext = NamingContextHelper.narrow(orb.resolve_initial_references("NameService"/*#Frozen*/));
            } catch( InvalidName inexc ) {
				// _logger.log(Level.WARNING,"jts.orb_not_running");
				if (_logger.isLoggable(Level.FINE)) {
					_logger.log(Level.FINE,"jts.orb_not_running");
				}
            } catch( Exception exc ) {
				// _logger.log(Level.WARNING,"jts.orb_not_running");
				if (_logger.isLoggable(Level.FINE)) {
					_logger.log(Level.FINE,"jts.orb_not_running");
				}
            }

        // Create a TransactionFactory object and register it with the naming service
        // if recoverable.

        if( factoryInstance == null )
            try {
                boolean localFactory = true;
                TransactionFactory factory = null;
                factoryInstance = new TransactionFactoryImpl();
                if (localFactory) {
                    factory = (TransactionFactory) factoryInstance;
                } else {
                    factory = factoryInstance.object();
                }

                // Since we are instantiating the TransactionFactory object
                // locally, we have a local transaction factory.

                Configuration.setFactory(factory, /*local factory*/ localFactory);

                if (Configuration.isLocalFactory() == false &&  namingContext != null) {
                    NameComponent nc = new NameComponent(TransactionFactoryHelper.id(),"");
                    NameComponent path[] = {nc};
                    namingContext.rebind(path,factory);
                }

                // Commented out by TN
                //if( !recoverable )
                //_logger.log(Level.WARNING,"jts.non_persistent_server");
            } catch( Exception exc ) {
				_logger.log(Level.WARNING,"jts.cannot_register_with_orb","TransactionFactory");
            }

            active = true; // transaction manager is alive and available
    }

    /**Request the transaction service to stop any further transactional activity.
     *
     * @param immediate  Indicates whether to ignore running transactions.
     *
     * @return
     *
     * @see
     */
    public static void shutdown( boolean immediate ) {
        // Remove the admin and factory objects from the naming service.

        if( namingContext != null )
            try {
                NameComponent nc = new NameComponent(TransactionFactoryHelper.id(),"");
                NameComponent path[] = {nc};
                namingContext.unbind(path);

                namingContext = null;
            } catch( Exception exc ) {}

        // Inform the local TransactionFactory and CurrentImpl classes that no more
        // transactional activity may occur.

        TransactionFactoryImpl.deactivate();
        CurrentImpl.deactivate();

        // Shut down the basic transaction services.

        currentInstance.shutdown(immediate);

        // Discard the factory and current instances.

        currentInstance = null;
        factoryInstance = null;
        //adminInstance   = null;
        active = false;
    }

    /**Determines whether the given object is a proxy.
     *
     * @param obj  The potential proxy.
     *
     * @return  Indicates whether the object is a proxy.
     *
     * @see
     */
    public final boolean isProxy( org.omg.CORBA.Object obj ) {

        // TN  POA changes
        return !( StubAdapter.isStub(obj) && StubAdapter.isLocal(obj) );
    }

    /**Creates the POA objects which are used for objects within the JTS.
     *
     * @param
     *
     * @return
     *
     * @exception Exception  The operation failed.
     *
     * @see
     */
    final static void createPOAs()
        throws Exception {

        POA rootPOA = (POA)orb.resolve_initial_references("RootPOA"/*#Frozen*/);

        // Create the POA used for CoordinatorResource objects.

        POA CRpoa = null;
        if( recoverable ) {

            // Create the POA with PERSISTENT and USE_SERVANT_MANAGER policies.

            Policy[] tpolicy = new Policy[2];
            tpolicy[0] = rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
            tpolicy[1] = rootPOA.create_request_processing_policy(RequestProcessingPolicyValue.USE_SERVANT_MANAGER);
            CRpoa = rootPOA.create_POA("com.sun.jts.CosTransactions.CoordinatorResourcePOA"/*#Frozen*/, null, tpolicy);

            // Register the ServantActivator with the POA, then activate POA.

            CoordinatorResourceServantActivator crsa = new CoordinatorResourceServantActivator(orb);
            CRpoa.set_servant_manager(crsa);
        }

        // If the process is not recoverable, then we do not create a persistent POA.

        else
            CRpoa = rootPOA;

        Configuration.setPOA("CoordinatorResource"/*#Frozen*/,CRpoa);

        // Create the POA used for RecoveryCoordinator objects.

        POA RCpoa = null;
        if( recoverable ) {

            // Create the POA with PERSISTENT and USE_SERVANT_MANAGER policies.

            Policy[] tpolicy = new Policy[2];
            tpolicy[0] = rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT);
            tpolicy[1] = rootPOA.create_request_processing_policy(RequestProcessingPolicyValue.USE_SERVANT_MANAGER);
            RCpoa = rootPOA.create_POA("com.sun.jts.CosTransactions.RecoveryCoordinatorPOA"/*#Frozen*/, null, tpolicy);

            // Register the ServantActivator with the POA, then activate POA.

            RecoveryCoordinatorServantActivator rcsa = new RecoveryCoordinatorServantActivator(orb);
            RCpoa.set_servant_manager(rcsa);
        }

        // If the process is not recoverable, then we do not create a persistent POA.

        else
            RCpoa = rootPOA;

        Configuration.setPOA("RecoveryCoordinator"/*#Frozen*/,RCpoa);

        // Create the POA used for Coordinator objects.

        POA Cpoa = rootPOA.create_POA("CoordinatorPOA"/*#Frozen*/, null, null);
        //  POA Cpoa = rootPOA;
        Configuration.setPOA("Coordinator"/*#Frozen*/,Cpoa);

        // Create the POA used for transient objects.

        Configuration.setPOA("transient"/*#Frozen*/,rootPOA);

        //  rootPOA.the_activator(new JTSAdapterActivator(orb));
        CRpoa.the_POAManager().activate();
        RCpoa.the_POAManager().activate();
        Cpoa.the_POAManager().activate();
        rootPOA.the_POAManager().activate();

        poasCreated = true;

    }
}

/**The RecoveryCoordinatorServantActivator class provides the means to locate
 * instances of the RecoveryCoordinator class using the transaction identifier
 * and the RecoveryManager.
 *
 * @version 0.01
 *
 * @author Simon Holdsworth, IBM Corporation
 *
 * @see
*/
//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By     Change Description
//   0.01  SAJH   Initial implementation.
//------------------------------------------------------------------------------

class RecoveryCoordinatorServantActivator extends LocalObject implements ServantActivator {
    private ORB orb = null;

    /*
     * COMMENT(Ram J) The _ids() method needs to be removed/modified
     * (along with LocalObjectImpl) once OMG standardizes the local
     * object interfaces.
     *
     * TN - removed ids after switching to LocalObject
     */

     // Type-specific CORBA::Object operations
    /*
    private static String[] __ids = {
        "IDL:omg.org/PortableServer/ServantActivator:1.0",
        "IDL:omg.org/PortableServer/ServantManager:1.0"
    };

    public String[] _ids () {
        return __ids;
    }
    */

    /**Creates the servant activator for the RecoveryCoordinator class.
     *
     * @param  orb  The ORB.
     *
     * @return
     *
     * @see
     */
    RecoveryCoordinatorServantActivator(ORB orb) {
        this.orb = orb;
    }

    /**Returns the servant object which corresponds to the given object identity
     * for the given POA.
     *
     * @param oid      The object identifier.
     * @param adapter  The POA.
     *
     * @return  The servant.
     *
     * @see
     */
    public Servant incarnate( byte[] oid, POA adapter )
        throws org.omg.PortableServer.ForwardRequest {

        Servant servant = new RecoveryCoordinatorImpl(oid);

        return servant;
    }

    /**Does nothing.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    public void etherealize( byte[] oid,
                             POA adapter,
                             Servant servant,
                             boolean cleanup_in_progress,
                             boolean remaining_activations ) {
    }
}

/**The CoordinatorResourceServantActivator class provides the means to locate
 * instances of the CoordinatorResource class after a failure, using the
 * transaction identifier and the RecoveryManager.
 *
 * @version 0.01
 *
 * @author Simon Holdsworth, IBM Corporation
 *
 * @see
*/
//----------------------------------------------------------------------------
// CHANGE HISTORY
//
// Version By     Change Description
//   0.01  SAJH   Initial implementation.
//------------------------------------------------------------------------------

class CoordinatorResourceServantActivator extends LocalObject implements ServantActivator {
    private ORB orb = null;

    /*
     * COMMENT(Ram J) The _ids() method needs to be removed/modified
     * (along with LocalObjectImpl) once OMG standardizes the local
     * object interfaces.
     *
     * TN - removed ids after switching to LocalObject
     */

    /*
     // Type-specific CORBA::Object operations
    private static String[] __ids = {
        "IDL:omg.org/PortableServer/ServantActivator:1.0",
        "IDL:omg.org/PortableServer/ServantManager:1.0"
    };

    public String[] _ids () {
        return __ids;
    }
    */

    /**Creates the servant activator for the CoordinatorResource class.
     *
     * @param  orb  The ORB.
     *
     * @return
     *
     * @see
     */
    CoordinatorResourceServantActivator(ORB orb) {
        this.orb = orb;
    }

    /**Returns the servant object which corresponds to the given object identity
     * for the given POA.
     *
     * @param oid      The object identifier.
     * @param adapter  The POA.
     *
     * @return  The servant.
     *
     * @see
     */
    public Servant incarnate( byte[] oid, POA adapter ) 
        throws org.omg.PortableServer.ForwardRequest {
        Servant servant = new CoordinatorResourceImpl(oid);
        return servant;
    }

    /**Does nothing.
     *
     * @param
     *
     * @return
     *
     * @see
     */
    public void etherealize( byte[] oid,
                             POA adapter,
                             Servant servant,
                             boolean cleanup_in_progress,
                             boolean remaining_activations ) {
    }
}

class JTSAdapterActivator extends LocalObject implements AdapterActivator {
    private ORB orb;

    /*
     * COMMENT(Ram J) The _ids() method needs to be removed/modified
     * (along with LocalObjectImpl) once OMG standardizes the local
     * object interfaces.
     *
     * TN - removed ids after switching to LocalObject
     */

    /*
    // Type-specific CORBA::Object operations
    private static String[] __ids = {
        "IDL:omg.org/PortableServer/AdapterActivator:1.0"
    };

    public String[] _ids() {
        return __ids;
    }
    */

    JTSAdapterActivator(ORB orb) {
        this.orb = orb;
    }

    public boolean unknown_adapter(POA parent, String name) {

        try {
            DefaultTransactionService.createPOAs();
        } catch( Exception exc ) {
        }

        return true;
    }
}
