/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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.
 *
 * 
 * 
 * @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;
	}
    }
}
