drools6.3.0.Final spring integration - spring

I am new to drools and I have got a problem while I am trying to integrate drools with spring following the guide of the drools doc page.Here's the link :http://docs.jboss.org/drools/release/6.3.0.Final/drools-docs/html_single/index.html#d0e13904.
I write some code and a junit test case.Everything seems go well,the case runs correctly and no exception was thrown.But the rules are not fired.I am sure that the rule file is ok.
Here is the rule file:
package com.huateng.risk;
import com.huateng.risk.fact.ConsumeFact;
global com.huateng.risk.service.NoticeService noticeService;
rule "day consume times limit"
//day consume times limit 5
when
$info : ConsumeFact (dayTimes >= 5 , checked == true);
then
//noticeService.notice("day consume times limit over");
System.out.println("day consume times limit over");
$info.setChecked(false);
update($info);
end
rule "day consume amount limit"
//3000
when
$info : ConsumeFact (dayAmt >= 3000 , checked == true );
then
//noticeService.notice("day consume amt limit over");
System.out.println("day consume amt limit over");
$info.setChecked(false);
update($info);
end
rule "month consume times limit"
//day consume times limit 15
when
$info : ConsumeFact (monthTimes >= 15 , checked == true );
then
//noticeService.notice("month consume times limit over");
System.out.println("month consume times limit over");
$info.setChecked(false);
update($info);
end
rule "month consume amount limit"
//3000
when
$info : ConsumeFact (monthAmt >= 8000 , checked == true );
then
//noticeService.notice("month consume amt limit over");
System.out.println("month consume amt limit over");
$info.setChecked(false);
update($info);
end
rule "consume amount limit"
//2000
when
$info : ConsumeFact (amt >= 2000 , checked == true );
then
//noticeService.notice("consume amt limit over");
System.out.println("consume amt limit over");
$info.setChecked(false);
update($info);
end
I have tested this drl file with the code :
package com.huateng.risk.demo;
import org.kie.api.KieServices;
import org.kie.api.event.rule.DebugAgendaEventListener;
import org.kie.api.event.rule.DebugRuleRuntimeEventListener;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import com.huateng.risk.fact.ConsumeFact;
import com.huateng.risk.service.NoticeServiceImpl;
/**
* This is a sample file to launch a rule package from a rule source file.
*/
public class RiskHandleMain {
public static final void main(final String[] args) {
// KieServices is the factory for all KIE services
KieServices ks = KieServices.Factory.get();
// From the kie services, a container is created from the classpath
KieContainer kc = ks.getKieClasspathContainer();
execute( kc );
}
public static void execute( KieContainer kc ) {
// From the container, a session is created based on
// its definition and configuration in the META-INF/kmodule.xml file
KieSession ksession = kc.newKieSession("RiskHandleKS");
// Once the session is created, the application can interact with it
// In this case it is setting a global as defined in the
// org/drools/examples/helloworld/HelloWorld.drl file
ksession.setGlobal( "noticeService", new NoticeServiceImpl() );
// The application can also setup listeners
ksession.addEventListener( new DebugAgendaEventListener() );
ksession.addEventListener( new DebugRuleRuntimeEventListener() );
// To setup a file based audit logger, uncomment the next line
// KieRuntimeLogger logger = ks.getLoggers().newFileLogger( ksession, "./helloworld" );
// To setup a ThreadedFileLogger, so that the audit view reflects events whilst debugging,
// uncomment the next line
// KieRuntimeLogger logger = ks.getLoggers().newThreadedFileLogger( ksession, "./helloworld", 1000 );
// The application can insert facts into the session
final ConsumeFact info = new ConsumeFact();
info.setAmt(2000L);
info.setDayTimes(8L);
info.setDayAmt(2000L);
info.setMonthTimes(8L);
info.setMonthAmt(5000L);
ksession.insert( info );
// and fire the rules
ksession.fireAllRules();
// Remove comment if using logging
// logger.close();
// and then dispose the session
ksession.dispose();
}
}
In my spring integration case, i have two spring bean files:
The first:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- proxy-target-class -->
<aop:aspectj-autoproxy proxy-target-class="true" />
<context:component-scan base-package="com.huateng.risk,org.kie.spring" />
<import resource="classpath:spring-drools.xml" />
</beans>
And the second:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:kie="http://drools.org/schema/kie-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://drools.org/schema/kie-spring http://drools.org/schema/kie-spring.xsd ">
<kie:kmodule id="riskKmodule">
<kie:kbase name="riskKbase" packages="com.huateng.risk">
<kie:ksession name="ksRiskHandle">
<!-- <kie:fileLogger file="drools" threaded="true"
interval="10" /> -->
<!-- <kie:batch>
<kie:set-global identifier="noticeSerivce">
<bean class="com.huateng.risk.service.NoticeServiceImpl"/>
</kie:set-global>
</kie:batch> -->
<!-- <kie:listeners>
<kie:ruleRuntimeEventListener
ref="org.kie.api.event.rule.DebugRuleRuntimeEventListener" />
<kie:agendaEventListener
ref="org.kie.api.event.rule.DebugAgendaEventListener" />
<kie:processEventListener
ref="org.drools.core.event.DebugProcessEventListener" />
</kie:listeners> -->
</kie:ksession>
</kie:kbase>
</kie:kmodule>
</beans>
In the second file, the kbase configurs the resources package which exactly stores the drl files.
I have got two questions:
1 As I wrote before, my test case runs normally with no exceptions,but the rules are not fired.And when i debug my code,i find the something wrong.
Here is my code:
package com.huateng.risk;
import java.util.Collection;
import java.util.List;
import org.kie.api.KieBase;
import org.kie.api.cdi.KBase;
import org.kie.api.cdi.KSession;
import org.kie.api.definition.KiePackage;
import org.kie.api.definition.rule.Rule;
import org.kie.api.event.rule.AgendaEventListener;
import org.kie.api.runtime.Environment;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.huateng.risk.fact.ConsumeFact;
import com.huateng.risk.service.NoticeService;
#Component("riskHandle")
public class RiskHandle {
#Autowired
private NoticeService noticeService;
#KSession("ksRiskHandle")
private KieSession ksession;
public void execute(ConsumeFact info ) {
try{
ksession.insert( info );
Environment env = ksession.getEnvironment();
KieBase kbase = ksession.getKieBase();
Collection<KiePackage> packages = kbase.getKiePackages();
KiePackage pkg = kbase.getKiePackage("com.huateng.risk");
Rule rule = kbase.getRule("com.huateng.risk", "day consume times limit");
ksession.fireAllRules();
ksession.dispose();
}catch(Exception e){
e.printStackTrace();
}
}
public void setNoticeService(NoticeService noticeService) {
this.noticeService = noticeService;
}
public void setKsession(KieSession ksession) {
this.ksession = ksession;
}
}
When I was debugging my code, i found the "packages" was empty array.So was the "pkg" and the "rule".It seems that the kbase did not loaded the resources stored in the sub-folder "/com/huateng/risk" under the "src/main/resources" which is defined as a source folder by maven default.
So Have i done wrong somewhere with spring integration?
2 I am trying to set globals to do some bussiness stuff.Let's back forward to the second spring bean file.When i uncommented the set-globals config ,i got an exception : Unexpected globals.
How to configure the globals with spring integration ?
Here is the set-globals config in the second spring bean file:
<kie:batch>
<kie:set-global identifier="noticeSerivce">
<bean class="com.huateng.risk.service.NoticeServiceImpl"/>
</kie:set-global>
</kie:batch>

Related

High Efficient MDB using spring jms or ntegration

I have requirement where I need receive message from MQ every seconds , the message will be in XML format , so i need to write a high effecient MDB. Problem here is I am new to MQ,JMS,Spring JMS and Spring Integration but not spring tough.
Based on google search so far I am able to write below code
MDB
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
public class MyMessageListener implements MessageListener{
#Override
public void onMessage(Message m) {
TextMessage message=(TextMessage)m;
try{
System.out.println(message.getText());
}catch (Exception e) {e.printStackTrace(); }
}
}
XMl config
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:brokerURL="tcp://localhost:61616" />
<bean id="listener" class="com.springexample.MyMessageListener"></bean>
<jms:listener-container container-type="default" connection-factory="connectionFactory"
acknowledge="auto">
<jms:listener destination="myfirstqueue" ref="listener" method="onMessage"></jms:listener>
</jms:listener-container>
</beans>
I know what i have written is very basic one but i dont know what is the efficient one , how to provide transaction support and how to handle such load where i am going to recieve message every seconds.
Should i use Spring JMS or Spring Integration any help is much appreciated.
The question you asked is which to use: definitely go Spring JMS

Dynamically provide directory and file name to ftp outbound gateway

We have a requirement for the FTP client to download a file whose name and directory is provided at run-time. So, the FTP client may be asked to download file1.txt from foo1/foo2 directory path on the remote server.
We do have a solution using Spring Integration FTP outbound gateway. With this solution to make it dynamic:
the ApplicationContext for the gateway is created
the gateway properties get set using file name and remote directory path
the file is downloaded
the ApplicationContext is closed.
What we're not happy about is that the ApplicationContext is created and closed every time which obviously affects performance. Is there a way to dynamically pass the file name and the directory path to the gateway without reloading the Appplication Context every time?
Your help will be greatly appreciated.
Here's the main code and configuration:
package com.cvc.ipcdservice.ftp;
import java.util.List;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.StandardEnvironment;
public class DynamicFtpClient {
private static final Logger LOGGER = LoggerFactory
.getLogger(DynamicFtpClient.class);
public void download(final FtpMetaData ftpMetaData) {
final ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
new String[] { "/META-INF/spring/integration/FtpOutboundGateway-context.xml" },
false);
setEnvironment(ctx, ftpMetaData);
ctx.refresh();
final ToFtpFlowGateway toFtpFlow = ctx.getBean(ToFtpFlowGateway.class);
// execute the flow (mget to download from FTP server)
final List<Boolean> downloadResults = toFtpFlow.mGetFiles("/");
LOGGER.info(
"Completed downloading from remote FTP server. ftpMetaData:{}, downloadResults.size:{} ",
ftpMetaData, downloadResults.size());
ctx.close();
}
/**
* Populate {#code ConfigurableApplicationContext} with Provider-specific
* FTP properties.
*
* #param ctx
* #param customer
*/
private void setEnvironment(final ConfigurableApplicationContext ctx,
final FtpMetaData ftpMetaData) {
final StandardEnvironment env = new StandardEnvironment();
final Properties props = new Properties();
// populate properties for customer
props.setProperty("ftp.host", ftpMetaData.getHost());
props.setProperty("ftp.port", ftpMetaData.getPort());
props.setProperty("ftp.userid", ftpMetaData.getUserName());
props.setProperty("ftp.password", ftpMetaData.getPassword());
// props.setProperty("remote.directory", "/");
// WARNING: the file name pattern has to be surrounded by single-quotes
props.setProperty("ftp.remote.filename.pattern",
"'" + ftpMetaData.getFileNamePattern() + "'");
props.setProperty("ftp.local.dir", ftpMetaData.getLocalDirectory());
final PropertiesPropertySource pps = new PropertiesPropertySource(
"ftpprops", props);
env.getPropertySources().addLast(pps);
ctx.setEnvironment(env);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-ftp="http://www.springframework.org/schema/integration/ftp"
xsi:schemaLocation="http://www.springframework.org/schema/integration/ftp http://www.springframework.org/schema/integration/ftp/spring-integration-ftp.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder/>
<int:gateway id="gw" service-interface="com.cvc.ipcdservice.ftp.ToFtpFlowGateway"
default-request-channel="inbound"/>
<bean id="ftpSessionFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.host}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.userid}"/>
<property name="password" value="${ftp.password}"/>
</bean>
<int-ftp:outbound-gateway id="gatewayGET"
local-directory="${ftp.local.dir}"
session-factory="ftpSessionFactory"
request-channel="inbound"
command="mget"
command-options="-P"
expression="${ftp.remote.filename.pattern}"/>
</beans>
There is no need to create the context for each request.
Instead of using a literal for the expression:
props.setProperty("ftp.remote.filename.pattern",
"'" + ftpMetaData.getFileNamePattern() + "'");
Use an expression based on the request; e.g.
props.setProperty("ftp.remote.filename.pattern",
"payload");
Then simply send the required path in your gateway call...
final List<Boolean> downloadResults = toFtpFlow.mGetFiles("/some/path/*.txt");

Spring MVC validation ignored

I have Spring MVC app on Tomcat. I'm trying to add validation in it.
For some reason my validation is ignored and not checked.
Here is my POJO
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
And controller
import org.springframework.validation.BindingResult;
import javax.validation.Valid;
#Controller
#SessionAttributes("goal")
public class GoalController {
#RequestMapping(value = "addGoal", method = RequestMethod.POST)
public String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "addGoal";
}
return "redirect:addMinutes.html";
}
}
But even if I try to put any negative values as "minutes" there no errors produced.
Added configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven/>
<context:component-scan base-package="com.xlab.ice.mvc.controller"/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />
<mvc:resources mapping="/pdfs/**" location="pdf"/>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource" p:basename="messages"/>
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" p:defaultLocale="en"/>
<mvc:interceptors>
<bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor" p:paramName="lang"/>
</mvc:interceptors>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" p:order="0"/>
</beans>
Can you please also confirm that you have a JSR 303 implementation in your classpath - say hibernate-validator jar files.
You need <mvc:annotation-driven /> to enable jsr-303 validation, This is needed for the #Valid annotation to actually do anything.
try adding <mvc:annotation-driven/> in servlet context XML if not done.
schemaLocation the mvc entry should contain these two:
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
Solution is simple.
Validation libraries must be placed into classpath.
DISCLAIMER : In the absence of complete relevant controller code and the suspicion that Validator library might be missing in classpath, here is a small test to just check the configuration. You can use this very quickly to find if all configurations are in place with just removing the session attribute part.
Let's give it a try. To simplify testing with a REST Client and also incomplete code I have not used SessionAttributes. Also there is Hibernate validator library on my runtime classpath. Also I have used ResponseBody annotation to let the Spring automatically convert the String to response text without making use of alternate view technology (like jsp).
import org.hibernate.validator.constraints.Range;
public class Goal {
#Range(min = 1, max = 15)
private int minutes;
public int getMinutes() {
return minutes;
}
public void setMinutes(int minutes) {
this.minutes = minutes;
}
}
Then my controller
#Controller
public class GoalController {
#RequestMapping(value = "/addGoal", method = RequestMethod.POST)
public #ResponseBody String updGoal(#ModelAttribute("goal") #Valid Goal goal, BindingResult result) {
System.out.println(goal.getMinutes());
System.out.println(result.hasErrors());
if(result.hasErrors()) {
return "Errors !";
}
return "No Errors !";
}
}
Hitting the application with a rest client on http://localhost:8080/testApp/addGoal with POST parameters as minutes=1 gives me response as No Errors ! while with minutes=-1 gives me Errors !

Spring not detecting #Scheduled Methods

I am running Spring 3.2.3.RELEASE and I have several methods within #Service decorated classes that are Scheduled tasks and thus decorated with the #Scheduled annotation.
All the Spring beans are detected and instantiated within the container, however the #Scheduled annotations are never invoked.
I have several application contexts, but the main file is described below as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<import resource="classpath:core-properties.xml" />
<import resource="classpath:core-datasource.xml" />
<import resource="classpath:core-task-scheduler.xml" />
<context:component-scan base-package="com.atlaschase.project.core">
<context:exclude-filter type="regex"
expression="com.atlaschase.project.core.services.jms.*" />
<context:exclude-filter type="regex"
expression="com.atlaschase.project.core.services.processor.*" />
<context:exclude-filter type="regex"
expression="com.atlaschase.project.core.services.remote.*" />
<context:exclude-filter type="regex"
expression="com.atlaschase.project.core.bootstrap.populators.*" />
</context:component-scan>
<bean id="bufferPopulator" class="com.atlaschase.project.core.services.jms.buffer.BufferPopulator"/>
<bean id="eventBuffer" class="com.atlaschase.project.core.services.jms.buffer.EventBuffer"/>
<bean id="processorEventHandler" class="com.atlaschase.project.core.services.jms.buffer.ProcessorEventHandler"/>
The other important file is imported as "core-task-scheduler.xml". This is configured as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="1"/>
<task:scheduler id="myScheduler" pool-size="5"/>
</beans>
Originally, the executor reference (above) was defined in the main application context, but I split this out as an import after reading that other similar problems had been solved in this way. Unfortunately this has not helped. I have also moved the import declarations around, but this seems to have no effect.
Declaring Scheduled methods in the Spring Application Context directly works fine - however I am very keen to use annotations.
Here is the annotated class:
**
* Service that batches the saving of position updates into a periodically executed
* process.
*/
#Service
public class PositionAggregatorService {
static Logger logger = Logger.getLogger(AircraftPositionAggregatorService.class);
// The service will not execute within 1 minute of a previous batch attempt.
// This paramater is updated by a message from another node indicating that
// they are commencing a batch save of position updates.
private DateTime blockingTime;
private Map<Long, List<Position>> positions;
#Autowired
private PositionDao positionDao;
#Autowired
private InternalPublisher internalEventPublisher;
#PostConstruct
private void initialise(){
positions = new ConcurrentHashMap<Long, List<Position>>();
}
/*
* Scheduled to execute every 10 minutes as long current time is not
* before the blocking time threshold.
*
* */
#Scheduled(fixedRate=6000)
public void batchSavePositionUpdates(){
if(blockingTime != null){
if(DateTime.now(DateTimeZone.UTC).isBefore(blockingTime)){
return;
}
}
PositionPersistStartedNotification started = new PositionPersistStartedNotification(DateTime.now(DateTimeZone.UTC), DateTime.now(DateTimeZone.UTC).plusMinutes(2));
internalEventPublisher.publishPositionPersistStartedNotification(started);
DateTime mostRecentUpdateTime = null;
List<Position> positionsToSave = new ArrayList<Position>();
for(Long flightId : positions.keySet()){
List<Position> positionList = positions.get(flightId);
for(Position position : positionList){
if(mostRecentUpdateTime == null){
mostRecentUpdateTime = new DateTime(position.getTime());
}
else{
DateTime positionTime = new DateTime(position.getTime());
if(positionTime.isAfter(mostRecentUpdateTime)){
mostRecentUpdateTime = positionTime;
}
}
positionsToSave.add(position);
}
}
Boolean successfulSave = false;
try{
positionDao.save(positionsToSave);
successfulSave = true;
logger.info(positionsToSave.size() + " Position Updates saved successfully");
}catch(Exception exception){
logger.error("Failed to persist latest position updates to database");
logger.error(exception);
}
if(successfulSave){
removeSavedPositions(mostRecentUpdateTime);
PositionPersistEndedNotification ended = new PositionPersistEndedNotification(DateTime.now(DateTimeZone.UTC), mostRecentUpdateTime);
internalEventPublisher.publishPositionPersistEndedNotification(ended);
}
}
}
Any help would be appreciated.
Is the Spring context loaded successfully in runtime? I see you have some inconsistencies in namespace definitions where various versions co-exist in the xsd (3.0 and 3.2). Can you try having consistently the same version in these namespaces and try again?
The application was being started by listening for the context to be loaded and then executing a method upon the ContextRefreshedEvent.
When I removed this ApplicationEventListener from the application and simply invoked a public method on the bean to start the service (instead of relying in the ApplicationEventListener) - the application then started normally with all #Scheduled annotations working as expected.

Use of Spring Framework for RabbitMQ is reducing the performance

I have created a producer, which was using com.rabbitmq.client.connectionFactory and was sending 1,000,000 messages (40 Bytes) in 100 seconds.
But now I want an spring abstraction. I was unable to use com.rabbitmq.client.connectionFactory rather I had to use org.springframework.amqp.rabbit.connection.SingleConnectionFactory. Using this connection factory only 100,000 messages (40 Bytes) are send to the broker in 100 seconds.
Does anybody have experience why the performance is reduced so much (around 90%).
The code using "import com.rabbitmq.client.ConnectionFactory;" is ->
package Multiple_queues_multiple_consumers;
import java.io.IOException;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
public class Producer {
private static Connection myConnection;
private static Channel myChannel;
public static String myQueueName;
public static void main(String[] args) throws IOException {
long startTime=0;
int count=0;
ConnectionFactory myFactory=new ConnectionFactory();
myFactory.setHost("localhost");
try {
myConnection = myFactory.newConnection();
myChannel = myConnection.createChannel();
String myExchange = "wxyzabc";
String myBody = "This is a message : message numberxxxxxx";
String myRoutingKey = "RoutingKey";
myQueueName = "new_Queue";
myChannel.exchangeDeclare(myExchange, "direct", true, false, null);
myChannel.queueDeclare(myQueueName, true, false, false, null);
myChannel.queueBind(myQueueName, myExchange, myRoutingKey);
startTime=System.currentTimeMillis();
AMQP.BasicProperties properties = new AMQP.BasicProperties();
properties.setDeliveryMode(2);
startTime=System.currentTimeMillis();
while(count++<=10000){
myChannel.basicPublish(myExchange, myRoutingKey, true, true, properties, myBody.getBytes() );
}
System.out.println(System.currentTimeMillis()-startTime);
} catch (Exception e){
System.exit(0);
}
}
}
The code using SpringFramework is :->
Producer1.java
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Producer1 {
public static void main(String[] args) {
ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("Producer1.xml");
AmqpAdmin amqpAdmin = context.getBean(RabbitAdmin.class);
Queue queue = new Queue("sampleQueue");
DirectExchange exchange = new DirectExchange("myExchange");
Binding binding = new Binding(queue, exchange, "");
amqpAdmin.declareQueue(queue);
amqpAdmin.declareExchange(exchange);
amqpAdmin.declareBinding(binding);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
String routingKey = "";
String myBody = "This is a message : message numberxxxxxx";
Message Msg = new Message(myBody.getBytes(), null);
int count=0;
long CurrTime = System.currentTimeMillis();
while(count++<=10000){
rabbitTemplate.send(routingKey, Msg);
//System.out.println("Message Sent");
}
System.out.println(System.currentTimeMillis()-CurrTime);
}
}
Producer1.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- Define a connectionFactory -->
<bean id="rabbitConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
<property name="host" value="localhost" />
</bean>
<bean id="connectionFactory" class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory">
<constructor-arg ref="rabbitConnectionFactory"/>
</bean>
<!-- Tell the Admin bean about that connectionFactory and initialize it, create a queue and an exchange on Rabbit Broker using the RabbitTemplate provided by Spring framework-Rabbit APIs -->
<bean id="Admin" class="org.springframework.amqp.rabbit.core.RabbitAdmin">
<constructor-arg ref="connectionFactory" />
</bean>
<bean id="rabbitTemplate" class="org.springframework.amqp.rabbit.core.RabbitTemplate"
p:connectionFactory-ref="connectionFactory"
p:routingKey="myRoutingKey"
p:exchange="myExchange" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Define a connectionFactory -->
<bean id="connectionFactory" class="com.rabbitmq.client.connectionFactory">
<constructor-arg value="localhost" />
<property name="username" value="guest" />
<property name="password" value="guest" />
</bean>
<bean id="Admin" class="org.springframework.amqp.rabbit.core.RabbitAdmin">
<constructor-arg ref="connectionFactory" />
</bean>
</beans>
Using this xml file, the error appears saying org.springframework.amqp.rabbit.core.RabbitAdmin could not cast com.rabbitmq.client.connectionFactory for connectionfactory bean.
The exact error is: "nested exception is java.lang.IllegalStateException: Cannot convert value of type [com.rabbitmq.client.ConnectionFactory] to required type [org.springframework.amqp.rabbit.core.RabbitTemplate]: no matching editors or conversion strategy found" .
Hence i have to use bean:
<bean id="connectionFactory"
class="org.springframework.amqp.rabbit.connection.SingleConnectionFactory">
</bean>
Are you sure that you used the same Rabbit MQ broker? Could you be using a broker on a different server, or an upgraded/downgraded version of RabbitMQ?
The other thing to look at is your jvm. Is it possible that you don't have enough memory configured and now the garbage collector is thrashing? Run top and see if the jvm's memory usage is close to the configured memory size.
Are you using an old version of RabbitMQ. Lots of Linux distros include RabbitMQ 1.7.2 which is an old version that has problems with large numbers of messages. Large is hard to define because it depends on your RAM, but RabbitMQ does not like to use more than 40% of RAM because it needs to copy a persistence transaction log in order to process it and clean it for log rollover. This can cause RabbitMQ to crash, and, of course, processing the huge logs will slow it down. RabbitMQ 2.4.1 handles the persister logs much better, in smaller chunks, and it also has much, much faster message routing code.
This still sounds to me like a Java problem, either Spring is just a pig and is terribly inefficient, or you have not given your jvm enough RAM to avoid frequent gc runs. What setting are you using for -Xmx?

Resources