Uploaded image for project: 'JBoss Web Services'
  1. JBoss Web Services
  2. JBWS-3628

Add system property expansion capability to .wsdl files

      The customer is doing JMS-based webservices using the following declarations in their wsdl file (referred to using the wsdllocation annotation):
      <soapjms:jndiConnectionFactoryName>myqueue</soapjms:jndiConnectionFactoryName> <soapjms:jndiInitialContextFactory>com.vendor.InitialContextFactory</soapjms:jndiInitialContextFactory>
      <soapjms:jndiURL>$MY_URL</soapjms:jndiURL>

      The $MY_URL is the url of the messaging server. Since property replacement does not work for wsdl files they need to manually modify this before deploying and package separate versions of their app for each environment. If property expansion worked in the .wsdl this would no longer be necessary.

            [JBWS-3628] Add system property expansion capability to .wsdl files

            Alessio Soldano added a comment - - edited

            Solved; documentation at https://docs.jboss.org/author/display/JBWS/Advanced+User+Guide#AdvancedUserGuide-WSDLsystempropertiesexpansion

            In particular, references to system properties delimited with "@" (both starting and ending delimiter) will be expanded.

            Alessio Soldano added a comment - - edited Solved; documentation at https://docs.jboss.org/author/display/JBWS/Advanced+User+Guide#AdvancedUserGuide-WSDLsystempropertiesexpansion In particular, references to system properties delimited with "@" (both starting and ending delimiter) will be expanded.

            Attaching JBWS-3628.diff patch file for adding a XMLStreamReaderWrapper into the CXF WSDLManagerImpl. The wrapper makes CXF use a custom StreamReaderDelegate to read wsdl docs before passing them to wsdl4j.
            The delegate expands system properties in attribute and element values.
            CXF 3.0.1 is required (CXF-5866).

            We need a testcase for verification; if there's need to set a system property on server side for testing purpose, the RemoteDeployer should be extended to allow doing that. See https://docs.jboss.org/author/display/AS71/CLI+Recipes#CLIRecipes-Adding%2CreadingandremovingsystempropertyusingCLI for recipies on setting system props in the model.

            Alessio Soldano added a comment - Attaching JBWS-3628 .diff patch file for adding a XMLStreamReaderWrapper into the CXF WSDLManagerImpl. The wrapper makes CXF use a custom StreamReaderDelegate to read wsdl docs before passing them to wsdl4j. The delegate expands system properties in attribute and element values. CXF 3.0.1 is required (CXF-5866). We need a testcase for verification; if there's need to set a system property on server side for testing purpose, the RemoteDeployer should be extended to allow doing that. See https://docs.jboss.org/author/display/AS71/CLI+Recipes#CLIRecipes-Adding%2CreadingandremovingsystempropertyusingCLI for recipies on setting system props in the model.

            r searls added a comment -

            The current proposed enhancement from cxf 2.7.11 will not work for CXF 3.0.0.
            This version of CXF is blocking access to the value of any wsdl "extension"
            stmt such as JndiConnectionFactoryNameType, JndiInitialContextFactoryType,
            and JndiURLType. It is doing this by generating on the fly proprietary classes
            using org.apache.cxf.common.util.ASMHelper, These classes JndiConnectionFactoryNameTypeExtensibility,
            JndiInitialContextFactoryTypeExtensibility, and JndiURLTypeExtensibility, provide
            access to all "extension" information except the value.

            WSDLManagerImpl loads from the bus an extension class ConfiguredBeanLocator.
            JAXBExtensionHelper is eventually called. This class has method, createExtensionClass
            which uses org.apache.cxf.common.util.ASMHelper to generate an alternate "extension"
            class on the fly. There is a serializer and deserializer associated with each
            class preforms the read and write the xml element.

            r searls added a comment - The current proposed enhancement from cxf 2.7.11 will not work for CXF 3.0.0. This version of CXF is blocking access to the value of any wsdl "extension" stmt such as JndiConnectionFactoryNameType, JndiInitialContextFactoryType, and JndiURLType. It is doing this by generating on the fly proprietary classes using org.apache.cxf.common.util.ASMHelper, These classes JndiConnectionFactoryNameTypeExtensibility, JndiInitialContextFactoryTypeExtensibility, and JndiURLTypeExtensibility, provide access to all "extension" information except the value. WSDLManagerImpl loads from the bus an extension class ConfiguredBeanLocator. JAXBExtensionHelper is eventually called. This class has method, createExtensionClass which uses org.apache.cxf.common.util.ASMHelper to generate an alternate "extension" class on the fly. There is a serializer and deserializer associated with each class preforms the read and write the xml element.

            There's no need for any change in CXF in order for using a custom WSDLFactory. We can simply set the javax.wsdl.factory.WSDLFactory system property or add a proper META-INF/services/javax.wsdl.factory.WSDLFactory entry in our jbossws-cxf-client jar.

            Alessio Soldano added a comment - There's no need for any change in CXF in order for using a custom WSDLFactory. We can simply set the javax.wsdl.factory.WSDLFactory system property or add a proper META-INF/services/javax.wsdl.factory.WSDLFactory entry in our jbossws-cxf-client jar.

            r searls added a comment -

            Proposed patch to CXF for JBossWS to provide a custom WSDLFactory.

            r searls added a comment - Proposed patch to CXF for JBossWS to provide a custom WSDLFactory.

            r searls added a comment -

            I recant. I can override protected methods of com.ibm.wsdl.xml.WSDLReaderImpl, that makes things much easier and cleaner to customize.

            r searls added a comment - I recant. I can override protected methods of com.ibm.wsdl.xml.WSDLReaderImpl, that makes things much easier and cleaner to customize.

            r searls added a comment -

            CXF would need only a minor modification in order for us to register an custom
            WSDLFactoryImpl through the bus for CXF's WSDLManager to reference. Our WSDLFactoryImpl class would override the newWSDLReader method and return a custom WSDLReaderImpl class.

            It would be most desirable to subclass com.ibm.wsdl.xml.WSDLReaderImpl and override
            select parsing methods, however is this not possible because the methods of interest
            are "protected". I see two solutions. One, copy the contents of com.ibm.wsdl.xml.WSDLReaderImpl into our WSDLReaderImpl class and make the desired modifications to the protected methods. Two, in our WSDLFactoryImpl override method newDefinition() and return a custom impl of DefinitionImpl and in DefinitionImpl override methods like createService() and return a custom ServiceImpl class (and the like) that does our bidding. This would allow com.ibm.wsdl.xml.WSDLReaderImpl to be used as is.

            Because of the nature of what we are currently customizing I am more in favor of
            option one. This provides us full access to the wsdl contents with the smallest
            number of classes that needs to be maintained. Option two seems like over kill
            and requires a larger number of custom classes to be maintained by us.

            What are your thoughts?

            r searls added a comment - CXF would need only a minor modification in order for us to register an custom WSDLFactoryImpl through the bus for CXF's WSDLManager to reference. Our WSDLFactoryImpl class would override the newWSDLReader method and return a custom WSDLReaderImpl class. It would be most desirable to subclass com.ibm.wsdl.xml.WSDLReaderImpl and override select parsing methods, however is this not possible because the methods of interest are "protected". I see two solutions. One, copy the contents of com.ibm.wsdl.xml.WSDLReaderImpl into our WSDLReaderImpl class and make the desired modifications to the protected methods. Two, in our WSDLFactoryImpl override method newDefinition() and return a custom impl of DefinitionImpl and in DefinitionImpl override methods like createService() and return a custom ServiceImpl class (and the like) that does our bidding. This would allow com.ibm.wsdl.xml.WSDLReaderImpl to be used as is. Because of the nature of what we are currently customizing I am more in favor of option one. This provides us full access to the wsdl contents with the smallest number of classes that needs to be maintained. Option two seems like over kill and requires a larger number of custom classes to be maintained by us. What are your thoughts?

            OK, I see. So we can't "fool" CXF into using a different location for the wsdl. Btw, I also remember that CXF has a wsdl cache mechanism that is based on the wsdl url, so playing with the wsdl location url might definitely be risky.
            The idea of running a filter at the time CXF parses the wsdl into the WSDL4J Definition model is interesting; something I've noticed in the patch is that is applies to the JMS wsdl extensibility element only. So we'd need to clarify in advance which elements actually support property expansion and which do not.
            Something I've been thinking about is why don't we run the filter directly at WSDL4J level? javax.wsdl.factory.WSDLFactory (which is used by CXF to parse the wsdl) lets us have a different implementation of WSDLFactory (see newInstance() and findFactoryImplName() methods). We could have a custom WSDLFactory impl that runs our filters and eventually delegates to the default WSDLFactory impl (or to what would have been resolved before, but that's an impl detail).
            The filtering could either be performed through pluggable filters as you did in your last patch for cxf, or (better, I think) be StAX / DOM based on the whole document (depends on which readWSDL method flavor is called).
            The advantage are no need to have changes in CXF and a solution that applies to any component relying on the WSDLReader.

            Alessio Soldano added a comment - OK, I see. So we can't "fool" CXF into using a different location for the wsdl. Btw, I also remember that CXF has a wsdl cache mechanism that is based on the wsdl url, so playing with the wsdl location url might definitely be risky. The idea of running a filter at the time CXF parses the wsdl into the WSDL4J Definition model is interesting; something I've noticed in the patch is that is applies to the JMS wsdl extensibility element only. So we'd need to clarify in advance which elements actually support property expansion and which do not. Something I've been thinking about is why don't we run the filter directly at WSDL4J level? javax.wsdl.factory.WSDLFactory (which is used by CXF to parse the wsdl) lets us have a different implementation of WSDLFactory (see newInstance() and findFactoryImplName() methods). We could have a custom WSDLFactory impl that runs our filters and eventually delegates to the default WSDLFactory impl (or to what would have been resolved before, but that's an impl detail). The filtering could either be performed through pluggable filters as you did in your last patch for cxf, or (better, I think) be StAX / DOM based on the whole document (depends on which readWSDL method flavor is called). The advantage are no need to have changes in CXF and a solution that applies to any component relying on the WSDLReader.

            r searls added a comment -

            This will not work because CXF is inspecting the @WebService annotation to retrieve
            path to the wsdl and then extracting the wsdl from the archive. There is no way
            for us to pass information about an altered wsdl file written to disk or an InputStream
            to said file.

            I recommend we request cxf to allow us to pass a filter class through the bus extensions
            map, that cxf runs immediately after they read the wsdl into the Definition class.
            Our filter class will post process the contents of the Definition class before
            subsequent cxf processing.

            r searls added a comment - This will not work because CXF is inspecting the @WebService annotation to retrieve path to the wsdl and then extracting the wsdl from the archive. There is no way for us to pass information about an altered wsdl file written to disk or an InputStream to said file. I recommend we request cxf to allow us to pass a filter class through the bus extensions map, that cxf runs immediately after they read the wsdl into the Definition class. Our filter class will post process the contents of the Definition class before subsequent cxf processing.

            I agree on the complexity of plugging into CXF processing of wsdl. That's why I suggested considering feeding CXF with an already modified wsdl document. Post-processing the wsdl after org.jboss.wsf.stack.cxf.deployment.EndpointImpl call to super.doPublish(addr) is not going to work, as CXF would run using the wsdl with references to the properties (not expanded).
            Perhaps we can pre-process the wsdl (as an XML doc, not as a javax.wsdl.Definition) to expand the properties, write it somewhere, set it into the EndpointImpl (see e.g. NonSpringBusHolder#configure(...) method) and let CXF rely on it. I'm saying "perhaps", as I fear there might be some places where CXF is looking for @WebService annotations and directly getting the wsdlLocation, but we should verify and perhaps that's not correct when the EndpointImpl wsdlLocation is set).

            Alessio Soldano added a comment - I agree on the complexity of plugging into CXF processing of wsdl. That's why I suggested considering feeding CXF with an already modified wsdl document. Post-processing the wsdl after org.jboss.wsf.stack.cxf.deployment.EndpointImpl call to super.doPublish(addr) is not going to work, as CXF would run using the wsdl with references to the properties (not expanded). Perhaps we can pre-process the wsdl (as an XML doc, not as a javax.wsdl.Definition) to expand the properties, write it somewhere, set it into the EndpointImpl (see e.g. NonSpringBusHolder#configure(...) method) and let CXF rely on it. I'm saying "perhaps", as I fear there might be some places where CXF is looking for @WebService annotations and directly getting the wsdlLocation, but we should verify and perhaps that's not correct when the EndpointImpl wsdlLocation is set).

              rhn-support-asoldano Alessio Soldano
              rhn-support-dboeren David Boeren
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

                Created:
                Updated:
                Resolved: