/*
 * Copyright (c) 2004, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.corba.se.impl.presentation.rmi ;

import java.lang.reflect.InvocationHandler ;
import java.lang.reflect.Proxy ;
import java.lang.reflect.Method ;

import org.omg.CORBA.portable.ObjectImpl ;

import java.io.ObjectStreamException ;
import java.io.Serializable ;

import com.sun.corba.se.spi.presentation.rmi.IDLNameTranslator ;
import com.sun.corba.se.spi.presentation.rmi.PresentationManager ;
import com.sun.corba.se.spi.presentation.rmi.DynamicStub ;

import com.sun.corba.se.spi.orbutil.proxy.LinkedInvocationHandler ;
import com.sun.corba.se.spi.orbutil.proxy.InvocationHandlerFactory ;
import com.sun.corba.se.spi.orbutil.proxy.DelegateInvocationHandlerImpl ;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandler ;
import com.sun.corba.se.spi.orbutil.proxy.CompositeInvocationHandlerImpl ;
import java.security.AccessController;
import java.security.PrivilegedAction;

public class InvocationHandlerFactoryImpl implements InvocationHandlerFactory
{
    private final PresentationManager.ClassData classData ;
    private final PresentationManager pm ;
    private Class[] proxyInterfaces ;

    public InvocationHandlerFactoryImpl( PresentationManager pm,
        PresentationManager.ClassData classData )
    {
        this.classData = classData ;
        this.pm = pm ;

        Class[] remoteInterfaces =
            classData.getIDLNameTranslator().getInterfaces() ;
        proxyInterfaces = new Class[ remoteInterfaces.length + 1 ] ;
        for (int ctr=0; ctr<remoteInterfaces.length; ctr++)
            proxyInterfaces[ctr] = remoteInterfaces[ctr] ;

        proxyInterfaces[remoteInterfaces.length] = DynamicStub.class ;
    }

    private class CustomCompositeInvocationHandlerImpl extends
        CompositeInvocationHandlerImpl implements LinkedInvocationHandler,
        Serializable
    {
        private transient DynamicStub stub ;

        public void setProxy( Proxy proxy )
        {
            ((DynamicStubImpl)stub).setSelf( (DynamicStub)proxy ) ;
        }

        public Proxy getProxy()
        {
            return (Proxy)((DynamicStubImpl)stub).getSelf() ;
        }

        public CustomCompositeInvocationHandlerImpl( DynamicStub stub )
        {
            this.stub = stub ;
        }

        /** Return the stub, which will actually be written to the stream.
         * It will be custom marshalled, with the actual writing done in
         * StubIORImpl.  There is a corresponding readResolve method on
         * DynamicStubImpl which will re-create the full invocation
         * handler on read, and return the invocation handler on the
         * readResolve method.
         */
        public Object writeReplace() throws ObjectStreamException
        {
            return stub ;
        }
    }

    public InvocationHandler getInvocationHandler()
    {
        final DynamicStub stub = new DynamicStubImpl(
            classData.getTypeIds() ) ;

        return getInvocationHandler( stub ) ;
    }

    // This is also used in DynamicStubImpl to implement readResolve.
    InvocationHandler getInvocationHandler( DynamicStub stub )
    {
        // Create an invocation handler for the methods defined on DynamicStub,
        // which extends org.omg.CORBA.Object.  This handler delegates all
        // calls directly to a DynamicStubImpl, which extends
        // org.omg.CORBA.portable.ObjectImpl.
        final InvocationHandler dynamicStubHandler =
            DelegateInvocationHandlerImpl.create( stub ) ;

        // Create an invocation handler that handles any remote interface
        // methods.
        final InvocationHandler stubMethodHandler = new StubInvocationHandlerImpl(
            pm, classData, stub ) ;

        // Create a composite handler that handles the DynamicStub interface
        // as well as the remote interfaces.
        final CompositeInvocationHandler handler =
            new CustomCompositeInvocationHandlerImpl( stub ) ;

        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            @Override
            public Void run() {
        handler.addInvocationHandler( DynamicStub.class,
            dynamicStubHandler ) ;
        handler.addInvocationHandler( org.omg.CORBA.Object.class,
            dynamicStubHandler ) ;
        handler.addInvocationHandler( Object.class,
            dynamicStubHandler ) ;
                return null;
            }
        });


        // If the method passed to invoke is not from DynamicStub or its superclasses,
        // it must be from an implemented interface, so we just handle
        // all of these with the stubMethodHandler.  This used to be
        // done be adding explicit entries for stubMethodHandler for
        // each remote interface, but that does not work correctly
        // for abstract interfaces, since the graph analysis ignores
        // abstract interfaces in order to compute the type ids
        // correctly (see PresentationManagerImpl.NodeImpl.getChildren).
        // Rather than produce more graph traversal code to handle this
        // problem, we simply use a default.
        // This also points to a possible optimization: just use explict
        // checks for the three special classes, rather than a general
        // table lookup that usually fails.
        handler.setDefaultHandler( stubMethodHandler ) ;

        return handler ;
    }

    public Class[] getProxyInterfaces()
    {
        return proxyInterfaces ;
    }
}
