/*
 * 
 * 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.constants;

import java.io.ObjectStreamException;
import java.net.NoRouteToHostException;
import java.net.PortUnreachableException;
import java.net.ProtocolException;
import java.rmi.RemoteException;
import java.rmi.ConnectIOException;
import java.rmi.NoSuchObjectException;
import java.rmi.MarshalException;
import java.rmi.UnmarshalException;
import java.rmi.UnexpectedException;
import java.rmi.UnknownHostException;
import java.rmi.ServerException;
import java.rmi.ServerError;
import net.jini.io.UnsupportedConstraintException;

/**
 * Various constants useful in processing exceptions
 *
 * @author Sun Microsystems, Inc.
 *
 */
public class ThrowableConstants {
    /**
     * Value returned by <code>retryable</code> to indicate that the
     * passed <code>Throwable</code> does not provide any new
     * information on the state of the object that threw it.
     * @see #retryable
     */
    final static public int INDEFINITE = 0;

    /**
     * Value returned by <code>retryable</code> to indicate that the
     * passed <code>Throwable</code> implies that retrying the
     * operation that threw the <code>Throwable</code> with the same
     * arguments and the same expected return value would not be
     * fruitful.  
     * @see #retryable
     */
    final static public int BAD_INVOCATION = 1;

    /**
     * Value returned by <code>retryable</code> to indicate that the
     * passed <code>Throwable</code> implies that any further
     * operations on the object that threw the <code>Throwable</code>
     * would not be fruitful.  
     * @see #retryable
     */
    final static public int BAD_OBJECT = 2;

    /**
     * Value returned by <code>retryable</code> to indicate that the
     * passed <code>Throwable</code> was of a type that could not be
     * classified.
     * @see #retryable
     */
    final static public int UNCATEGORIZED = 3;

    /**
     * Attempt to classify the passed <code>Throwable</code> in terms of 
     * what it implies about the probability of success of future operations
     * on the object that threw the exception. <p>
     *
     * Note, the classification used by this method tends to assume
     * the worst. For exceptions that represent conditions that could
     * get better by themselves but probably will not, it will return
     * <code>BAD_OBJECT</code> or <code>BAD_INVOCATION</code> instead
     * of <code>INDEFINITE</code>. This makes it suitable for
     * situations where it is better to give up, fail early, and
     * notify the next layer up that something is wrong than to
     * continue silently and retry. It is probably not a good choice
     * for situations where the stakes are higher, like deciding when
     * to give up on a prepared transaction.
     *
     * @return <code>INDEFINITE</code>, <code>BAD_INVOCATION</code>,
     * or <code>BAD_OBJECT</code> if the exception is a
     * <code>RuntimeException</code>, <code>Error</code>, or
     * <code>java.rmi.RemoteException</code> depending on the details of 
     * the <code>Throwable</code>.  Otherwise return 
     * <code>UNCATEGORIZED</code>
     * @throws NullPointerException if the passed <code>Throwable</code> is
     * <code>null</code>
     */
    public static int retryable(Throwable t) {
	if (t == null) 
	    throw new NullPointerException("Must pass a non-null Throwable");

	if (t instanceof RuntimeException) {
	    return BAD_INVOCATION;
	}

	if (t instanceof Error) {
	    if ((t instanceof OutOfMemoryError) ||
		(t instanceof LinkageError))
	    {
		return INDEFINITE;
	    }

	    if (t instanceof StackOverflowError)
		return BAD_INVOCATION;

	    return BAD_OBJECT;
	}

	if (t instanceof RemoteException) {
	    final RemoteException re = (RemoteException)t;

	    if (re instanceof NoSuchObjectException ||
		re instanceof UnexpectedException ||
		re instanceof UnknownHostException) 
	    {
		return BAD_OBJECT;	    
	    }

	    final Throwable detail = re.detail;
	    if (detail == null)
		return INDEFINITE;
	    	    
	    if (re instanceof MarshalException || 
		re instanceof UnmarshalException) 
	    {
		if (detail instanceof ObjectStreamException)
		    return BAD_INVOCATION;

		final int drs = retryable(detail);

		if (drs == BAD_OBJECT || drs == BAD_INVOCATION)
		    return BAD_INVOCATION;

		return INDEFINITE;
	    }

	    if (re instanceof ConnectIOException) {
		if (detail instanceof NoRouteToHostException  ||
		    detail instanceof PortUnreachableException ||
		    detail instanceof ProtocolException)
		{
		    return BAD_OBJECT;
		}

		if (detail instanceof UnsupportedConstraintException ||
		    detail instanceof ObjectStreamException)
		{
		    return BAD_INVOCATION;
		}

		return INDEFINITE;
	    }

	    if (re instanceof ServerException) {
		final int drs = retryable(detail);
		if (drs == BAD_OBJECT)
		    return BAD_INVOCATION;

		return drs;
	    }

	    if (re instanceof ServerError) {
		return retryable(detail);
	    }

	    return INDEFINITE;
	}
	    
	return UNCATEGORIZED;

    }
}
