/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.savara.protocol.internal.contract.generator; import java.util.logging.Logger; import javax.xml.namespace.QName; import org.savara.protocol.model.util.InteractionUtil; import org.savara.protocol.model.util.TypeSystem; import org.savara.common.logging.FeedbackHandler; import org.savara.common.logging.MessageFormatter; import org.savara.common.model.annotation.Annotation; import org.savara.common.model.annotation.AnnotationDefinitions; import org.savara.contract.model.Contract; import org.savara.contract.model.FaultDetails; import org.savara.contract.model.Interface; import org.savara.contract.model.MessageExchangePattern; import org.savara.contract.model.Namespace; import org.savara.contract.model.OneWayRequestMEP; import org.savara.contract.model.RequestResponseMEP; import org.savara.contract.model.Type; import org.savara.contract.model.TypeDefinition; import org.scribble.protocol.model.DefaultVisitor; import org.scribble.protocol.model.Protocol; import org.scribble.protocol.model.Interaction; import org.scribble.protocol.model.ProtocolModel; import org.scribble.protocol.model.Run; import org.scribble.protocol.model.Role; import org.scribble.protocol.model.TypeImport; import org.scribble.protocol.model.TypeImportList; import org.scribble.protocol.model.TypeReference; import org.scribble.protocol.util.RunUtil; import org.scribble.protocol.util.TypesUtil; /** * This class examines a protocol to determine the contract that represents * the static functional interface to the role's behaviour. * */ public class ContractIntrospector extends DefaultVisitor { private Contract m_contract=new Contract(); private java.util.Set<Protocol> m_processedProtocols=null; private Role m_serverRole=null; private java.util.Set<Role> m_clientRoles=null; private Protocol m_protocol=null; private FeedbackHandler m_feedbackHandler=null; private static Logger logger = Logger.getLogger(ContractIntrospector.class.getName()); /** * Constructor for the contract introspector. * * @param protocol The protocol to introspect * @param clients The optional set of client roles * @param server The server role * @param handler The feedback handler */ public ContractIntrospector(Protocol protocol, java.util.Set<Role> clients, Role server, FeedbackHandler handler) { this(protocol, clients, server, null, null, handler); } /** * Constructor for the contract introspector. * * @param protocol The protocol being introspected * @param clients The optional set of client roles * @param server The server role * @param contract The optional contract being derived * @param processed The optional set of protocols currently processed * @param handler The feedback handler */ public ContractIntrospector(Protocol protocol, java.util.Set<Role> clients, Role server, Contract contract, java.util.Set<Protocol> processed, FeedbackHandler handler) { m_contract = contract; m_clientRoles = clients; m_serverRole = server; m_feedbackHandler = handler; if (m_contract == null) { m_contract = new Contract(); m_contract.setName(m_serverRole.getName()); java.util.List<Annotation> annotations=AnnotationDefinitions.getAnnotations(protocol.getAnnotations(), AnnotationDefinitions.INTERFACE); if (annotations != null) { for (Annotation annotation : annotations) { String namespace=(String)annotation.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY); String role=(String)annotation.getProperties().get(AnnotationDefinitions.ROLE_PROPERTY); if (namespace != null && role != null && role.equals(m_serverRole.getName())) { m_contract.setNamespace(namespace); break; } } } } if (processed != null) { m_processedProtocols = processed; } else { m_processedProtocols = new java.util.HashSet<Protocol>(); } m_protocol = protocol; } /** * This method returns the contract being derived. * * @return The contract */ public Contract getContract() { return(m_contract); } /** * This method returns the feedback handler. * * @return The feedback handler */ protected FeedbackHandler getFeedbackHandler() { return(m_feedbackHandler); } /** * This method returns the interface. * * @param model The protocol model * @param intfNamespace The namespace * @param intfName The name * @return The interface */ public Interface getInterface(ProtocolModel model, String intfNamespace, String intfName) { QName qname=null; // TODO: Check Contract/Interface - whether should have separate name/namespace? if (intfName == null || intfName.trim().length() == 0) { // Search for interface annotation on protocol Annotation ann=AnnotationDefinitions.getAnnotationWithProperty(model.getProtocol().getAnnotations(), AnnotationDefinitions.INTERFACE, "role", m_serverRole.getName()); if (ann != null) { qname = new QName((String)ann.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY), (String)ann.getProperties().get(AnnotationDefinitions.NAME_PROPERTY)); } else { qname = new QName(m_contract.getNamespace(), m_serverRole.getName()); } } else { qname = new QName(intfNamespace, intfName); } Interface ret=getContract().getInterface(qname.getNamespaceURI(), qname.getLocalPart()); if (ret == null) { // Create interface for the role ret = new Interface(); ret.setName(qname.getLocalPart()); ret.setNamespace(qname.getNamespaceURI()); addNamespace(model, qname.getNamespaceURI()); getContract().getInterfaces().add(ret); } return(ret); } /** * This method returns the set of processed protocols. * * @return The processed protocols */ public java.util.Set<Protocol> getProcessedProtocols() { return(m_processedProtocols); } /** * This method introspects the supplied protocol to derive information * that can be used to define the functional contract for the role * associated with the protocol. * * @param conv The located protocol */ public void process() throws IllegalStateException { if (m_protocol == null) { throw new IllegalStateException(MessageFormatter.format( java.util.PropertyResourceBundle.getBundle( "org.savara.contract.Messages"), "SAVARA-CONTRACT-00001")); } m_protocol.visit(this); // Ensure only interfaces with atleast one operation remain java.util.Iterator<Interface> iter=getContract().getInterfaces().iterator(); while (iter.hasNext()) { Interface intf=iter.next(); if (intf.getMessageExchangePatterns().size() == 0) { iter.remove(); } } } public boolean start(Protocol elem) { // Only visit children if same protocol that is being visited return(elem == m_protocol); } public void accept(Run elem) { Protocol toProtocol=RunUtil.getInnerProtocol(elem.getEnclosingProtocol(), elem.getProtocolReference()); if (toProtocol != null) { // Check if protocol already processed to avoid stack overflow if (m_processedProtocols.contains(toProtocol) == false) { m_processedProtocols.add(toProtocol); // TODO: Need to map roles Role mappedServerRole=m_serverRole; java.util.Set<Role> mappedClientRoles=m_clientRoles; ContractIntrospector ci=new ContractIntrospector(toProtocol, mappedClientRoles, mappedServerRole, getContract(), getProcessedProtocols(), getFeedbackHandler()); ci.process(); } else { logger.fine("Invoked definition not found for "+elem.getProtocolReference()); } } else { logger.fine("Run protocol not returned - possibly external"); } } /* public boolean process(ModelObject obj) { boolean f_visitChildren=true; if (obj.getClass() == Protocol.class) { // Only visit children if same protocol that is being visited f_visitChildren = (obj == m_protocol); } else if (obj.getClass() == Run.class) { Run run=(Run)obj; Protocol toProtocol=run.getProtocol(); if (toProtocol != null) { // Check if protocol already processed to avoid stack overflow if (m_processedProtocols.contains(toProtocol) == false) { m_processedProtocols.add(toProtocol); // TODO: Need to map roles Role mappedServerRole=m_serverRole; java.util.Set<Role> mappedClientRoles=m_clientRoles; ContractIntrospector ci=new ContractIntrospector(toProtocol, mappedClientRoles, mappedServerRole, getContract(), getProcessedProtocols(), getJournal()); ci.process(); } else { logger.fine("Invoked definition not found for "+run.getProtocolReference()); } } else { logger.fine("Run protocol not returned - possibly external"); } } else if (obj.getClass() == Interaction.class) { accept((Interaction)obj); } return(f_visitChildren); } */ protected boolean isRoleRelevant(java.util.List<Role> roles) { boolean ret=false; for (Role role : roles) { ret = isRoleRelevant(role); if (ret) { break; } } return(ret); } protected boolean isRoleRelevant(Role role) { boolean ret=false; ret = (role != null && role.equals(m_serverRole)); return(ret); } public void accept(Interaction interaction) { // Check if interaction is relevant for the server role if (!isRoleRelevant(interaction.getEnclosingProtocol().getLocatedRole()) && !isRoleRelevant(interaction.getFromRole()) && !isRoleRelevant(interaction.getToRoles())) { return; } // Check if interface has been specified String intfName=null; String intfNamespace=null; Annotation intfAnn=AnnotationDefinitions.getAnnotation(interaction.getAnnotations(), AnnotationDefinitions.INTERFACE); if (intfAnn != null) { intfName = (String)intfAnn.getProperties().get(AnnotationDefinitions.NAME_PROPERTY); intfNamespace = (String)intfAnn.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY); } // Receiving a request - so record this in the contract Interface intf=getInterface(interaction.getModel(), intfNamespace, intfName); // Check if receiving a request if (InteractionUtil.isRequest(interaction) && !InteractionUtil.isSend(interaction, m_serverRole)) { String op=interaction.getMessageSignature().getOperation(); if (op == null) { op = InteractionUtil.getRequestLabel(interaction); } if (op != null && (m_clientRoles == null || interaction.getFromRole() == null || m_clientRoles.contains(interaction.getFromRole()))) { // Check if message exchange pattern exists for operation MessageExchangePattern mep=intf.getMessageExchangePatternForOperation(op); if (mep == null) { // Create new MEP if (InteractionUtil.getRequestLabel(interaction) != null) { mep = new RequestResponseMEP(); } else { mep = new OneWayRequestMEP(); } mep.setOperation(op); for (int i=0; i < interaction.getMessageSignature().getTypeReferences().size(); i++) { mep.getTypes().add(convertType(interaction.getMessageSignature().getTypeReferences().get(i))); } intf.getMessageExchangePatterns().add(mep); } } } else if (InteractionUtil.isResponse(interaction) && InteractionUtil.isSend(interaction, m_serverRole)) { String op=interaction.getMessageSignature().getOperation(); if (op == null) { op = InteractionUtil.getReplyToLabel(interaction); } if (op != null) { // Check if message exchange pattern exists for operation MessageExchangePattern mep=intf.getMessageExchangePatternForOperation(op); if (mep instanceof RequestResponseMEP) { RequestResponseMEP rrmep=(RequestResponseMEP)mep; if (InteractionUtil.isFaultResponse(interaction)) { String faultName=InteractionUtil.getFaultName(interaction); if (rrmep.getFaultDetails(faultName) == null) { FaultDetails fd=new FaultDetails(); fd.setName(faultName); for (int i=0; i < interaction.getMessageSignature().getTypeReferences().size(); i++) { fd.getTypes().add(convertType(interaction.getMessageSignature().getTypeReferences().get(i))); } rrmep.getFaultDetails().add(fd); } } else if (rrmep.getResponseTypes().size() == 0) { if (interaction.getMessageSignature().getTypeReferences().size() > 1) { getFeedbackHandler().error("Response has more than one type", null); } else if (interaction.getMessageSignature().getTypeReferences().size() == 1) { rrmep.getResponseTypes().add(convertType( interaction.getMessageSignature().getTypeReferences().get(0))); } } } } } } /** * This method introspects the supplied interaction to generate Message Exchange * Patterns on the contract interface. * * @param interaction The interaction */ /* public boolean start(When when) { // Check if interaction is relevant for the server role if (!isRoleRelevant(((Choice)when.getParent()).enclosingProtocol().getRole()) && !isRoleRelevant(((Choice)when.getParent()).getFromRole()) && !isRoleRelevant(((Choice)when.getParent()).getToRole())) { return(true); } // Check if interface has been specified String intfName=null; Annotation intfAnn=AnnotationDefinitions.getAnnotation(when.getAnnotations(), AnnotationDefinitions.INTERFACE); if (intfAnn != null) { intfName = (String)intfAnn.getProperties().get(AnnotationDefinitions.NAME_PROPERTY); } // Receiving a request - so record this in the contract Interface intf=getInterface(when.getModel(), intfName); // Check if receiving a request if (InteractionUtil.isRequest(when) && !InteractionUtil.isSend(when, m_serverRole)) { if (when.getMessageSignature().getOperation() != null) { // Check if message exchange pattern exists for operation MessageExchangePattern mep=intf.getMessageExchangePatternForOperation( when.getMessageSignature().getOperation()); if (mep == null) { // Create new MEP if (InteractionUtil.getRequestLabel(when) != null) { mep = new RequestResponseMEP(); } else { mep = new OneWayRequestMEP(); } mep.setOperation(when.getMessageSignature().getOperation()); for (int i=0; i < when.getMessageSignature().getTypeReferences().size(); i++) { mep.getTypes().add(convertType(when.getMessageSignature().getTypeReferences().get(i))); } intf.getMessageExchangePatterns().add(mep); } } } else if (InteractionUtil.isResponse(when) && InteractionUtil.isSend(when, m_serverRole)) { if (when.getMessageSignature().getOperation() != null) { // Check if message exchange pattern exists for operation MessageExchangePattern mep=intf.getMessageExchangePatternForOperation( when.getMessageSignature().getOperation()); if (mep instanceof RequestResponseMEP) { RequestResponseMEP rrmep=(RequestResponseMEP)mep; if (InteractionUtil.isFaultResponse(when)) { String faultName=InteractionUtil.getFaultName(when); if (rrmep.getFaultDetails(faultName) == null) { FaultDetails fd=new FaultDetails(); fd.setName(faultName); for (int i=0; i < when.getMessageSignature().getTypeReferences().size(); i++) { fd.getTypes().add(convertType(when.getMessageSignature().getTypeReferences().get(i))); } rrmep.getFaultDetails().add(fd); } } else if (rrmep.getResponseTypes().size() == 0) { if (when.getMessageSignature().getTypeReferences().size() > 1) { getFeedbackHandler().error("Response has more than one type", null); } else if (when.getMessageSignature().getTypeReferences().size() == 1) { rrmep.getResponseTypes().add(convertType( when.getMessageSignature().getTypeReferences().get(0))); } } } } } return(true); } */ /** * This method converts a protocol type reference into a contract model type. * * @param tref The protocol type reference * @return The type */ public Type convertType(TypeReference tref) { Type ret=new Type(); if (getContract().getTypeDefinition(tref.getName()) == null) { TypeDefinition td=new TypeDefinition(); td.setName(tref.getName()); TypeImport ti=TypesUtil.getTypeImport(tref); if (ti != null) { TypeImportList til=(TypeImportList)ti.getParent(); td.setDataType(ti.getDataType().getDetails()); if (til != null) { // Associate annotations from type import list with type definition for (org.scribble.common.model.Annotation ann : til.getAnnotations()) { if (ann instanceof org.savara.common.model.annotation.Annotation) { td.getAnnotations().add((org.savara.common.model.annotation.Annotation)ann); } } td.setTypeSystem(til.getFormat()); if (til.getFormat() != null && TypeSystem.XSD.equals(til.getFormat()) && ti.getDataType() != null && ti.getDataType().getDetails() != null) { QName qname=QName.valueOf(ti.getDataType().getDetails()); addNamespace(tref.getModel(), qname.getNamespaceURI()); } } } // Copy properties that may provide additional information about the type td.getProperties().putAll(tref.getProperties()); getContract().getTypeDefinitions().add(td); } ret.setName(tref.getName()); return(ret); } protected void addNamespace(ProtocolModel model, String namespace) { // Check if namespace has been defined for location if (getContract().getNamespaceForURI(namespace) == null && model != null) { java.util.List<Annotation> annotations= AnnotationDefinitions.getAnnotations(model.getProtocol().getAnnotations(), AnnotationDefinitions.TYPE); for (Annotation ann : annotations) { String nstxt=(String)ann.getProperties().get(AnnotationDefinitions.NAMESPACE_PROPERTY); if (nstxt != null && nstxt.equals(namespace)) { Namespace ns=new Namespace(); ns.setURI(namespace); ns.setPrefix((String)ann.getProperties().get(AnnotationDefinitions.PREFIX_PROPERTY)); ns.setSchemaLocation((String)ann.getProperties().get(AnnotationDefinitions.LOCATION_PROPERTY)); getContract().getNamespaces().add(ns); break; } } } } }