View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package net.jini.core.entry;
19  
20  import java.io.InvalidObjectException;
21  import java.io.IOException;
22  import java.io.PrintStream;
23  import java.io.PrintWriter;
24  import java.io.ObjectInputStream;
25  
26  /**
27   * Thrown when one tries to get an <code>Entry</code> from a service,
28   * but the entry is unusable (due to serialization or other errors).
29   * Normally <code>partialEntry</code> points to an entry with as many
30   * fields as possible filled in, with the array <code>unusableFields</code>
31   * naming the fields that could not be deserialized and the array
32   * <code>nestedExceptions</code> having the corresponding exception.
33   * <p>
34   * If the serialized <code>Entry</code> was corrupt enough that no
35   * attempt could even be made to deserialize its fields,
36   * <code>partialEntry</code> and <code>unusableFields</code> will be
37   * <code>null</code>, and <code>nestedExceptions</code> will be an
38   * array with one element that is the offending exception.  This will
39   * typically be because one or more of the classes of the <code>Entry</code>
40   * type itself cannot be loaded.
41   * <p>
42   * The names in <code>unusableFields</code> can be used together with
43   * the reflection mechanisms of <code>java.lang.reflect</code> to
44   * examine the full state of the object.
45   * 
46   * @author Sun Microsystems, Inc.
47   *
48   * @since 1.0
49   */
50  public class UnusableEntryException extends Exception {
51      static final long serialVersionUID = -2199083666668626172L;
52      //NOTE: Cannot implement @AtomicSerial until fields are private and final
53      // and all invariants can be enforced.
54      /**
55       * The partial entry.  Fields that could not be deserialized
56       * will be <code>null</code>.
57       *
58       * @serial
59       */
60      public Entry partialEntry;
61  
62      /**
63        *	The names of the unusable fields.  If the entry was entirely
64        * unusable, <code>unusableFields</code> will be <code>null</code>.
65        *
66        * @serial
67        * @deprecated this field will be made private, use {@link #getUnusableFields() }
68        */
69      public String[] unusableFields;
70  
71      /**
72       * The exception that caused the failure for the corresponding
73       * field named in unusableFields.  If the entry was entirely
74       * unusable, <code>nestedExceptions</code> will be an array with
75       * the one exception that prevented its use.
76       *
77       * @serial
78       * @deprecated this field will be made private, use {@link #getNestedExceptions() }
79       */
80      public Throwable[] nestedExceptions;
81  
82      /**
83       * Create an exception for the given partial entry and vectors of
84       * bad field names/nested exception pairs.
85       *
86       * @param partial     the Entry object on which the exception occurred
87       * @param badFields   a String array containing the bad field names
88       * @param exceptions  an array of Throwable objects associated with the
89       *                    bad field names
90       *
91       * @throws IllegalArgumentException if <code>partial</code> is
92       *     <code>null</code> and <code>badFields</code> is not
93       *     <code>null</code> or <code>exceptions</code> does not have
94       *     exactly one element or if <code>partial</code> is
95       *     non-<code>null</code> and <code>badFields</code> and
96       *     <code>exceptions</code> are not the same length
97       *
98       * @throws NullPointerException if <code>partial</code> is
99       *     non-<code>null</code> and <code>badFields</code> or any
100      *     element of <code>badFields</code> is <code>null</code>, or
101      *     if <code>exceptions</code> or any element of
102      *     <code>exceptions</code> is <code>null</code>
103      */
104     public UnusableEntryException(Entry partial, String[] badFields,
105 	Throwable[] exceptions)
106     {
107 	super();
108 	//TODO, once fields are private check invariants prior to calling
109 	// super() using static method.
110 	if (partial == null) {
111 	    if (exceptions.length != 1) {
112 		throw new IllegalArgumentException("If partial is null " +
113 		    "exceptions must have one element");
114 		}
115 
116 	    if (badFields != null) {
117 		throw new IllegalArgumentException("If partial is null " +
118 						   "badFields must be null");
119 	    }
120 	} else {
121 	    if (badFields.length != exceptions.length) {
122 		throw new IllegalArgumentException("If partial is non-null " +
123                     "badFields and exceptions must have same length");
124 	    }
125 	}
126 
127 	if (badFields != null) {
128 	    for (int i=0; i<badFields.length; i++) {
129 		if (badFields[i] == null)
130 		    throw new NullPointerException("badFields has a null element");
131 	    }
132 	}
133 
134 	for (int i=0; i<exceptions.length; i++) {
135 	    if (exceptions[i] == null) 
136 		throw new NullPointerException("exceptions has a null element");
137 	}
138 
139 	partialEntry = partial;
140 	unusableFields = badFields;
141 	nestedExceptions = exceptions;
142     }
143 
144     /**
145      * Create an exception for a nested exception that prevented even an
146      * attempt to build an entry.
147      *
148      * @param e a Throwable representing the nested exception
149      * @throws NullPointerException if <code>e</code> is <code>null</code>
150      */
151     public UnusableEntryException(Throwable e) {
152 	if (e == null)
153 	    throw new NullPointerException("e must be non-null");
154 	
155 	partialEntry = null;
156 	unusableFields = null;
157 	nestedExceptions = new Throwable[] { e };
158     }
159     
160     /**
161      *	The names of the unusable fields.  
162      * 
163      * @return The names of the unusable fields or null if the entry was
164      * entirely unusable.
165      * @since 3.1.0
166      */
167     public String [] getUnusableFields(){
168 	return unusableFields;
169     }
170    
171     /**
172      * The exception that caused the failure for the corresponding
173      * field named in unusableFields.  If the entry was entirely
174      * unusable, <code>nestedExceptions</code> will be an array with
175      * the one exception that prevented its use.
176      *
177      * @return The exception that caused the failure for the corresponding
178      * field named in getUnusableFields.
179      * @since 3.1.0
180      */ 
181     public Throwable [] getNestedExceptions(){
182 	return nestedExceptions;
183     }
184  
185     /**
186      * @throws InvalidObjectException if:
187      * <ul>
188      * <li> <code>partialEntry</code> is <code>null</code> and
189      *      <code>unusableFields</code> is not <code>null</code> or
190      *      <code>nestedExceptions</code> does not have exactly one
191      *      element,
192      * <li> if <code>partialEntry</code> is non-<code>null</code> and
193      *      <code>unusableFields</code> and
194      *      <code>nestedExceptions</code> are not the same length,
195      * <li> if <code>partialEntry</code> is non-<code>null</code> and
196      *      <code>unusableFields</code> is <code>null</code> or 
197      *      any element of <code>unusableFields</code> is
198      *      <code>null</code>, or
199      * <li> if <code>nestedExceptions</code> or any element of 
200      *     <code>nestedExceptions</code> is <code>null</code>
201      * </ul>
202      * @param in ObjectInputStream
203      * @throws ClassNotFoundException if class not found.
204      * @throws IOException if a problem occurs during de-serialization.
205      */
206     private void readObject(ObjectInputStream in) 
207 	throws IOException, ClassNotFoundException
208     {
209 	in.defaultReadObject();
210 
211 	if (partialEntry == null) {
212 	    if (nestedExceptions.length != 1) {
213 		throw new InvalidObjectException("If partialEntry is null " +
214 		    "nestedExceptions must have one element");
215 		}
216 
217 	    if (unusableFields != null) {
218 		throw new InvalidObjectException("If partialEntry is null " +
219 						 "unusableFields must be null");
220 	    }
221 	} else {
222 	    if (unusableFields == null)
223 		throw new InvalidObjectException("unusableFields is null");
224 
225 	    if (nestedExceptions == null)
226 		throw new InvalidObjectException("nestedExceptions is null");
227 
228 	    if (unusableFields.length != nestedExceptions.length) {
229 		throw new InvalidObjectException("If partialEntry is non-null " +
230                     "unusableFields and nestedExceptions must have same length");
231 	    }
232 	}
233 
234 	if (unusableFields != null) {
235 	    for (int i=0; i<unusableFields.length; i++) {
236 		if (unusableFields[i] == null)
237 		    throw new InvalidObjectException("unusableFields has a " +
238 						     "null element");
239 	    }
240 	}
241 
242 	for (int i=0; i<nestedExceptions.length; i++) {
243 	    if (nestedExceptions[i] == null) 
244 		throw new InvalidObjectException("nestedExceptions has a null " +
245 						 "element");
246 	}
247     }
248 
249     /** 
250      * @throws InvalidObjectException if called
251      */
252     private void readObjectNoData() throws InvalidObjectException {
253 	throw new InvalidObjectException(
254 	    "UnusableEntryExceptions should always have data");
255     }
256 
257     /**
258      * Calls {@link #printStackTrace(PrintStream) printStackTrace(System.err)}.
259      */
260     public void printStackTrace() { 
261         printStackTrace(System.err);
262     }
263 
264     /**
265      * Calls {@link Exception#printStackTrace(PrintStream)
266      * super.printStackTrace(s)} and then calls {@link
267      * Throwable#printStackTrace(PrintStream) printStackTrace(s)} on
268      * each exception in <code>nestedExceptions</code>.
269      */
270     public void printStackTrace(PrintStream s) {
271         synchronized (s) {
272 	    super.printStackTrace(s);
273 
274 	    if (unusableFields == null) {
275 		s.println("Total unmarshalling failure, cause was:");
276 		nestedExceptions[0].printStackTrace(s);
277 	    } else {
278 		s.println("Partial unmarshalling failure");
279 		for (int i=0; i<nestedExceptions.length; i++) {
280 		    s.println(unusableFields[i] + 
281 			" field could not be unmarshalled because of:");
282 		    nestedExceptions[i].printStackTrace(s);
283 		}
284 	    }
285         }
286     }
287 
288     /**
289      * Calls {@link Exception#printStackTrace(PrintWriter)
290      * super.printStackTrace(s)} and then calls {@link
291      * Throwable#printStackTrace(PrintWriter) printStackTrace(s)} on
292      * each exception in <code>nestedExceptions</code>.
293      */
294     public void printStackTrace(PrintWriter s) { 
295         synchronized (s) {
296 	    super.printStackTrace(s);
297 
298 	    if (unusableFields == null) {
299 		s.println("Total unmarshalling failure, cause was:");
300 		nestedExceptions[0].printStackTrace(s);
301 	    } else {
302 		s.println("Partial unmarshalling failure");
303 		for (int i=0; i<nestedExceptions.length; i++) {
304 		    s.println(unusableFields[i] + 
305 			" field could not be unmarshalled because of:");
306 		    nestedExceptions[i].printStackTrace(s);
307 		}
308 	    }
309         }
310     }
311 }