Exception in combination of #DirtiesContext and FlywayConfig bean in integration test - java

I have a problem when adding new test. And the problem is I think related to #DirtiesContext. I tried removing and adding it but nothing works in combination. Test 1 is using Application Context as well.
the following two are running together and no issue.
Test 1
#ActiveProfiles({"aws", "local"})
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UnauthorizedControllerTest {
private static final Logger LOGGER = LoggerFactory.getLogger(UnauthorizedControllerTest.class);
#Autowired
private TestRestTemplate testRestTemplate;
#LocalServerPort
private int port;
#Autowired
private ApplicationContext context;
private Map<Class<?>, List<String>> excludedMethodsPerController;
#Before
public void setUp() {
excludedMethodsPerController = excludedMethodsPerController();
}
#Test
public void contextStarts() {
assertNotNull(context);
}
#Test
public void controllerCall_WithoutAuthorization_ReturnsUnauthorized() {
Map<String, Object> controllerBeans = context.getBeansWithAnnotation(Controller.class);
for (Object controllerInstance : controllerBeans.values()) {
LOGGER.info("Checking controller {}", controllerInstance);
checkController(controllerInstance);
}
}
public void checkController(Object controllerInstance) {
// Use AopUtils for the case that spring wraps the controller in a proxy
Class<?> controllerClass = AopProxyUtils.ultimateTargetClass(controllerInstance);
Method[] allMethods = controllerClass.getDeclaredMethods();
for (Method method : allMethods) {
LOGGER.info("Checking method: {}", method.getName());
if (!isCallable(controllerClass, method)) {
continue;
}
String urlPrefix = urlPrefix(controllerClass);
Mapping mapping = Mapping.of(method.getAnnotations());
for (String url : mapping.urls) {
for (RequestMethod requestMethod : mapping.requestMethods) {
ResponseEntity<String> exchange = exchange(urlPrefix + url, requestMethod);
String message = String.format("Failing %s.%s", controllerClass.getName(), method.getName());
assertEquals(message, HttpStatus.UNAUTHORIZED, exchange.getStatusCode());
}
}
}
}
private ResponseEntity<String> exchange(String apiEndpoint, RequestMethod requestMethod) {
return testRestTemplate.exchange(url(replacePathVariables(apiEndpoint)), HttpMethod.resolve(requestMethod.name()), null, String.class);
}
private String urlPrefix(Class<?> aClass) {
if (!aClass.isAnnotationPresent(RequestMapping.class)) {
return "";
}
RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
return annotation.value()[0];
}
private String url(String url) {
return "http://localhost:" + port + url;
}
private boolean isCallable(Class<?> controller, Method method) {
return Modifier.isPublic(method.getModifiers())
&& !isExcluded(controller, method)
&& !isExternal(controller);
}
private boolean isExcluded(Class<?> controller, Method method) {
List<String> excludedMethodsPerController = this.excludedMethodsPerController.getOrDefault(controller, new ArrayList<>());
return excludedMethodsPerController.contains(method.getName());
}
private boolean isExternal(Class<?> controller) {
return controller.getName().startsWith("org.spring");
}
private String replacePathVariables(String url) {
return url.replaceAll("\\{[^\\/]+}", "someValue");
}
/**
* There must be a really good reason to exclude the method from being checked.
*
* #return The list of urls that must not be checked by the security
*/
private static Map<Class<?>, List<String>> excludedMethodsPerController() {
Map<Class<?>, List<String>> methodPerController = new HashMap<>();
methodPerController.put(AuthenticationController.class, Collections.singletonList("generateAuthorizationToken"));
methodPerController.put(SystemUserLoginController.class, Arrays.asList("systemUserLogin", "handleException"));
methodPerController.put(ValidationController.class, Collections.singletonList("isValid"));
return methodPerController;
}
}
Test 2
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles({"aws", "local"})
public class RoleAdminControllerAuditTest {
private static final String DOMAIN_NAME = "testDomain";
private static final String APP_NAME_1 = "testApp_1";
private static final String APP_NAME_2 = "testApp_2";
private static final String ROLE_NAME = "testRole";
private static final String USER_NAME = "testUser";
#Autowired
AuditRepository auditRepository;
#Autowired
RoleAdminController roleAdminController;
#MockBean
RoleAdminService roleAdminService;
#MockBean
RoleAdminInfoBuilder infoBuilder;
#MockBean
AppInfoBuilder appInfoBuilder;
#MockBean
BoundaryValueService boundaryValueService;
#MockBean
RoleService roleService;
#MockBean
private SecurityService securityService;
private static final String URS_USER = "loggedInUser";
private static final String BOUNDARY_VALUE_KEY = "11";
private static final String BOUNDARY_VALUE_NAME = "Schulenberg";
private String auditEventDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
#BeforeClass
public static void setupTestEnv() {
// https://github.com/localstack/localstack/issues/592
System.setProperty("com.amazonaws.sdk.disableCbor", "true");
}
#Before
public void setUp() throws Exception {
auditRepository.clean();
when(securityService.getLoggedInUser()).thenReturn(new TestHelper.FakeUser(URS_USER));
//when(roleService.addRoleToApp(any(), any(), eq(ROLE_NAME))).thenReturn(TestHelper.initRole(ROLE_NAME));
when(boundaryValueService.findBoundaryValueById(eq(123L))).thenReturn(initBoundaryValue(BOUNDARY_VALUE_KEY, BOUNDARY_VALUE_NAME));
when(boundaryValueService.findBoundaryValueById(eq(666L))).thenReturn(initBoundaryValue(BOUNDARY_VALUE_KEY, BOUNDARY_VALUE_NAME));
}
#Test
public void addUserAsRoleAdminLogged() throws UserIsAlreadyRoleAdminException, RoleNotFoundException, BoundaryValueNotFoundException {
User user = initUser(USER_NAME);
List<RoleAdminInfo> roleAdminInfos = getRoleAdminInfos();
roleAdminController.addUserAsRoleAdmin(user, roleAdminInfos);
List<String> result = auditRepository.readAll();
assertEquals("some data", result.toString());
}
#Test
public void removeUserAsRoleAdminLogged() throws RoleNotFoundException, BoundaryValueNotFoundException {
User user = initUser(USER_NAME);
Long roleId = Long.valueOf(444);
Role role = initRole("test-role");
role.setApp(initApp("test-app"));
role.setDomain(initDomain("test-domain"));
when(roleService.getRoleByIdOrThrow(roleId)).thenReturn(role);
roleAdminController.removeUserAsRoleAdmin(user, roleId, Long.valueOf(666));
List<String> result = auditRepository.readAll();
assertEquals("some data", result.toString());
}
#Test
public void removeRoleAdminPermission() throws RoleNotFoundException, BoundaryValueNotFoundException {
User user = initUser(USER_NAME);
List<RoleAdminInfo> roleAdminInfos = getRoleAdminInfos();
roleAdminController.removeRoleAdminPermission(user, roleAdminInfos);
List<String> result = auditRepository.readAll();
assertEquals(1, result.size());
assertEquals("some data", result.toString());
}
private List<RoleAdminInfo> getRoleAdminInfos() {
RoleAdminInfo info1 = initRoleAdminInfo(DOMAIN_NAME, ROLE_NAME, APP_NAME_1);
RoleAdminInfo info2 = initRoleAdminInfo(DOMAIN_NAME, ROLE_NAME, APP_NAME_2);
info1.setBoundaryValueId(123L);
info1.setBoundaryValueKey(BOUNDARY_VALUE_KEY);
info1.setBoundaryValueName(BOUNDARY_VALUE_NAME);
return Arrays.asList(info1, info2);
}
}
Test 3 (newly added one)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = FlywayConfig.class)
#DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_CLASS)
#ActiveProfiles({"aws", "local"})
public class BoundaryValueDeltaControllerTest {
private static final String API_V1 = "/api/v1/";
#Autowired
TestRestTemplate testRestTemplate;
#Autowired
private DomainBuilder domainBuilder;
#Autowired
private AppBuilder appBuilder;
#Autowired
private UserBuilder userBuilder;
#Autowired
private DomainAdminBuilder domainAdminBuilder;
#Autowired
private BoundarySetBuilder boundarySetBuilder;
#MockBean
private LoginUserProvider loginUserProvider;
#MockBean
private LoginTokenService loginTokenService;
#MockBean
private BoundaryServiceAdapter serviceAdapter;
#LocalServerPort
private int port;
LoginUserInfo loggedInUser;
#Before
public void setUp() {
clear();
}
#After
public void tearDown() {
clear();
}
#Test
public void updateBoundaryValuesFromApi() throws UrsBusinessException {
Domain domain = domainBuilder.persist();
App app = appBuilder.persist(domain);
BoundarySet boundarySet = boundarySetBuilder.persist(domain);
User user = userBuilder.persist(domain.getAuthor().getUsername());
aLoggedInUser(domain.getAuthor().getUsername());
domainAdminBuilder.persist(user, domain);
mockReadInfoFromApiUsingApp();
ResponseEntity<String> response = callUpdateBoundaryValuesFromApi(domain, boundarySet, app);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertNotNull(response.getBody());
}
private void mockReadInfoFromApiUsingApp() throws UrsBusinessException {
BoundaryValueInfo boundaryValueInfo = new BoundaryValueInfo();
boundaryValueInfo.setBoundaryValueId(10L);
boundaryValueInfo.setBoundaryValueKey("boundaryValueKey");
boundaryValueInfo.setBoundaryValueName("boundaryValuename");
when(serviceAdapter.readInfoFromApiUsingApp(any(), any(), any())).thenReturn(new BoundaryValueInfo[]{boundaryValueInfo});
}
private ResponseEntity<String> callUpdateBoundaryValuesFromApi(Domain domain, BoundarySet boundarySet, App app) {
String url = url(API_V1 + "domains/" + domain.getName() + "/boundarysets/" + boundarySet.getBoundarySetName() + "/app/" + app.getName()+ "/updatefromapi/");
return testRestTemplate.exchange(url,HttpMethod.GET, null, String.class);
}
private String url(String url) {
return "http://localhost:" + port + url;
}
private void aLoggedInUser(String username) {
Claims claims = Jwts.claims();
claims.put("username", username);
loggedInUser = LoginUserInfo.parse(claims);
when(loginUserProvider.getLoggedInUser()).thenReturn(loggedInUser);
when(loginTokenService.parseToken(any())).thenReturn(loggedInUser);
}
private void clear() {
appBuilder.deleteAll();
boundarySetBuilder.deleteAll();
domainAdminBuilder.deleteAll();
domainBuilder.deleteAll();
userBuilder.deleteAll();
}
}
Flyway config
#TestConfiguration
public class FlywayConfig {
#Bean
public FlywayMigrationStrategy clean() {
return flyway -> {
flyway.clean();
flyway.migrate();
};
}
}
And I am getting below exception while running all together.
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java.....
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.internal.exception.FlywaySqlException:
Unable to obtain connection from database: Too many connections
---------------------------------------------------------------
SQL State : 08004
Error Code : 1040
Message : Too many connections
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1762)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515)
... 49 more
Caused by: org.flywaydb.core.internal.exception.FlywaySqlException:
Unable to obtain connection from database: Too many connections
I am struggling since yesterday's and you might find duplicate but I tried to add the more details today. please guide me here.

you must add the configuration for flyway
flyway.url=jdbc:postgresql://xxx.eu-west-2.rds.amazonaws.com:5432/xxx
flyway.user=postgres
flyway.password=xxx

Related

Having issue in testing with spring cloud stream with kafka binding

I have following Binding
public interface KafkaBinding {
String DATA_OUT = "dataout";
#Output(DATA_OUT)
MessageChannel dataOut();
}
Here is kafka utility
#EnableBinding(KafkaBinding.class)
public class KafkaStreamUtil {
private final MessageChannel out;
public KafkaStreamUtil(KafkaBinding binding) {
this.out = binding .dataOut();
}
public void SendToKafkaTopic(List<data> dataList){
dataList
.stream()
.forEach(this::sendMessgae);
}
private void sendMessgae(Data data) {
Message<Data> message = MessageBuilder
.withPayload(data)
.setHeader(KafkaHeaders.MESSAGE_KEY, data.getRequestId().getBytes())
.build();
try {
this.out.send(message);
} catch (Exception e) {
log.error(e.getMessage(),e);
}
}
My test class
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(classes = {KafkaStreamUtil.class,KafkaBinding.class})
#DirtiesContext(classMode= DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class KafkaStreamUtilTest {
private static final String TEST_TOPIC1 = "data";
private static final String GROUP_NAME = "embeddedKafkaApplication";
#ClassRule
public static EmbeddedKafkaRule kafkaRule =
new EmbeddedKafkaRule(1, true, TEST_TOPIC1);
public static EmbeddedKafkaBroker embeddedKafka = kafkaRule.getEmbeddedKafka();
#Autowired
private KafkaStreamUtil kUtil;
#Autowired
private KafkaBinding binding;
#BeforeClass
public static void setupProperties() {
System.setProperty("spring.cloud.stream.kafka.binder.brokers", embeddedKafka.getBrokersAsString());
System.setProperty("spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms", "1000");
System.setProperty("spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde", "org.apache.kafka.common.serialization.Serdes$StringSerde");
System.setProperty("spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde", "org.apache.kafka.common.serialization.Serdes$StringSerde");
System.setProperty("spring.cloud.stream.bindings.dataout.destination", "data");
System.setProperty("spring.cloud.stream.bindings.dataout.producer.header-mode", "raw");
System.setProperty("spring.autoconfigure.exclude","org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration");
System.setProperty("spring.kafka.consumer.value-deserializer","org.apache.kafka.common.serialization.StringDeserializer");
System.setProperty("spring.cloud.stream.bindings.input.consumer.headerMode","raw");
System.setProperty("spring.cloud.stream.bindings.input.group","embeddedKafkaApplication");
System.setProperty("spring.kafka.consumer.group-id","EmbeddedKafkaIntTest");
}
#Before
public void setUp() throws Exception {
kUtil = new KafkaStreamUtil(binding);
}
#Test
public void sendToKafkaTopic() {
kUtil.SendToKafkaTopic(dataList);
Map<String, Object> consumerProps = KafkaTestUtils.consumerProps(GROUP_NAME, "false", embeddedKafka);
consumerProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consumerProps.put("key.deserializer", StringDeserializer.class);
consumerProps.put("value.deserializer", StringDeserializer.class);
DefaultKafkaConsumerFactory<String, String> cf = new DefaultKafkaConsumerFactory<>(consumerProps);
Consumer<String, String> consumer = cf.createConsumer();
consumer.subscribe(Collections.singleton(TEST_TOPIC1));
ConsumerRecords<String, String> records = consumer.poll(10_000);
consumer.commitSync();
consumer.close();
Assert.assertNotNull(records);
}
}
I am unable to get binding with Kafka util in test the binding is always null. Please let know what I am missing. I am able to test with springboottest but it is loading all beans I just want to load necessary componens required for this test.

Embedded Kafka starting with wrong number of partitions

I have started an instance of EmbeddedKafka in a JUnit test. I can read the records that I have pushed to my stream correctly in my application, but one thing I have noticed is that I only have one partition per topic. Can anyone explain why?
In my application I have the following:
List<PartitionInfo> partitionInfos = consumer.partitionsFor(topic);
This returns a list with one item. When running against local Kafka with 3 partitions, it returns a list with 3 items as expected.
And my test looks like:
#RunWith(SpringRunner.class)
#SpringBootTest
#EmbeddedKafka(partitions = 3)
#ActiveProfiles("inmemory")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
#TestPropertySource(
locations = "classpath:application-test.properties",
properties = {"app.onlyMonitorIfDataUpdated=true"})
public class MonitorRestKafkaIntegrationTest {
#Autowired
private EmbeddedKafkaBroker embeddedKafkaBroker;
#Value("${spring.embedded.kafka.brokers}")
private String embeddedBrokers;
#Autowired
private WebApplicationContext wac;
#Autowired
private JsonUtility jsonUtility;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = webAppContextSetup(wac).build();
UserGroupInformation.setLoginUser(UserGroupInformation.createRemoteUser("dummyUser"));
}
private ResultActions interactiveMonitoringREST(String eggID, String monitoringParams) throws Exception {
return mockMvc.perform(post(String.format("/eggs/%s/interactive", eggID)).contentType(MediaType.APPLICATION_JSON_VALUE).content(monitoringParams));
}
#Test
#WithMockUser("super_user")
public void testEmbeddedKafka() throws Exception {
Producer<String, String> producer = getKafkaProducer();
sendRecords(producer, 3);
updateConn();
interactiveMonitoringREST(EGG_KAFKA, monitoringParams)
.andExpect(status().isOk())
.andDo(print())
.andExpect(jsonPath("$.taskResults[0].resultDetails.numberOfRecordsProcessed").value(3))
.andExpect(jsonPath("$.taskResults[0].resultDetails.numberOfRecordsSkipped").value(0));
}
private void sendRecords(Producer<String, String> producer, int records) {
for (int i = 0; i < records; i++) {
String val = "{\"auto_age\":" + String.valueOf(i + 10) + "}";
producer.send(new ProducerRecord<>(testTopic, String.valueOf(i), val));
}
producer.flush();
}
private Producer<String, String> getKafkaProducer() {
Map<String, Object> prodConfigs = new HashMap<>(KafkaTestUtils.producerProps(embeddedKafkaBroker));
return new DefaultKafkaProducerFactory<>(prodConfigs, new StringSerializer(), new StringSerializer()).createProducer();
}
private void updateConn() throws Exception {
String conn = getConnectionREST(CONN_KAFKA).andReturn().getResponse().getContentAsString();
ConnectionDetail connectionDetail = jsonUtility.fromJson(conn, ConnectionDetail.class);
connectionDetail.getDetails().put(ConnectionDetailConstants.CONNECTION_SERVER, embeddedBrokers);
String updatedConn = jsonUtility.toJson(connectionDetail);
updateConnectionREST(CONN_KAFKA, updatedConn).andExpect(status().isOk());
}
}
You need to tell the broker to pre-create the topics...
#SpringBootTest
#EmbeddedKafka(topics = "foo", partitions = 3)
class So57481979ApplicationTests {
#Test
void testPartitions(#Autowired KafkaAdmin admin) throws InterruptedException, ExecutionException {
AdminClient client = AdminClient.create(admin.getConfig());
Map<String, TopicDescription> map = client.describeTopics(Collections.singletonList("foo")).all().get();
System.out.println(map.values().iterator().next().partitions().size());
}
}
Or set the num.partitions broker property if you want the broker to auto-create the topics for you on first use.
We should probably automatically do that, based on the partitions property.
I found bootstrapServersProperty is important in #EmbeddedKafka, which is used to populate the property in the application-test.yml, which then can be used to create consumers/listener containers.

Override Ribbon Server List to get a list of host names from consul

I am trying to override Ribbon Server List to get a list of host names from consul. I have the consul piece working properly(when testing with hardcode values) to get the hostname and port for a service. The issue I am having is when I try to autowire in IClientConfig. I get an exception that IClientConfig bean could not be found. How do I override the ribbon configurations and autowire IClientConfig in the ribbonServerList method.
I have tried following the instructions here at http://projects.spring.io/spring-cloud/spring-cloud.html#_customizing_the_ribbon_client on how to customize ribbon client configuration. I keep getting the following error:
Description:
Parameter 0 of method ribbonServerList in com.intradiem.enterprise.keycloak.config.ConsulRibbonSSLConfig required a bean of type 'com.netflix.client.config.IClientConfig' that could not be found.
Which is causing spring-boot to fail.
Bellow are the classes that I am trying to use to create
AutoConfiguration Class:
#Configuration
#EnableConfigurationProperties
#ConditionalOnBean(SpringClientFactory.class)
#ConditionalOnProperty(value = "spring.cloud.com.intradiem.service.apirouter.consul.ribbon.enabled", matchIfMissing = true)
#AutoConfigureAfter(RibbonAutoConfiguration.class)
#RibbonClients(defaultConfiguration = ConsulRibbonSSLConfig.class)
//#RibbonClient(name = "question-answer-provider", configuration = ConsulRibbonSSLConfig.class)
public class ConsulRibbonSSLAutoConfig
{
}
Configuration Class:
#Component
public class ConsulRibbonSSLConfig
{
#Autowired
private ConsulClient client;
private String serviceId = "client";
public ConsulRibbonSSLConfig() {
}
public ConsulRibbonSSLConfig(String serviceId) {
this.serviceId = serviceId;
}
#Bean
#ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig clientConfig) {
ConsulSSLServerList serverList = new ConsulSSLServerList(client);
serverList.initWithNiwsConfig(clientConfig);
return serverList;
}
}
ServerList Code:
public class ConsulSSLServerList extends AbstractServerList<Server>
{
private final ConsulClient client;
private String serviceId = "client";
public ConsulSSLServerList(ConsulClient client) {
this.client = client;
}
#Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.serviceId = clientConfig.getClientName();
}
#Override
public List<Server> getInitialListOfServers() {
return getServers();
}
#Override
public List<Server> getUpdatedListOfServers() {
return getServers();
}
private List<Server> getServers() {
List<Server> servers = new ArrayList<>();
Response<QueryExecution> results = client.executePreparedQuery(serviceId, QueryParams.DEFAULT);
List<QueryNode> nodes = results.getValue().getNodes();
for (QueryNode queryNode : nodes) {
QueryNode.Node node = queryNode.getNode();
servers.add(new Server(node.getMeta().containsKey("secure") ? "https" : "http", node.getNode(), queryNode.getService().getPort()));
}
return servers;
}
#Override
public String toString() {
final StringBuilder sb = new StringBuilder("ConsulSSLServerList{");
sb.append("serviceId='").append(serviceId).append('\'');
sb.append('}');
return sb.toString();
}
}

No transaction is currently active using #EnableTransactionManagement

First the stacktrace:
org.springframework.dao.InvalidDataAccessApiUsageException:
Exception Description: No transaction is currently active; nested exception is javax.persistence.TransactionRequiredException:
Exception Description: No transaction is currently active
org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:316)
org.springframework.orm.jpa.DefaultJpaDialect.translateExceptionIfPossible(DefaultJpaDialect.java:121)
org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:91)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
com.sun.proxy.$Proxy299.saveAndFlush(Unknown Source)
I'm pretty sure it's a stupid error but I can't seem to find what I am doing wrong:
The #Service:
#Service
public class BookInfoTracker implements InfoTracker
{
#Autowired
#Qualifier("bookTrackerRepository")
private TrackerRepository bookTrackerRepository;
public BookInfoTracker() {}
public BookInfoTracker (TrackerRepository bookTrackerRepository)
{
this.bookTrackerRepository = bookTrackerRepository;
}
#Transactional
#Override
public void track (long idBooking, String idLeg, String bookingEmail, int sequence, String event)
{
BookInfo ci = new BookInfo(idBooking, idLeg, bookingEmail, new Date(), sequence, event);
bookTrackerRepository.saveAndFlush(ci);
}
#Override
public int getSequenceFrom (long idBooking, String idLeg)
{
BookInfo tracked = findLastTrackedBookFrom(bookTrackerRepository.getLastTrackedBookByIdBookingAndIdLeg(idBooking, idLeg));
return null == tracked? 0 : tracked.getSequence()+1;
}
private BookInfo findLastTrackedBookFrom (List<BookInfo> trackedBooks)
{
return trackedBooks.isEmpty() ? null : trackedBooks.get(0);
}
}
There is a base class for jpa configuration:
#EnableLoadTimeWeaving(aspectjWeaving=AspectJWeaving.ENABLED)
public abstract class JpaConfiguration
{
public JpaConfiguration ()
{
super();
}
public abstract LocalContainerEntityManagerFactoryBean buildEntityManagerFactory () throws SQLException;
#Bean(name = "transactionManager")
public PlatformTransactionManager buildTransactionManager () throws SQLException
{
JpaTransactionManager manager = new JpaTransactionManager();
LocalContainerEntityManagerFactoryBean factory = buildEntityManagerFactory();
manager.setEntityManagerFactory(factory.getObject());
return manager;
}
protected Database toDatabase (String databaseProductName)
{
for (Database database : Database.values())
if (databaseProductName.equalsIgnoreCase(database.toString())) return database;
return null;
}
protected LocalContainerEntityManagerFactoryBean initializeFactory (DataSource datasource, String persistenceUnitName, String... packagesToScan) throws SQLException
{
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setDataSource(datasource);
factory.setPersistenceUnitName(persistenceUnitName);
factory.setPackagesToScan(packagesToScan);
EclipseLinkJpaVendorAdapter jpaAdaptor = new EclipseLinkJpaVendorAdapter();
jpaAdaptor.setDatabase(toDatabase(datasource.getConnection().getMetaData().getDatabaseProductName()));
jpaAdaptor.setShowSql(true);
factory.setJpaVendorAdapter(jpaAdaptor);
Properties jpaProperties = new Properties();
jpaProperties.put("eclipselink.ddl-generation", "none");
jpaProperties.put("eclipselink.ddl-generation.output-mode", "database");
jpaProperties.put("eclipselink.logging.level.sql", "FINE");
jpaProperties.put("eclipselink.logging.parameters", "true");
jpaProperties.put("eclipselink.cache.shared.default", "false");
jpaProperties.put("eclipselink.target-database", "MySQL");
jpaProperties.put("eclipselink.weaving","false");
factory.setJpaProperties(jpaProperties);
factory.afterPropertiesSet();
return factory;
}
}
and then the specific configuration, a subclass:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages = {"com.book.tracking.repositories"}, entityManagerFactoryRef="bookManagerFactory")
#Profile({"integration","test","release"})
public class JpaBookConfiguration extends JpaConfiguration
{
#Autowired
#Qualifier("bookDs")
private DataSource datasource;
#Override
#Bean(name = "bookManagerFactory")
public LocalContainerEntityManagerFactoryBean buildEntityManagerFactory () throws SQLException
{
return initializeFactory(datasource, "book.jpa", "com.book.tracking.entity");
}
}
then the restservice:
#Controller
#ControllerAdvice
#RequestMapping("/bookservice")
public class BookRestService implements IBookService
{
private static final String LEG_VALID_PATTERN = "^(20)\\d\\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])_[A-Z]{3}-[A-Z]{3}$";
#Autowired
#Qualifier("bookBuilder")
private IBookBuilder bookBuilder;
#Autowired
#Qualifier("bookInfoTracker")
private InfoTracker bookInfoTracker;
#Autowired
#Qualifier("bookBookingEventsGenerator")
private BookingEventsGenerator bookBookingEventsGenerator;
public BookRestService (IBookBuilder bookBuilder,
InfoTracker infoTracker, BookingEventsGenerator bookBookingEventsGenerator)
{
this.bookBookingEventsGenerator = bookBookingEventsGenerator;
this.bookBuilder = bookBuilder;
this.bookInfoTracker = infoTracker;
}
public BookRestService ()
{
}
#Override
#RequestMapping(value="/generate/{idBooking}/{idLeg}")
public ResponseEntity<String> generateOneWayEvent (#PathVariable("idBooking") final long idBooking, #PathVariable("idLeg") final String idLeg)
{
return executeEventCommand(new EventCommand() {
#Override
public BookingEvent execute() {
BookingEvent event = bookBookingEventsGenerator.generateEventFrom(idBooking, idLeg);
return event;
}
}, idBooking, idLeg, "PUBLISH");
}
private ResponseEntity<String> executeEventCommand (EventCommand eventCommand, long idBooking, String idLeg, String operation)
{
try {
validate(idLeg);
logFor(idBooking, idLeg, "generateOneWayEvent");
HttpHeaders headers = modifyResponseAccordingTo(idLeg);
BookingEvent event = eventCommand.execute();
int sequence = bookInfoTracker.getSequenceFrom(idBooking, event.getBookingIdentifier());
String bookIcs = generateBookFrom(event, sequence);
logger.debug(bookInfoTracker.getClass());
//exception here
bookInfoTracker.track(idBooking, event.getBookingIdentifier(), bookBookingEventsGenerator.getContactEmail(), sequence, operation);
return new ResponseEntity<String>(bookIcs, headers, HttpStatus.OK );
} catch(LegNotFoundException lnfe){
logger.error(lnfe);
return new ResponseEntity<String>("Something went wrong", HttpStatus.NOT_FOUND);
}
}
/** other methods **/
}
It seems like it's not considering #Transactional annotation maybe? But I used #EnableTransactionManagement. Can you see something wrong in my configuration?
UPDATE:
#Configuration
#Profile({"integration", "release"})
public class IBookSpringConfiguration
{
#Autowired
#Qualifier("bookRestService")
private IBookService bookRestService;
#Autowired
#Qualifier("bookTrackerRepository")
private TrackerRepository bookTrackerRepository;
public ReloadableResourceBundleMessageSource getMessageBundle(){
ReloadableResourceBundleMessageSource bundle = new ReloadableResourceBundleMessageSource();
bundle.setBasename("book-message");
return bundle;
}
#Bean(name = "bookInfoTracker")
public InfoTracker getBookTrackingService(){
return new BookInfoTracker(bookTrackerRepository);
}
#Bean(name = "bookBuilder")
public IBookBuilder getBookBookingService(){
return new BookBuilder(getMessageBundle());
}
#Bean(name = "i18n")
public I18nUtilACL getI18n(){
return new I18nUtilBook(MultiSourceSiteCustomizer.getInstance());
}
#Bean(name = "bookBookingEventsGenerator")
public BookingEventsGenerator getBookBookingEventsGenerator(){
return new BookEventsGenerator();
}
#Bean(name = "bookBookingLegFactory")
public BookingLegEventsGenerator getBookBookingLegFactory(){
return new BookBookingLegEventsGenerator();
}
#Bean(name = "bookBookingFlightEventsGenerator")
public BookingEventsGenerator getBookBookingFlightEventsGenerator(){
return new BookEventsGenerator();
}
#Bean(name = "bookRestService")
#Scope("singleton")
public IBookService getBookRestService ()
{
return new BookRestService();
}
}
SOLVED BUT DON'T KNOW WHY:
I just merge parent and subclass of the JpaConfiguration in JpaBookConfiguration. Now it's working. Don't know why. Can you explain?

Play2 Java - InvalidPropertyException when testing controller with form

I'd like to test a controller method named authenticate(), which has very simple logic: validating email and password from request and returning the result as JSON.
public class Users extends Controller {
static Form<User> userForm = Form.form(User.class);
public static Result login() {
return ok(views.html.users.login.render(userForm));
}
public static Result authenticate() {
Form<User> filledForm = userForm.bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(views.html.users.login.render(filledForm));
} else {
ObjectNode result = Json.newObject();
User u = filledForm.get();
if (User.isAuthValid(u.email, u.password))
result.put("status", "OK");
else
result.put("status", "Authentication failed");
return ok(result);
}
}
}
Following is the test code for authenticate():
#Test
public void callAuthenticate() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
But I got an error with following stacktrace:
javax.validation.ValidationException: HV000041: Call to TraversableResolver.isReachable() threw an exception.
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1230)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:438)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:387)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:351)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:303)
at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:133)
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:194)
at play.data.Form.bind(Form.java:327)
at play.data.Form.bindFromRequest(Form.java:215)
at controllers.Users.authenticate(Users.java:50)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at play.core.Router$HandlerInvoker$$anon$6$$anon$2.invocation(Router.scala:164)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:31)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:74)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:73)
at play.libs.F$Promise$PromiseActor.onReceive(F.java:420)
at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:159)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:425)
at akka.actor.ActorCell.invoke(ActorCell.scala:386)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:230)
at akka.dispatch.Mailbox.run(Mailbox.scala:212)
at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:502)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'email' of bean class [models.User]: No property 'email' found
at org.springframework.beans.BeanWrapperImpl.getPropertyDescriptor(BeanWrapperImpl.java:337)
at play.db.ebean.Model._idAccessors(Model.java:47)
at play.db.ebean.Model._getId(Model.java:67)
at play.db.ebean.Model.hashCode(Model.java:208)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.buildHashCode(SingleThreadCachedTraversableResolver.java:153)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:114)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:96)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:41)
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1221)
... 26 more
When testing on the browser, it worked as expected. But only JUnit test fails with InvalidPropertyException. What's wrong with my test code?
FYI, here's the model User:
#Entity
public class User extends Model {
#Id
#Required
#NonEmpty
public String email;
public String nickname;
#Required
public String password;
public String salt;
public static Finder<String, User> find = new Finder<String, User>(
String.class, User.class);
public static User findByEmail(String email) {
return find.where().eq("email", email).findUnique();
}
public static boolean isAuthValid(String email, String password) {
User user = findByEmail(email);
if (user == null)
return false;
return user.isValidPassword(password);
}
public boolean isValidPassword(String password) {
return this.password.equals(DigestUtils.md5Hex(password + this.salt));
}
}
Thanks for any advices/corrections.
You need to run the test inside a "Fake" application, so the binding can work. So, you're Test will look like this:
#Test
public void callAuthenticate() {
running(fakeApplication(), new Runnable() {
public void run() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
}
}

Categories