Thursday, 11 April 2013

Using an embedded Mule to lookup a JMS connection factory with Spring on JBoss 4.2.3


My problem

I recently had the pleasure of working with Mule.  In my use case I wanted to lookup a JMS queue, post to that queue and receive a reply – a synchronous JMS bridge pattern.  This would be packaged into a web application that uses Spring, and deployed on JBoss 4.2.3.  With such a “simple” solution I can’t see why I ran into problems…


My error in the JBoss console was as follows:
Caused by: org.mule.retry.RetryPolicyExhaustedException: Unsupported ConnectionFactory type: $Proxy286
                at org.mule.retry.policies.AbstractPolicyTemplate.execute(AbstractPolicyTemplate.java:105)
                at org.mule.transport.AbstractConnector.connect(AbstractConnector.java:1616)
                at org.mule.transport.jms.JmsConnector.connect(JmsConnector.java:460)
                at org.mule.transport.AbstractConnector.start(AbstractConnector.java:428)
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.lang.reflect.Method.invoke(Method.java:601)
                at org.mule.lifecycle.phases.DefaultLifecyclePhase.applyLifecycle(DefaultLifecyclePhase.java:225)
                at org.mule.lifecycle.RegistryLifecycleManager$RegistryLifecycleCallback.onTransition(RegistryLifecycleManager.java:276)
                at org.mule.lifecycle.RegistryLifecycleManager.invokePhase(RegistryLifecycleManager.java:155)
                at org.mule.lifecycle.RegistryLifecycleManager.fireLifecycle(RegistryLifecycleManager.java:126)
                at org.mule.registry.AbstractRegistryBroker.fireLifecycle(AbstractRegistryBroker.java:80)
                at org.mule.registry.MuleRegistryHelper.fireLifecycle(MuleRegistryHelper.java:120)
                at org.mule.lifecycle.MuleContextLifecycleManager$MuleContextLifecycleCallback.onTransitio



Searching for a solution (ask Google approach)

As usual I ended up trawling through loads of forums, Stackoverflow, MuleSoft, etc and of course the Mule documentation.  The main stumbling block seemed to be my desire to use the local JNDI lookup.  Mule is predominantly used for integration and as such will usually need to run standalone.  However, my use case called for it to run embedded in my web application and I didn’t want to specify the JNDI configuration of each Web container I choose to deploy on.

Mule docs:
Possible issues:





My configuration

My configuration was pretty straight forward.

web.xml

<!-- Mule Bridge Settings -->
<resource-ref id="ResourceRef_jmsConnectionFactory">
       <description>Used to get connections to JMS queue, using JMS as a bridge to Mule</description>
       <res-ref-name>jms/jmsConnectionFactory</res-ref-name>
       <res-type>javax.jms.ConnectionFactory</res-type>
       <res-auth>Container</res-auth>
</resource-ref>

jboss-web.xml

    <resource-ref>
        <res-ref-name>jms/jmsConnectionFactory</res-ref-name>
        <res-type>javax.jms.ConnectionFactory</res-type>
        <jndi-name>java:/ConnectionFactory</jndi-name>
    </resource-ref>

uil2-service.xml

   <mbean code="org.jboss.naming.LinkRefPairService"
          name="jboss.jms:alias=QueueConnectionFactory">
      <attribute name="JndiName">QueueConnectionFactory</attribute>
      <attribute name="RemoteJndiName">ConnectionFactory</attribute>
      <attribute name="LocalJndiName">java:/JmsXA</attribute>
      <depends>jboss:service=Naming</depends>
   </mbean>




Debugging the problem

Debugging the problem was a little tedious as I mostly received exceptions in the JBoss log and not much else.  In hindsight, perhaps attaching a debugger and stepping through the Mule source might have been quicker. The main points were to confirm that I could connect to the queue, I could lookup the queue in my web application, and I could  



Now for the solution

Unfortunately we need one new class in our web application to help lookup the queue and this class depends on Spring.

package my.package;

import javax.naming.NamingException;

import org.mule.transport.jms.jndi.AbstractJndiNameResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jndi.JndiTemplate;

public class SpringJndiNameResolver extends AbstractJndiNameResolver implements InitializingBean {
    private static Logger logger = LoggerFactory.getLogger(SpringJndiNameResolver.class);
    private JndiTemplate jndiTemplate;

    @Override
    public void afterPropertiesSet() throws Exception {
        if (jndiTemplate == null) {
            jndiTemplate = new JndiTemplate();
        }
    }

    @Override
    public Object lookup(String name) throws NamingException {
        Object object = null;
        if (name != null) {
            logger.debug("Looking up name "+name);
            object = jndiTemplate.lookup(name);
            logger.debug("Object "+object+" found for name "+name);
        }
        return object;
    }

    public JndiTemplate getJndiTemplate() {
        return jndiTemplate;
    }

    public void setJndiTemplate(JndiTemplate jndiTemplate) {
        this.jndiTemplate = jndiTemplate;
    }
}

mule-config.xml (client or local side)

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:jms="http://www.mulesoft.org/schema/mule/jms"
     xmlns:file="http://www.mulesoft.org/schema/mule/file"
     xmlns:vm="http://www.mulesoft.org/schema/mule/vm"
     xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd ">
    <spring:beans>
        <spring:bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate"/>
    </spring:beans>

     <!--
     The following suggestion works:
     http://stackoverflow.com/questions/12461714/mule-embedded-use-the-containers-own-jndiinitialfactory
     -->
     <jms:connector
           name="local-jms-connector"
           connectionFactoryJndiName="java:comp/env/jms/jmsConnectionFactory"
           jndiDestinations="true"
           forceJndiDestinations="true"
           specification="1.1" doc:name="JMS">
    <jms:custom-jndi-name-resolver class="com.temenos.hothouse.mule.SpringJndiNameResolver">
           <spring:property name="jndiTemplate" ref="jndiTemplate"/>
    </jms:custom-jndi-name-resolver>   
     </jms:connector>

<!--
throws exception with "Unsupported ConnectionFactory type: $Proxy338"
     <spring:bean id="jmsConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
           <spring:property name="jndiName" value="java:env/comp/jms/jmsConnectionFactory"/>
           <spring:property name="lookupOnStartup" value="false"/>
           <spring:property name="cache" value="true"/>
           <spring:property name="proxyInterface" value="javax.jms.ConnectionFactory"/>
     </spring:bean>

    <jms:connector name="local-jms-connector" connectionFactory-ref="jmsConnectionFactory"
        jndiDestinations="true" forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS Local">
    </jms:connector>
 -->   
   
    <!-- TODO Can't seem to get the local jms connection factory lookup to work
    (even this remote way barfs with the same exception as above) -->
<!--
    <jms:connector name="remote-jms-connector" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory"
        jndiProviderUrl="jnp://127.0.0.1:1099"
        maxRedelivery="1"
        connectionFactoryJndiName="QueueConnectionFactory" jndiDestinations="true"
        forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS">
    </jms:connector>
 -->

    <flow name="vm://Forecast-view-command" doc:name="vm://Forecast-view-command">
        <vm:inbound-endpoint exchange-pattern="request-response" path="Forecast-view-command" doc:name="VM"/>
        <logger message="VM Request:  #[payload]" level="INFO" doc:name="Logger"/>
        <set-payload value="#[mule:message.payload(java.lang.String)]" doc:name="Set Payload"/>
        <file:outbound-endpoint path="c:\logs\" outputPattern="forecast-request-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
        <jms:outbound-endpoint exchange-pattern="request-response" queue="queue/MuleRequest" doc:name="MuleRequest" connector-ref="local-jms-connector" />
        <file:outbound-endpoint path="c:\logs\" outputPattern="forecast-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
        <jms:jmsmessage-to-object-transformer doc:name="JmsMessage to Object"/>
        <object-to-string-transformer doc:name="Object to String"/>
        <logger message="Response:  #[payload]" level="INFO" doc:name="Logger"/>
    </flow>

</mule>


mule-config.xml (server or remote side)

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:mulexml="http://www.mulesoft.org/schema/mule/xml" xmlns:stdio="http://www.mulesoft.org/schema/mule/stdio"
      xmlns:jms="http://www.mulesoft.org/schema/mule/jms" xmlns:data-mapper="http://www.mulesoft.org/schema/mule/ee/data-mapper" xmlns:file="http://www.mulesoft.org/schema/mule/file" xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:vm="http://www.mulesoft.org/schema/mule/vm" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.3.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="
http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/current/mule-xml.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/stdio http://www.mulesoft.org/schema/mule/stdio/current/mule-stdio.xsd
http://www.mulesoft.org/schema/mule/jms http://www.mulesoft.org/schema/mule/jms/current/mule-jms.xsd
http://www.mulesoft.org/schema/mule/ee/data-mapper http://www.mulesoft.org/schema/mule/ee/data-mapper/current/mule-data-mapper.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd
http://www.mulesoft.org/schema/mule/vm http://www.mulesoft.org/schema/mule/vm/current/mule-vm.xsd ">
    <custom-transformer class="org.mule.transformer.codec.XmlEntityDecoder" name="XmlEntityDecoder" doc:name="Java"/>

    <jms:connector name="remote-jms-connector" jndiInitialFactory="org.jnp.interfaces.NamingContextFactory"
        jndiProviderUrl="jnp://127.0.0.1:1099"
        maxRedelivery="1"
        connectionFactoryJndiName="java:/QueueConnectionFactory" jndiDestinations="true"
        forceJndiDestinations="true" disableTemporaryReplyToDestinations="true" doc:name="JMS">
        <!--retry:forever-policy frequency="2000"/-->
    </jms:connector>
    <data-mapper:config name="wsdlresponsetoentityresponse" transformationGraphPath="wsdlresponsetoentityresponse.grf" doc:name="DataMapper"/>
    <data-mapper:config name="reqtores" transformationGraphPath="reqtores.grf" doc:name="reqtores"/>

    <flow name="ProcessFromQueue" doc:name="ProcessFromQueue">
        <jms:inbound-endpoint queue="queue/MuleRequest" connector-ref="remote-jms-connector" doc:name="Request" />
        <set-payload value="#[mule:message.payload(java.lang.String)]" doc:name="Set Payload"/>
        <logger message="Before WSDL #[payload]" level="INFO" doc:name="Logger"/>
        <set-payload value="#[xpath('//viewcommand/pathparameters/postcode').text]" doc:name="Extract PostCode"/>
        <set-variable variableName="Requested_PostCode" value="#[payload]" doc:name="Set Requested_PostCode"/>
        <logger message="Requested_PostCode:  #[Requested_PostCode]" level="INFO" doc:name="Logger"/>
        <flow-ref name="ForecastWSDL" doc:name="ForecastWSDL"/>
        <logger message="Latest Forecast:  #[xpath('//forecastResult/forecasts/com.cdyne.ws.weatherws.Forecast[1]/desciption').text]" level="INFO" doc:name="Logger"/>
        <data-mapper:transform config-ref="wsdlresponsetoentityresponse" doc:name="DataMapper">
            <data-mapper:input-arguments>
                <data-mapper:input-argument key="Requested_PostCode">#[Requested_PostCode]</data-mapper:input-argument>
            </data-mapper:input-arguments>
        </data-mapper:transform>
        <object-to-string-transformer doc:name="Object to String"/>
        <jms:outbound-endpoint queue="queue/MuleResponse" connector-ref="remote-jms-connector" doc:name="Response"/>
    </flow>
    <sub-flow name="ForecastWSDL" doc:name="ForecastWSDL">
        <outbound-endpoint exchange-pattern="request-response" address="wsdl-cxf:http://wsf.cdyne.com/WeatherWS/weather.asmx?WSDL&amp;method=GetCityForecastByZIP" doc:name="WSDL"/>
        <mulexml:object-to-xml-transformer doc:name="Object to XML"/>
        <stdio:outbound-endpoint system="OUT" doc:name="STDIO"/>
        <file:outbound-endpoint path="c:\logs\" outputPattern="wsdl-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
<!--         <file:outbound-endpoint path="c:\logs" outputPattern="jms-response-#[function:datestamp].xml" responseTimeout="10000" doc:name="File"/>
 -->
     </sub-flow>
    <flow name="mule-configFlow1" doc:name="mule-configFlow1">
        <data-mapper:transform config-ref="reqtores" doc:name="DataMapper">
            <data-mapper:input-arguments>
                <data-mapper:input-argument key="Requested_PostCode">#[Requested_PostCode]</data-mapper:input-argument>
            </data-mapper:input-arguments>
        </data-mapper:transform>
    </flow>
</mule>

Wednesday, 13 February 2013

Create an OData service from EDMX



This entry in my blog is part of my 30 blogs in 30 days  Fortunately I didn't commit to 30 consecutive days...  The one I'm trying to 'tick off' today is the "Create Mock Responder" item.  Here my idea was to show how we could create an OData service, from an EDMX file, with an in-memory database serving up mock data.  This might be a really useful idea if your User-Agent development team cannot see the live / real data due to security restrictions.

So here we go:

Starting with an existing EDMX, demonstrate how to create a mock responder


The main point of this blog is to show how to create an OData service if you already have an EDMX file.  An EDMX file can be created using Visual Studio or, as you may already know, any existing OData service has a $metadata resource that serves up metadata in EDMX format.  The service that I'm going to 'clone' today is currently available online here with many others:

  1. Follow the instructions for creating an IRIS quickstart project, do step 1 and stop there.
    mvn archetype:generate -DgroupId=
    com.mycorp.test -DartifactId=DemoService -DarchetypeRepository=https://repository-aphethean.forge.cloudbees.com/snapshot/ -DarchetypeGroupId=com.temenos.interaction -DarchetypeArtifactId=interaction-sdk-archetype -DarchetypeVersion=0.2.0-SNAPSHOT -DinteractiveMode=false


    Our first little gotcha with this service is that we don't use the name you provided for the RIM generation - I get the following error if I specify anything other than DemoService (we'll fix this later https://github.com/aphethean/IRIS/issues/41):
    [ERROR] Failed to execute goal com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT:rim-generate (default) on project ODataTestService: Execution default of goal com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT:rim-generate failed: java.io.FileNotFoundException: C:\temp\ODataTestService\src\main\resources\ODataTestService.rim (The system cannot find the file specified) -> [Help 1]

  2. Save the service metadata as service.edmx into your new project directory.  This file name is configurable, just look in the pom.xml

    http://services.odata.org/OData/OData.svc/$metadata
  3. Now execute Step 2 of the quickstart

    mvn interaction-sdk:gen


    Our second little challenge with this service is that we don't seem to support complex types - I get the following error:
    [ERROR] Failed to execute goal com.temenos.interaction:interaction-sdk-plugin:0.2.0-SNAPSHOT:gen (default-cli) on project ODataTestService: Execution default-cli of goal com.temenos.interaction:interaction-sdk-plugin:0.2.0-SNAPSHOT:gen failed: Entity property type ODataDemo.Address not supported -> [Help 1]
    In the interest of this blog I can simply comment it out and carry on (we'll fix it later https://github.com/aphethean/IRIS/issues/42):

    <EntityType Name="Supplier">
    ...<!--        <Property Name="Address" Type="ODataDemo.Address" Nullable="false" /> -->
  4. Supply some test data in src/main/resources/META-INF/responder_insert.sql

    If you try to start your service now you'll probably see an error like the following:
    2013-02-13 16:13:20.637:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [META-INF/resourcemanager-context.xml]: Invocation of init method failed; nested exception is org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ODataDemo.Product.null in ODataDemo.Category.Products:org.hibernate.AnnotationException: mappedBy reference an unknown target entity property: ODataDemo.Product.null in ODataDemo.Category.Products
    At the moment we need a little help to figure out the entity relationships.  You could fix the generated JPA classes, or you could add the EDMX referential constraint, join ID columns, and regenerate.  That's what we'll do here, by merging the following into our EDMX:

    Add the following properties to Product:
            <Property Name="categoryID" Type="Edm.Int32" Nullable="false" />
        <Property Name="supplierID" Type="Edm.Int32" Nullable="false" />

    Update the following associations:
        <Association Name="Product_Category_Category_Products">        <End Role="Product_Category" Type="ODataDemo.Product" Multiplicity="*" />        <End Role="Category_Products" Type="ODataDemo.Category" Multiplicity="0..1" />                <ReferentialConstraint>                    <Principal Role="Category_Products">                        <PropertyRef Name="ID"/>                    </Principal>                    <Dependent Role="Product_Category">                        <PropertyRef Name="categoryID"/>                    </Dependent>                </ReferentialConstraint>      </Association>      <Association Name="Product_Supplier_Supplier_Products">        <End Role="Product_Supplier" Type="ODataDemo.Product" Multiplicity="*" />        <End Role="Supplier_Products" Type="ODataDemo.Supplier" Multiplicity="0..1" />                <ReferentialConstraint>                    <Principal Role="Supplier_Products">                        <PropertyRef Name="ID"/>                    </Principal>                    <Dependent Role="Product_Supplier">                        <PropertyRef Name="supplierID"/>                    </Dependent>                </ReferentialConstraint>      </Association>
  5. Start your service
    mvn jetty:run
  6. Go to http://localhost:8080/responder/

Want to see this in action? View it now on YouTube

Cloudbees private repository

My problem

Recently I was attempting to use a Cloudbees 'clickstart' to help get users of my project up and running.  This has worked in the past and was going great, but then for some unknown reason my maven project dependencies could not be found.

My error was the typical maven dependency error:
cause : Failed to read artifact descriptor for com.temenos.interaction:interaction-sdk-rim-plugin:jar:0.2.0-SNAPSHOT
Stack trace : 
org.apache.maven.plugin.PluginResolutionException: Plugin com.temenos.interaction:interaction-sdk-rim-plugin:0.2.0-SNAPSHOT or one of its dependencies could not be resolved: Failed to read artifact descriptor for com.temenos.interaction:interaction-sdk-rim-plugin:jar:0.2.0-SNAPSHOT


Searching for a solution (ask Google approach)

There didn't seem to be any information related to my problem and I started to doubt that I could actually use a 'repository' declaration in my pom.xml with Cloudbees.  Thankfully that was just a red herring and Cloudbees supports pom.xml repository declarations just fine.  Whether their usage is a good idea or not is a separate question.


Debugging the issue

The best thing you can do in these situations is turn on some debugging and see what's going on.  I changed my Jenkins build target to '-X clean install'.  This shows all the dependency resolution information.  Sure enough my repo was never accessed, but I still could tell why.  I happened to notice that the private repo being access had a familiar id 'cloudbees-private-snapshot-repository'.  This id happened to be the same id I choose to use for my external maven repository.  Change the name and bobs your uncle, it all works again.


My configuration

Didn't work:
<repositories>
<repository>
<id>cloudbees-private-snapshot-repository</id>
<url>https://repository-aphethean.forge.cloudbees.com/snapshot/</url>
<releases><enabled>true</enabled></releases> 
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>

Works:
<repositories>
<repository>
<id>cloudbees-aphethean-snapshot-repository</id>
<url>https://repository-aphethean.forge.cloudbees.com/snapshot/</url>
<releases><enabled>true</enabled></releases> 
<snapshots><enabled>true</enabled></snapshots>
</repository>
</repositories>