/*
 * 
 * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * The contents of this file are subject to the Sun Community Source
 * License v3.0/Jini Technology Specific Attachment v 1.0 (the
 * "License"). You may not use this file except in compliance with the
 * License. You may obtain a copy of the License at
 * http://wwws.sun.com/software/jini/licensing/SCSL3_JiniTSA1.html.
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Reference Code is Jini Technology Core Platform code, v2.0. The
 * Developer of the Reference Code is Sun Microsystems, Inc.
 * 
 * Contributor(s): Sun Microsystems, Inc.
 * 
 * The contents of this file comply with the Jini Technology Core
 * Platform Compatibility Kit, v2.0A.
 * 
 * Tester(s): Sun Microsystems, Inc.
 * 
 * Test Platform(s):
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Solaris(TM)SPARC/x86
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Linux (Intel x86)
 * Java 2 SDK, Standard Edition, Version 1.4.2_03 for Windows (Intel Platform)
 * 
 */

package com.sun.jini.phoenix;

import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.NoSuchObjectException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.ExportException;
import java.rmi.server.ObjID;
import java.rmi.server.UnicastRemoteObject;
import net.jini.export.Exporter;
import sun.rmi.server.UnicastServerRef;
import sun.rmi.transport.LiveRef;

/**
 * JRMP exporter to export an object using a well-known object identifier.
 * This exporter implementation is only designed to work with
 * Java(TM) 2 Standard Edition implementations from Sun Microsystems(TM), Inc.
 *
 * @author Sun Microsystems, Inc.
 * 
 * @since 2.0
 */
public class SunJrmpExporter implements Exporter {
    /**
     * The object identifier.
     */
    private final int id;
    /**
     * The port.
     */
    private final int port;
    /**
     * Weak reference to the impl.
     */
    private WeakReference ref;

    private static final Method exportMethod;

    static {
	try {
	    Class[] paramTypes =
		new Class[] { Remote.class, Object.class, boolean.class };
	    exportMethod = UnicastServerRef.class.getMethod("exportObject",
							    paramTypes);
	} catch (NoSuchMethodException nsme) {
	    throw (Error) new NoSuchMethodError().initCause(nsme);
	}
    }

    /**
     * Creates a JRMP exporter that exports using the specified well-known
     * object identifier and the specified port.
     *
     * @param id the object identifier
     * @param port the port (if zero, an anonymous port will be chosen)
     */
    public SunJrmpExporter(int id, int port) {
	this.id = id;
	this.port = port;
    }

    /**
     * @throws NullPointerException {@inheritDoc}
     * @throws IllegalStateException {@inheritDoc}
     */
    public Remote export(Remote impl) throws ExportException {
	if (ref != null) {
	    throw new IllegalStateException("already used for export");
	} else if (impl == null) {
	    throw new NullPointerException();
	}
	ref = new WeakReference(impl);
	UnicastServerRef uref = getServerRef(new LiveRef(new ObjID(id), port));
	try {
	    Object[] args = new Object[] { impl, null, Boolean.TRUE };
	    return (Remote) exportMethod.invoke(uref, args);
	} catch (InvocationTargetException e) {
	    Throwable t = e.getTargetException();
	    if (t instanceof ExportException) {
		throw (ExportException) t;
	    } else if (t instanceof RemoteException) {
		throw new ExportException("export failed", (Exception) t);
	    } else if (t instanceof RuntimeException) {
		throw (RuntimeException) t;
	    } else if (t instanceof Error) {
		throw (Error) t;
	    } else {
		throw new AssertionError(t);
	    }
	} catch (IllegalAccessException e) {
	    throw (Error) new IllegalAccessError().initCause(e);
	}
    }

    UnicastServerRef getServerRef(LiveRef lref) {
	return new UnicastServerRef(lref);
    }

    /**
     * @throws IllegalStateException {@inheritDoc}
     */
    public boolean unexport(boolean force) {
	if (ref == null) {
	    throw new IllegalStateException(
				 "no object exported through this exporter");
	}
	Remote impl = (Remote) ref.get();
	if (impl == null) {
	    return true;
	}
	try {
	    return UnicastRemoteObject.unexportObject(impl, force);
	} catch (NoSuchObjectException ex) {
	    return true;
	}
    }
}
