001package org.andromda.cartridges.webservice.metafacades;
002
003import java.util.Collection;
004import java.util.HashMap;
005import java.util.Iterator;
006import java.util.Map;
007import org.andromda.cartridges.webservice.WebServiceGlobals;
008import org.andromda.cartridges.webservice.WebServiceUtils;
009import org.andromda.core.metafacade.MetafacadeBase;
010import org.andromda.core.metafacade.ModelValidationMessage;
011import org.andromda.metafacades.uml.MetafacadeUtils;
012import org.andromda.metafacades.uml.ModelElementFacade;
013import org.andromda.metafacades.uml.ParameterFacade;
014import org.andromda.metafacades.uml.Service;
015import org.andromda.metafacades.uml.UMLProfile;
016import org.andromda.translation.ocl.validation.OCLExpressions;
017import org.andromda.translation.ocl.validation.OCLIntrospector;
018import org.apache.commons.lang.StringUtils;
019import org.apache.log4j.Logger;
020
021/**
022 * MetafacadeLogic implementation for org.andromda.cartridges.webservice.metafacades.WebServiceOperation.
023 *
024 * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation
025 * @author Bob Fields
026 */
027public class WebServiceOperationLogicImpl
028    extends WebServiceOperationLogic
029{
030    private static final long serialVersionUID = 34L;
031    /**
032     * @param metaObject
033     * @param context
034     */
035    public WebServiceOperationLogicImpl(
036        Object metaObject,
037        String context)
038    {
039        super(metaObject, context);
040    }
041
042    /**
043     * The logger instance.
044     */
045    private static final Logger logger = Logger.getLogger(WebServiceOperationLogicImpl.class);
046
047    /**
048     * @return getOwner().hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE) or hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE_OPERATION)
049     * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#isExposed()
050     */
051    protected boolean handleIsExposed()
052    {
053        // Private methods are for doc and future use purposes, but are allowed.
054        boolean visibility = this.getVisibility().equals("public") || this.getVisibility().equals("protected");
055        return visibility && (this.getOwner().hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE) ||
056        this.hasStereotype(UMLProfile.STEREOTYPE_WEBSERVICE_OPERATION));
057    }
058
059    /**
060     * The prefix given to the test implementation operation names.
061     */
062    private static final String TEST_IMPLEMENTATION_OPERATION_NAME_PREFIX =
063        "testImplementationOperationNamePrefix";
064
065    /**
066     * Gets the test implementation operation name prefix.
067     */
068    private String getTestImplementationOperationNamePrefix()
069    {
070        return String.valueOf(
071            this.getConfiguredProperty(TEST_IMPLEMENTATION_OPERATION_NAME_PREFIX));
072    }
073
074    /**
075     * @return getTestImplementationOperationNamePrefix() + StringUtils.capitalize(this.getTestName())
076     * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationName()
077     */
078    protected String handleGetTestImplementationName()
079    {
080        return this.getTestImplementationOperationNamePrefix() +
081        StringUtils.capitalize(this.getTestName());
082    }
083
084    /**
085     * @return "this." + this.getTestImplementationSignature()
086     * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationCall()
087     */
088    protected String handleGetTestImplementationCall()
089    {
090        return "this." + this.getTestImplementationSignature();
091    }
092
093    /**
094     * @return this.getTestImplementationOperationNamePrefix() + StringUtils.capitalize(this.getTestSignature())
095     * @see org.andromda.cartridges.webservice.metafacades.WebServiceOperation#getTestImplementationSignature()
096     */
097    protected String handleGetTestImplementationSignature()
098    {
099        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}