Spring static factory with factory-method and parameter - spring

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!!!

Related

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>

Can we use Autowiring byName and byType on same bean? if yes then how?

I have tried autowiring byName and byType for different beans and it's working fine. But I want to use byName and byType autowiring on the same bean, can it be done? I have tried several way but it doesn't work.
<beans>
<bean id="car" class="com.Car" autowire="byName">
<property name="carName" value="BMW" />
</bean>
<bean id="wheel" class="com.Wheel">
<property name="wheelType" value="with Tube" />
</bean>
<bean id="wheel1" class="com.Wheel">
<property name="wheelType" value="tubeless" />
</bean>
<bean id="Byke" class="com.Byke" autowire="byType">
<property name="bykeName" value="Honda" />
</bean>
<bean id="Wheel" class="com.Wheel">
<property name="wheelType" value="No Information" />
</bean>
</beans>
In short answer is NO. For explanation see the relevant API doc which clearly takes only one argument for type of autowiring you need viz. autowireMode For quick reference pasting signature - java.lang.Object createBean(java.lang.Class<?> beanClass, int autowireMode ,boolean dependencyCheck) throws BeansException

outbound-gateway with Basic Authentication in spring-integration

With spring-integration I would like to call an outbound-gateway with an Basic Authentication.
I have something like this :
<int-http:inbound-gateway id="versionRequestGateway"
supported-methods="POST" request-channel="requestVersionChannel"
reply-channel="requestTransformerVersionChannel"
path="/consultersite" reply-timeout="10000" request-payload-type="java.lang.String">
</int-http:inbound-gateway>
<int-http:outbound-gateway order="1" request-channel="requestVersionChannel"
url-expression="#urlExpressionGateway.getUrlFor(payload) + '/consultersite'"
reply-channel="responseVersionChannel"
http-method="POST"
expected-response-type="java.lang.String" >
</int-http:outbound-gateway>
The URL of outbound-gateway is dynamic.
I decide to use rest-template attribute on the outbound-gateway, with this :
<bean id="httpClientParams" class="org.apache.commons.httpclient.params.HttpClientParams">
<property name="authenticationPreemptive" value="true"/>
<property name="connectionManagerClass" value="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/>
</bean>
<bean id="httpClient" class="org.apache.commons.httpclient.HttpClient">
<constructor-arg ref="httpClientParams"/>
</bean>
<bean id="httpClientFactory" class="org.springframework.http.client.CommonsClientHttpRequestFactory">
<constructor-arg ref="httpClient"/>
</bean>
<bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
<constructor-arg ref="httpClientFactory"/>
</bean>
It's work when I inject an UsernamePasswordCredentials in an ApplicationListener after spring application context is loaded.
HttpClient client = ctx.getBean("httpClient", HttpClient.class);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(AuthScope.ANY, credentials);
But according the url of outbound-gateway, username and password are different.
How can I do to use the good username/password according the url outbound-gateway ?
It was necessary to implement my own BasicSecureSimpleClientHttpRequestFactory extends SimpleClientHttpRequestFactory to map information Credentials according to the URL of connection. Hope an implementation Spring will be available one day ...
Thanks.
Do not type any Java code you can use a combination of Spring WS HttpComponentsMessageSender and Spring WEB HttpComponentsClientHttpRequestFactory:
<bean id="httpComponentsMessageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<property name="credentials">
<bean class="org.apache.commons.httpclient.UsernamePasswordCredentials">
<constructor-arg value="userName"/>
<constructor-arg value="password"/>
</bean>
</property>
</bean>
<bean id="clientHttpRequestFactory" class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory">
<property name="httpClient" value="#{httpComponentsMessageSender.httpClient}"/>
</bean>
<int-http:outbound-gateway url-expression="#urlExpressionGateway.getUrlFor(payload) + '/consultersite'"
request-factory="clientHttpRequestFactory"/>
I can believe, that my answer might not be full for your case.
However I hope it can help a bit.
Maybe there is need to implement your own HttpComponentsClientHttpRequestFactory#createRequest to authenticate at runtime and do this:
method.addRequestHeader(new Header(WWW_AUTH_RESP, authstring, true));
Take a look into source code of HttpMethodDirector#authenticateHost
you should use org.apache.http.auth.UsernamePasswordCredentials implementation instead of org.apache.commons.httpclient.UsernamePasswordCredentials
<beans:bean id="httpComponentsMessageSender" class="org.springframework.ws.transport.http.HttpComponentsMessageSender">
<beans:property name="credentials">
<beans:bean class="org.apache.http.auth.UsernamePasswordCredentials">
<beans:constructor-arg value="user"/>
<beans:constructor-arg value="password"/>
</beans:bean>
</beans:property>
</beans:bean>

spring DefaultMessageListenerContainer and websphere MQ - failed to connect Queue Manager

This is my first time to post question on stackoverflow. I tried as much formatting for code/question and try to as much clear as i can do. apologize and explain if there's any err. recorrect in my next question.
I am newbie in try implementing service through soap over jms using websphereMq and spring JMS functionality.
I have make sure below things
binding file generated without any error encounter
Status of queue manager and Queue are up and running.
I encounter below error while try putting message into websphereMQ
com.ibm.mq.MQException: JMSCMQ0001: WebSphere MQ call failed with compcode '2' ('MQCC_FAILED') reason '2058' ('MQRC_Q_MGR_NAME_ERROR')
I have done homework regarding this error. This error may occurs due to unavailability of Queue manager but i see QM is up and running. where i am doing wrong? How can i resolved this error to put message successfully to webspherMQ using spring functionlity?
TestClass
public class JmsTransportWebServiceIntegrationTest {
private static final String expectedResponseContent = "<tns:placeOrderResponse xmlns:tns=\"http://www.packtpub.com/liverestaurant/OrderService/schema\"><tns:refNumber>order-xxxx_yyyy_1234</tns:refNumber></tns:placeOrderResponse>";
#Autowired
private WebServiceTemplate webServiceTemplate;
public void setWebServiceTemplate(WebServiceTemplate webServiceTemplate) {
this.webServiceTemplate = webServiceTemplate;
}
#Test
public void testSendReceive() throws Exception {
InputStream is = new JmsTransportWebServiceIntegrationTest().getClass().getResourceAsStream("placeOrderRequest.xml");
StreamSource source = new StreamSource(is);
StringResult result = new StringResult();
webServiceTemplate.sendSourceAndReceiveToResult(source, result);
XMLAssert.assertXMLEqual("Invalid content received", expectedResponseContent, result.toString());
} }
applicationContext.xml
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<map>
<entry key="java.naming.factory.initial"
value="com.sun.jndi.fscontext.RefFSContextFactory"/>
<entry key="java.naming.provider.url" value="file:C:/JNDI-Directory" />
</map>
</property>
</bean>
<bean id="ibm-mq-jms-qcf" class= "org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>jms/mdpSampleQCF</value>
</property>
</bean>
<!-- Bean for JMS Destination -->
<bean id="ibm-mq-queue" class= "org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate">
<ref bean="jndiTemplate"/>
</property>
<property name="jndiName">
<value>jms/mdpSampleQueue</value>
</property>
</bean>
<bean id="listenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="1" />
<property name="connectionFactory" ref="ibm-mq-jms-qcf" />
<property name="destination" ref="ibm-mq-queue" />
<property name="messageListener">
<bean class="org.springframework.ws.transport.jms.WebServiceMessageListener">
<property name="messageFactory" ref="messageFactory"/>
<property name="messageReceiver" ref="messageDispatcher"/>
</bean>
</property>
</bean>
<bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"/>
<bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="messageSender">
<bean class="org.springframework.ws.transport.jms.JmsMessageSender">
<property name="connectionFactory" ref="ibm-mq-jms-qcf"/>
</bean>
</property>
<property name="defaultUri" value="jms:mdpSampleQueue?deliveryMode=NON_PERSISTENT"/>
</bean>
<bean id="messageDispatcher" class="org.springframework.ws.soap.server.SoapMessageDispatcher">
<property name="endpointMappings">
<bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootQNameEndpointMapping">
<property name="defaultEndpoint">
<bean class="com.packtpub.liverestaurant.service.endpoint.SimplePayloadEndpoint">
<property name="orderService">
<bean class="com.packtpub.liverestaurant.service.OrderServiceImpl"/>
</property>
</bean>
</property>
</bean>
</property>
</bean
I know this is quite old question, but maybe my answer will help someone in future.
I'm using WebSphere MQ 7.5 now and in installation there are also some Java classes. One that helped me a lot is called MQIVP.java in my installation in c:\Program Files (x86)\IBM\WebSphere MQ\tools\wmqjava\samples. Very good to test setting with this class first. From source of that class we can find that 2058 stands for:
Reason: 2058 - Queue manager name not valid or not known.
Action: Amend the queue manager name and retry.
Simply pressing enter at the queue manager name prompt will
connect to the default queue manager.

Is it possible to alias bean class names in Spring?

I have a string property which looks similar to the following example:
<property name="mappingData">
<list>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean class="com.company.product.longNamingStandard.migration.extractor.FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
The long class name(s) effect readability & also create a refactoring overhead.
Is it possible to alias the class name and use a short name to declare the beans? Or is there an alternate best practice I'm missing?
Probably a bit late for you, but hopefully useful for others:
You can use parent beans to accomplish this.
First declare a parent bean as a template:
<bean id="FieldMapping" class="com.company.product.longNamingStandard.migration.extractor.FieldMapping"/>
Then use it elsewhere, using the parent attribute.
<property name="mappingData">
<list>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.user"/>
<property name="mapping" value="DocUsers"/>
</bean>
<bean parent="FieldMapping">
<property name="elementName" value="entitlement.contributor"/>
<property name="mapping" value="DocContributors"/>
</bean>
</list>
</property>
Please note my convention here is to use upper case id's here for the parent template beans.
each <bean/> comes with an attribute of name and id to help you reference those beans later in your configuration.
I would suggest using the id for declaring the bean.
your config could look like:
<bean id="fooBean" class="com.example.foo"/>
<bean id="barBean" class="com.example.bar"/>
<list>
<ref>fooBean</ref>
<ref>barBean</ref>
</list>
You may try to represent your mapping in some short form, and then convert it to the list of FieldMappings. For example, mappings from your snippet may be represented as a map.
As a theoretic exercise in Spring 3 you can do this with Spring Expression Language (if FieldMapping has the apropriate constructor):
<util:map id = "m">
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</util:map>
...
<property name = "mappingData"
value = "#{m.![new com.company.product.longNamingStandard.migration.extractor.FieldMapping(key, value)]}" />
If this expression is too obscure, you may implement a FactoryBean to take a short form of your mapping data (for example, a map, as in this example) and return a configured list of FieldMappings:
<property name = "mappingData">
<bean class = "FieldMappingListFactoryBean">
<property name = "mappings">
<map>
<entry name = "entitlement.user" value = "DocUsers" />
<entry name = "entitlement.contributor" value = "DocContributors" />
</map>
</property>
</bean>
</property>
However, if your field mappings are some kind of reusable DSL, you may try to think about implementing a namespace extension.
I found a way to simulate an effect similar to a "import com.Foo;" in java code. The best option I could find was to use a PropertyPlaceholderConfigurer with local properties defined. Using your example, here's the configuration that you would put at the top of your spring config file to define a "class_FieldMapping" property:
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<description>Define properties equivalent to "import foo;" in java source</description>
<property name="properties">
<props>
<prop key="class_FieldMapping">com.company.product.longNamingStandard.migration.extractor.FieldMapping</prop>
</props>
</property>
</bean>
Then, you can use that property within your beans:
<property name="mappingData">
<list>
<bean class="${class_FieldMapping}">
...
</bean>
<bean class="${class_FieldMapping}">
...
</bean>
</list>
</property>
This has the benefit that use can also use it for things where you actually need the class name, and can't reference an instance of an object:
<util:constant static-field="${class_FieldMapping}.MYSTATICVAR" />
Why not declare those inner beans as separate top-level beans with their own names, and then reference them in the list ?
If I use PropertyPlaceholderConfigurer it leads to several exceptions in debug log. It works, but it seems it doesn't work on the first try.

Resources