1   /***
2    * Redistribution and use of this software and associated documentation
3    * ("Software"), with or without modification, are permitted provided
4    * that the following conditions are met:
5    *
6    * 1. Redistributions of source code must retain copyright
7    *    statements and notices.  Redistributions must also contain a
8    *    copy of this document.
9    *
10   * 2. Redistributions in binary form must reproduce the
11   *    above copyright notice, this list of conditions and the
12   *    following disclaimer in the documentation and/or other
13   *    materials provided with the distribution.
14   *
15   * 3. The name "Exolab" must not be used to endorse or promote
16   *    products derived from this Software without prior written
17   *    permission of Exoffice Technologies.  For written permission,
18   *    please contact info@exolab.org.
19   *
20   * 4. Products derived from this Software may not be called "Exolab"
21   *    nor may "Exolab" appear in their names without prior written
22   *    permission of Exoffice Technologies. Exolab is a registered
23   *    trademark of Exoffice Technologies.
24   *
25   * 5. Due credit should be given to the Exolab Project
26   *    (http://www.exolab.org/).
27   *
28   * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29   * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30   * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31   * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
32   * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39   * OF THE POSSIBILITY OF SUCH DAMAGE.
40   *
41   * Copyright 2004-2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42   *
43   * $Id: ExceptionTestCase.java,v 1.4 2005/05/24 05:50:56 tanderson Exp $
44   */
45  package org.exolab.jms.net.invoke;
46  
47  import java.rmi.RemoteException;
48  import java.util.Map;
49  import java.util.HashMap;
50  import java.io.IOException;
51  
52  import org.apache.commons.logging.Log;
53  import org.apache.commons.logging.LogFactory;
54  import junit.framework.AssertionFailedError;
55  import junit.framework.Protectable;
56  import junit.framework.TestCase;
57  
58  import org.exolab.jms.net.ExceptionService;
59  import org.exolab.jms.net.ExceptionServiceImpl;
60  import org.exolab.jms.net.orb.ORB;
61  import org.exolab.jms.net.orb.ORBFactory;
62  import org.exolab.jms.net.proxy.Proxy;
63  import org.exolab.jms.net.proxy.RemoteInvocationException;
64  import org.exolab.jms.net.registry.Registry;
65  import org.exolab.jms.net.util.SSLUtil;
66  
67  
68  /***
69   * Tests exception handling.
70   *
71   * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
72   * @version $Revision: 1.4 $ $Date: 2005/05/24 05:50:56 $
73   */
74  public abstract class ExceptionTestCase extends TestCase {
75  
76      /***
77       * The export URI.
78       */
79      private final String _uri;
80  
81      /***
82       * The route URI.
83       */
84      private final String _routeURI;
85  
86      /***
87       * Connection properties used when establishing a connection to the remote
88       * ORB. May be <code>null</code>
89       */
90      private final Map _connectionProps;
91  
92      /***
93       * Connection properties used when constructing the local ORB. May
94       * be <code>null</code>
95       */
96      private final Map _acceptorProps;
97      
98      /***
99       * The ORB.
100      */
101     private ORB _orb;
102 
103     /***
104      * The exception service proxy.
105      */
106     private ExceptionService _service;
107 
108     /***
109      * The logger.
110      */
111     private static final Log _log = LogFactory.getLog(ExceptionTestCase.class);
112 
113     /***
114      * Exception service name.
115      */
116     private static final String EXCEPTION_SERVICE = "exception";
117 
118 
119     /***
120      * Construct an instance of this class for a specific test case.
121      *
122      * @param name the name of test case
123      * @param uri  the export URI
124      */
125     public ExceptionTestCase(String name, String uri) {
126         this(name, uri, null, null);
127     }
128 
129     /***
130      * Construct an instance of this class for a specific test case.
131      *
132      * @param name       the name of test case
133      * @param uri        the export URI
134      * @param routeURI   the route URI
135      */
136     public ExceptionTestCase(String name, String uri, String routeURI) {
137         this(name, uri, routeURI, null, null);
138     }
139 
140     /***
141      * Construct an instance of this class for a specific test case.
142      *
143      * @param name       the name of test case
144      * @param uri        the export URI
145      * @param properties connection properties. May be <code>null</code>
146      */
147     public ExceptionTestCase(String name, String uri, Map properties) {
148         this(name, uri, null, properties, properties);
149     }
150 
151     /***
152      * Construct an instance of this class for a specific test case.
153      *
154      * @param name       the name of test case
155      * @param uri        the export URI
156      * @param routeURI   the route URI
157      * @param properties connection properties. May be <code>null</code>
158      */
159     public ExceptionTestCase(String name, String uri, String routeURI,
160                              Map properties) {
161         this(name, uri, routeURI, properties, properties);
162     }
163 
164     /***
165      * Construct an instance of this class for a specific test case.
166      *
167      * @param name       the name of test case
168      * @param uri        the export URI
169      * @param routeURI   the route URI
170      * @param connectionProps connection properties. May be <code>null</code>
171      * @param acceptorProps acceptor properites. May be <code>null</code> 
172      */
173     public ExceptionTestCase(String name, String uri, String routeURI,
174                              Map connectionProps, Map acceptorProps) {
175         super(name);
176         _uri = uri;
177         _routeURI = routeURI;
178         _connectionProps = connectionProps;
179         _acceptorProps = acceptorProps;
180     }
181 
182     /***
183      * Verifies that a declared <code>Throwable</code> is propagated to the
184      * client.
185      *
186      * @throws Exception for any error
187      */
188     public void testDeclaredThrowable() throws Exception {
189         Protectable protectable = new Protectable() {
190             public void protect() throws Throwable {
191                 _service.throwThrowable();
192             }
193         };
194         checkException(protectable, Throwable.class, null);
195     }
196 
197     /***
198      * Verifies that a declared <code>Exception</code> is propagated to the
199      * client.
200      *
201      * @throws Exception for any error
202      */
203     public void testDeclaredException() throws Exception {
204         Protectable protectable = new Protectable() {
205             public void protect() throws Throwable {
206                 _service.throwException();
207             }
208         };
209         checkException(protectable, Exception.class, null);
210     }
211 
212     /***
213      * Verifies that a declared <code>Error</code> is propagated to the client.
214      *
215      * @throws Exception for any error
216      */
217     public void testDeclaredError() throws Exception {
218         Protectable protectable = new Protectable() {
219             public void protect() throws Throwable {
220                 _service.throwError();
221             }
222         };
223         checkException(protectable, Error.class, null);
224     }
225 
226     /***
227      * Verifies that an undeclared <code>Error</code> is propagated to the
228      * client, wrapped in a {@link RemoteInvocationException}.
229      *
230      * @throws Exception for any error
231      */
232     public void testUndeclaredError() throws Exception {
233         Protectable protectable = new Protectable() {
234             public void protect() throws Throwable {
235                 _service.throwUndeclaredError();
236             }
237         };
238         checkException(protectable, RemoteInvocationException.class,
239                        Error.class);
240     }
241 
242     /***
243      * Verifies that a declared <code>RuntimeException</code> is propagated to
244      * the client.
245      *
246      * @throws Exception for any error
247      */
248     public void testDeclaredRuntimeException() throws Exception {
249         Protectable protectable = new Protectable() {
250             public void protect() throws Throwable {
251                 _service.throwRuntimeException();
252             }
253         };
254         checkException(protectable, RuntimeException.class, null);
255     }
256 
257     /***
258      * Verifies that an undeclared <code>RuntimeException</code> is propagated
259      * to the client, wrapped in a {@link RemoteInvocationException}.
260      *
261      * @throws Exception for any error
262      */
263     public void testUndeclaredRuntimeException() throws Exception {
264         Protectable protectable = new Protectable() {
265             public void protect() throws Throwable {
266                 _service.throwUndeclaredRuntimeException();
267             }
268         };
269         checkException(protectable, RemoteInvocationException.class,
270                        RuntimeException.class);
271     }
272 
273     /***
274      * Verifies that a declared <code>RemoteException</code> is propagated to
275      * the client.
276      *
277      * @throws Exception for any error
278      */
279     public void testDeclaredRemoteException() throws Exception {
280         Protectable protectable = new Protectable() {
281             public void protect() throws Throwable {
282                 _service.throwRemoteException();
283             }
284         };
285         checkException(protectable, RemoteException.class, null);
286     }
287 
288     /***
289      * Verifies that an undeclared <code>Error</code> thrown by a method
290      * declaring <code>RemoteException</code> is propagated to the client,
291      * wrapped in a <code>RemoteException</code>.
292      *
293      * @throws Exception for any errror
294      */
295     public void testUndeclaredError2() throws Exception {
296         Protectable protectable = new Protectable() {
297             public void protect() throws Throwable {
298                 _service.throwUndeclaredError2();
299             }
300         };
301         checkException(protectable, RemoteException.class, Error.class);
302     }
303 
304     /***
305      * Verifies that an undeclared <code>RuntimeException</code> thrown by a
306      * method declaring <code>RemoteException</code> is propagated to the
307      * client, wrapped in a <code>RemoteException</code>.
308      *
309      * @throws Exception for any errror
310      */
311     public void testUndeclaredRuntimeException2() throws Exception {
312         Protectable protectable = new Protectable() {
313             public void protect() throws Throwable {
314                 _service.throwUndeclaredRuntimeException2();
315             }
316         };
317         checkException(protectable, RemoteException.class,
318                        RuntimeException.class);
319     }
320 
321     /***
322      * Verifies that an undeclared <code>RemoteInvocationException</code> thrown
323      * by a method is propagated to the client unchanged.
324      *
325      * @throws Exception for any errror
326      */
327     public void testUndeclaredRemoteInvocationException() throws Exception {
328         Protectable protectable = new Protectable() {
329             public void protect() throws Throwable {
330                 _service.throwUndeclaredRemoteInvocationException();
331             }
332         };
333         checkException(protectable, RemoteInvocationException.class, null);
334     }
335 
336     /***
337      * Returns connection properties for establishing a connection to the remote
338      * ORB.
339      *
340      * @return the connection properties, or <code>null</code> if the default
341      *         connection properties should be used
342      * @throws IOException if a store cannot be found
343      */
344     protected Map getConnectionProperties() throws IOException {
345         Map properties = new HashMap();
346         properties.put(ORB.PROVIDER_URI, getServerURI());
347         if (_connectionProps != null) {
348             properties.putAll(_connectionProps);
349         }
350         return properties;
351     }
352 
353     /***
354      * Returns the acceptor properties to use when accepting connections.
355      *
356      * @return the acceptor properties, or <code>null</code> if the default
357      *         connection properties should be used
358      * @throws Exception for any error
359      */
360     protected Map getAcceptorProperties() throws Exception {
361         Map properties = new HashMap();
362         properties.put(ORB.PROVIDER_URI, _uri);
363         if (_acceptorProps != null) {
364             properties.putAll(_acceptorProps);
365         }
366         return properties;
367     }
368 
369     /***
370      * Helper to return the server URI.
371      *
372      * @return the server URI
373      */
374     protected String getServerURI() {
375         return (_routeURI != null) ? _routeURI : _uri;
376     }
377 
378     /***
379      * Sets up the test case.
380      *
381      * @throws Exception for any error
382      */
383     protected void setUp() throws Exception {
384         _log.debug("setUp() [test=" + getName() + ", uri=" + _uri + "]");
385         _orb = ORBFactory.createORB(getAcceptorProperties());
386         if (_routeURI != null) {
387             _orb.addRoute(_uri, _routeURI);
388         }
389         Registry serverRegistry = _orb.getRegistry();
390 
391         Proxy proxy = _orb.exportObject(new ExceptionServiceImpl());
392         serverRegistry.bind(EXCEPTION_SERVICE, proxy);
393 
394         // get a proxy to the registry, and look up the service
395         Registry clientRegistry = _orb.getRegistry(getConnectionProperties());
396         _service = (ExceptionService) clientRegistry.lookup(EXCEPTION_SERVICE);
397     }
398 
399     /***
400      * Cleans up the test case.
401      *
402      * @throws Exception for any error
403      */
404     protected void tearDown() throws Exception {
405         _log.debug("tearDown() [test=" + getName() + ", uri=" + _uri + "]");
406         _orb.shutdown();
407 
408         // reset any SSL properties that may have been set.
409         SSLUtil.clearProperties();
410     }
411 
412     /***
413      * Verifies that a method throws an exception of the expected type.
414      *
415      * @param protectable wraps the method to invoke
416      * @param expected    the expected type of the exception
417      * @param nested      the expected type of the nested exception, or
418      *                    <code>null</code>, if no nested exception is expected
419      */
420     private void checkException(Protectable protectable, Class expected,
421                                 Class nested) {
422         try {
423             protectable.protect();
424             fail("Expected exception of type=" + expected.getName()
425                  + " to be thrown");
426         } catch (RemoteInvocationException exception) {
427             checkExceptionType(exception, expected);
428             if (nested != null) {
429                 checkNestedExceptionType(exception.getTargetException(),
430                                          nested);
431             }
432         } catch (RemoteException exception) {
433             checkExceptionType(exception, expected);
434             if (nested != null) {
435                 checkNestedExceptionType(exception.detail, nested);
436             }
437         } catch (AssertionFailedError error) {
438             throw error;
439         } catch (Throwable throwable) {
440             checkExceptionType(throwable, expected);
441         }
442     }
443 
444     /***
445      * Verifies that an exception is of the expected type.
446      *
447      * @param exception the exception to check
448      * @param expected  the expected exception type
449      */
450     private void checkExceptionType(Throwable exception, Class expected) {
451         Class actual = exception.getClass();
452         if (!actual.equals(expected)) {
453             fail("Expected exception of type=" + expected.getName()
454                  + " to be thrown, but got type=" + actual.getName()
455                  + ", message=" + exception.getMessage());
456         }
457     }
458 
459     /***
460      * Verifies that a nested exception is of the expected type.
461      *
462      * @param exception the exception to check
463      * @param expected  the expected exception type
464      */
465     private void checkNestedExceptionType(Throwable exception,
466                                           Class expected) {
467         Class actual = exception.getClass();
468         if (!actual.equals(expected)) {
469             fail("Expected nested exception of type=" + expected.getName()
470                  + " but got type=" + actual.getClass().getName()
471                  + ", message=" + exception.getMessage());
472         }
473     }
474 
475 }