Automating endpoint WSDL creation with WebSphere Service Registry and Repository

The recommended approach to defining a web service in WSDL is to split the definition across three files: interface, binding, and endpoint. While this approach lets you maintain a clean separation between the elements being defined, it requires you to either update the endpoint WSDL document, or create new endpoint WSDL documents as your service moves from one environment (development, test, staging, production) to another. Generally speaking, the only difference between each of the environment-specific endpoint WSDLs is the value of the location attribute on the soap:address element. This article shows you how to use a WSRR plug-in to automate the creation of environment-specific endpoint WSDLs within WebSphere Service Registry and Repository.

Martin Smithson (msmiths@uk.ibm.com), Senior IT Specialist, IBM

Photo of Martin SmithsonMartin Smithson is a Product Architect on the WebSphere Service Registry and Repository Development team at the IBM Software Lab in Hursley, UK. He has 17 years of experience in the IT industry in software development and technical consultancy. His areas of expertise include WebSphere Application Server and the architecture, design, and development of J2EE applications. He has published several developerWorks articles and co-authored the following IBM Redbooks: WebSphere Application Server V6.1 System Management and Configuration, WebSphere Application Server V6 System Management and Configuration Handbook, WebSphere Service Registry and Repository Handbook, and CCF Connectors and Database Connections Using WebSphere Advanced Edition. Martin also developed the IBM Client Application Tool for JMS. You can contact Martin at msmiths@uk.ibm.com


developerWorks Contributing author
        level

15 May 2013

Introduction

Web services are described using Web Services Description Language (WSDL) documents where a WSDL document simply contains a set of definitions. These Web Service Definitions are often provided at three levels, as follows:

  • Service interface definitions
    Describe the interfaces in terms of operations and signatures provided by a service. These will often reference XML schemas to define common message formats or operation parameters.
  • Service binding definitions
    Describe how the interfaces are represented in the infrastructure and over the wire. This defines the transport protocols that are supported such as SOAP, JMS, and others. This references the service Interface definition to indicate exactly which operations are supported over this transport protocol.
  • Service endpoint definitions
    Describe each individual deployed service and describe how a consumer finds and connects to a service. They define the endpoints and indicate which bindings and thus interfaces are supported on each endpoint.

It is generally considered to be good practice to define each of the three levels of service definition in separate WSDL documents, with each document importing other documents as needed. This approach results in cleaner service definitions and maximizes the ability to reuse service definitions. While the separation of service definitions across multiple files is not enforced by IBM® WebSphere® WSRR, it is supported.

The implication of following this approach when using WSRR as a repository for your service artifacts is that you need to load an endpoint WSDL document into WSRR for each of the environments that a service is deployed to as it moves through the service development lifecycle. While this might not be too onerous when considering a single service, the overhead of generating these endpoint WSDL documents might become an issue as the number of services grows. It is also worth noting that, since WSRR can be configured to match your SOA Governance needs, the number of environments that are represented in WSRR could grow over time as you become more mature with your SOA adoption, with each additional environment requiring an additional endpoint WSDL document to be generated for a service.

This article describes the implementation of a WSRR plug-in that can be used to automatically generate endpoint WSDL documents for environments that are defined within WSRR.

WSRR plug-ins

The first thing that we need to do is to identify the type of WSRR plug-in that need to implement. WSRR defines a number of System Programming Interfaces (SPIs) that allow you to hook into the standard processing that is performed by WSRR. Classes written to these interfaces are referred to as WSRR plug-ins, and they enable you to implement code that enforces the governance processes defined by the business design within your SOA environment. There are currently three types of plug-ins defined in WSRR:

  • Validation plug-ins
    Are invoked at the beginning of calls to WSRR API methods, validation plug-ins enable you to validate that the operation being performed on the objects within WSRR complies with the policies defined in your SOA environment. Returning an error from a validation plug-in prevents the operation from being executed.
  • Modification plug-ins
    Are invoked after any database updates associated with WSRR API methods have taken place, but before the associated transaction has been committed. They enable you to modify the objects that are the target of the relevant operations and to create new objects within WSRR. Returning an error from a modification plug-in forces the associated transaction to roll back.
  • Notification plug-ins
    Are invoked at the end of calls to WSRR API methods. They let you perform various post-processing tasks based on the result of the operation requested. A notification plug-in cannot force the associated transaction to roll back.

This WSRR plug-in chain is shown below:

WSRR plug-in chain
WSRR plug-in chain

WSRR modification plug-ins

As mentioned above, the purpose of a modification plug-in is to modify "something" during the processing of the WSRR operation that triggered the plug-in. For example, you might want to modify one or more of the objects that have already been affected by the WSRR operation in order to add some additional metadata.

Another example of how you might want to use a modifier is to create some new objects in WSRR and relate them in some way to the objects that have already been affected by the WSRR operation. This is exactly what we want to do in our scenario. We want to be able to detect that an endpoint WSDL document has been loaded into WSRR and then copy this WSDL document a number of times. For each copy of the endpoint WSDL document that we create, we want to rewrite the location attribute on the soap:address element so that it is specific to a given environment.

Modifier interfaces

The modifier interfaces defined by the WSRR API are shown in diagram below:

Method invocation

The methods defined by the ServiceRegistryGovernanceModifier interface are invoked only for objects that are currently part of a governed lifecycle. That is, they will be invoked on a governance modifier only as a result of invoking a WSRR Governance API method on a governed object.

Modifier interfaces
Modifier interfaces

There are actually two modifier interfaces defined by the API. The ServiceRegistryModifier interface defines the methods that you should implement if you need to modify objects in WSRR as a result of invoking operations on the WSRR Core API, such as create, update or delete. The ServiceRegistryGovernanceModifier interface extends the ServiceRegistryModifier interface and defines additional methods that you should implement if you need to modify objects in WSRR as a result of performing governance operations in WSRR.

For our purposes, we need to implement a modification plug-in that will be invoked when WSDLDocument objects are created in WSRR. Therefore, the modification plug-in only needs to implement the ServiceRegistryModifier interface. As a result, and describing the ServiceRegistryGovernanceModifier interface is outside of the scope of this article -- for more information on it, see ServiceRegistryGovernanceModifier interface in the WSRR V8 information center.

Create method

ServiceRegistryStatus create(OriginalObject newObject)

The create method is invoked when an instance of an OriginalObject is created in WSRR, but before the associated transaction has been committed. The create method is passed a reference to the OriginalObject that was created. The modifier is able to modify this object to ensure that it complies with the relevant governance policies that are in place before it returns from the create method. It can also create new objects in WSRR in response to the object being created.

The create method must return a ServiceRegistryStatus object to indicate whether the modification process succeeded or failed. If the create method returns a SUCCESS or WARNING return code in the ServiceRegistryStatus object, WSRR will continue with the create operation and commit the transaction.

If the create method returns an ERROR return code in the ServiceRegistryStatus object, WSRR will throw a ServiceRegistryModificationException from the invoked WSRR API method and then roll back the transaction, meaning that neither the create operation nor any operations performed by the plug-in will take effect.

Update method

ServiceRegistryStatus update(BaseObject oldObject, BaseObject newObject)

The update method is invoked when an existing OriginalObject within WSRR is updated, but before the associated transaction has been committed. The update method is passed references to the object in its current state (oldObject) and its new state (newObject) in order to perform the required modification tasks. The modifier can modify the new state (newObject) of the object to ensure that it complies with the relevant governance policies that are in place before it returns from the update method.

In the case of Document objects, the required new object state provided by the newObject parameter might have no content present -- for example if you have retrieved only the metadata for the document and want to update only that metadata. If no content is present, the getContent() method returns null. Because there is no content on the new document state, you can assume that any previous document content has not been changed. If you need the modifier to access the document content, you can safely use the value from the current object state provided by the oldObject parameter.

The update method must return a ServiceRegistryStatus object to indicate whether the modification process succeeded or failed. If the update method returns a SUCCESS or WARNING return code in the ServiceRegistryStatus object, WSRR will continue with the update operation and commit the transaction, and both the update, and any operations performed by the modifier, will take effect.

If the update method returns an ERROR return code in the ServiceRegistryStatus object, WSRR will throw a ServiceRegistryModificationException from the invoked WSRR API method and will roll back the transaction, meaning that neither the update operation nor any operations performed by the plug-in will take effect.

Delete method

ServiceRegistryStatus delete(OriginalObject oldObject)

The delete method is invoked when an existing OriginalObject within WSRR is deleted, but before the associated transaction has been committed. The delete method is passed a reference to the object that is being deleted. Obviously, there is no point in the plug-in performing any modifications to the object that is being deleted, but it is able to modify other objects in WSRR in response to the delete taking place.

The delete method must return a ServiceRegistryStatus object to indicate whether the modification process succeeded or failed. If the delete method returns a SUCCESS or WARNING return code in the ServiceRegistryStatus object, WSRR will continue with the delete operation and commit the transaction, and both the delete, and any operations performed by the modifier, will take effect.

If the delete method returns an ERROR return code in the ServiceRegistryStatus object, WSRR will throw a ServiceRegistryModificationException from the invoked WSRR API method, and WSRR will roll back the transaction, meaning that neither the delete operation nor any operations performed by the plug-in will take effect.

Scenario

Before describing the actual implementation of the WSRR modification plug-in, this section describes the scenario in more detail. Assume that in the SOA environment, developers always generate endpoint WSDL documents that contain the actual production endpoint for the service. When one of these endpoint WSDL documents is loaded into WSRR, the modification plug-in needs to apply the appropriate Environment classification to mark it as the Production endpoint WSDL document. It then needs to iterate over all of the other immediate subclasses of the Environment classification that have been defined within WSRR, and create a copy of the endpoint WSDL for each environment. For each copy it needs to:

  1. Rename the port in the endpoint WSDL document to include an environment specific suffix, such as _Development or _Staging. This renaming enables you to uniquely identify the port that is specific to each environment.
  2. Rewrite the endpoint URL using an environment-specific template of some form.
  3. Classify the new endpoint WSDL document with the appropriate environment classification.
  4. Locate the SOAPServiceEndpoint correlated object that is created when the new endpoint WSDL document is loaded, and classify it with the appropriate environment classification.

There are several ways that you can define the template for the endpoint URL. One approach is to define them in a properties file and package it in the same JAR file that contains the modification plug-in. The problem with this approach is that if you need to define a new environment in WSRR in the future, you would need to modify the properties file and redeploy the updated plug-in JAR to WSRR.

A more flexible approach is to use ManualSOAPEndpoint objects within WSRR. You can manually create one of these objects to represent each environment defined within your SOA, and classify each one with the appropriate Environment classification. You can use the SOAP Location property to contain the actual template for the URL.

Modifier implementation

Sample code

The sample code in this article contains only a limited amount of logging and error checking. Production code would require more extensive code to ensure that errors are caught and handled appropriately.

The sections below describe various aspects of the implementation of the modification plug-in. For brevity, the article does not described every method of the plug-in implementation in detail, and instead focuses on those methods that drive the bulk of the processing within the plug-in. For a complete view of the plug-in implementation, see the source code.

Basic class definition

The code in Listing1 below simply defines the EndpointWSDLCopyModifier class, which implements the ServiceRegistryModifier interface. Since this modifier needs to be invoked only when objects are created in WSRR, this class defines only stub implementations of the update and delete methods. It also defines a number of member variables that are used when processing the WSDL documents and accessing WSRR, which can be categorized as follows:

  • WSRR delegate objects are used to simplify the process of accessing WSRR. They are business delegates that provide a wrapper around the WSRR local EJB interfaces and hide the details of using the EJB API from the client. Because these delegates access the WSRR EJB API using local interfaces, they can be used only from within plug-in code.
  • Since the bulk of the processing that this plug-in performs is parsing and modifying WSDL documents, a number of member variables will be defined.
  • BSRSDOHelper simplifies the process of modifying in-memory representations of WSRR objects, performing such tasks as adding relationships and retrieving property values.
  • The modifier also defines a number of other string constants omitted from the listing for brevity. To see the complete listing, see the actual source code.
Listing1. Class definition
public class EndpointWSDLCopyModifier implements ServiceRegistryModifier {

    // The WSRR delegate objects
    private RepositoryDelegate repositoryDelegate = null;
    private OntologyDelegate ontologyDelegate = null;
    
    // Objects used to parse the WSDL documents
    private WSDLFactory wsdlFactory = null;
    private ExtensionRegistry extRegistry = null;
    private DocumentBuilderFactory docBuilderfactory = null;
    private DocumentBuilder docBuilder = null;
    private XPathExpression portTypeExpression = null;
    private XPathExpression bindingExpression = null;
    private XPathExpression serviceExpression = null;
    private static final String XPATH_EXPR_WSDL_PORT_TYPE = "//portType";
    private static final String XPATH_EXPR_WSDL_BINDING = "//binding";
    private static final String XPATH_EXPR_WSDL_SERVICE = "//service";
    private static final String WSDL4J_VERBOSE = "javax.wsdl.verbose";
    private static final String WSDL4J_IMPORT_DOCUMENTS = "javax.wsdl.importDocuments";

    private BSRSDOHelper helper = BSRSDOHelper.INSTANCE;
    
    @Override
    public ServiceRegistryStatus delete(OriginalObject arg0) {
        return null;
    }

    @Override
    public ServiceRegistryStatus update(BaseObject arg0, BaseObject arg1) {
        return null;
    }
}

Constructor

The code in Listing 2 below defines the constructor for the EndpointWSDLCopyModifier class. The implementation of the constructor is straightforward, simply performing one-off initialization of various member variables:

Listing 2. Constructor
public EndpointWSDLCopyModifier() {

  try {
    repositoryDelegate = DelegateFactory.createRepositoryDelegate();
    ontologyDelegate = DelegateFactory.createOntologyDelegate();

    wsdlFactory = WSDLFactory.newInstance();
    extRegistry = wsdlFactory.newPopulatedExtensionRegistry();

    docBuilderfactory = DocumentBuilderFactory.newInstance();
    docBuilder = docBuilderfactory.newDocumentBuilder();
    docBuilderfactory.setNamespaceAware(true);

    XPathFactory xPathFactory = XPathFactory.newInstance();
    XPath xPath = xPathFactory.newXPath();
    portTypeExpression = xPath.compile(XPATH_EXPR_WSDL_PORT_TYPE);
    bindingExpression = xPath.compile(XPATH_EXPR_WSDL_BINDING);
    serviceExpression = xPath.compile(XPATH_EXPR_WSDL_SERVICE);
  } catch (ServiceRegistryRuntimeException e) {
    e.printStackTrace();
  } catch (WSDLException e) {
    e.printStackTrace();
  } catch (ParserConfigurationException e) {
    e.printStackTrace();
  } catch (XPathExpressionException e) {
    e.printStackTrace();
  }
}

Create method

The code in Listing 3 below defines the create method for the plug-in. This method is invoked when any OriginalObjects are created within WSRR. The first check you need to perform, therefore, is to see whether the object being created is a WSDLDocument object. If it is not, then this plug-in is not interested in it and it allows WSRR to continue processing.

Other important checks are also performed within the same if statement:

  • The isEndpointWSDL method checks to see if the WSDL document that has been created is an endpoint WSDL document.
  • The call to the hasEnvironmentClassification method checks to make sure that the WSDLDocument object has not already been classified with an Environment classification. This check is important, because the EndpointWSDLCopyModifier plug-in will be creating WSDLDocument objects itself. Since the plug-in will be classifying these objects with an Environment classification, this check lets you detect when the plug-in is invoked as a result of creating objects itself, thus avoiding an infinite loop.

Once you have determined that you are interested in the WSDLDocument that is being created, the next block of code performs processing on this object. As mentioned previously, you can assume that the endpoint WSDL document that is being created is for the production environment in an SOA, so the code classifies the object with the appropriate Environment classification. It also locates the SOAPServiceEndpoint and classifies it in the same way.

The for loop iterates over the Environment classifications that have been defined in WSRR, excluding the Production environment classification, and creates a copy of the production endpoint WSDL document. During the copy process, it rewrites the location attribute of the wsdlsoap:address element based on the template for the environment defined in the ManualSOAPEndpoint object. It then loads the new environment-specific endpoint WSDL document into WSRR, relating it to the WSDL binding document that already exists in WSRR. Finally, it locates the SOAPServiceEndpoint object that is created as a result of loading the new endpoint WSDL document and classifies it in the same way.

Listing 3. Create method
@Override
public ServiceRegistryStatus create(OriginalObject newObject) {
  ServiceRegistryStatus status = new ServiceRegistryStatus();
  /*
   * Check to see if the object that is being created is an endpoint WSDL document that is
   * not already classified with an environment classification.
   */
  if (  newObject instanceof WSDLDocument
     && isEndpointWSDL((WSDLDocument)newObject)
     && !hasEnvironmentClassification((WSDLDocument)newObject)
     ) {
    try {
      /*
       * We assume that the endpoint WSDL that is being created is for the production
       * environment.  Classify both the WSDL document and the SOAP Service Endpoint
       * objects accordingly.
       */
      List<String> classificationURIs
        = (List<String>)newObject.getClassificationURIs();
      classificationURIs.add(OWL_URI_PRODUCTION);
      repositoryDelegate.update(newObject);
      GenericObject soapServiceEndpoint
        = getSOAPServiceEndpoint((WSDLDocument)newObject);
      if (soapServiceEndpoint != null) {
        List<String> soapServiceEndpointClassificationURIs
          = (List<String>)soapServiceEndpoint.getClassificationURIs();
        soapServiceEndpointClassificationURIs.add(OWL_URI_PRODUCTION);
        repositoryDelegate.update(soapServiceEndpoint);
      } else {
        System.out.println("Unable to find the SOAPServiceEndpoint for the new endpoint");
      }

      /*
       * Iterate over all of the subclasses of the environment classification.  For each
       * environment (except the Production environment), copy the production endpoint
       * WSDL, rewrite the location attribute of the soap:address element, classify the
       * document with the appropriate endpoint classification and create it in WSRR.
       */
      List<OntologyClass> environmentClassifications
        = getAllEnvironmentClassifications();
      for (OntologyClass environmentClassification : environmentClassifications) {
        if (!environmentClassification.getUri().equals(OWL_URI_PRODUCTION)) {
          WSDLDocument originalEndpointWSDL = (WSDLDocument)newObject;
          WSDLDocument newEndpointWSDL = createEndpointWSDLDocumentObject(
            (WSDLDocument)newObject, environmentClassification);
          WSDLDocument bindingWSDL = getWSDLBindingDocument(originalEndpointWSDL);

          /*
           * Create the GO that will link the endpoint WSDL to the binding WSDL.  Add
           * the "related" WSDL documents to the GO as the target of some relationship.
           */
          GenericObject documentContainer = (GenericObject)DataFactory.INSTANCE.create(
            TypeConstants.SR_URI, TypeConstants.TYPE_GENERICOBJECT);
          documentContainer.setName("### Document Container ###");
          ArrayList<BaseObject> targetObjects = new ArrayList<BaseObject>();
          targetObjects.add(bindingWSDL);
          targetObjects.add(newEndpointWSDL);
          helper.addRelationship(documentContainer, DOCUMENTS, targetObjects);
          String bsrURI = repositoryDelegate.create(documentContainer);

          // Delete the GO since it is not required once the documents have been related
          repositoryDelegate.delete(bsrURI);

          /*
           * Find the new SOAP Service Endpoint that was created as a result of loading
           * the new endpoint WSDL and classify with the relevant environment
           * classification.
           */
          soapServiceEndpoint = getSOAPServiceEndpoint(newEndpointWSDL);
          if (soapServiceEndpoint != null) {
            List<String> soapServiceEndpointClassificationURIs
              = (List<String>)soapServiceEndpoint.getClassificationURIs();
            soapServiceEndpointClassificationURIs.add(
              environmentClassification.getUri());
            repositoryDelegate.update(soapServiceEndpoint);
          } else {
            System.out.println("Unable to find the SOAPServiceEndpoint for the new
              endpoint!!!");
          }
        }
      } // FOR
    } catch (ServiceRegistryException e) {
      e.printStackTrace();
    }
  }        
  return status;
}

isEndpointWSDL method

The code in Listing 4 below defines the isEndpointWSDL method, which is a helper method that inspects the content of the WSDL document to determine if it is an endpoint WSDL. It uses XPath expressions to attempt find certain elements within the WSDL document. It defines an endpoint WSDL document as a WSDL document that contains a service element but does not contain either a portType element or a binding element.

Listing 4. isEndpointWSDL method
private boolean isEndpointWSDL(WSDLDocument wsdlDocument) {

  // Create the variable to return
  boolean isEndpointWSDL = false;
        
  try {
    ByteArrayInputStream bais = new ByteArrayInputStream(wsdlDocument.getContent());
    Document doc = docBuilder.parse(bais);
    if (  portTypeExpression.evaluate(doc, XPathConstants.NODE) == null
       && bindingExpression.evaluate(doc, XPathConstants.NODE) == null
       && serviceExpression.evaluate(doc, XPathConstants.NODE) != null
       ) {
      isEndpointWSDL = true;
    }
  } catch (IOException e) {
    e.printStackTrace();
  } catch (SAXException e) {
    e.printStackTrace();
  } catch (XPathExpressionException e) {
    e.printStackTrace();
  }
  return isEndpointWSDL;
}

hasEnvironmentClassification method

The code in Listing 5 below defines the hasEnvironmentClassification method, which is a helper method that determines whether the object in question has been classified with an Environment classification. It iterates over the classifications on the object and checks each one to see if it is a subclass of the Environment classification.

Listing 5. hasEnvironmentClassification method
private boolean hasEnvironmentClassification(WSDLDocument wsdlDocument) {

  // Create the variable to return
  boolean hasEnvironmentClassification = false;

  try {
    List<String> classifications
      = (List<String>)wsdlDocument.getClassificationURIs();
    for (String classification : classifications) {
      if (ontologyDelegate.isSubClassOf(classification, OWL_URI_ENVIRONMENT)) {
        hasEnvironmentClassification = true;
        break;
      }
    } // FOR
  } catch (ServiceRegistryException e) {
    e.printStackTrace();
  }        
  return hasEnvironmentClassification;
}

createEndpointWSDLDocumentObject method

The code in Listing 6 below defines the createEndpointWSDLDocumentObject method, which creates the WSDLDocument object that represents the environment-specific copy of the endpoint WSDL document. It uses the getWSDLDocumentName helper method to add an environment-specific suffix to the name of the new WSDL document. The content for the new WSDL document is generated by the createEndpointWSDLDocumentContent method.

Listing 6. createEndpointWSDLDocumentObject method
private WSDLDocument createEndpointWSDLDocumentObject(WSDLDocument originalEndpointWSDL
  , OntologyClass environmentClassification) {

  // Create the variable to return
  WSDLDocument endpointWsdl = null;
        
  try {
    /*
     * Create the WSDLDocument object for the new endpoint.  This includes creating the
     * content for the document with the rewritten endpoint URL.
     */
    byte[] content = createEndpointWSDLDocumentContent(originalEndpointWSDL,
      environmentClassification);
    endpointWsdl = (WSDLDocument)SRXMLHelper.INSTANCE.load(content,
      TypeConstants.TYPE_WSDLDOCUMENT);
    String wsdlDocumentName = getWSDLDocumentName(originalEndpointWSDL,
      environmentClassification); 
    endpointWsdl.setName(wsdlDocumentName);
    endpointWsdl.setLocation(wsdlDocumentName);
    endpointWsdl.setVersion(originalEndpointWSDL.getVersion());
            
    /*
     * Don't forget to classify the document with the appropriate environment
     * classification.
     */
    List<String> classificationURIs
      = (List<String>)endpointWsdl.getClassificationURIs();
    classificationURIs.add(environmentClassification.getUri());
  } catch (ServiceRegistryIllegalArgumentException e) {
    e.printStackTrace();
  }       
  return endpointWsdl;
}

createEndpointWSDLDocumentContent method

The code in Listing 7 below defines the createEndpointWSDLDocumentContent method, which copies the content of the production endpoint WSDL document and rewrites the location attribute of the wsdlsoap:address element. It does this by parsing the content of the original WSDL document and creating an in memory representation of it in the form of a javax.wsdl.Definition object. Components of this object are then manipulated to modify the relevant elements based on the template definition. The modified Definition object is then serialized to a byte array.

Listing 7. The createEndpointWSDLDocumentContent Method
private byte[] createEndpointWSDLDocumentContent(WSDLDocument originalEndpointWSDL,
  OntologyClass environmentClassification) {

  // Create the variable to return
  byte[] wsdlContent = null;

  try {
    // Retrieve the WSDL definition for the document
    Definition definition = getWSDLDefinition(originalEndpointWSDL);
            
    /*
     * Retrieve the SOAP address defined within the endpoint WSDL and rewrite the URL
     * contained in the location attribute.
     */
    SOAPAddress soapAddress = getSOAPAddress(definition, environmentClassification);
    if (soapAddress != null) {
      // Retrieve the template URI  for the specified target environment.
      URI currentSOAPAddressURI = new URI(soapAddress.getLocationURI());
      URI templateURI = getEndpointURITemplate(environmentClassification);
      if (templateURI != null) {
        URI newSOAPAddressURI = new URI( templateURI.getScheme()
                                       , currentSOAPAddressURI.getUserInfo()
                                       , templateURI.getHost()
                                       , templateURI.getPort()
                                       , currentSOAPAddressURI.getPath()
                                       , currentSOAPAddressURI.getQuery()
                                       , currentSOAPAddressURI.getFragment()
                                       );
        soapAddress.setLocationURI(newSOAPAddressURI.toASCIIString());
        System.out.println("New Endpoint: " + newSOAPAddressURI.toASCIIString());
      }
    } else {
      System.out.println("Endpoint WSDL did not contain a SOAP address element!!!");
    }
   
    // Write the generated WSDL to the return byte array 
    WSDLWriter wsdlWriter = wsdlFactory.newWSDLWriter();
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    wsdlWriter.writeWSDL(definition, baos);
    baos.flush();
    wsdlContent = baos.toByteArray();
  } catch (WSDLException e) {
    e.printStackTrace();
  } catch (URISyntaxException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  return wsdlContent;
}

Compiling the modification plug-in in WSRR Studio

In order to test the modification plug-in, you need to compile the source code, package the generated class file into a JAR file, and load the JAR file into WSRR. Fortunately, WSRR Studio performs most of the heavy lifting for you. The sections below show you how to perform these tasks.

Creating the WSRR Configuration Project

This article assumes that you are starting with an empty workspace in WSRR Studio. If you already have the WSRR Configuration Project created within your WSRR Studio workspace, you can ignore the steps in this section. If not, follow the steps show below to create a WSRR Configuration Project:

  1. Open WSRR Studio.
  2. Change to the WSRR Configuration perspective.
  3. Select File => New => WSRR Configuration Project:
    Creating the WSRR Configuration Project from the File menu
    Creating the WSRR Configuration Project from the File menu
  4. The Create a WSRR Configuration Project dialog is displayed. Enter GEP_V80 in the Configuration Project name entry field, and select Governance Enablement Profile -- WSRR V8.0 from the list of available Configuration Profile Templates. The dialog should look like this:
    Creating the WSRR Configuration Project
    Creating the WSRR Configuration Project
  5. Click Finish. The new WSRR Configuration Project is created and is visible in the WSRR Configuration Project Explorer view.

Importing the modification plug-in source code

Next, import the source code for the modification plug-in into the WSRR Configuration Project that you just created:

  1. In the WSRR Configuration Project Explorer view, expand GEP_V80 and right-click on the src folder.
  2. Select New => Package, as shown below:
    Creating a new Java package
    Creating a new Java package
  3. The New Java Package dialog is displayed. In the Name entry field, enter com.ibm.serviceregistry.sample:
    New Java Package dialog
    New Java Package dialog
  4. Click Finish.
  5. The com.ibm.serviceregistry.sample java package is created and is displayed under the src folder in the WSRR Configuration Project Explorer view.
  6. Right-click on the com.ibm.serviceregistry.sample package and select Import:
    Importing into a Java package
    Importing into a Java package
  7. The Import dialog is displayed. For the Import Source, select File System:
    Specifying the Import Source
    Specifying the Import Source
  8. Click Next, and then click Browse.
  9. The Import from Directory dialog is displayed. Navigate to and select the directory that contains the Java source file for the modification plug-in.
  10. Click OK.
  11. Back in the Import dialog, select the EndpointWSDLCopyModifier.java file, as shown below:
    Specifying the file to import
    Specifying the file to import
  12. Click Finish. The EndpointWSDLCopyModifier.java file is imported into the com.ibm.serviceregistry.sample Java package.

Modifying the project build path

WSRR Studio versions

If your version of WSRR Studio is earlier than V7.5.0.2, you may also need to add the sdo-int.jar file from the root of the <WSRR_INSTALL> directory to the build path of the WSRR Configuration Profile project.

The EndpointWSDLCopyModifier.java file that has been imported into the WSRR Configuration Profile project will have a number of compile errors, because it uses classes from the WSDL4J library that ships as part of WebSphere Application Server. Therefore you need to add this library to the build path of the project, by using the following steps:

  1. Right-click on the src folder and select Build Path => Configure Build Path:
    Configuring Java build path
    Configuring Java build path
  2. The Properties for GEP_V80 dialog is displayed with Java Build Path selected in the left panel. In the right panel, select the Libraries tab.
  3. Click Add External JARs:
    Adding External JARs to Java build path
    Adding External JARs to Java build path
  4. The JAR Selection dialog is displayed. Navigate to the <WAS_INSTALL>/plugins directory and select com.ibm.ws.prereg.wsdl4j.jar:
    Select the JAR file
    Select the JAR file
  5. Click OK. com.ibm.ws.prereg.wsdl4j.jar is added to the list of libraries included on the build path for the GEP_V80 project, as shown below:
    Updated build path
    The Updated build path
  6. Click OK.

Updating the modification properties

The EndpointWSDLCopyModifier class now compiles successfully. You now need to modify the configuration of WSRR to ensure that it is invoked when objects are created at runtime. In order to do this, you need to update the ModificationProperties.properties file:

  1. In the WSRR Configuration Project Explorer view, expand GEP_V80 => Configuration Profile Files => Modifiers.
  2. Open the Modification properties plug-in (ModificationProperties) file:
    Updating the Modification Properties
    Updating the Modification Properties
  3. The ModificationProperties.properties file is opened in the Properties File Editor. Add com.ibm.serviceregistry.sample.EndpointWSDLCopyModifier to the end of the comma-separated list of modifiers.
  4. Save and close the ModificationProperties.properties file.

Generating the WSRR artifacts

The real power in WSRR Studio is its ability to translate the artifacts defined in the workspace into the various configuration artifacts that can be loaded into WSRR. For example, for each business model defined in a WSRR Configuration Profile project, a corresponding OWL file is generated that contains an OWL representation of each of the types defined within the business model. It is this OWL file that is published into WSRR.

In the context of our modification plug-in, WSRR Studio packages the resources within the src folder into a JAR file and places the JAR file within the Configuration Profile Files => Plug-in JARs folder. By default, this file includes a number of properties files that contain the translations of error messages used by the governance policies defined in the WSRR Configuration Profile project.

The name of the generated JAR file matches the name of the WSRR Configuration Profile project. In this example, the name of the generated JAR file is GEP_V80.jar. In order to generate the WSRR artifacts, perform the following steps:

  1. Right-click on the GEP_V80 project and select Generate All WSRR Artifacts:
    Generating the WSRR Artifacts
    Generating the WSRR Artifacts
  2. The progress of the generation process is displayed in the Progress Information dialog:
    Generation Progress Dialog
    Generation Progress Dialog

    Generation errors

    If an error occurs while generating the WSRR artifacts, see the Problems view for details of the errors.

  3. After the artifact generation process is completed, the outcome is displayed in the WSRR Configuration Artifacts Generation dialog:
    WSRR Configuration Artifacts Generation dialog
    WSRR Configuration Artifacts Generation dialog
  4. Click OK.

Synchronizing the changes with WSRR

Configuring WSRR Locations

This article assumes that you already have at least one WSRR Location configured within your WSRR Studio workspace. Therefore, configuring a WSRR Location is beyond the scope of this article. For more information on that topic, see Configuring WSRR Studio in the WSRR information center.

Another powerful feature of WSRR Studio, introduced in WSRR V7.5, is the ability to synchronize a WSRR Configuration Profile project in your workspace with an actual instance of WSRR running in your environment. This capability extends the Team Synchronizing perspective in Eclipse to enable WSRR Studio to retrieve configuration files from the active profile within the specified WSRR instance and compare them to the files in the workspace. Files that have been modified in the workspace are then displayed in the Synchronize view, and individual files can be pushed into WSRR, which is called partial publish since it does not require you to publish an entire WSRR Configuration Profile ZIP file to WSRR. In order to synchronize the relevant artifacts with WSRR, perform the following steps:

  1. Right-click on the GEP_V80 project and select Synchronize Profile with WSRR:
    Synchronizing Profile with WSRR
    Synchronizing Profile with WSRR
  2. The Synchronize dialog is displayed. A single WSRR Studio workspace can contain multiple WSRR Configuration Profile projects and multiple WSRR Location definitions. This dialog lets you specify the WSRR configuration project and WSRR instance to compare, that is, the source and target configuration profiles to compare. Ensure that GEP_V80 is selected in the Configuration Project drop down and that you have specified the correct target WSRR instance in the Target WSRR Server drop down, as shown below:
    Specifying synchronization source and target
    Specifying synchronization source and target
  3. Click Finish. Synchronization progress is displayed in the Progress Information dialog:
    Synchronization Progress dialog
    Synchronization Progress dialog
  4. After WSRR Studio has finished comparing the profiles, the Synchronize view will be updated to list all of the configuration files in the workspace that are different from those loaded in the WSRR instance. We are interested in pushing the GEP_V80.jar and ModificationProperties.properties files to the target WSRR instance. In the Synchronize view, expand GEP_V80 and then expand both the Configuration Profile Files/PLUGIN_JAR and Configuration Profile Files/PLUGIN_PROPERTIES folders.
  5. Using the Ctrl key, select both GEP_V80.jar and ModificationProperties.properties.
  6. Right-click on one of the selected files and select Commit:
    Committing the files to WSRR
    Commiting the files to WSRR
  7. The Commit dialog is displayed, listing the configuration files that have been selected for publishing to the specified WSRR instance, and the action that will be performed on the target server. The dialog should look like this:
    Performing the commit
    Performing the commit
  8. Click Finish.

Creating the endpoint template objects

Before you can test the plug-in, you need to create the objects that will contain the URL templates for each of the relevant environments. Recall that you decided to use ManualSOAPEndpoint objects to store the templates for the URLs, classifying each object with the relevant Environment classification. In order to create the ManualSOAPEndpoint template objects, perform the following steps:

  1. In a web browser, log on to the Business Space user interface and change to the Service Registry for Development space. You may need to create a new space based on the Service Registry for Development template if one does not already exist.
  2. On the Overview tab, find the Service Registry Actions widget and click Create a manual SOAP endpoint:
    Creating manual SOAP endpoint
    Creating manual SOAP endpoint

    The Service Name, Service Namespace, and Port Name properties are not used by the EndpointWSDLCopyModifier class. You must specify values for them, however, because the definition of ManualSOAPEndpoint defines them to be required properties.

  3. The Create a Manual SOAP Endpoint dialog is displayed. Enter the information shown below:
    Table 1. Manual SOAP endpoint template details for development environment
    Property NameProperty Value
    NameDevelopment SOAP endpoint template
    DescriptionURL template object for development environment.
    SOAP Locationhttp://com.ibm.wsrr.samples.development
    Service Namedevelopment
    Service Namespacedevelopment
    Port Namedevelopment
  4. Click Add for the Environment property:
    Adding an Environment classification
    Adding an Environment classification
  5. The Environment dialog opens. Select Development from the list of environments:
    Specifying the Development environment
    Specifying the Development environment
  6. Click Close. The Create a Manual SOAP endpoint dialog should look like this:
    Specifying manual SOAP endpoint details
    Specifying manual SOAP endpoint details
  7. Click Finish. The manual SOAP endpoint will be created, and you will be taken to the Browse tab in order to view the details of the new object. Click the Overview tab.
  8. Repeat the process described above to create the template objects for the Staging and Test environments, using the information in Tables 2 and 3 respectively to classify each of the objects with the relevant Environment classification:
Table 2. Manual SOAP endpoint template details for the staging environment
Property NameProperty Value
NameStaging SOAP endpoint template
DescriptionURL template object for staging environment.
SOAP Locationhttp://com.ibm.wsrr.samples.staging
Service Namespacestaging
Service Namestaging
Port Namestaging
Table 3. Manual SOAP endpoint template details for the test environment
Property NameProperty Value
NameTest SOAP endpoint template
DescriptionThe URL template object for test environment
SOAP Locationhttp://com.ibm.wsrr.samples.test
Service Namespacetest
Service Nametest
Port Nametest

Testing the plug-in

You are now ready to test the EndpointWSDLCopyModifier plug-in. The ZIP file provided with this article contains a set of sample WSDL documents for a Stock Quote service. You can load these WSDL documents into WSRR to demonstrate the behaviour of the plug-in:

  1. In a web browser, log on to the Business Space user interface and change to the Service Registry for Development space. You may need to create a new space based on the Service Registry for Development template if one does not already exist.
  2. On the Overview tab, find the Service Registry Actions widget and click Load Documents:
    Loading WSDL Documents into WSRR
    Loading WSDL Documents into WSRR
  3. The Load Documents dialog opens. Click Browse:
    Browsing for the WSDL document
    Browsing for the WSDL document
  4. The File Upload dialog specific to your browser is displayed. Navigate to the directory that contains the WSDL documents provided with this article.
  5. Select StockQuoteService.wsdl and click Open.
  6. Back in the Load Documents dialog, click Next.
  7. StockQuoteService.wsdl will be pushed to WSRR and analyzed. WSRR will notice that the WSDL document imports StockQuoteBinding.wsdl and will prompt you to load this document as well. Click Add next to StockQuoteBinding.wsdl:
    Loading the dependent WSDL documents into WSRR
    Loading the dependent WSDL documents into WSRR
  8. On the next panel, click Browse and navigate to the directory that contains the WSDL documents provided with this article.
  9. Select StockQuoteBinding.wsdl, click Open, and then click OK.
  10. Repeat this process to load StockQuoteInterface.wsdl.
  11. There should now be no other document dependencies to resolve. Click Finish:
    Completing the Load Process
    Completing the Load Process
  12. At this point, the three WSDL documents that provide the complete definition of the Stock Quote service are loaded into WSRR. During the processing of these documents, WSRR will invoke the EndpointWSDLCopyModifier plug-in. After processing is complete a confirmation panel is displayed:
    Load documents confirmation panel
    Load cocuments confirmation panel
  13. Click Close.
  14. In order to verify that the additional endpoint WSDL documents have been created, you need to perform a search for WSDL documents in the Business Space user interface so that they are displayed in the Service Registry Collection widget. In the Service Registry Search widget in the top right of Overview tab, select WSDL Document in the type dropdown:
    Searching for WSDL documents
    Searching for WSDL documents
  15. Click Search. The Service Registry Collection widget will be populated with the search results. In this case, this is the collection of WSDL Documents that have been loaded into WSRR. The additional endpoint WSDL documents that were created by the EndpointWSDLCopyModifier plug-in should be visible, as shown below:
    Verifying the Endpoint WSDL Documents
    Verifying the Endpoint WSDL Documents
  16. Click StockQuoteService_Development.wsdl.
  17. You will be taken to the Browse tab in order to view the details of the StockQuoteService_Development.wsdl document. In the Service Registry Detail widget, click StockQuoteService_Development.wsdl:
    Viewing WSDL document content
    Viewing WSDL document content
  18. When prompted, open the file with a file editor or viewer. Notice that the values of the name attribute on the port element and the location attribute on the soap:address element are specific to the development environment.

Conclusion

This article provided an example implementation of and demonstrated the behaviour of the WSRR modification plug-in, which copies endpoint WSDL documents that are loaded into WSRR.

Acknowledgements

The author would like to thank IBM WSRR Product Developers Tim Baldwin and David Seager for reviewing this article.


Download

DescriptionNameSize
Code sampleDownload.zip8 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=929712
ArticleTitle=Automating endpoint WSDL creation with WebSphere Service Registry and Repository
publish-date=05152013