Source Code Cross Referenced for ServiceCaller.java in  » Web-Services » xins » org » xins » common » service » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Web Services » xins » org.xins.common.service 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * $Id: ServiceCaller.java,v 1.76 2007/03/15 17:08:27 agoubard Exp $
003:         *
004:         * Copyright 2003-2007 Orange Nederland Breedband B.V.
005:         * See the COPYRIGHT file for redistribution and use restrictions.
006:         */
007:        package org.xins.common.service;
008:
009:        import java.util.Iterator;
010:
011:        import org.xins.common.Log;
012:        import org.xins.common.MandatoryArgumentChecker;
013:        import org.xins.common.TimeOutController;
014:        import org.xins.common.TimeOutException;
015:        import org.xins.common.Utils;
016:
017:        /**
018:         * Abstraction of a service caller for a TCP-based service. Service caller
019:         * implementations can be used to perform a call to a service, and potentially
020:         * fail-over to other back-ends if one is not available.
021:         *
022:         * <a name="section-descriptors"></a>
023:         * <h2>Descriptors</h2>
024:         *
025:         * <p>A service caller has a link to a {@link Descriptor} instance, which
026:         * describes which back-ends to call. A <code>Descriptor</code> describes
027:         * either a single back-end or a group of back-ends. A single back-end is
028:         * represented by a {@link TargetDescriptor} instance, while a groups of
029:         * back-ends is represented by a {@link GroupDescriptor} instance. Both are
030:         * subclasses of class <code>Descriptor</code>.
031:         *
032:         * <p>There is only one type of target descriptor, but there are
033:         * different types of group descriptor:
034:         *
035:         * <ul>
036:         * <li><em>ordered</em>: underlying descriptors are iterated over in
037:         *     sequential order;
038:         * <li><em>random</em>: underlying descriptors are iterated over in random
039:         *     order.
040:         * </ul>
041:         *
042:         * <p>Note that group descriptors may contain other group descriptors.
043:         *
044:         * <a name="section-timeouts"></a>
045:         * <h2>Time-outs</h2>
046:         *
047:         * <p>Target descriptors support three kinds of time-out:
048:         *
049:         * <ul>
050:         * <li><em>total time-out</em>: limits the duration of a call,
051:         *     including connection time, time used to send the request, time used to
052:         *     receive the response, etcetera;
053:         * <li><em>connection time-out</em>: limits the time for attempting to
054:         *     establish a connection;
055:         * <li><em>socket time-out</em>: limits the time for attempting to receive
056:         *     data on a socket.
057:         * </ul>
058:         *
059:         * <a name="section-lbfo"></a>
060:         * <h2>Load-balancing and fail-over</h2>
061:         *
062:         * Service callers can help in evenly distributing processing across
063:         * available resources. This load-balancing is achieved by using a group
064:         * descriptor which iterates over the underlying descriptors in a
065:         * <em>random</em> order.
066:         *
067:         * <p>Unlike load-balancing, fail-over allows the detection of a failure and
068:         * the migration of the processing to a similar, redundant back-end. This can
069:         * be achieved using any type of group descriptor (either <em>ordered</em> or
070:         * <em>random</em>).
071:         *
072:         * <p>Not all calls should be retried on a different back-end. For example, if
073:         * a call fails because the back-end indicates the request is considered
074:         * incorrect, then it may be considered unappropriate to try other back-ends.
075:         * The {@link #shouldFailOver(CallRequest,CallConfig,CallExceptionList)
076:         * shouldFailOver} method determines whether a failed call will be retried.
077:         *
078:         * <p>Consider the following hypothetical scenario. A company has two data
079:         * centers, a primary site and a secondary backup site. The primary site has
080:         * 3 back-ends running an <em>eshop</em> service, while the hot backup site
081:         * has only 2 such back-ends. The back-ends at the primary site should always
082:         * be preferred over the back-ends at the backup site. At each site, load
083:         * should be evenly distributed among the available back-ends within that
084:         * site.
085:         *
086:         * <p>Such a scenario can be converted to a descriptor configuration such as
087:         * the following:
088:         *
089:         * <ul>
090:         * <li>the service caller uses a group descriptor called <em>All</em> of type
091:         *     <em>ordered</em>. This group descriptor contains 2 other group
092:         *     descriptors: <em>MainSite</em> and <em>BackupSite</em>.
093:         * <li>the group descriptor <em>MainSite</em> is of type <em>random</em> and
094:         *     contains 3 target descriptors, called <em>Main1</em>,
095:         *     <em>Main2</em> and <em>Main3</em>.
096:         * <li>the group descriptor <em>BackupSite</em> is also of type
097:         *     <em>random</em> and contains 2 target descriptors, called
098:         *     <em>Backup1</em> and <em>Backup2</em>.
099:         * </ul>
100:         *
101:         * <p>Now if the service caller performs a call, it will first randomly select
102:         * one of <em>Main1</em>, <em>Main2</em> and <em>Main3</em>. If the call
103:         * fails and fail-over is considered allowable, it will retry the call with
104:         * one of the other back-ends in the <em>MainSite</em> group. If none of the
105:         * back-ends in the <em>MainSite</em> group succeeds, it will randomly select
106:         * back-ends from the <em>BackupSite</em> group until the call has succeeded
107:         * or until all back-ends were tried.
108:         *
109:         * <a name="section-callconfig"></a>
110:         * <h2>Call configuration</h2>
111:         *
112:         * <p>Some aspects of a call can be configured using a {@link CallConfig}
113:         * object. For example, the <code>CallConfig</code> base class indicates
114:         * whether fail-over is unconditionally allowed. Like this, some aspects of
115:         * the behaviour of the caller can be tweaked.
116:         *
117:         * <p>There are different places where a <code>CallConfig</code> can be
118:         * applied:
119:         *
120:         * <ul>
121:         *    <li>associated with a <code>ServiceCaller</code>;
122:         *    <li>associated with a <code>CallRequest</code>;
123:         *    <li>passed with the call method.
124:         * </ul>
125:         *
126:         * <p>First of all, each <code>ServiceCaller</code> instance will have a
127:         * fall-back <code>CallConfig</code>.
128:         *
129:         * <p>Secondly, a {@link CallRequest} instance may have a
130:         * <code>CallConfig</code> associated with it as well. If it does, then this
131:         * overrides the one on the <code>ServiceCaller</code> instance.
132:         *
133:         * <p>Finally, a <code>CallConfig</code> can be passed as an argument to the
134:         * call method. If it is, then this overrides any other settings.
135:         *
136:         * <a name="section-implementations"></a>
137:         * <h2>Subclass implementations</h2>
138:         *
139:         * <p>This class is abstract and is intended to be have service-specific
140:         * subclasses, e.g. for HTTP, FTP, JDBC, etc.
141:         *
142:         * <p>Normally, a subclass should be stick to the following rules:
143:         *
144:         * <ol>
145:         *    <li>There should be a constructor that accepts only a {@link Descriptor}
146:         *        object. This constructor should call
147:         *        <code>super(descriptor, null)</code>.
148:         *        This descriptor should document the same exceptions as the
149:         *        {@link #ServiceCaller(Descriptor,CallConfig)} constructor.
150:         *    <li>There should be a constructor that accepts both a
151:         *        {@link Descriptor} and a service-specific call config object
152:         *        (derived from {@link CallConfig}).  This constructor should call
153:         *        <code>super(descriptor, callConfig)</code>.
154:         *        This descriptor should document the same exceptions as the
155:         *        {@link #ServiceCaller(Descriptor,CallConfig)} constructor.
156:         *    <li>The method {@link #isProtocolSupportedImpl(String)} should be
157:         *        implemented.
158:         *    <li>There should be a <code>call</code> method that accepts only a
159:         *        service-specific request object (derived from {@link CallRequest}).
160:         *        It should call
161:         *        {@link #doCall(CallRequest,CallConfig) doCall}<code>(request, null)</code>.
162:         *    <li>There should be a <code>call</code> method that accepts both a
163:         *        service-specific request object (derived from {@link CallRequest}).
164:         *        and a service-specific call config object (derived from
165:         *        {@link CallConfig}).  It should call
166:         *        {@link #doCall(CallRequest,CallConfig) doCall}<code>(request, callConfig)</code>.
167:         *    <li>The method
168:         *        {@link #doCallImpl(CallRequest,CallConfig,TargetDescriptor)} must
169:         *        be implemented as specified.
170:         *    <li>The {@link #createCallResult(CallRequest,TargetDescriptor,long,CallExceptionList,Object) createCallResult}
171:         *        method must be implemented as specified.
172:         *    <li>To control when fail-over is applied, the method
173:         *        {@link #shouldFailOver(CallRequest,CallConfig,CallExceptionList)}
174:         *        may also be implemented. The implementation can assume that
175:         *        the passed {@link CallRequest} object is an instance of the
176:         *        service-specific call request class and that the passed
177:         *        {@link CallConfig} object is an instance of the service-specific
178:         *        call config class.
179:         * </ol>
180:         *
181:         * @version $Revision: 1.76 $ $Date: 2007/03/15 17:08:27 $
182:         * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
183:         *
184:         * @since XINS 1.0.0
185:         */
186:        public abstract class ServiceCaller {
187:
188:            /**
189:             * The descriptor for this service. Can be <code>null</code>.
190:             */
191:            private Descriptor _descriptor;
192:
193:            /**
194:             * The fall-back call config object for this service caller. Can only be
195:             * <code>null</code> if this is an old-style service caller.
196:             */
197:            private CallConfig _callConfig;
198:
199:            /**
200:             * Constructs a new <code>ServiceCaller</code> with the specified
201:             * <code>CallConfig</code>.
202:             *
203:             * <p>The descriptor is not mandatory. However, no calls can be made with
204:             * this service caller until the descriptor is set.
205:             *
206:             * @param descriptor
207:             *    the descriptor of the service, or <code>null</code>.
208:             *
209:             * @param callConfig
210:             *    the {@link CallConfig} object, or <code>null</code> if the default
211:             *    should be used.
212:             *
213:             * @throws UnsupportedProtocolException
214:             *    if <code>descriptor</code> is or contains a {@link TargetDescriptor}
215:             *    with an unsupported protocol (<em>since XINS 1.2.0</em>).
216:             *
217:             * @since XINS 1.1.0
218:             */
219:            protected ServiceCaller(Descriptor descriptor, CallConfig callConfig)
220:                    throws UnsupportedProtocolException {
221:
222:                // Store information
223:                setDescriptor(descriptor);
224:
225:                // If no CallConfig is specified, then use a default one
226:                if (callConfig == null) {
227:
228:                    // Call getDefaultCallConfig() to get the default config...
229:                    try {
230:                        callConfig = getDefaultCallConfig();
231:
232:                        // ...it should not throw any exception...
233:                    } catch (Throwable t) {
234:                        throw Utils.logProgrammingError(t);
235:                    }
236:
237:                    // ...and it should never return null.
238:                    if (callConfig == null) {
239:                        throw Utils
240:                                .logProgrammingError("Method returned null, although that is disallowed by the ServiceCaller.getDefaultCallConfig() contract.");
241:                    }
242:                }
243:
244:                // Set call configuration
245:                _callConfig = callConfig;
246:            }
247:
248:            /**
249:             * Asserts that the specified target descriptor is considered acceptable
250:             * for this service caller. If not, an exception is thrown.
251:             *
252:             * @param target
253:             *    the {@link TargetDescriptor} to test, should not be
254:             *    <code>null</code>.
255:             *
256:             * @throws IllegalArgumentException
257:             *    if <code>target == null</code>.
258:             *
259:             * @throws UnsupportedProtocolException
260:             *    if the protocol in the target descriptor is unsupported.
261:             *
262:             * @since XINS 1.2.0
263:             */
264:            public final void testTargetDescriptor(TargetDescriptor target)
265:                    throws IllegalArgumentException,
266:                    UnsupportedProtocolException {
267:
268:                // Check preconditions
269:                MandatoryArgumentChecker.check("target", target);
270:
271:                try {
272:                    if (!isProtocolSupported(target.getProtocol())) {
273:                        throw new UnsupportedProtocolException(target);
274:                    }
275:                } catch (UnsupportedOperationException exception) {
276:                    // ignore
277:                }
278:            }
279:
280:            /**
281:             * Checks if the specified protocol is supported (wrapper method). The
282:             * protocol is the part in a URL before the string <code>"://"</code>).
283:             *
284:             * <p>For example:
285:             *
286:             * <ul>
287:             *    <li>in the URL <code>"http://www.google.nl"</code>, the protocol is
288:             *        <code>"http"</code>;
289:             *
290:             *    <li>in the URL <code>"jdbc:mysql://we.are.the.b.org/mydb/"</code>,
291:             *        the protocol is <code>"jdbc:mysql"</code>.
292:             * </ul>
293:             *
294:             * <p>This method first checks the argument. If it is <code>null</code>,
295:             * then an exception is thrown. Otherwise, the result of a call to
296:             * {@link #isProtocolSupportedImpl(String)} is returned, passing the
297:             * supplied protocol, but in lowercase. This method may then throw an
298:             * {@link UnsupportedOperationException} if it is not implemented (default
299:             * behavior).
300:             *
301:             * @param protocol
302:             *    the protocol, should not be <code>null</code>.
303:             *
304:             * @return
305:             *    <code>true</code> if the specified protocol is supported, or
306:             *    <code>false</code> if it is not.
307:             *
308:             * @throws IllegalArgumentException
309:             *    if <code>protocol == null</code>.
310:             *
311:             * @throws UnsupportedOperationException
312:             *    if this method is not implemented (probably because this
313:             *    <code>ServiceCaller</code> implementation was originally written with
314:             *    XINS 1.0.x or XINS 1.1.x)
315:             *
316:             * @since XINS 1.2.0
317:             */
318:            public final boolean isProtocolSupported(String protocol)
319:                    throws IllegalArgumentException,
320:                    UnsupportedOperationException {
321:
322:                // Check preconditions
323:                MandatoryArgumentChecker.check("protocol", protocol);
324:
325:                return isProtocolSupportedImpl(protocol.toLowerCase());
326:            }
327:
328:            /**
329:             * Checks if the specified protocol is supported (implementation method).
330:             * The protocol is the part in a URL before the string <code>"://"</code>).
331:             *
332:             * <p>This method should only ever be called from the
333:             * {@link #isProtocolSupported(String)} method.
334:             *
335:             * <p>The implementation of this method in class <code>ServiceCaller</code>
336:             * throws an {@link UnsupportedOperationException}.
337:             *
338:             * @param protocol
339:             *    the protocol, guaranteed not to be <code>null</code> and guaranteed
340:             *    to be in lower case.
341:             *
342:             * @return
343:             *    <code>true</code> if the specified protocol is supported, or
344:             *    <code>false</code> if it is not.
345:             *
346:             * @throws UnsupportedOperationException
347:             *    if this method is not implemented (probably because this
348:             *    <code>ServiceCaller</code> implementation was originally written with
349:             *    XINS 1.0.x or XINS 1.1.x)
350:             *
351:             * @since XINS 1.2.0
352:             */
353:            protected boolean isProtocolSupportedImpl(String protocol)
354:                    throws UnsupportedOperationException {
355:                throw new UnsupportedOperationException();
356:            }
357:
358:            /**
359:             * Sets the descriptor.
360:             *
361:             * @param descriptor
362:             *    the descriptor for this service, or <code>null</code>.
363:             *
364:             * @throws UnsupportedProtocolException
365:             *    if <code>descriptor</code> is or contains a {@link TargetDescriptor}
366:             *    with an unsupported protocol.
367:             *
368:             * @since XINS 1.2.0
369:             */
370:            public void setDescriptor(Descriptor descriptor)
371:                    throws UnsupportedProtocolException {
372:
373:                // Test the protocol for all TargetDescriptors
374:                if (descriptor != null) {
375:                    Iterator targets = descriptor.iterateTargets();
376:                    while (targets.hasNext()) {
377:                        testTargetDescriptor((TargetDescriptor) targets.next());
378:                    }
379:                }
380:
381:                // Store it
382:                _descriptor = descriptor;
383:            }
384:
385:            /**
386:             * Returns the descriptor. If the descriptor is currently unset, then
387:             * <code>null</code> is returned.
388:             *
389:             * <p><em>Since XINS 1.2.0, this method may return <code>null</code>.</em>
390:             *
391:             * @return
392:             *    the descriptor for this service, or <code>null</code> if it is
393:             *    currently unset.
394:             */
395:            public final Descriptor getDescriptor() {
396:                return _descriptor;
397:            }
398:
399:            /**
400:             * Sets the <code>CallConfig</code> associated with this service caller.
401:             *
402:             * <p>This method should only be called on new-style (XINS 1.1) service
403:             * callers that used the {@link #ServiceCaller(Descriptor,CallConfig)}
404:             * constructor.
405:             *
406:             * @param config
407:             *    the fall-back {@link CallConfig} object for this service caller,
408:             *    cannot be <code>null</code>.
409:             *
410:             * @throws IllegalArgumentException
411:             *    if <code>config == null</code>.
412:             *
413:             * @since XINS 1.2.0
414:             */
415:            protected final void setCallConfig(CallConfig config)
416:                    throws IllegalArgumentException {
417:
418:                // Check argument
419:                MandatoryArgumentChecker.check("config", config);
420:
421:                _callConfig = config;
422:            }
423:
424:            /**
425:             * Returns the <code>CallConfig</code> associated with this service caller.
426:             *
427:             * @return
428:             *    the fall-back {@link CallConfig} object for this service caller,
429:             *    never <code>null</code>.
430:             *
431:             * @since XINS 1.1.0
432:             */
433:            public final CallConfig getCallConfig() {
434:                return _callConfig;
435:            }
436:
437:            /**
438:             * Returns a default <code>CallConfig</code> object. This method is called
439:             * by the <code>ServiceCaller</code> constructor if no
440:             * <code>CallConfig</code> object was given.
441:             *
442:             * <p>Subclasses that support the new service calling framework (introduced
443:             * in XINS 1.1.0) <em>must</em> override this method to return a more
444:             * suitable <code>CallConfig</code> instance.
445:             *
446:             * <p>This method should never be called by subclasses.
447:             *
448:             * @return
449:             *    a new, appropriate, {@link CallConfig} instance, never
450:             *    <code>null</code>.
451:             *
452:             * @since XINS 1.1.0
453:             */
454:            protected abstract CallConfig getDefaultCallConfig();
455:
456:            /**
457:             * Attempts to execute the specified call request on one of the target
458:             * services, with the specified call configuration. During the execution,
459:             * {@link TargetDescriptor Target descriptors} will be picked and passed to
460:             * {@link #doCallImpl(CallRequest,CallConfig,TargetDescriptor)} until there
461:             * is one that succeeds, as long as fail-over can be done (according to
462:             * {@link #shouldFailOver(CallRequest,CallConfig,CallExceptionList)}).
463:             *
464:             * <p>If one of the calls succeeds, then the result is returned. If
465:             * none succeeds or if fail-over should not be done, then a
466:             * {@link CallException} is thrown.
467:             *
468:             * <p>Subclasses that want to use this method <em>must</em> implement
469:             * {@link #doCallImpl(CallRequest,CallConfig,TargetDescriptor)}. That
470:             * method is called for each call attempt to a specific service target
471:             * (represented by a {@link TargetDescriptor}).
472:             *
473:             * @param request
474:             *    the call request, not <code>null</code>.
475:             *
476:             * @param callConfig
477:             *    the call configuration, or <code>null</code> if the one defined for
478:             *    the call request should be used if specified, or otherwise the
479:             *    fall-back call configuration associated with this
480:             *    <code>ServiceCaller</code> (see {@link #getCallConfig()}).
481:             *
482:             * @return
483:             *    a combination of the call result and a link to the
484:             *    {@link TargetDescriptor target} that returned this result, if and
485:             *    only if one of the calls succeeded, could be <code>null</code>.
486:             *
487:             * @throws IllegalArgumentException
488:             *    if <code>request == null</code>.
489:             *
490:             * @throws IllegalStateException
491:             *    if the descriptor is currently unset (<em>since XINS 1.2.0</em>).
492:             *
493:             * @throws CallException
494:             *    if all call attempts failed.
495:             *
496:             * @since XINS 1.1.0
497:             */
498:            protected final CallResult doCall(CallRequest request,
499:                    CallConfig callConfig) throws IllegalArgumentException,
500:                    IllegalStateException, CallException {
501:
502:                // Check preconditions
503:                MandatoryArgumentChecker.check("request", request);
504:
505:                // Determine descriptor
506:                Descriptor descriptor = _descriptor;
507:                if (descriptor == null) {
508:                    throw new IllegalStateException(
509:                            "Descriptor is currently unset.");
510:                }
511:
512:                // Determine what config to use. The argument has priority, then the one
513:                // associated with the request and the fall-back is the one associated
514:                // with this service caller.
515:                if (callConfig == null) {
516:                    callConfig = request.getCallConfig();
517:                    if (callConfig == null) {
518:                        callConfig = _callConfig;
519:                    }
520:                }
521:
522:                // Keep a reference to the most recent CallException since
523:                // setNext(CallException) needs to be called on it to make it link to
524:                // the next one (if there is one)
525:                CallException lastException = null;
526:
527:                // Maintain the list of CallExceptions
528:                //
529:                // This is needed if a successful result (a CallResult object) is
530:                // returned, since it will contain references to the exceptions as well;
531:                //
532:                // Note that this object is lazily initialized because this code is
533:                // performance- and memory-optimized for the successful case
534:                CallExceptionList exceptions = null;
535:
536:                // Iterate over all targets
537:                Iterator iterator = descriptor.iterateTargets();
538:
539:                // There should be at least one target
540:                if (!iterator.hasNext()) {
541:                    throw Utils
542:                            .logProgrammingError("Descriptor returns no target descriptors.");
543:                }
544:
545:                // Loop over all TargetDescriptors
546:                boolean shouldContinue = true;
547:                while (shouldContinue) {
548:
549:                    // Get a reference to the next TargetDescriptor
550:                    TargetDescriptor target = (TargetDescriptor) iterator
551:                            .next();
552:
553:                    // Call using this target
554:                    Log.log_1301(target.getURL());
555:                    Object result = null;
556:                    boolean succeeded = false;
557:                    long start = System.currentTimeMillis();
558:                    try {
559:
560:                        // Attempt the call
561:                        result = doCallImpl(request, callConfig, target);
562:                        succeeded = true;
563:
564:                        // If the call to the target fails, store the exception and try the next
565:                    } catch (Throwable exception) {
566:
567:                        Log.log_1302(target.getURL());
568:
569:                        long duration = System.currentTimeMillis() - start;
570:
571:                        // If the caught exception is not a CallException, then
572:                        // encapsulate it in one
573:                        CallException currentException;
574:                        if (exception instanceof  CallException) {
575:                            currentException = (CallException) exception;
576:                        } else {
577:                            currentException = new UnexpectedExceptionCallException(
578:                                    request, target, duration, null, exception);
579:                        }
580:
581:                        // Link the previous exception (if there is one) to this one
582:                        if (lastException != null) {
583:                            lastException.setNext(currentException);
584:                        }
585:
586:                        // Now set this exception as the most recent CallException
587:                        lastException = currentException;
588:
589:                        // If this is the first exception being caught, then lazily
590:                        // initialize the CallExceptionList and keep a reference to the
591:                        // first exception
592:                        if (exceptions == null) {
593:                            exceptions = new CallExceptionList();
594:                        }
595:
596:                        // Store the failure
597:                        exceptions.add(currentException);
598:
599:                        // Determine whether fail-over is allowed and whether we have
600:                        // another target to fail-over to
601:                        boolean failOver = shouldFailOver(request, callConfig,
602:                                exceptions);
603:                        boolean haveNext = iterator.hasNext();
604:
605:                        // No more targets and no fail-over
606:                        if (!haveNext && !failOver) {
607:                            Log.log_1304();
608:                            shouldContinue = false;
609:
610:                            // No more targets but fail-over would be allowed
611:                        } else if (!haveNext) {
612:                            Log.log_1305();
613:                            shouldContinue = false;
614:
615:                            // More targets available but fail-over is not allowed
616:                        } else if (!failOver) {
617:                            Log.log_1306();
618:                            shouldContinue = false;
619:
620:                            // More targets available and fail-over is allowed
621:                        } else {
622:                            Log.log_1307();
623:                            shouldContinue = true;
624:                        }
625:                    }
626:
627:                    // The call succeeded
628:                    if (succeeded) {
629:                        long duration = System.currentTimeMillis() - start;
630:
631:                        return createCallResult(request, target, duration,
632:                                exceptions, result);
633:                    }
634:                }
635:
636:                // Loop ended, call failed completely
637:                Log.log_1303();
638:
639:                // Get the first exception from the list, this one should be thrown
640:                CallException first = exceptions.get(0);
641:
642:                throw first;
643:            }
644:
645:            /**
646:             * Calls the specified target using the specified subject. This method must
647:             * be implemented by subclasses. It is called as soon as a target is
648:             * selected to be called. If the call fails, then a {@link CallException}
649:             * should be thrown. If the call succeeds, then the call result should be
650:             * returned from this method.
651:             *
652:             * <p>Subclasses that want to use {@link #doCall(CallRequest,CallConfig)}
653:             * <em>must</em> implement this method.
654:             *
655:             * @param request
656:             *    the call request to be executed, never <code>null</code>.
657:             *
658:             * @param callConfig
659:             *    the call config to be used, never <code>null</code>; this is
660:             *    determined by {@link #doCall(CallRequest,CallConfig)} and is
661:             *    guaranteed not to be <code>null</code>.
662:             *
663:             * @param target
664:             *    the target to call, cannot be <code>null</code>.
665:             *
666:             * @return
667:             *    the result, if and only if the call succeeded, could be
668:             *    <code>null</code>.
669:             *
670:             * @throws ClassCastException
671:             *    if the specified <code>request</code> object is not <code>null</code>
672:             *    and not an instance of an expected subclass of class
673:             *    {@link CallRequest}.
674:             *
675:             * @throws IllegalArgumentException
676:             *    if <code>target == null || request == null</code>.
677:             *
678:             * @throws CallException
679:             *    if the call to the specified target failed.
680:             *
681:             * @since XINS 1.1.0
682:             */
683:            public abstract Object doCallImpl(CallRequest request,
684:                    CallConfig callConfig, TargetDescriptor target)
685:                    throws ClassCastException, IllegalArgumentException,
686:                    CallException;
687:
688:            /**
689:             * Constructs an appropriate <code>CallResult</code> object for a
690:             * successful call attempt. This method is called from
691:             * {@link #doCall(CallRequest,CallConfig)}.
692:             *
693:             * @param request
694:             *    the {@link CallRequest} that was to be executed, never
695:             *    <code>null</code> when called from {@link #doCall(CallRequest,CallConfig)}.
696:             *
697:             * @param succeededTarget
698:             *    the {@link TargetDescriptor} for the service that was successfully
699:             *    called, never <code>null</code> when called from
700:             *    {@link #doCall(CallRequest,CallConfig)}.
701:             *
702:             * @param duration
703:             *    the call duration in milliseconds, guaranteed to be a non-negative
704:             *    number when called from {@link #doCall(CallRequest,CallConfig)}.
705:             *
706:             * @param exceptions
707:             *    the list of {@link CallException} instances, or <code>null</code> if
708:             *    there were no call failures.
709:             *
710:             * @param result
711:             *    the result from the call, which is the object returned by
712:             *    {@link #doCallImpl(CallRequest,CallConfig,TargetDescriptor)}, can be
713:             *    <code>null</code>.
714:             *
715:             * @return
716:             *    a {@link CallResult} instance, never <code>null</code>.
717:             *
718:             * @throws ClassCastException
719:             *    if <code>request</code> and/or <code>result</code> are not of the
720:             *    correct class.
721:             */
722:            protected abstract CallResult createCallResult(CallRequest request,
723:                    TargetDescriptor succeededTarget, long duration,
724:                    CallExceptionList exceptions, Object result)
725:                    throws ClassCastException;
726:
727:            /**
728:             * Runs the specified task. If the task does not finish within the total
729:             * time-out period, then the thread executing it is interrupted using the
730:             * {@link Thread#interrupt()} method and a {@link TimeOutException} is
731:             * thrown.
732:             *
733:             * @param task
734:             *    the task to run, cannot be <code>null</code>.
735:             *
736:             * @param descriptor
737:             *    the descriptor for the target on which the task is executed, cannot
738:             *    be <code>null</code>.
739:             *
740:             * @throws IllegalArgumentException
741:             *    if <code>task == null || descriptor == null</code>.
742:             *
743:             * @throws IllegalThreadStateException
744:             *    if <code>descriptor.getTotalTimeOut() &gt; 0</code> and the task is a
745:             *    {@link Thread} which is already started.
746:             *
747:             * @throws SecurityException
748:             *    if the task did not finish within the total time-out period, but the
749:             *    interruption of the thread was disallowed (see
750:             *    {@link Thread#interrupt()}).
751:             *
752:             * @throws TimeOutException
753:             *    if the task did not finish within the total time-out period and was
754:             *    interrupted.
755:             */
756:            protected final void controlTimeOut(Runnable task,
757:                    TargetDescriptor descriptor)
758:                    throws IllegalArgumentException,
759:                    IllegalThreadStateException, SecurityException,
760:                    TimeOutException {
761:
762:                // Check preconditions
763:                MandatoryArgumentChecker.check("task", task, "descriptor",
764:                        descriptor);
765:
766:                // Determine the total time-out
767:                int totalTimeOut = descriptor.getTotalTimeOut();
768:
769:                // If there is no total time-out, then execute the task on this thread
770:                if (totalTimeOut < 1) {
771:                    task.run();
772:
773:                    // Otherwise a time-out controller will be used
774:                } else {
775:                    TimeOutController.execute(task, totalTimeOut);
776:                }
777:            }
778:
779:            /**
780:             * Determines whether a call should fail-over to the next selected target
781:             * based on a request, call configuration and exception list.
782:             * This method should only be called from
783:             * {@link #doCall(CallRequest,CallConfig)}.
784:             *
785:             * <p>This method is typically overridden by subclasses. Usually, a
786:             * subclass first calls this method in the superclass, and if that returns
787:             * <code>false</code> it does some additional checks, otherwise
788:             * <code>true</code> is immediately returned.
789:             *
790:             * <p>The implementation of this method in class {@link ServiceCaller}
791:             * returns <code>true</code> if and only if at least one of the following
792:             * conditions is true:
793:             *
794:             * <ul>
795:             * <li><code>callConfig.{@link CallConfig#isFailOverAllowed()
796:             *     isFailOverAllowed()}</code>
797:             * <li><code>exception instanceof {@link ConnectionCallException}</code>
798:             * </ul>
799:             *
800:             * @param request
801:             *    the request for the call, as passed to {@link #doCall(CallRequest,CallConfig)},
802:             *    should not be <code>null</code>.
803:             *
804:             * @param callConfig
805:             *    the call config that is currently in use, never <code>null</code>.
806:             *
807:             * @param exceptions
808:             *    the current list of {@link CallException}s; never
809:             *    <code>null</code>; get the most recent one by calling
810:             *    <code>exceptions.</code>{@link CallExceptionList#last() last()}.
811:             *
812:             * @return
813:             *    <code>true</code> if the call should fail-over to the next target, or
814:             *    <code>false</code> if it should not.
815:             *
816:             * @since XINS 1.1.0
817:             */
818:            protected boolean shouldFailOver(CallRequest request,
819:                    CallConfig callConfig, CallExceptionList exceptions) {
820:                MandatoryArgumentChecker.check("request", request,
821:                        "callConfig", callConfig, "exceptions", exceptions);
822:
823:                // Determine if fail-over is applicable
824:                boolean should = callConfig.isFailOverAllowed()
825:                        || (exceptions.last() instanceof  ConnectionCallException);
826:
827:                return should;
828:            }
829:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.