Using mocked dataSource bean when testing Camel route - spring-boot

I have a dataSource bean defined in spring xml as follows
<bean id="apacheBasicDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close" >
<property name="url" value="jdbc:oracle:thin:#(DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521))(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = myservice)))" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
...
</bean>
Now I want to test a rest route which do his job by calling stored procedure within a groovy script
<get uri="/utils/foo" produces="text/xml">
<route id="utils.foo">
<setBody>
<groovy>
import groovy.sql.Sql
def sql = Sql.newInstance(camelContext.getRegistry().lookupByName('apacheBasicDataSource'))
res = request.getHeader('invalues').every { invalue ->
sql.call("{? = call my_pkg.mapToSomeValue(?)}", [Sql.VARCHAR, invalue]) {
outValue -> do some stuff..
}
}
sql.close()
new StringBuilder().append(
some stuff..
)
</groovy>
</setBody>
</route>
</get>
I have my test class as follows, so now it works
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = {"classpath:camel.xml"})
public class EdiapiApplicationNewTest {
#Autowired
private CamelContext context;
#MockBean private DataSource dataSource;
#Mock private CallableStatement callableStatement;
#Mock private Connection connection;
#Mock private ResultSet resultSet;
#Test
public void testSimple() throws Exception {
when(callableStatement.getResultSet()).thenReturn(resultSet);
when(dataSource.getConnection()).thenReturn(connection);
when(connection.prepareCall(anyString())).thenReturn(callableStatement);
context.getRegistry().bind("apacheBasicDataSource", dataSource);
TestRestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:8080/utils/foo?invalues=foo,bar", String.class);
Assert.assertEquals("<value>false</value>", response.getBody());
}
}
But I want to be able to populate mocked ResultSet with some custom value to test against (at the moment it returns null)
I have tried something like
when(resultSet.next()).thenReturn(true).thenReturn(false);
when(resultSet.getString(anyInt())).thenReturn("some value");
With no success. Could someone give me a hint about it?
Thanks!

Finally I found the roots of the issue as well as a solution.
First, void call method should be mocked with doAnswer and not with thenReturn (obviously).
Second, I dealt with stored function, so there was no getResultSet method invoking on callableStatement, instead the getObject method was invoked, so this is the one should be mocked as
when(callableStatement.getObject(1)).thenAnswer(inv -> "some custom value");

Related

Hibernate ClassCastException when retrieving a class from query.getResultList

I created a small SpringBoot App which is used together with Hibernate to work with our Oracle Databse.
But I ran into the following Problem:
Whenever I load an Object from the database with SessionFactory.createQuery() and then query.getResultList(). I do get a List of Results with the correct Class annotations (when looking at the code in debug mode). But I cannot do MyClass x = list.get(0), even though the list is a List of MyClass. I can only get an Object but cannot "cast" to the correct class.
Uidnr is a simple Class with no join tables or any other dependencies on other tables form the database. Its only BigDecimal's, String's and Timestamp's.
Here is the code and configs of everything:
POST Endpoint:
#RequestMapping(value = "/e10", method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public String e10() {
Database db = new Database();
List<Uidnr> bel = db.getUidnrList(new BigDecimal("1009316"));
Uidnr element = bel.get(0); //CLASSCASTEXCEPTION HERE!!!
return element.getNr();
}
Method to get the Class from Database:
public List<Uidnr> getUidnrList(BigDecimal id) {
SessionFactory factory = null;
List<Uidnr> uidnr = new ArrayList<Uidnr>();
Session session = null;
try {
factory = getSessionFactory();
session = factory.openSession();
Query<Uidnr> query = session.createQuery("from Uidnr where adrid=:sblid", Uidnr.class);
query.setParameter("sblid", id);
uidnr = query.getResultList(); //This is a List<Uidnr
} catch (HibernateException ex) {
logger.error("Error loading Sendungen.", ex);
} finally {
close(factory, session);
}
return uidnr;
}
SessionFactory:
private SessionFactory getSessionFactory() {
SessionFactory sf = null;
File optextconf = new File("conf/hibernate.cfg.xml");
Configuration c = new Configuration();
if (optextconf.exists()) {
c.configure(optextconf);
c.addResource("eu/lbase/invsvc/app/model/internal/Uidnr.hbm.xml");
sf = c.buildSessionFactory();
logger.info("Session factory loaded from external file {}.", optextconf.getAbsolutePath());
} else {
logger.error("Configuration {} not found. Can not connect to database.", optextconf.getAbsolutePath());
}
return sf;
}
Uidnr.hbm.xml file located under src/main/resources, in a package called eu.lbase.invsvc.app.model.internal:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="eu.lbase.invsvc.app.model.internal.Uidnr" table="SUID_UIDNR">
<composite-id>
<key-property name="adrid" column="UID_ADRID" />
<key-property name="staid" column="UID_STAID" />
</composite-id>
<property name="nr" column="UID_NR"/>
<property name="stnr" column="UID_STNR"/>
<property name="deflt" column="UID_DEFLT"/>
<property name="aend" column="UID_AEND"/>
<property name="usrid" column="UID_USRID"/>
<property name="stktonr" column="UID_STKTONR"/>
<property name="zoktonr" column="UID_ZOKTONR"/>
<property name="vollmacht" column="UID_VOLLMACHT"/>
</class>
</hibernate-mapping>
Exception:
java.lang.ClassCastException: eu.lbase.invsvc.app.model.internal.Uidnr cannot be cast to eu.lbase.invsvc.app.model.internal.Uidnr
at eu.lbase.invsvc.app.controller.WebController.e10(WebController.java:91) ~[main/:na]
Some Tests:
System.out.println(bel.getClass()); //class java.util.ArrayList
Object test = bel.get(0);
System.out.println(test.getClass()); //class eu.lbase.invsvc.app.model.internal.Uidnr
System.out.println(bel.get(0) instanceof eu.lbase.invsvc.app.model.internal.Uidnr); //false

New database user instance created when Spring JDBC Template use calling Procedure

I have using Spring Mvc Jdbc template for calling procedures and functions.But the database connection is not close after the function call.Every time new database user instance create.Could anyone please give me the solution for solving this big problem.
#Autowired
#Qualifier("dbDataSource")
public DataSource dataSource;
public SimpleJdbcCall procReadData;
public PersonDTO readPersonData(Principal principal) throws SQLException {
List<PersonDTO> personDTOList = null;
Map<String,Object> results = null;
procReadData = new SimpleJdbcCall(dataSource).withProcedureName("GET_PAWS_PERSON_DETAILS");
procReadData.addDeclaredParameter(new SqlParameter("AV_USER_NAME", OracleTypes.VARCHAR));
procReadData.addDeclaredParameter( new SqlOutParameter( "CUR_GENERIC", OracleTypes.CURSOR,new PersonRowMapper()));
SqlParameterSource in = new MapSqlParameterSource().addValue("AV_USER_NAME", principal.getName());
results = procReadData.execute(in);
Set<String> keys = results.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = (String) iterator.next();
personDTOList = (List<PersonDTO>) results.get(key);
}
return personDTOList.get(0);
}
Database Configuration:
<Resource driverClassName="oracle.jdbc.OracleDriver" maxActive="300" maxIdle="100" maxWait="5000" name="jdbc/epaws" global="jdbc/epaws"
password="polusneu" type="javax.sql.DataSource" url="jdbc:oracle:thin:#192.168.1.60:1521:coeusnew"
username="polusneu" validationQuery="select 1 from dual"/>
applicationcontext.xml configuration
<bean id="dbDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/epaws"/>
<property name="resourceRef" value="true"/>
</bean>

Soap Fault Message Resolver isn't invoked after adding Wss4jSecurityInterceptor config

I have written a web service client (using Java Spring and JAXB Marshaller) that works with a 3rd party web service. When I send a valid request everything works well. When I send an invalid request then the web service server responds with a SOAP Fault. The client application just fails with a UnmarshallingFailureException
org.springframework.oxm.UnmarshallingFailureException: JAXB unmarshalling
exception; nested exception is javax.xml.bind.UnmarshalException:
unexpected element (uri:"http://schemas.xmlsoap.org/soap/envelope/", local:"Fault").
Appears to me that my ws client isn't able to decipher the SOAP fault returned by the web service. I wrote a custom FaultMessageResolver, but it doesn't get invoked (I set a breakpoint there but it doesn't hit. The FaultMessageResolver just worked fine before I added the Wss4jSecurityInterceptor for signature, encryption/decryption stuff). Here's the code:
public class VehicleServiceClientExceptionResolver implements FaultMessageResolver {
#Override
public void resolveFault(WebServiceMessage message) throws IOException {
SoapMessage soapMessage = (SoapMessage) message;
try {
JAXBContext context = JAXBContext.newInstance(ErrorMessages.class);
Unmarshaller unMarshaller = context.createUnmarshaller();
ErrorMessages errorMessages = (ErrorMessages)unMarshaller.unmarshal(soapMessage.getSoapBody().getFault().getFaultDetail().getDetailEntries().next().getSource());
if (errorMessages.getErrorMessage().size() > 0) {
throw new VehicleServiceClientException(errorMessages);
}
} catch (JAXBException e) {
LOGGER.debug(e.getMessage());
}
}
}
And this custom soap fault resolver is injected into client side web service template like below:
<bean id="vehicleQuotationWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate">
<constructor-arg ref="messageFactory"/>
<property name="interceptors">
<list>
<ref bean="wsSecurityInterceptor"/>
</list>
</property>
<property name="marshaller" ref="vehicleQuotationMarshaller" />
<property name="unmarshaller" ref="vehicleQuotationMarshaller" />
<property name="messageSender" ref="urlMessageSender"/>
<property name="faultMessageResolver" ref="vehicleServiceClientFaultMessageResolver" />
<property name="defaultUri" value="https://*********/*********Service"/>
</bean>
The most weird thing is although I got that unmarshall exception, I did see the encrypted server response was decrypted in my eclipse console when I change the log level from INFO to DEBUG, I am not sure where this DigesterOutputStream comes from, but I think it might be the key to solve this.
Anyone got any idea? Thanks!
DEBUG p.xml.dsig.internal.DigesterOutputStream:
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Id-af090516-9e00-4590-b481-c78e59d6b2fc"><soapenv:Fault><faultcode>soapenv:Client.Validation</faultcode><faultstring</faultstring><detail><em:ErrorMessages xmlns:em="urn:ford/errormessage/v1.0"><em:ErrorMessage><em:ErrorCode>GLSE903100</em:ErrorCode><em:ErrorDescription> CTT System Quote Id already exists ('1041')</em:ErrorDescription><em:ErrorTime>2014-05-16T15:13:20</em:ErrorTime></em:ErrorMessage></em:ErrorMessages></detail></soapenv:Fault></soapenv:Body>
I found the solution here: Adding a WebServiceMessageExtractor<Object> to:
WebServiceTemplate.sendAndReceive(
new WebServiceMessageCallback(),
new WebServiceMessageExtractor<Object>())
does the trick.
Another solution:
public class ExampleInterceptor implements ClientInterceptor {
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
var resp = (SoapMessage) messageContext.getResponse();
Optional.of(resp)
.filter(res -> !hasFault(res))
.orElseThrow(() -> new SoapFaultClientException(resp));
return true;
}
private boolean hasFault(final WebServiceMessage response) {
return Optional.ofNullable(response)
.filter(resp -> resp instanceof FaultAwareWebServiceMessage)
.map(resp -> (FaultAwareWebServiceMessage) resp)
.map(FaultAwareWebServiceMessage::hasFault)
.orElse(false);
}
}
#Configuration
public class ExampleConnectorConfig extends WSConnectorConfig
#Bean
public WSConnector soapConnector(Jaxb2Marshaller marshaller) {
var client = new WSConnector(messageFactory());
client.setInterceptors(new ClientInterceptor[]{new ExampleInterceptor()});
client.setDefaultUri(proxy);
return client;
}
//Example
#Bean
public SaajSoapMessageFactory messageFactory() {
SaajSoapMessageFactory messageFactory = new SaajSoapMessageFactory();
messageFactory.afterPropertiesSet();
return messageFactory;
}
}

JMS Rollback & redelivery not honoring the RedeliveryDelay configuration

I would like to have my Camel routes transactional with ActiveMQ. Rollback and maximum re-deliveries work fine, but not re-delivery delay, which should be incremental.
For example, when I failed to process message (raising an exception), it's redelivered 3 times (as expected), but with no time between it (which is not).
My Spring configuration:
<context:annotation-config/>
<context:component-scan base-package="fr.dush.poc.springplaceholder"/>
<spring:camelContext>
<spring:package>fr.dush.poc.springplaceholder.routes</spring:package>
<spring:contextScan/>
</spring:camelContext>
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
</bean>
<bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager"/>
</bean>
<bean id="PROPAGATION_REQUIRES_NEW" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager"/>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRES_NEW"/>
</bean>
Spring configuration continue in configuration bean:
#Component
public class CamelFactories {
private static final Logger LOGGER = LoggerFactory.getLogger(CamelFactories.class);
public static final int REDELIVERY_DELAY = 1000;
public static final int BACK_OFF_MULTIPLIER = 2;
public static final int HOUR = 3600000;
public static final int MAXIMUM_REDELIVERY_DELAY = 2 * HOUR;
public static final int MAXIMUM_REDELIVERIES = 3;
#Bean(name = "jmsConnectionFactory")
public ActiveMQConnectionFactory createFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory();
factory.setBrokerURL("tcp://localhost:61616");
RedeliveryPolicy policy = new RedeliveryPolicy() {
#Override
public long getNextRedeliveryDelay(long previousDelay) {
long nextDelay = super.getNextRedeliveryDelay(previousDelay);
LOGGER.warn("Previous delay={} ; This delay={} ", previousDelay, nextDelay);
return nextDelay;
}
};
policy.setMaximumRedeliveries(MAXIMUM_REDELIVERIES);
policy.setRedeliveryDelay(REDELIVERY_DELAY);
policy.setBackOffMultiplier(BACK_OFF_MULTIPLIER);
policy.setUseExponentialBackOff(true);
policy.setMaximumRedeliveryDelay(MAXIMUM_REDELIVERY_DELAY);
factory.setRedeliveryPolicy(policy);
return factory;
}
#Bean(name = "activemq")
public JmsComponent createJmsComponent(JmsTransactionManager transactionManager,
ActiveMQConnectionFactory connectionFactory) {
ActiveMQComponent component = new ActiveMQComponent();
component.setTransactionManager(transactionManager);
component.setConnectionFactory(connectionFactory);
component.setTransacted(true);
return component;
}
My route is quite simple:
public class CamelRouteBuilder extends SpringRouteBuilder {
#Override
public void configure() throws Exception {
Policy required = getApplicationContext().getBean("PROPAGATION_REQUIRED",
SpringTransactionPolicy.class);
from("activemq:queue:foo.bar")
.transacted()
.policy(required)
.log(LoggingLevel.INFO, "fr.dush.poc", "Receive message: ${body}")
.beanRef("serviceBean") // throw an exception
.to("mock:routeEnd");
}
}
And in my logs, I have this, 3 times with previous delay=0:
CamelFactories:36 - Previous delay=0 ; This delay=1000
It seems I'm not alone to have this issue, but I still didn't find solution...
Thanks,
-Dush
This is possibly resolved by setting cacheLevelName=CACHE_CONSUMER on the ActiveMQComponent. I had the same symptoms & this resolved it for me. On a related note, I also get out of order delivery of messages with a transacted component, unless I use CACHE_CONSUMER.
I still didn't find solution. But I found an alternative: retry API from CAMEL itself.
Configuration is very similar. Spring config example:
<redeliveryPolicyProfile id="infiniteRedeliveryPolicy"
asyncDelayedRedelivery="true"
redeliveryDelay="${camel.redelivery_delay}"
maximumRedeliveryDelay="${camel.maximum_redelivery_delay}"
maximumRedeliveries="${camel.infinite_redelivery}"
backOffMultiplier="${camel.back_off_multiplier}"
useExponentialBackOff="true"/>
<routeContext>
<route>
<!-- ... -->
<!-- Define behaviour in case of technical error -->
<onException redeliveryPolicyRef="infiniteRedeliveryPolicy">
<exception>java.lang.Exception</exception>
<handled>
<constant>false</constant>
</handled>
<log message="Message can't be processed for now. I'll retry later!" />
</onException>
</route>
</routeContext>
Consumers should be transactional if you want to keep not processed messages in the ActiveMQ queue, even if you shut down application.

Spring: import a module with specified environment

Is there anything that can achieve the equivalent of the below:
<import resource="a.xml">
<prop name="key" value="a"/>
</import>
<import resource="a.xml">
<prop name="key" value="b"/>
</import>
Such that the beans defined in resouce a would see the property key with two different values? The intention would be that this would be used to name the beans in the imports such that resource a.xml would appear:
<bean id="${key}"/>
And hence the application would have two beans named a and b now available with the same definition but as distinct instances. I know about prototype scope; it is not intended for this reason, there will be many objects created with interdepednencies that are not actually prototypes. Currently I am simply copying a.xml, creating b.xml and renaming all the beans using the equivalent of a sed command. I feel there must be a better way.
I suppose that PropertyPlaceholderConfigurers work on a per container basis, so you can't achieve this with xml imports.
Re The application would have two beans named a and b now available with the same definition but as distinct instances
I think you should consider creating additional application contexts(ClassPathXmlApplicationContext for example) manually, using your current application context as the parent application context.
So your many objects created with interdependencies sets will reside in its own container each.
However, in this case you will not be able to reference b-beans from a-container.
update you can postprocess the bean definitions(add new ones) manually by registering a BeanDefinitionRegistryPostProcessor specialized bean, but this solution also does not seem to be easy.
OK, here's my rough attempt to import xml file manually:
disclaimer: I'm very bad java io programmer actually so double check the resource related code :-)
public class CustomXmlImporter implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
private Map<String, String> properties;
public void setProperties(Map<String, String> properties) {
this.properties = properties;
}
public Map<String, String> getProperties() {
return properties;
}
private void readXml(XmlBeanDefinitionReader reader) {
InputStream inputStream;
try {
inputStream = new ClassPathResource(this.classpathXmlLocation).getInputStream();
} catch (IOException e1) {
throw new AssertionError();
}
try {
Scanner sc = new Scanner(inputStream);
try {
sc.useDelimiter("\\A");
if (!sc.hasNext())
throw new AssertionError();
String entireXml = sc.next();
PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${",
"}", null, false);
Properties props = new Properties();
props.putAll(this.properties);
String newXml = helper.replacePlaceholders(entireXml, props);
reader.loadBeanDefinitions(new ByteArrayResource(newXml.getBytes()));
} finally {
sc.close();
}
} finally {
try {
inputStream.close();
} catch (IOException e) {
throw new AssertionError();
}
}
}
private String classpathXmlLocation;
public void setClassPathXmlLocation(String classpathXmlLocation) {
this.classpathXmlLocation = classpathXmlLocation;
}
public String getClassPathXmlLocation() {
return this.classpathXmlLocation;
}
#Override
public void postProcessBeanDefinitionRegistry(
BeanDefinitionRegistry registry) throws BeansException {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(registry);
readXml(reader);
}
}
XML configuration:
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="a" />
</map>
</property>
</bean>
<bean class="CustomXmlImporter">
<property name="classPathXmlLocation" value="a.xml" />
<property name="properties">
<map>
<entry key="key" value="b" />
</map>
</property>
</bean>
this code loads the resources from classpath. I would think twice before doing something like that, anyway, you can use this as a starting point.

Resources