/*
 * 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 net.jini.discovery;

import net.jini.core.lookup.ServiceRegistrar;

import java.io.InvalidObjectException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.rmi.MarshalledObject;

/**
 * When unmarshalling an instance of <code>MarshalledObject</code>, different
 * exceptions can occur. An <code>IOException</code> can occur while
 * deserializing the object from its internal representation. A
 * <code>ClassNotFoundException</code> can occur if, while deserializing
 * the object from its internal representation, either the class file of
 * the object cannot be found, or the class file of either an interface
 * or a class referenced by the object being deserialized cannot be found. 
 * Typically, a <code>ClassNotFoundException</code> occurs when the codebase
 * from which to retrieve the needed class file is not currently available.
 * <p>
 * This class provides a mechanism that clients of the lookup discovery 
 * service may use for efficient handling of the exceptions that may 
 * occur when unmarshalling elements of a set of marshalled instances 
 * of the <code>ServiceRegistrar</code> interface. When elements in such
 * a set are unmarshalled, the <code>LookupUnmarshalException</code> class
 * may be used to collect and report pertinent information generated when
 * failure occurs while unmarshalling the elements of the set.
 * <p>
 * The information that may be of interest to entities that receive this
 * exception class is contained in the following fields: a set of
 * <code>ServiceRegistrar</code> instances in which each element is the
 * result of a successful unmarshalling attempt, a set of marshalled instances
 * of <code>ServiceRegistrar</code> in which each element could not be
 * successfully unmarshalled, and a set of exceptions (<code>IOException<code>,
 * <code>ClassNotFoundException</code>, or some unchecked exception) in which
 * each element corresponds to one of the unmarshalling failures.
 * <p>
 * Thus, when exceptional conditions occur while unmarshalling a set of 
 * marshalled instances of <code>ServiceRegistrar</code>, this class can
 * be used not only to indicate that an exceptional condition has occurred,
 * but also to provide information that can be used to perform error handling 
 * activities such as: determining if it is feasible to continue with 
 * processing, reporting exceptions, attempting recovery, and debugging.
 * <p>
 * Note that this exception class should be used only to report exceptional
 * conditions occurring when unmarshalling a set of marshalled instances 
 * of the <code>ServiceRegistrar</code> interface.
 *
 * 
 *
 * @see net.jini.core.lookup.ServiceRegistrar
 */
public class LookupUnmarshalException extends Exception {

    private static final long serialVersionUID = 2956893184719950537L;

    /**
     * Array containing the set of instances of <code>ServiceRegistrar</code>
     * that were successfully unmarshalled during the process in which at
     * least one failure occurred. This set may be <code>null</code> or
     * have zero length.
     *
     * @serial
     */
    private ServiceRegistrar[] registrars = null;

    /**
     * Array containing the set of <code>ServiceRegistrar</code> instances
     * that could not be unmarshalled. This set should not be <code>null</code>
     * and should contain at least one element.
     *
     * @serial
     */
    private MarshalledObject[] marshalledRegistrars = null;

    /**
     * Array containing the set of exceptions that occurred during the
     * unmarshalling process. Each element in this set should be an instance
     * of <code>IOException</code>, <code>ClassNotFoundException</code>, or
     * some unchecked exception. Furthermore, there should be a one-to-one
     * correspondence between each element in this set and each element in
     * the set of still-to-be-unmarshalled <code>ServiceRegistrar</code>
     * instances. That is, the element of this set corresponding to index i
     * should be an instance of the exception that occurred while attempting
     * to unmarshal the element at index i of <code>marshalledRegistrars<code>.
     * This set should not be <code>null</code> and should contain at least
     * one element.
     *
     * @serial
     */
    private Throwable[] exceptions = null;

    /**
     * Constructs a new instance of <code>LookupUnmarshalException</code>.
     *
     * @param registrars           Array containing the set of instances of
     *                             <code>ServiceRegistrar</code> that were
     *                             successfully unmarshalled.
     *                            
     * @param marshalledRegistrars Array containing the set of marshalled
     *                             <code>ServiceRegistrar</code> instances
     *                             that could not be unmarshalled.
     *                   
     * @param exceptions           Array containing the set of exceptions that
     *                             occurred during the unmarshalling process.
     *                             Each element in this set should be an
     *                             instance of <code>IOException</code>,
     *                             <code>ClassNotFoundException</code>, or
     *                             some unchecked exception. Furthermore,
     *                             there should be a one-to-one correspondence
     *                             between each element in this set and
     *                             each element in the
     *                             <code>marshalledRegistrars</code> parameter.
     * <p>
     *                             That is, the element of this set
     *                             corresponding to index i should be an
     *                             instance of the exception that occurred
     *                             while attempting to unmarshal the element
     *                             at index i of the
     *                             <code>marshalledRegistrars</code> parameter.
     *
     * @throws java.lang.NullPointerException this exception occurs
     *         when <code>null</code> is input for either the
     *         <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter.
     * @throws java.lang.IllegalArgumentException this exception occurs when
     *         either the <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter has zero length; or when the
     *         lengths of those two parameters are not equal.
     */
    public LookupUnmarshalException(ServiceRegistrar[] registrars,
                                    MarshalledObject[] marshalledRegistrars,
                                    Throwable[]        exceptions) 
    {
        super();
        init(registrars,marshalledRegistrars,exceptions);
    }//end constructor

    /**
     * Constructs a new instance of <code>LookupUnmarshalException</code>.
     *
     * @param registrars           Array containing the set of instances of
     *                             <code>ServiceRegistrar</code> that were
     *                             successfully unmarshalled.
     *                            
     * @param marshalledRegistrars Array containing the set of marshalled
     *                             <code>ServiceRegistrar</code> instances
     *                             that could not be unmarshalled.
     *                   
     * @param exceptions           Array containing the set of exceptions that
     *                             occurred during the unmarshalling process.
     *                             Each element in this set should be an
     *                             instance of <code>IOException</code>,
     *                             <code>ClassNotFoundException</code>, or
     *                             some unchecked exception. Furthermore,
     *                             there should be a one-to-one correspondence
     *                             between each element in this set and
     *                             each element in the
     *                             <code>marshalledRegistrars</code> parameter.
     * <p>
     *                             That is, the element of this set
     *                             corresponding to index i should be an
     *                             instance of the exception that occurred
     *                             while attempting to unmarshal the element
     *                             at index i of the
     *                             <code>marshalledRegistrars</code> parameter.
     *
     * @param message              <code>String</code> describing the nature
     *                             of the exception
     *
     * @throws java.lang.NullPointerException this exception occurs
     *         when <code>null</code> is input for either the
     *         <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter.
     * @throws java.lang.IllegalArgumentException this exception occurs when
     *         either the <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter has zero length; or when the
     *         lengths of those two parameters are not equal.
     */
    public LookupUnmarshalException(ServiceRegistrar[] registrars,
                                    MarshalledObject[] marshalledRegistrars,
                                    Throwable[]        exceptions, 
                                    String             message) 
    {
        super(message);
        init(registrars,marshalledRegistrars,exceptions);
    }//end constructor

    /**
     * Accessor method that returns an array consisting of instances of 
     * <code>ServiceRegistrar</code>, where each element of the array
     * corresponds to a successfully unmarshalled object. Note that the
     * same array is returned on each invocation of this method; that is,
     * a copy is not made.
     *
     * @return array of instances of <code>ServiceRegistrar</code>, where
     *         each element corresponds to a successfully unmarshalled object.
     */
    public ServiceRegistrar[] getRegistrars() {
        return registrars;
    }//end getRegistrars

    /**
     * Accessor method that returns an array consisting of instances of 
     * <code>MarshalledObject</code>, where each element of the array is a
     * marshalled instance of the <code>ServiceRegistrar</code> interface,
     * and corresponds to an object that could not be successfully
     * unmarshalled. Note that the same array is returned on each invocation
     * of this method; that is, a copy is not made.
     *
     * @return array of marshalled instances of <code>ServiceRegistrar</code>,
     *         where each element corresponds to an object in which failure
     *         occurred while attempting to unmarshal the object.
     */
    public MarshalledObject[] getMarshalledRegistrars() {
        return marshalledRegistrars;
    }//end getMarshalledRegistrars

    /**
     * Accessor method that returns an array consisting of instances of 
     * <code>Throwable</code>, where each element of the array corresponds
     * to one of the exceptions that occurred during the unmarshalling
     * process. Note that the same array is returned on each invocation
     * of this method; that is, a copy is not made.
     * <p>
     * Each element in the return set should be an instance of
     * <code>IOException</code>, <code>ClassNotFoundException</code>, or
     * some unchecked exception. Additionally, there should be a one-to-one
     * correspondence between each element in the array returned by this method
     * and the array returned by the <code>getMarshalledRegistrars<code>
     * method. That is, the i-th element of the set returned by this method
     * should be an instance of the exception that occurred while attempting
     * to unmarshal the i-th element of the set returned by the method
     * <code>getMarshalledRegistrars</code>.
     *
     * @return array of instances of <code>Throwable</code>, where each element
     *         corresponds to one of the exceptions that occurred during
     *         the unmarshalling process.
     */
    public Throwable[] getExceptions() {
        return exceptions;
    }//end getExceptions

    /**
     * Initializes the abstract state of this class.
     *
     * @param registrars           Array containing the set of instances of
     *                             <code>ServiceRegistrar</code> that were
     *                             successfully unmarshalled.
     *                            
     * @param marshalledRegistrars Array containing the set of marshalled
     *                             <code>ServiceRegistrar</code> instances
     *                             that could not be unmarshalled.
     *                   
     * @param exceptions           Array containing the set of exceptions that
     *                             occurred during the unmarshalling process.
     *
     * @throws java.lang.NullPointerException this exception occurs
     *         when <code>null</code> is input for either the
     *         <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter.
     * @throws java.lang.IllegalArgumentException this exception occurs when
     *         either the <code>marshalledRegistrars</code> parameter or the
     *         <code>exceptions</code> parameter has zero length; or when the
     *         lengths of those two parameters are not equal.
     */
    private void init(ServiceRegistrar[] registrars,
                      MarshalledObject[] marshalledRegistrars,
                      Throwable[]        exceptions) {
        /* Verify the input arguments */
        if(marshalledRegistrars == null) {
            throw new NullPointerException
                                      ("marshalledRegistrars cannot be null");
        }//endif
        if(exceptions == null) {
            throw new NullPointerException("exceptions cannot be null");
        }//endif
        if(marshalledRegistrars.length == 0) {
            throw new IllegalArgumentException
                                        ("marshalledRegistrars has 0 length");
        }//endif
        if(exceptions.length != marshalledRegistrars.length) {
            throw new IllegalArgumentException
                           ("exceptions.length ("+exceptions.length
                            +") is not equal to marshalledRegistrars.length "
                            +"("+marshalledRegistrars.length+")");
        }//endif
        this.registrars           = registrars;
        this.marshalledRegistrars = marshalledRegistrars;
        this.exceptions           = exceptions;
    }//end init

    /** 
     * When an instance of this class is deserialized, this method is
     * automatically invoked. This implementation of this method validates
     * the state of the deserialized instance.
     *
     * @throws <code>InvalidObjectException</code> if the state of the
     *         deserialized instance of this class is found to be invalid.
     */
    private void readObject(ObjectInputStream s)  
                               throws IOException, ClassNotFoundException
    {
        s.defaultReadObject();
        /* Verify marshalledRegistrars and exceptions fields */
        if(marshalledRegistrars == null) {
            throw new InvalidObjectException
                            ("LookupUnmarshalException.readObject "
                             +"failure - marshalledRegistrars field is null");
        }//endif
        if(exceptions == null) {
            throw new InvalidObjectException
                                      ("LookupUnmarshalException.readObject "
                                       +"failure - exceptions field is null");
        }//endif
        if(marshalledRegistrars.length == 0) {
            throw new InvalidObjectException
                             ("LookupUnmarshalException.readObject "
                              +"failure - marshalledRegistrars.length == 0");
        }//endif
        if(exceptions.length != marshalledRegistrars.length) {
            throw new InvalidObjectException
                           ("LookupUnmarshalException.readObject failure - "
                            +"exceptions.length ("+exceptions.length
                            +") is not equal to marshalledRegistrars.length "
                            +"("+marshalledRegistrars.length+")");
        }//endif

    }//end readObject

}//end class LookupUnmarshalException
