View Javadoc
1   package org.andromda.cartridges.webservice.metafacades;
2   
3   import java.util.Collection;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   import java.util.Map;
7   import org.andromda.cartridges.webservice.WebServiceGlobals;
8   import org.andromda.cartridges.webservice.WebServiceUtils;
9   import org.andromda.core.metafacade.MetafacadeBase;
10  import org.andromda.core.metafacade.ModelValidationMessage;
11  import org.andromda.metafacades.uml.MetafacadeUtils;
12  import org.andromda.metafacades.uml.ModelElementFacade;
13  import org.andromda.metafacades.uml.ParameterFacade;
14  import org.andromda.metafacades.uml.Service;
15  import org.andromda.metafacades.uml.UMLProfile;
16  import org.andromda.translation.ocl.validation.OCLExpressions;
17  import org.andromda.translation.ocl.validation.OCLIntrospector;
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.log4j.Logger;
20  
21  /**
22   * MetafacadeLogic implementation for org.andromda.cartridges.webservice.metafacades.WebServiceOperation.
23   *
24   * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation
25   * @author Bob Fields
26   */
27  public class WebServiceOperationLogicImpl
28      extends WebServiceOperationLogic
29  {
30      private static final long serialVersionUID = 34L;
31      /**
32       * @param metaObject
33       * @param context
34       */
35      public WebServiceOperationLogicImpl(
36          Object metaObject,
37          String context)
38      {
39          super(metaObject, context);
40      }
41  
42      /**
43       * The logger instance.
44       */
45      private static final Logger logger = Logger.getLogger(WebServiceOperationLogicImpl.class);
46  
47      /**
48       * @return getOwner().hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE) or hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE_OPERATION)
49       * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#isExposed()
50       */
51      protected boolean handleIsExposed()
52      {
53          // Private methods are for doc and future use purposes, but are allowed.
54          boolean visibility = this.getVisibility().equals("public") || this.getVisibility().equals("protected");
55          return visibility && (this.getOwner().hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE) ||
56          this.hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE_OPERATION));
57      }
58  
59      /**
60       * The prefix given to the test implementation operation names.
61       */
62      private static final String TEST_IMPLEMENTATION_OPERATION_NAME_PREFIX =
63          "testImplementationOperationNamePrefix";
64  
65      /**
66       * Gets the test implementation operation name prefix.
67       */
68      private String getTestImplementationOperationNamePrefix()
69      {
70          return String.valueOf(
71              this.getConfiguredProperty(TEST_IMPLEMENTATION_OPERATION_NAME_PREFIX));
72      }
73  
74      /**
75       * @return getTestImplementationOperationNamePrefix() + StringUtils.capitalize(this.getTestName())
76       * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationName()
77       */
78      protected String handleGetTestImplementationName()
79      {
80          return this.getTestImplementationOperationNamePrefix() +
81          StringUtils.capitalize(this.getTestName());
82      }
83  
84      /**
85       * @return "this." + this.getTestImplementationSignature()
86       * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationCall()
87       */
88      protected String handleGetTestImplementationCall()
89      {
90          return "this." + this.getTestImplementationSignature();
91      }
92  
93      /**
94       * @return this.getTestImplementationOperationNamePrefix() + StringUtils.capitalize(this.getTestSignature())
95       * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationSignature()
96       */
97      protected String handleGetTestImplementationSignature()
98      {
99          return this.getTestImplementationOperationNamePrefix() +
100         StringUtils.capitalize(this.getTestSignature());
101     }
102 
103     /**
104      * @return Operation Signature, taking WSSecurity and WSCustomHeader annotations into account
105      * @param withArgumentNames boolean Use argument names.
106      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getSignature()
107      */
108     protected String handleGetSignature(final boolean withArgumentNames)
109     {
110         Collection<ParameterFacade> arguments = this.getArguments();
111         String signature = MetafacadeUtils.getSignature(
112                 this.getName(),
113                 arguments,
114                 withArgumentNames,
115                 null);
116         Service service = this.getService();
117         if (this.hasStereotype("WSCustomHeader") || this.getService().hasStereotype("WSCustomHeader"))
118         {
119             String serviceElementName = (String) service.findTaggedValue("andromda_wsdl_header_element");
120             String serviceNamespace = (String) service.findTaggedValue("andromda_header_namespace");
121             String serviceClassName = WebServiceUtils.getPackageName(serviceNamespace) + '.' + serviceElementName;
122             String serviceParameterName = StringUtils.uncapitalize(serviceElementName);
123             signature = signature.substring(0, signature.length()-1);
124             if (!arguments.isEmpty())
125             {
126                 signature += ", ";
127             }
128             signature += serviceClassName;
129             if (withArgumentNames)
130             {
131                 signature += " " + serviceParameterName;
132             }
133             signature += ')';
134         }
135         if (this.hasStereotype("WSSecurity") || this.getService().hasStereotype("WSSecurity"))
136         {
137             String securityElementName = (String) service.findTaggedValue("andromda_wsdl_security_element");
138             String securityNamespace = (String) service.findTaggedValue("andromda_security_namespace");
139             String securityClassName = WebServiceUtils.getPackageName(securityNamespace) + '.' + securityElementName;
140             String securityParameterName = StringUtils.uncapitalize(securityElementName);
141             signature = signature.substring(0, signature.length()-1);
142             if (!arguments.isEmpty() || this.hasStereotype("WSCustomHeader") || this.getService().hasStereotype("WSCustomHeader"))
143             {
144                 signature += ", ";
145             }
146             signature += securityClassName;
147             if (withArgumentNames)
148             {
149                 signature += " " + securityParameterName;
150             }
151             signature += ')';
152         }
153         return signature;
154     }
155 
156     /**
157      * The prefix given to the junit test operations.
158      */
159     private static final String TEST_NAME_PREFIX = "test";
160 
161     /**
162      * @return TEST_NAME_PREFIX + StringUtils.capitalize(this.getName())
163      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestName()
164      */
165     protected String handleGetTestName()
166     {
167         return TEST_NAME_PREFIX + StringUtils.capitalize(this.getName());
168     }
169 
170     /**
171      * @return "this." + this.getSignature()
172      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestCall()
173      */
174     protected String handleGetTestCall()
175     {
176         return "this." + this.getSignature();
177     }
178 
179     /**
180      * @return this.getTestName() + "()"
181      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestSignature()
182      */
183     protected String handleGetTestSignature()
184     {
185         return this.getTestName() + "()";
186     }
187 
188     /**
189      * The property defining the default style to give the web services.
190      */
191     private static final String PROPERTY_DEFAULT_PARAMETER_STYLE = "defaultParameterStyle";
192     private static final String DEFAULT = "default";
193     private static final String EMPTY_STRING = "";
194     private static final String BOOLEAN_FALSE = "false";
195 
196     /**
197      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#handleGetParameterStyle()
198      */
199     @Override
200     protected String handleGetParameterStyle()
201     {
202         String style = (String)this.findTaggedValue(WebServiceGlobals.WEB_SERVICE_PARAMETER_STYLE);
203         if (StringUtils.isEmpty(style) || style.equals(DEFAULT))
204         {
205             style = String.valueOf(this.getConfiguredProperty(PROPERTY_DEFAULT_PARAMETER_STYLE));
206         }
207         if (StringUtils.isEmpty(style) || style.equals(DEFAULT))
208         {
209             style = "wrapped";
210         }
211         return style;
212     }
213     /**
214      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestCacheType()
215      */
216     @Override
217     protected String handleGetRestCacheType()
218     {
219         String cacheType = (String)this.findTaggedValue(WebServiceGlobals.CACHE_TYPE);
220         if (!this.isRest() || StringUtils.isBlank(cacheType) || cacheType.equals(DEFAULT))
221         {
222             cacheType = EMPTY_STRING;
223         }
224         return cacheType;
225     }
226 
227     /**
228      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestConsumes()
229      */
230     @Override
231     protected String handleGetRestConsumes()
232     {
233         String consumes = (String)this.findTaggedValue(WebServiceGlobals.REST_CONSUMES);
234         if (!this.isRest() || StringUtils.isBlank(consumes) || consumes.equals(DEFAULT))
235         {
236             consumes = EMPTY_STRING;
237         }
238         else
239         {
240             consumes = translateMediaType(consumes);
241         }
242         return consumes;
243     }
244 
245     /**
246      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestPartType()
247      */
248     @Override
249     protected String handleGetRestPartType()
250     {
251         String partType = (String)this.findTaggedValue(WebServiceGlobals.REST_PART_TYPE);
252         if (!this.isRest() || StringUtils.isBlank(partType) || partType.equals(DEFAULT))
253         {
254             partType = EMPTY_STRING;
255         }
256         return partType;
257     }
258 
259     private static final String SLASH = "/";
260     private static final String QUOTE = "\"";
261     private static final String LBRACKET = "{";
262     private static final String RBRACKET = "}";
263     /**
264      * Create default REST URL of /methodname/parameter/{parameter}/
265      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestPath()
266      */
267     @Override
268     protected String handleGetRestPath()
269     {
270         String path = (String)this.findTaggedValue(WebServiceGlobals.REST_PATH);
271         StringBuilder pathBuffer = new StringBuilder();
272         if (!this.isRest() || StringUtils.isBlank(path) || path.equals(DEFAULT))
273         {
274             pathBuffer.append(QUOTE).append(SLASH).append(this.getName().toLowerCase()).append(SLASH);
275             //path = SLASH + this.getName().toLowerCase() + SLASH;
276             Iterator<ParameterFacade> parameters = this.getArguments().iterator();
277             while (parameters.hasNext())
278             {
279                 ParameterFacade param = parameters.next();
280                 //if (WebServiceUtils.isSimpleType(param))
281                 //{
282                     String paramName = param.getName();
283                     pathBuffer.append(paramName).append(SLASH).append(LBRACKET).append(paramName).append(RBRACKET).append(SLASH);
284                 //}
285             }
286             pathBuffer.append(QUOTE);
287         }
288         else
289         {
290             if (StringUtils.isBlank(path))
291             {
292                 path = EMPTY_STRING;
293             }
294             pathBuffer.append(path);
295             if (!path.startsWith(QUOTE))
296             {
297                 pathBuffer.insert(0, QUOTE);
298             }
299             if (!path.endsWith(QUOTE) || path.length()<2)
300             {
301                 pathBuffer.append(QUOTE);
302             }
303         }
304         return pathBuffer.toString();
305     }
306 
307     /**
308      * Create test REST URL of /methodname/parameter/{parameter}/
309      * Substitutes test values for parameters
310      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestPath()
311      */
312     @Override
313     protected String handleGetRestTestPath()
314     {
315         String path = (String)this.findTaggedValue(WebServiceGlobals.REST_PATH);
316         StringBuilder pathBuffer = new StringBuilder();
317         WebServiceLogic service = (WebServiceLogic)this.getService();
318         String servicePath = service.getRestPath();
319         WebServiceUtils wsutils = new WebServiceUtils();
320         if (!this.isRest() || StringUtils.isBlank(path) || path.equals(DEFAULT))
321         {
322             pathBuffer.append(SLASH).append(this.getName().toLowerCase()).append(SLASH);
323             Iterator<ParameterFacade> parameters = this.getArguments().iterator();
324             while (parameters.hasNext())
325             {
326                 ParameterFacade param = parameters.next();
327                 //System.out.println("handleGetRestTestPath param=" + param.getName() + " servicePath=" + servicePath + " value=" + wsutils.createConstructor(param));
328                 if (WebServiceUtils.isSimpleType(param))
329                 {
330                     String paramValue = wsutils.createConstructor(param);
331                     // Only use the value if constructor returns new Class()
332                     if (paramValue.indexOf('(') > 0)
333                     {
334                         paramValue = paramValue.substring(paramValue.indexOf('(')+1, paramValue.indexOf(')'));
335                     }
336                     pathBuffer.append(param.getName()).append(SLASH).append(paramValue).append(SLASH);
337                 }
338             }
339             path = pathBuffer.toString();
340         }
341         else
342         {
343             if (StringUtils.isBlank(path))
344             {
345                 path = EMPTY_STRING;
346             }
347             // StringBuffer doesn't have replace(String, String) API
348             path = pathBuffer.append(path).toString();
349             Iterator<ParameterFacade> parameters = this.getArguments().iterator();
350             while (parameters.hasNext())
351             {
352                 ParameterFacade param = parameters.next();
353                 if (WebServiceUtils.isSimpleType(param))
354                 {
355                     String paramValue = wsutils.createConstructor(param).replace("\"", "");
356                     if (paramValue.indexOf('(') > 0)
357                     {
358                         paramValue = paramValue.substring(paramValue.indexOf('(')+1, paramValue.indexOf(')'));
359                     }
360                     path = StringUtils.replace(path, LBRACKET + param.getName() + RBRACKET, paramValue);
361                 }
362                 //System.out.println("handleGetRestTestPath param=" + param.getName() + " servicePath=" + servicePath + " value=" + wsutils.createConstructor(param) + " path=" + path);
363             }
364         }
365         path = servicePath + path;
366         path = path.replaceAll("\"", "");
367         path = path.replaceAll("//", "/");
368         return path;
369     }
370 
371     /**
372      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestProduces()
373      */
374     @Override
375     protected String handleGetRestProduces()
376     {
377         String produces = (String)this.findTaggedValue(WebServiceGlobals.REST_PRODUCES);
378         // default types: text for simple types, XML for complex types
379         if (!this.isRest() || produces == DEFAULT)
380         {
381             // See if the service class has REST_produces attribute set...
382             produces = (String)this.getOwner().findTaggedValue(WebServiceGlobals.REST_PRODUCES);
383             if (produces == DEFAULT)
384             {
385                 // Default produces type for simple or complex return types
386                 if (WebServiceUtils.isSimpleType(this.getReturnType()))
387                 {
388                     produces = "text/plain";
389                 }
390                 else
391                 {
392                     produces = "application/xml";
393                 }
394             }
395         }
396         else
397         {
398             produces = translateMediaType(produces);
399         }
400         return produces;
401     }
402 
403     /**
404      * Returns map of ProviderMediaType enumeration values to Provider/Consumer text
405      */
406     private static Map<String, String> getMediaTranslation()
407     {
408         final Map<String, String> mediaMap = new HashMap<String, String>();
409         mediaMap.put("default", QUOTE + "application/xml" + QUOTE);
410         mediaMap.put("ApplicationAtom", QUOTE + "application/atom+xml" + QUOTE);
411         mediaMap.put("ApplicationAtomEntry", QUOTE + "application/atom+xml;type=entry" + QUOTE);
412         mediaMap.put("ApplicationAtomXML", QUOTE + "application/atom+xml" + QUOTE);
413         mediaMap.put("ApplicationFastinfoset", QUOTE + "application/fastinfoset" + QUOTE);
414         mediaMap.put("ApplicationFormURLEncoded", QUOTE + "application/x-www-form-urlencoded" + QUOTE);
415         mediaMap.put("ApplicationJSON", QUOTE + "application/json" + QUOTE);
416         mediaMap.put("ApplicationOctetStream", QUOTE + "application/octet-stream" + QUOTE);
417         mediaMap.put("ApplicationSvgXML", QUOTE + "application/svg+xml" + QUOTE);
418         mediaMap.put("ApplicationXhtmlXML", QUOTE + "application/xhtml+xml" + QUOTE);
419         mediaMap.put("ApplicationXML", QUOTE + "application/xml" + QUOTE);
420         mediaMap.put("ApplicationXMLAll", QUOTE + "application/*+xml" + QUOTE);
421         mediaMap.put("ApplicationYaml", QUOTE + "application/yaml" + QUOTE);
422         mediaMap.put("MultipartAlternative", QUOTE + "multipart/alternative" + QUOTE);
423         mediaMap.put("MultipartFixed", QUOTE + "multipart/fixed" + QUOTE);
424         mediaMap.put("MultipartFormData", QUOTE + "multipart/form-data" + QUOTE);
425         mediaMap.put("MultipartMixed", QUOTE + "multipart/mixed" + QUOTE);
426         mediaMap.put("MultipartRelated", QUOTE + "multipart/related" + QUOTE);
427         mediaMap.put("TextPlain", QUOTE + "text/plain" + QUOTE);
428         mediaMap.put("TextXML", QUOTE + "text/xml" + QUOTE);
429         mediaMap.put("TextXYaml", QUOTE + "text/xyaml" + QUOTE);
430         mediaMap.put("TextYaml", QUOTE + "text/xml" + QUOTE);
431         mediaMap.put("Wildcard", QUOTE + "*/*" + QUOTE);
432         return mediaMap;
433     }
434 
435     /**
436      * Translates Media Enumeration Type into string for produces/consumes annotation
437      * @param input ProviderMediaType Enumeration value to be translated for Annotation
438      * @return getMediaTranslation().get(input)
439      */
440     public static String translateMediaType(String input)
441     {
442         String output = null;
443         if (StringUtils.isBlank(input) || input.equals(DEFAULT) || !getMediaTranslation().containsKey(input))
444         {
445             output = getMediaTranslation().get(DEFAULT);
446         }
447         else
448         {
449             output = getMediaTranslation().get(input);
450         }
451         return output;
452     }
453 
454     /**
455      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestProvider()
456      */
457     @Override
458     protected String handleGetRestProvider()
459     {
460         String provider = (String)this.findTaggedValue(WebServiceGlobals.REST_PROVIDER);
461         if (!this.isRest() || StringUtils.isBlank(provider) || provider.equals(DEFAULT))
462         {
463             provider = EMPTY_STRING;
464         }
465         return provider;
466     }
467 
468     private static final String GET = "@javax.ws.rs.GET";
469     private static final String AT = "@javax.ws.rs.";
470     /**
471      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestRequestType()
472      */
473     @Override
474     protected String handleGetRestRequestType()
475     {
476         String requestType = (String)this.findTaggedValue(WebServiceGlobals.REST_REQUEST_TYPE);
477         if (!this.isRest() || StringUtils.isBlank(requestType) || requestType.equals(DEFAULT))
478         {
479             requestType = GET;
480         }
481         else if (!requestType.startsWith(AT))
482         {
483             requestType = AT + requestType;
484         }
485         return requestType;
486     }
487 
488     /**
489      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRestRequestType()
490      */
491     @Override
492     protected int handleGetRestSuspend()
493     {
494         String suspend = (String)this.findTaggedValue(WebServiceGlobals.REST_SUSPEND);
495         if (!this.isRest() || StringUtils.isBlank(suspend) || suspend.equals(DEFAULT) || !StringUtils.isNumeric(suspend))
496         {
497             return 0;
498         }
499         return Integer.parseInt(suspend);
500     }
501 
502     /**
503      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRolesAllowed()
504      */
505     @Override
506     protected String handleGetRolesAllowed()
507     {
508         String rolesAllowed = (String)this.findTaggedValue(WebServiceGlobals.REST_ROLES_ALLOWED);
509         if (!this.isRest() || StringUtils.isBlank(rolesAllowed) || rolesAllowed.equals(DEFAULT))
510         {
511             rolesAllowed = EMPTY_STRING;
512         }
513         return rolesAllowed;
514     }
515 
516     /**
517      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#handleIsRest()
518      */
519     @Override
520     protected boolean handleIsRest()
521     {
522         String rest = (String)this.findTaggedValue(WebServiceGlobals.REST);
523         if (StringUtils.isBlank(rest) || rest.equals(DEFAULT))
524         {
525             rest = (String)this.getOwner().findTaggedValue(WebServiceGlobals.REST);
526             if (StringUtils.isBlank(rest) || rest.equals(DEFAULT))
527             {
528                 rest = BOOLEAN_FALSE;
529             }
530         }
531         return Boolean.valueOf(rest);
532     }
533 
534     /**
535      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#getRolesAllowed()
536      */
537     @Override
538     protected boolean handleIsRestEncoded()
539     {
540         String restEncoded = (String)this.findTaggedValue(WebServiceGlobals.REST_ENCODED);
541         if (!this.isRest() || StringUtils.isBlank(restEncoded) || restEncoded.equals(DEFAULT))
542         {
543             restEncoded = BOOLEAN_FALSE;
544         }
545         return Boolean.valueOf(restEncoded);
546     }
547 
548     /**
549      * @see org.andromda.cartridges.webservice.metafacades.WebServiceLogic#isRestAtom()
550      */
551     @Override
552     protected boolean handleIsRestAtom()
553     {
554         WebServiceLogic service = (WebServiceLogic)this.getService();
555         boolean rest = this.isRest();
556         boolean restAtom = false;
557         if (rest)
558         {
559             restAtom = this.getRestProduces().contains("atom");
560             if (!restAtom)
561             {
562                 restAtom = service.getRestProduces().indexOf("atom") > -1;
563             }
564         }
565         return restAtom;
566     }
567 
568     /**
569      * Return the value from WebServiceOperation andromda_webservice_operationName, or just the operation.name
570      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#handleGetOperationName()
571      */
572     @Override
573     protected String handleGetOperationName()
574     {
575         String serviceName = (String)this.findTaggedValue(WebServiceGlobals.WEB_SERVICE_NAME);
576         if (StringUtils.isBlank(serviceName))
577         {
578             serviceName = this.getName();
579         }
580         return serviceName;
581     }
582 
583     /**
584      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#handleIsWebFaultOnAllExceptions()
585      */
586     @Override
587     protected boolean handleIsWebFaultOnAllExceptions()
588     {
589         boolean result = true;
590         String webserviceStack = String.valueOf(this.getConfiguredProperty("webserviceStack"));
591         if (webserviceStack.equals("cxf") || webserviceStack.equals("jaxws"))
592         {
593             Collection<ModelElementFacade> exceptions = this.getExceptions();
594             for (ModelElementFacade exception : exceptions)
595             {
596                 // Log the missing exception class, since validation message only shows the service operation name
597                 if (!exception.hasStereotype(UMLProfile.STEREOTYPE_WEB_FAULT))
598                 {
599                     result = false;
600                     logger.warn(exception.getFullyQualifiedName() + " WebFault stereotype missing from operation exception thrown by " + this.getFullyQualifiedName());
601                 }
602             }
603         }
604         return result;
605     }
606 
607     /**
608      * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperationLogic#handleGetWebServicePackage()
609      */
610     @Override
611     protected ModelElementFacade handleGetWebServicePackage()
612     {
613         return this.getOwner().getPackage();
614     }
615 
616     /**
617      * <p><b>Constraint:</b> org::andromda::cartridges::webservice::metafacades::WebServiceOperation::operation must start with a lowercase letter</p>
618      * <p><b>Error:</b> Operation name must start with a lowercase letter.</p>
619      * @param validationMessages Collection<ModelValidationMessage>
620      * @see MetafacadeBase#validateInvariants(Collection validationMessages)
621      */
622     @Override
623     public void validateInvariants(Collection<ModelValidationMessage> validationMessages)
624     {
625         super.validateInvariants(validationMessages);
626         try
627         {
628             final Object contextElement = this.THIS();
629             final String name = (String)OCLIntrospector.invoke(contextElement,"name");
630             boolean constraintValid = OCLExpressions.equal(
631                 name.substring(0,1).toLowerCase(),
632                 name.substring(0,1));
633             if (!constraintValid)
634             {
635                 validationMessages.add(
636                     new ModelValidationMessage(
637                         (MetafacadeBase)contextElement ,
638                         "org::andromda::cartridges::webservice::metafacades::WebServiceOperation::operation must start with a lowercase letter",
639                         "Operation name must start with a lowercase letter."));
640             }
641         }
642         catch (Throwable th)
643         {
644             Throwable cause = th.getCause();
645             int depth = 0; // Some throwables have infinite recursion
646             while (cause != null && depth < 7)
647             {
648                 th = cause;
649                 depth++;
650             }
651             logger.error("Error validating constraint 'org::andromda::cartridges::webservice::WebServicePackage::operation must start with a lowercase letter' ON "
652                 + this.THIS().toString() + ": " + th.getMessage(), th);
653         }
654     }
655 }