How to dynamically pass the property in Spring FTP Outboud channel context xml - spring

The FTPapplicationContext.xml has the configuration to create the beans for FTPchannel. here it takes the property values from the application.proprty. Now my question is how to dynamically set the property while on the run time.
Context.xml:
<bean id="ftpClientFactory" class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${host}"/>
<property name="port" value="${availableServerPort}"/>
<property name="username" value="${userid}"/>
<property name="password" value="${password}"/>
</bean>
<int:channel id="ftpChannel"/>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
remote-directory="/temp"
session-factory="ftpClientFactory">
<int-ftp:request-handler-advice-chain>
<int:retry-advice />
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
application.prperties:
userid=Administrator
password=password
availableServerPort=21
host=Testfar
FTP code:
ConfigurableApplicationContext context =
new FileSystemXmlApplicationContext("context.xml");
MessageChannel ftpChannel = context.getBean("ftpChannel",MessageChannel.class);
File file1 = new File("test.txt");
Message<File> fileMessage = MessageBuilder.withPayload(file1).build();
ftpChannel.send(fileMessage);
context.close();
In application.properiites file, I can setup a default host address but Here I need to pass the host address dynamically based on the user input. Could you please advice me on how to achieve that. It would be great If you suggest/provide some ideas.

Related

Is there a way to use environment variable in payara-resources.xml when creating connector-connection-pool

I am running an application on Payara micro and trying to create a connector-connection-pool in the payara-resources.xml file that uses environment variables to pass in data as below:
<connector-connection-pool resource-adapter-name="wmq.jmsra" name="jms/MyConnectionPool"
connection-definition-name="javax.jms.ConnectionFactory">
<property name="transportType" value="CLIENT"/>
<property name="port" value="${ENV=CONFIGURATION_PORT}"/>
<property name="channel" value="${ENV=CONFIGURATION_CHANNEL}"/>
<property name="queueManager" value="${ENV=CONFIGURATION_MANAGER}"/>
<property name="username" value="${ENV=CONFIGURATION_USERNAME}"/>
<property name="hostName" value="${ENV=CONFIGURATION_HOST}"/>
</connector-connection-pool>
However this fails with the error below, but when I hardcode the values it works fine:
... 320 more
Caused by: com.ibm.mq.connector.DetailedResourceException: MQJCA1012: Failed to create a JMS connection factory., error code: MQJCA1012 A JCA ManagedConnectionFactory object was not able to create a WebSphere MQ classes for JMS ConnectionFactory object. Check the properties of the ConnectionFactory object.
... 321 more
Caused by: com.ibm.msg.client.jms.DetailedJMSException: JMSCC0005: The specified value '${ENV=CONFIGURATION_MANAGER}' is not allowed for 'XMSC_WMQ_QUEUE_MANAGER'.
The given value is not allowed for the property specified.
...
... 324 more
In the same file, I have created a jdbc-connection-pool in a similar way but it is able to resolve the environment variable successfully work:
<jdbc-connection-pool datasource-classname="org.postgresql.ds.PGConnectionPoolDataSource"
name="my_database" res-type="javax.sql.ConnectionPoolDataSource">
<property name="port" value="5432"/>
<property name="user" value="${ENV=DB_USER}"/>
<property name="password" value="${ENV=DB_PWD}"/>
<property name="ServerName" value="${ENV=DB_HOST}"/>
<property name="DatabaseName" value="${ENV=DB_NAME}"/>
</jdbc-connection-pool>
I solved my problem by moving away from defining resources in payara-resources.xml. I created a new class and defined all the resources in Java using these annotations, #DataSourceDefinition, #ConnectionFactoryDefinition, #AdministeredObjectDefinition and #MailSessionDefinition.
So the connector-connection-pool that was giving problems ended up looking like this:
#ConnectionFactoryDefinitions({
#ConnectionFactoryDefinition(
name = "java:app/jms/MyConnectionPool",
interfaceName = "javax.jms.ConnectionFactory",
resourceAdapter = "wmq.jmsra",
properties = {
"transactionSupport=XATransaction",
"transportType=CLIENT",
"channel=${ENV=CONFIGURATION_CHANNEL}",
"queueManager=${ENV=CONFIGURATION_MANAGER}",
"hostName=${ENV=CONFIGURATION_HOST}",
"port=${ENV=CONFIGURATION_PORT}",
"username=${ENV=CONFIGURATION_USERNAME}",
}
),
// other connection pools went here...
})

Listener method not called when using Spring JMS

I have an application built on Spring 4.1. I am trying to create a JMS Listener using XML configuration and trying to convert the incoming XML message to Java Object. Below is my xml configuration:
<jms:listener-container concurrency="10"
connection-factory="connectionFactory"
message-converter="marshallingMessageConverter">
<jms:listener destination="destination.name" ref="messageListener" method="processMessage"/>
</jms:listener-container>
<bean id="messageListener" class="com.example.CustomMessageListener">
</bean>
<bean id="marshallingMessageConverter" class="org.springframework.jms.support.converter.MarshallingMessageConverter">
<property name="marshaller" ref="xmlMarshaller"/>
<property name="unmarshaller" ref="xmlMarshaller"/>
</bean>
<bean id="xmlMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound" value="com.example.CustomObject"/>
</bean>
The Class com.example.CustomMessageListener looks like below:
public class CustomMessageListener{
public void processMessage(Message message, CustomObject object){
//Do Something.
}
Now when I post a message into the destination queue, the method processMessage() on the Listener is not getting called and I am getting the below error in spring logs:
Failed to invoke target method 'processMessage' with arguments {com.example.CustomObject#52ee271d};
nested exception is java.lang.NoSuchMethodException:
com.example.CustomMessageListener.processMessage(com.example.CustomObject).
Now if I change the Listerner method's argument to just accept the CustomObject, it works and I get the CustomObject properly constructed from the XML:
public void processMessage(CustomObject, object)
But I also need the original javax.jms.Message instance and according to this documentation, it should be possible to receive that instance by specifying it in the parameter list.
Can somebody please help me out here?
try this it should work
<jms:listener-container concurrency="10" connection-factory="connectionFactory" >
<jms:listener destination="destination.name" ref="defaultMessageListener" />
</jms:listener-container>
<bean id="messageListener" class="com.example.CustomMessageListener">
</bean>
<bean id="marshallingMessageConverter" class="org.springframework.jms.support.converter.MarshallingMessageConverter">
<property name="marshaller" ref="xmlMarshaller"/>
<property name="unmarshaller" ref="xmlMarshaller"/>
</bean>
<bean id="xmlMarshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound" value="com.example.CustomObject"/>
</bean>
<bean id="payloadArgumentResolver" class="org.springframework.messaging.handler.annotation.support.PayloadArgumentResolver">
<property name="converter" ref="marshallingMessageConverter"/>
</bean>
<bean id="handlerMethod" class="org.springframework.messaging.handler.invocation.InvocableHandlerMethod">
<constructor-arg
ref="messageListener" index="0"/>
<constructor-arg
value="processMessage" index="1"/>
<constructor-arg
value="javax.jms.Message" type="java.lang.Class" index="2"/>
<constructor-arg
value="com.example.CustomObject" type="java.lang.Class" index="3"/>
<property name="argumentResolvers" ref="payloadArgumentResolver" />
</bean>
<bean id="defaultMessageListener" class="org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter">
<property name="handlerMethod" ref="handlerMethod" />
</bean>

Spring static factory with factory-method and parameter

I have a problem transfering code to Spring applicationContext.xml
The source is:
File inFile = new File ("path/to/file/", "fileName.docx")
WordprocessingMLPackage wordMLPackage = Docx4J.load(inFile);
My not working solution is:
<bean id="inFile" class="java.io.File">
<constructor-arg value="path/to/file/" />
<constructor-arg value="fileName.docx" />
</bean>
<bean id="docx4j" class="org.docx4j.Docx4J" factory-method="load">
<constructor-arg ref="inFile" />
</bean>
<bean id="wordprocessingMLPackage" class="org.docx4j.openpackaging.packages.WordprocessingMLPackage" factory-bean="docx4j" />
What I'm getting out of the bean "wordprocessingMLPackage" is indeed an instance of the Class WordprocessingMLPackage, but it seems empty although the File I'm trying to load isn't (and yes, the path is doublechecked).
When trying
MainDocumentPart mdp = wordprocessingMLPackage.getMainDocumentPart();
List<Object> content = mdp.getContent();
I'm getting a NullPointerException because mdp is null!
Has anyone an idea... or even a solution?
============================================================
I found a solution especially for my problem.
Here is the source of Docx4j.load():
public static WordprocessingMLPackage load(File inFile) throws Docx4JException {
return WordprocessingMLPackage.load(inFile);
}
That means I can create an instance of WordprocessingMLPackage by its static self!
The code which is working:
<bean id="wordprocessingMLPackage" class="org.docx4j.openpackaging.packages.WordprocessingMLPackage" factory-method="load">
<constructor-arg ref="baseDocument" />
</bean>
So I found a lucky "workaround" for the original problem.
Since this question isn't urgent any more, I'm still interested in the correct solution, especially in a solution which allows injecting the WordprocessingMLPackage in other beans.
Thank you!
Here you need to make use of MethodInvokingFactoryBean as detailed below.
<bean id="beanId"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.docx4j.Docx4J" />
<property name="targetMethod" value="load"/>
<property name="arguments">
<list>
<ref bean="inFile" />
</list>
</property>
</bean>
In your code get hold of applicationContext instance and invoke the below LOC
WordprocessingMLPackage ml = (WordprocessingMLPackage) applicationContext.getBean("beanId");
Let know in comments if you face any issues.
As Bond - Java Bond stated this works:
<bean id="inFile" class="java.io.File">
<constructor-arg value="path/to/file/" />
<constructor-arg value="fileName.docx" />
</bean>
<bean id="beanId" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="org.docx4j.Docx4J" />
<property name="targetMethod" value="load"/>
<property name="arguments">
<list>
<ref bean="inFile" />
</list>
</property>
</bean>
You can now use the bean as
WordprocessingMLPackage ml = (WordprocessingMLPackage) applicationContext.getBean("beanId");
or you can inject the bean directly as
<bean id="service" class="app.service.Service">
<property name="wordprocessingMLPackage" ref="beanId" />
</bean>
Thank you!!!

Spring Integration, jms Inbound gateway for WMQ; Unable to consume messages

I have recently started exploring Spring Integration as that is one of the option we want to evaluate for our project.
The issue i am facing is below.
I have created a JMS inbound gateway to listen to WMQ queue and i am expecting the inboudnd gateway (using DML) should pick up the messages as and when they are available on queue(event - driven).
But some how the example isn't working. It fails to pick messages from the queue. However i can see (using a tool) that there are consumers created on the queue.
Help here is really appreaciated.
Code snippet below.
<bean id="mqFactory" class="com.ibm.mq.jms.MQConnectionFactory">
<property name="hostName" value="${mq.hostName}"/>
<property name="port" value="${mq.port}"/>
<property name="queueManager" value="${mq.queueManager}"/>
<property name="channel" value="${mq.channel}"/>
<property name="transportType" value="${mq.transportType}"/>
<property name="SSLCipherSuite" value="${mq.SSLCipherSuite}"/>
</bean>
<bean id="inCachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="mqFactory" />
<property name="sessionCacheSize" value="5" />
</bean>
<bean id="requestQueue-mq" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${mq.example.queue}"/>
</bean>
<bean id="demoBean" class="com.jpmchase.example.spring.DemoBean">
</bean>
<jms:inbound-gateway id="wMQ_in_gateway" concurrent-consumers="2" max-concurrent-consumers="5" connection-factory="inCachingConnectionFactory" request-destination="requestQueue-mq"
request-channel="demoChannel" />
<integration:channel id="demoChannel">
</integration:channel>
<integration:service-activator input-channel="demoChannel" ref="demoBean"/>
Below is the service-activator java code.
enter #MessageEndpoint
public class DemoBean {
#ServiceActivator
public String upperCase(String input) {
System.out.println("inside the service activator " + input);
return "JMS response: " + input.toUpperCase();
}
here

Spring-JMS(Websphere MQ)

I have the below configurations in Spring , it is working fine but performance is too low (it takes 1 min for 20 messages). Can you please suggest me changes to increase the performance.
<bean id="jmsConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType"><value>1</value></property>
<property name="queueManager"><value></value></property>
<property name="hostName"><value></value></property>
<property name="port"><value></value></property>
<property name="channel"><value></value></property>
<property name="clientId"><value></value></property>
</bean>
<bean id="SenderJMSTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory"><ref bean="jmsConnectionFactory" /> </property>
<property name="pubSubDomain"><value>false</value></property>
<property name="defaultDestination"><ref bean="senderQueue" /></property>
</bean>
<bean id="senderQueue" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="TEST" />
<property name="baseQueueManagerName"><value>tree.queue.manager</value></property>
<property name="baseQueueName"><value>ORANGE.QUEUE</value></property>
</bean>
<bean id="jmsSender" class="org.tree.jms.spring.JMSSender">
<property name="jmsTemplate"><ref bean="SenderJMSTemplate"/></property>
</bean>
I am calling from spring as
JMSSender obj = (JMSSender) context.getBean("jmsSender");
And My Sender program is :
#Cacheable("message")
public void sendMesage() {
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session)throws JMSException {
message = (Message) session.createTextMessage(stringBuffer.toString());
return message;
}
});
}
}
A common problem when using JMSTemplate to send messages out of JavaEE containers is the it's extremly slow since it acquires a new connection for each message (and then closes it). You would probably need a pooled/cached connection to gain speed here.
Read this article, it's written for ActiveMQ, but applies in a similar way to WebSphere MQ: http://activemq.apache.org/jmstemplate-gotchas.html
You can setup a cached connection factory in spring using something like this:
<bean id="cachedConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory"
p:targetConnectionFactory-ref="jmsConnectionFactory"
p:sessionCacheSize="10" />
Then use it instead of the original one for JMS connections.

Resources