001: /*
002: * $Id: CallingConventionManager.java,v 1.85 2007/09/18 08:45:06 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.server;
008:
009: import java.lang.reflect.Constructor;
010: import java.util.ArrayList;
011: import java.util.Arrays;
012: import java.util.HashMap;
013: import java.util.HashSet;
014: import java.util.Iterator;
015: import java.util.List;
016: import java.util.Map;
017: import java.util.Set;
018:
019: import javax.servlet.http.HttpServletRequest;
020:
021: import org.xins.common.Utils;
022: import org.xins.common.collections.InvalidPropertyValueException;
023: import org.xins.common.collections.MissingRequiredPropertyException;
024: import org.xins.common.collections.PropertyReader;
025: import org.xins.common.manageable.BootstrapException;
026: import org.xins.common.manageable.InitializationException;
027: import org.xins.common.manageable.Manageable;
028: import org.xins.common.text.TextUtils;
029:
030: /**
031: * Manages the <code>CallingConvention</code> instances for the API.
032: *
033: * @version $Revision: 1.85 $ $Date: 2007/09/18 08:45:06 $
034: * @author <a href="mailto:mees.witteman@orange-ftgroup.com">Mees Witteman</a>
035: * @author <a href="mailto:anthony.goubard@japplis.com">Anthony Goubard</a>
036: * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a>
037: *
038: * @see CallingConvention
039: */
040: class CallingConventionManager extends Manageable {
041:
042: /**
043: * The name of the bootstrap property that specifies the name of the default
044: * calling convention.
045: */
046: private static final String API_CALLING_CONVENTION_PROPERTY = "org.xins.api.calling.convention";
047:
048: /**
049: * The name of the bootstrap property that specifies the class of the default
050: * calling convention.
051: */
052: private static final String API_CALLING_CONVENTION_CLASS_PROPERTY = "org.xins.api.calling.convention.class";
053:
054: /**
055: * The name of the request parameter that specifies the name of the calling
056: * convention to use.
057: */
058: static final String CALLING_CONVENTION_PARAMETER = "_convention";
059:
060: /**
061: * The name of the XINS standard calling convention.
062: */
063: private static final String STANDARD_CALLING_CONVENTION = "_xins-std";
064:
065: /**
066: * The XINS XML calling convention.
067: */
068: private static final String XML_CALLING_CONVENTION = "_xins-xml";
069:
070: /**
071: * The XINS XSLT calling convention.
072: */
073: private static final String XSLT_CALLING_CONVENTION = "_xins-xslt";
074:
075: /**
076: * The name of the SOAP calling convention.
077: *
078: * @since XINS 1.3.0
079: */
080: private static final String SOAP_CALLING_CONVENTION = "_xins-soap";
081:
082: /**
083: * The name of the SOAP calling convention with mapping.
084: *
085: * @since XINS 2.1
086: */
087: private static final String SOAP_MAP_CALLING_CONVENTION = "_xins-soap-map";
088:
089: /**
090: * The name of the XML-RPC calling convention.
091: *
092: * @since XINS 1.3.0
093: */
094: private static final String XML_RPC_CALLING_CONVENTION = "_xins-xmlrpc";
095:
096: /**
097: * The name of the JSON-RPC calling convention.
098: *
099: * @since XINS 2.0.
100: */
101: public static final String JSON_RPC_CALLING_CONVENTION = "_xins-jsonrpc";
102:
103: /**
104: * The name of the JSON calling convention. The call is a Yahoo! style call.
105: *
106: * @since XINS 2.0.
107: */
108: private static final String JSON_CALLING_CONVENTION = "_xins-json";
109:
110: /**
111: * List of the names of the calling conventions currently included in
112: * XINS.
113: */
114: private static final List CONVENTIONS = Arrays.asList(new String[] {
115: STANDARD_CALLING_CONVENTION, XML_CALLING_CONVENTION,
116: XSLT_CALLING_CONVENTION, SOAP_CALLING_CONVENTION,
117: SOAP_MAP_CALLING_CONVENTION, XML_RPC_CALLING_CONVENTION,
118: JSON_RPC_CALLING_CONVENTION, JSON_CALLING_CONVENTION });
119:
120: /**
121: * Array of type <code>Class</code> that is used when constructing a
122: * <code>CallingConvention</code> instance via RMI.
123: */
124: private static final Class[] CONSTRUCTOR_ARG_CLASSES = { API.class };
125:
126: /**
127: * Placeholder object used to indicate that the construction of a calling
128: * convention object failed. Never <code>null</code>.
129: */
130: private static final Object CREATION_FAILED = new Object();
131:
132: /**
133: * The API. Never <code>null</code>.
134: */
135: private final API _api;
136:
137: /**
138: * The name of the default calling convention. There is always a default
139: * calling convention (at least after bootstrapping).
140: *
141: * <p>This field is initialized during bootstrapping.
142: */
143: private String _defaultConventionName;
144:
145: /**
146: * The names of the possible calling conventions.
147: */
148: private List _conventionNames;
149:
150: /**
151: * Map containing all calling conventions. The key is the name of the
152: * calling convention, the value is the calling convention object, or
153: * {@link #CREATION_FAILED} if the calling convention object could not be
154: * constructed.
155: */
156: private final HashMap _conventions;
157:
158: /**
159: * Creates a <code>CallingConventionManager</code> for the specified API.
160: *
161: * @param api
162: * the API, cannot be <code>null</code>.
163: */
164: CallingConventionManager(API api) {
165:
166: // Store the reference to the API
167: _api = api;
168:
169: // Fill the list of the convention names with the pre defined conventions
170: _conventionNames = new ArrayList();
171: _conventionNames.addAll(CONVENTIONS);
172:
173: // Create a map to store the conventions in
174: _conventions = new HashMap(12);
175:
176: }
177:
178: /**
179: * Performs the bootstrap procedure (actual implementation).
180: *
181: * @param properties
182: * the bootstrap properties, not <code>null</code>.
183: *
184: * @throws MissingRequiredPropertyException
185: * if a required property is not given.
186: *
187: * @throws InvalidPropertyValueException
188: * if the value of a certain property is invalid.
189: *
190: * @throws BootstrapException
191: * if the bootstrapping failed for any other reason.
192: */
193: protected void bootstrapImpl(PropertyReader properties)
194: throws MissingRequiredPropertyException,
195: InvalidPropertyValueException, BootstrapException {
196:
197: // Determine the name and class of the custom calling convention
198: _defaultConventionName = determineDefaultConvention(properties);
199:
200: // Append the defined calling conventions
201: Iterator itCustomCC = properties.getNames();
202: while (itCustomCC.hasNext()) {
203: String nextProperty = (String) itCustomCC.next();
204: if (nextProperty
205: .startsWith(API_CALLING_CONVENTION_PROPERTY + '.')
206: && !nextProperty
207: .equals(API_CALLING_CONVENTION_CLASS_PROPERTY)) {
208: String conventionName = nextProperty.substring(32,
209: nextProperty.length() - 6);
210: _conventionNames.add(conventionName);
211: }
212: }
213:
214: // Construct and bootstrap the default calling convention
215: CallingConvention cc = create(properties,
216: _defaultConventionName);
217:
218: // If created, store the object and attempt bootstrapping
219: if (cc != null) {
220: _conventions.put(_defaultConventionName, cc);
221: bootstrap(_defaultConventionName, cc, properties);
222:
223: if (cc.getState() != Manageable.BOOTSTRAPPED) {
224: throw new BootstrapException(
225: "Failed to bootstrap the default calling convention.");
226: }
227:
228: // Otherwise, if it's the default calling convention, fails
229: } else {
230: throw new BootstrapException(
231: "Failed to create the default calling convention.");
232: }
233: }
234:
235: /**
236: * Determines the default calling convention.
237: *
238: * @param properties
239: * the bootstrap properties, cannot be <code>null</code>.
240: *
241: * @return
242: * the name of the default calling convention, never <code>null</code>.
243: *
244: * @throws MissingRequiredPropertyException
245: * if a required property is not given.
246: *
247: * @throws InvalidPropertyValueException
248: * if the value of a certain property is invalid.
249: */
250: private String determineDefaultConvention(PropertyReader properties)
251: throws MissingRequiredPropertyException,
252: InvalidPropertyValueException {
253:
254: // Name of the default calling convention (if any)
255: String name = TextUtils.trim(properties
256: .get(API_CALLING_CONVENTION_PROPERTY), null);
257:
258: // No calling convention defined
259: if (name == null) {
260:
261: // Log: No custom calling convention specified
262: Log.log_3246();
263:
264: // Fallback to the XINS-specified default calling convention
265: name = STANDARD_CALLING_CONVENTION;
266: }
267:
268: // Log: Determined default calling convention
269: Log.log_3245(name);
270:
271: // Return the name of the default calling convention
272: return name;
273: }
274:
275: /**
276: * Constructs the calling convention with the specified name, using the
277: * specified bootstrap properties. This method is called for both
278: * <em>regular</em> and <em>custom</em> calling conventions.
279: *
280: * <p>If the name does not identify a recognized calling convention, then
281: * <code>null</code> is returned.
282: *
283: * @param properties
284: * the bootstrap properties, cannot be <code>null</code>.
285: *
286: * @param name
287: * the name of the calling convention to construct, cannot be
288: * <code>null</code>.
289: *
290: * @return
291: * a non-bootstrapped {@link CallingConvention} instance that matches
292: * the specified name, or <code>null</code> if no match is found.
293: */
294: private CallingConvention create(PropertyReader properties,
295: String name) {
296:
297: // Determine the name of the CallingConvention class
298: String className;
299: if (name.charAt(0) == '_') {
300: className = classNameForRegular(name);
301: } else {
302: className = properties.get(API_CALLING_CONVENTION_PROPERTY
303: + '.' + name + ".class");
304: }
305:
306: // If the class could not be determined, then return null
307: if (className == null) {
308: Log.log_3239(null, name, null);
309: return null;
310: }
311:
312: Log.log_3237(name, className);
313:
314: // Construct a CallingConvention instance
315: CallingConvention cc = construct(name, className);
316:
317: // NOTE: Logging of construction failures is done in construct(...)
318:
319: // Constructed successfully
320: if (cc != null) {
321: cc.setAPI(_api);
322: cc.setConventionName(name);
323: }
324:
325: return cc;
326: }
327:
328: /**
329: * Determines the name of the class that represents the regular calling
330: * convention with the specified name. A <em>regular</em> calling
331: * convention is one that comes with the XINS framework.
332: *
333: * @param name
334: * the name of the calling convention, should not be <code>null</code>
335: * and should normally starts with an underscore character
336: * (<code>'_'</code>).
337: *
338: * @return
339: * the name of the {@link CallingConvention} class that matches the
340: * specified calling convention name, or <code>null</code> if unknown.
341: */
342: private String classNameForRegular(String name) {
343:
344: // XINS standard
345: if (name.equals(STANDARD_CALLING_CONVENTION)) {
346: return "org.xins.server.StandardCallingConvention";
347:
348: // XINS XML
349: } else if (name.equals(XML_CALLING_CONVENTION)) {
350: return "org.xins.server.XMLCallingConvention";
351:
352: // XSLT
353: } else if (name.equals(XSLT_CALLING_CONVENTION)) {
354: return "org.xins.server.XSLTCallingConvention";
355:
356: // SOAP
357: } else if (name.equals(SOAP_CALLING_CONVENTION)) {
358: return "org.xins.server.SOAPCallingConvention";
359:
360: // SOAP MAP
361: } else if (name.equals(SOAP_MAP_CALLING_CONVENTION)) {
362: return "org.xins.server.SOAPMapCallingConvention";
363:
364: // XML-RPC
365: } else if (name.equals(XML_RPC_CALLING_CONVENTION)) {
366: return "org.xins.server.XMLRPCCallingConvention";
367:
368: // JSON-RPC
369: } else if (name.equals(JSON_RPC_CALLING_CONVENTION)) {
370: return "org.xins.server.JSONRPCCallingConvention";
371:
372: // JSON
373: } else if (name.equals(JSON_CALLING_CONVENTION)) {
374: return "org.xins.server.JSONCallingConvention";
375:
376: // Unrecognized
377: } else {
378: return null;
379: }
380: }
381:
382: /**
383: * Constructs a new <code>CallingConvention</code> instance by class name.
384: *
385: * @param name
386: * the name of the calling convention, cannot be <code>null</code>.
387: *
388: * @param className
389: * the name of the class, cannot be <code>null</code>.
390: *
391: * @return
392: * the constructed {@link CallingConvention} instance, or
393: * <code>null</code> if the construction failed.
394: */
395: private CallingConvention construct(String name, String className) {
396:
397: // Try to load the class
398: Class clazz;
399: try {
400: clazz = Class.forName(className);
401: } catch (Throwable exception) {
402: Log.log_3239(exception, name, className);
403: return null;
404: }
405:
406: // Get the constructor that accepts an API argument
407: Constructor con = null;
408: try {
409: con = clazz.getConstructor(CONSTRUCTOR_ARG_CLASSES);
410: } catch (NoSuchMethodException exception) {
411: // fall through, do not even log
412: }
413:
414: // If there is such a constructor, invoke it
415: if (con != null) {
416:
417: // Invoke it
418: Object[] args = { _api };
419: try {
420: CallingConvention cc = (CallingConvention) con
421: .newInstance(args);
422: return cc;
423:
424: // If the constructor exists but failed, then construction failed
425: } catch (Throwable exception) {
426: Utils.logIgnoredException(exception);
427: return null;
428: }
429: }
430:
431: // Secondly try a constructor with no arguments
432: try {
433: CallingConvention cc = (CallingConvention) clazz
434: .newInstance();
435: return cc;
436: } catch (Throwable exception) {
437: Log.log_3239(exception, name, className);
438: return null;
439: }
440: }
441:
442: /**
443: * Bootstraps the specified calling convention.
444: *
445: * @param name
446: * the name of the calling convention, cannot be <code>null</code>.
447: *
448: * @param cc
449: * the {@link CallingConvention} object to bootstrap, cannot be
450: * <code>null</code>.
451: *
452: * @param properties
453: * the bootstrap properties, cannot be <code>null</code>.
454: */
455: private void bootstrap(String name, CallingConvention cc,
456: PropertyReader properties) {
457:
458: // Bootstrapping calling convention
459: Log.log_3240(name);
460:
461: try {
462: cc.bootstrap(properties);
463:
464: // Missing property
465: } catch (MissingRequiredPropertyException exception) {
466: Log.log_3242(name, exception.getPropertyName(), exception
467: .getDetail());
468:
469: // Invalid property
470: } catch (InvalidPropertyValueException exception) {
471: Log.log_3243(name, exception.getPropertyName(), exception
472: .getPropertyValue(), exception.getReason());
473:
474: // Catch BootstrapException and any other exceptions not caught
475: // by previous catch statements
476: } catch (Throwable exception) {
477: Log.log_3244(exception, name);
478: }
479: }
480:
481: /**
482: * Performs the initialization procedure (actual implementation).
483: *
484: * @param properties
485: * the initialization properties, not null.
486: *
487: * @throws MissingRequiredPropertyException
488: * if a required property is not given.
489: *
490: * @throws InvalidPropertyValueException
491: * if the value of a certain property is invalid.
492: *
493: * @throws InitializationException
494: * if the initialization failed, for any other reason.
495: */
496: protected void initImpl(PropertyReader properties)
497: throws MissingRequiredPropertyException,
498: InvalidPropertyValueException, InitializationException {
499:
500: // Loop through all CallingConvention instances
501: Iterator iterator = _conventions.entrySet().iterator();
502: while (iterator.hasNext()) {
503:
504: // Determine the name and get the CallingConvention instance
505: Map.Entry entry = (Map.Entry) iterator.next();
506: String name = (String) entry.getKey();
507: Object cc = entry.getValue();
508:
509: // Process this CallingConvention only if it was created OK
510: if (cc != CREATION_FAILED) {
511:
512: // Initialize the CallingConvention
513: CallingConvention conv = (CallingConvention) cc;
514: init(name, conv, properties);
515:
516: // Fail if the *default* calling convention fails to initialize
517: if (!conv.isUsable()
518: && name.equals(_defaultConventionName)) {
519: throw new InitializationException(
520: "Failed to initialize the default calling convention \""
521: + name + "\".");
522: }
523: }
524: }
525: }
526:
527: /**
528: * Initializes the specified calling convention.
529: *
530: * <p>If the specified calling convention is not even bootstrapped, the
531: * initialization is not even attempted.
532: *
533: * @param name
534: * the name of the calling convention, cannot be <code>null</code>.
535: *
536: * @param cc
537: * the {@link CallingConvention} object to initialize, cannot be
538: * <code>null</code>.
539: *
540: * @param properties
541: * the initialization properties, cannot be <code>null</code>.
542: */
543: private void init(String name, CallingConvention cc,
544: PropertyReader properties) {
545:
546: // If the CallingConvention is not even bootstrapped, then do not even
547: // attempt to initialize it
548: if (!cc.isBootstrapped()) {
549: return;
550: }
551:
552: // Initialize calling convention
553: Log.log_3435(name);
554:
555: try {
556: cc.init(properties);
557:
558: // Missing property
559: } catch (MissingRequiredPropertyException exception) {
560: Log.log_3437(name, exception.getPropertyName(), exception
561: .getDetail());
562:
563: // Invalid property
564: } catch (InvalidPropertyValueException exception) {
565: Log.log_3438(name, exception.getPropertyName(), exception
566: .getPropertyValue(), exception.getReason());
567:
568: // Catch InitializationException and any other exceptions not caught
569: // by previous catch statements
570: } catch (Throwable exception) {
571: Log.log_3439(exception, name);
572: }
573: }
574:
575: /**
576: * Determines the calling convention to use for the specified request.
577: *
578: * @param request
579: * the incoming request, cannot be <code>null</code>.
580: *
581: * @return
582: * the calling convention to use, never <code>null</code>.
583: *
584: * @throws InvalidRequestException
585: * if the request is considered invalid, for example because the calling
586: * convention specified in the request is unknown.
587: */
588: CallingConvention getCallingConvention(HttpServletRequest request)
589: throws InvalidRequestException {
590:
591: // Get the value of the input parameter that determines the convention
592: String ccName = request
593: .getParameter(CALLING_CONVENTION_PARAMETER);
594:
595: // If a calling convention is specified then use that one
596: if (!TextUtils.isEmpty(ccName)) {
597: CallingConvention cc = getCallingConvention(ccName);
598: if (!Arrays.asList(cc.getSupportedMethods(request))
599: .contains(request.getMethod())
600: && !"OPTIONS".equals(request.getMethod())) {
601: String detail = "Calling convention \"" + ccName
602: + "\" does not support the \""
603: + request.getMethod() + "\" for this request.";
604: Log.log_3507(ccName, detail);
605: throw new InvalidRequestException(detail);
606: }
607: return cc;
608:
609: // Otherwise try to detect which one is appropriate
610: } else {
611: return detectCallingConvention(request);
612: }
613: }
614:
615: /**
616: * Gets the calling convention for the given name.
617: *
618: * <p>The returned calling convention is bootstrapped and initialized.
619: *
620: * @param name
621: * the name of the calling convention to retrieve, should not be
622: * <code>null</code>.
623: *
624: * @return
625: * the calling convention initialized, never <code>null</code>.
626: *
627: * @throws InvalidRequestException
628: * if the calling convention name is unknown.
629: */
630: private CallingConvention getCallingConvention(String name)
631: throws InvalidRequestException {
632:
633: // Get the CallingConvention object
634: Object o = _conventions.get(name);
635:
636: // Not found
637: if (o == null && !_conventionNames.contains(name)) {
638: String detail = "Calling convention \"" + name
639: + "\" is unknown.";
640: Log.log_3507(name, detail);
641: throw new InvalidRequestException(detail);
642: } else if (o == null) {
643:
644: // Create the asked calling convention and initiaze it
645: CallingConvention cc = create(
646: _api.getBootstrapProperties(), name);
647:
648: // If created, store the object and attempt bootstrapping
649: if (cc != null) {
650: o = cc;
651: _conventions.put(name, cc);
652: bootstrap(name, cc, _api.getBootstrapProperties());
653: init(name, cc, _api.getRuntimeProperties());
654: } else {
655: o = CREATION_FAILED;
656: _conventions.put(name, o);
657: }
658: }
659:
660: // Creation failed
661: if (o == CREATION_FAILED) {
662: String detail = "Calling convention \"" + name
663: + "\" is known, but could not be created.";
664: Log.log_3507(name, detail);
665: throw new InvalidRequestException(detail);
666:
667: // Calling convention is recognized and was created OK
668: } else {
669:
670: // Not usable (so not bootstrapped and initialized)
671: CallingConvention cc = (CallingConvention) o;
672: if (!cc.isUsable()) {
673: String detail = "Calling convention \"" + name
674: + "\" is known, but is uninitialized.";
675: Log.log_3507(name, detail);
676: throw new InvalidRequestException(detail);
677: }
678:
679: return cc;
680: }
681: }
682:
683: /**
684: * Gets the calling convention for the given name, or <code>null</code> if
685: * the calling convention is not found or not usable.
686: *
687: * <p>The returned calling convention is bootstrapped and initialized.
688: *
689: * @param name
690: * the name of the calling convention to retrieve, should not be
691: * <code>null</code>.
692: *
693: * @return
694: * the calling convention, or <code>null</code>.
695: */
696: CallingConvention getCallingConvention2(String name) {
697:
698: try {
699: return getCallingConvention(name);
700: } catch (InvalidRequestException ex) {
701: return null;
702: }
703: }
704:
705: /**
706: * Attempts to detect which calling convention is the most appropriate for
707: * an incoming request. This method is called when the calling convention
708: * is not explicitly specified in the request.
709: *
710: * <p>The {@link CallingConvention#matchesRequest(HttpServletRequest)}
711: * method is used to determine which calling conventions match. Then
712: * the following algorithm is used to chose one:
713: *
714: * <ul>
715: * <li>if the default calling convention matches, use that;
716: * <li>otherwise if the {@link XSLTCallingConvention} matches and at
717: * least one of the parameters specific for the this calling
718: * convention is set, then use it;
719: * <li>otherwise if the {@link StandardCallingConvention} matches, use
720: * that;
721: * <li>otherwise if there is exactly one other calling convention that
722: * matches, use that one;
723: * <li>if none of the calling conventions match, throw an
724: * {@link InvalidRequestException}, indicating that no match could
725: * be found;
726: * <li>if multiple calling conventions match, throw an
727: * {@link InvalidRequestException}, indicating that several matches
728: * were found;
729: * </ul>
730: *
731: * @param request
732: * the incoming request, cannot be <code>null</code>.
733: *
734: * @return
735: * the calling convention to use, never <code>null</code>.
736: *
737: * @throws InvalidRequestException
738: * if the request is considered invalid, for example because the calling
739: * convention specified in the request is unknown.
740: */
741: CallingConvention detectCallingConvention(HttpServletRequest request)
742: throws InvalidRequestException {
743:
744: // Log: Request does not specify any calling convention
745: Log.log_3508();
746:
747: // See if the default calling convention matches
748: CallingConvention defCC = getCallingConvention2(_defaultConventionName);
749: if (defCC != null && defCC.matchesRequest(request)) {
750: Log.log_3509(defCC.getClass().getName());
751: return defCC;
752: }
753:
754: // If not, see if XSLT-specific properties are set /and/ _xins-xslt matches
755: CallingConvention xslCC = getCallingConvention2("_xins-xslt");
756: if (xslCC != null && xslCC != defCC
757: && xslCC.matchesRequest(request)) {
758:
759: // Determine if one of the two XSLT-specific parameters is set
760: String p1 = request
761: .getParameter(XSLTCallingConvention.TEMPLATE_PARAMETER);
762: String p2 = request
763: .getParameter(XSLTCallingConvention.CLEAR_TEMPLATE_CACHE_PARAMETER);
764:
765: // Use the XSLT calling convention if and only if at least one of the
766: // parameters is actually set
767: if (!(TextUtils.isEmpty(p1) && TextUtils.isEmpty(p2))) {
768: Log.log_3509(XSLTCallingConvention.class.getName());
769: return xslCC;
770: }
771: }
772:
773: // If not, see if _xins-std matches
774: CallingConvention stdCC = getCallingConvention2("_xins-std");
775: if (stdCC != null && stdCC != defCC
776: && stdCC.matchesRequest(request)) {
777: Log.log_3509(StandardCallingConvention.class.getName());
778: return stdCC;
779: }
780:
781: // Local variable to hold the first matching calling convention
782: CallingConvention matching = null;
783:
784: // Determine which calling conventions match
785: Iterator itConventionNames = _conventionNames.iterator();
786: while (itConventionNames.hasNext()) {
787: String name = (String) itConventionNames.next();
788: Object value = getCallingConvention2(name);
789:
790: // if the value is null, that's maybe an initialization problem
791: if (value == null) {
792: value = _conventions.get(name);
793: }
794:
795: // Skip all values that are not CallingConvention instances
796: // Skip also the default and the standard calling conventions, we
797: // already established that they cannot handle the request
798: if (value == CREATION_FAILED || value == defCC
799: || value == stdCC) {
800: continue;
801: }
802:
803: // Convert the value to a CallingConvention
804: CallingConvention cc = (CallingConvention) value;
805:
806: // Determine whether this one can handle it
807: if (cc.matchesRequest(request)) {
808:
809: // First match
810: if (matching == null) {
811: matching = cc;
812:
813: // Fail: Multiple matches
814: } else {
815: Log.log_3511();
816: String multipleMatches = "Request does not specify a calling "
817: + "convention, it cannot be handled by the "
818: + "default calling convention and multiple "
819: + "calling conventions are able to handle it: \"";
820: String message = multipleMatches
821: + matching.getClass().getName() + "\", \""
822: + cc.getClass().getName() + "\".";
823: throw new InvalidRequestException(message);
824: }
825: }
826: }
827:
828: // One match
829: if (matching != null) {
830: return matching;
831:
832: // Fail: No matches
833: } else {
834: Log.log_3510();
835: String noMatches = "Request does not specify a calling convention, it "
836: + "cannot be handled by the default calling convention and it was "
837: + "not possible to find any calling convention that can handle it.";
838: throw new InvalidRequestException(noMatches);
839: }
840: }
841:
842: /**
843: * Returns the set of HTTP methods supported for function invocations. This
844: * is the union of the methods supported by the individual calling
845: * conventions for invoking functions, so excluding the <em>OPTIONS</em>
846: * method. The latter cannot be used for function invocations, only to
847: * determine which HTTP methods are available. See
848: * {@link CallingConvention#getSupportedMethods()}.
849: *
850: * @return
851: * the {@link Set} of supported HTTP methods, never <code>null</code>.
852: *
853: * @throws IllegalStateException
854: * if this calling convention manager is not yet bootstrapped and
855: * initialized, see {@link #isUsable()}.
856: */
857: final Set getSupportedMethods() throws IllegalStateException {
858:
859: // Make sure this Manageable object is bootstrapped and initialized
860: assertUsable();
861:
862: HashSet supportedMethods = new HashSet();
863: Iterator itConventionNames = _conventionNames.iterator();
864: while (itConventionNames.hasNext()) {
865:
866: String name = (String) itConventionNames.next();
867: Object convention = getCallingConvention2(name);
868:
869: // if the value is null, that's maybe an initialization problem
870: if (convention == null) {
871: convention = _conventions.get(name);
872: }
873:
874: // Add all methods supported by the calling convention
875: if (convention instanceof CallingConvention) {
876: CallingConvention cc = (CallingConvention) convention;
877: supportedMethods.addAll(Arrays.asList(cc
878: .getSupportedMethods()));
879: }
880: }
881:
882: return supportedMethods;
883: }
884: }
|