View Javadoc

1   /*
2    * Copyright 2005-2008 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ldap;
18  
19  import java.io.IOException;
20  import java.io.ObjectOutputStream;
21  import java.io.Serializable;
22  
23  import javax.naming.Name;
24  
25  import org.springframework.core.NestedRuntimeException;
26  
27  /**
28   * Base class for exception thrown by the framework whenever it encounters a
29   * problem related to LDAP.
30   * 
31   * @author Ulrik Sandberg
32   * @since 1.2
33   */
34  public abstract class NamingException extends NestedRuntimeException {
35  
36      private Throwable cause;
37  
38      /**
39       * Overrides {@link NestedRuntimeException#getCause()} since serialization
40       * always tries to serialize the base class before the subclass. Our
41       * <tt>cause</tt> may have a <tt>resolvedObj</tt> that is not
42       * serializable. By storing the cause in this class, we get a chance at
43       * temporarily nulling the cause before serialization, thus in effect making
44       * the current instance serializable.
45       */
46      public Throwable getCause() {
47          // Even if you cannot set the cause of this exception other than through
48          // the constructor, we check for the cause being "this" here, as the cause
49          // could still be set to "this" via reflection: for example, by a remoting
50          // deserializer like Hessian's.
51          return (this.cause == this ? null : this.cause);
52      }
53  
54      /**
55       * Constructor that takes a message.
56       * 
57       * @param msg
58       *            the detail message
59       */
60      public NamingException(String msg) {
61          super(msg);
62      }
63  
64      /**
65       * Constructor that allows a message and a root cause.
66       * 
67       * @param msg
68       *            the detail message
69       * @param cause
70       *            the cause of the exception. This argument is generally
71       *            expected to be a proper subclass of
72       *            {@link javax.naming.NamingException}.
73       */
74      public NamingException(String msg, Throwable cause) {
75          super(msg);
76          this.cause = cause;
77      }
78  
79      /**
80       * Constructor that allows a plain root cause, intended for subclasses
81       * mirroring corresponding <code>javax.naming</code> exceptions.
82       * 
83       * @param cause
84       *            the cause of the exception. This argument is generally
85       *            expected to be a proper subclass of
86       *            {@link javax.naming.NamingException}.
87       */
88      public NamingException(Throwable cause) {
89          this(cause != null ? cause.getMessage() : null, cause);
90      }
91  
92      /**
93       * Convenience method to get the explanation associated with this exception,
94       * if the root cause was an instance of {@link javax.naming.NamingException}.
95       * 
96       * @return a detail string explaining more about this exception if the root
97       *         cause is an instance of javax.naming.NamingException, or
98       *         <code>null</code> if there is no detail message for this
99       *         exception
100      */
101     public String getExplanation() {
102         if (getCause() instanceof javax.naming.NamingException) {
103             return ((javax.naming.NamingException) getCause()).getExplanation();
104         }
105         return null;
106     }
107 
108     /**
109      * Convenience method to get the unresolved part of the name associated with
110      * this exception, if the root cause was an instance of
111      * {@link javax.naming.NamingException}.
112      * 
113      * @return a composite name describing the part of the name that has not
114      *         been resolved if the root cause is an instance of
115      *         javax.naming.NamingException, or <code>null</code> if the
116      *         remaining name field has not been set
117      */
118     public Name getRemainingName() {
119         if (getCause() instanceof javax.naming.NamingException) {
120             return ((javax.naming.NamingException) getCause())
121                     .getRemainingName();
122         }
123         return null;
124     }
125 
126     /**
127      * Convenience method to get the leading portion of the resolved name
128      * associated with this exception, if the root cause was an instance of
129      * {@link javax.naming.NamingException}.
130      * 
131      * @return a composite name describing the the leading portion of the name
132      *         that was resolved successfully if the root cause is an instance
133      *         of javax.naming.NamingException, or <code>null</code> if the
134      *         resolved name field has not been set
135      */
136     public Name getResolvedName() {
137         if (getCause() instanceof javax.naming.NamingException) {
138             return ((javax.naming.NamingException) getCause())
139                     .getResolvedName();
140         }
141         return null;
142     }
143 
144     /**
145      * Convenience method to get the resolved object associated with this
146      * exception, if the root cause was an instance of
147      * {@link javax.naming.NamingException}.
148      * 
149      * @return the object that was resolved so far if the root cause is an
150      *         instance of javax.naming.NamingException, or <code>null</code>
151      *         if the resolved object field has not been set
152      */
153     public Object getResolvedObj() {
154         if (getCause() instanceof javax.naming.NamingException) {
155             return ((javax.naming.NamingException) getCause()).getResolvedObj();
156         }
157         return null;
158     }
159 
160     /**
161      * Checks if the <tt>resolvedObj</tt> of the causing exception is
162      * suspected to be non-serializable, and if so temporarily nulls it before
163      * calling the default serialization mechanism.
164      * 
165      * @param stream
166      *            the stream onto which this object is serialized
167      * @throws IOException
168      *             if there is an error writing this object to the stream
169      */
170     private void writeObject(ObjectOutputStream stream) throws IOException {
171         Object resolvedObj = getResolvedObj();
172         boolean serializable = resolvedObj instanceof Serializable;
173         if (resolvedObj != null && !serializable) {
174             // the cause is of this type, since resolvedObj is not null
175             javax.naming.NamingException namingException = (javax.naming.NamingException) getCause();
176             namingException.setResolvedObj(null);
177             try {
178                 stream.defaultWriteObject();
179             } finally {
180                 namingException.setResolvedObj(resolvedObj);
181             }
182         } else {
183             stream.defaultWriteObject();
184         }
185     }
186 }