I had add #Transactional on the method in service layer.
#Transactional(readOnly = false)
public void add(UserFollow uf){
UserFollow db_uf = userFollowRepository.findByUserIdAndFollowUserId(uf.getUserId(), uf.getFollowUserId());
if(db_uf == null) {
userFollowRepository.save(uf);
userCountService.followInc(uf.getFollowUserId(), true);
userCountService.fansInc(uf.getUserId(), true);
throw new RuntimeException();// throw an Exception
}
}
userFollowRepository.save(uf); still save seccessful,doesn't rollback...
i enable transaction manager on the Application.
#Configuration
#ComponentScan
#EnableAutoConfiguration
#EnableJpaRepositories
#EnableTransactionManagement
public class Application {
#Bean
public AppConfig appConfig() {
return new AppConfig();
}
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
i move #Transactional to Control layer, it works, the code:
#Transactional
#RequestMapping(value="following", method=RequestMethod.POST)
public MyResponse follow(#RequestBody Map<String, Object> allRequestParams){
MyResponse response = new MyResponse();
Integer _userId = (Integer)allRequestParams.get("user_id");
Integer _followUserId = (Integer)allRequestParams.get("follow_user_id");
userFollowService.add(_userId, _followUserId); //this will throw an exception, then rollback
return response;
}
can anyone tell me reason, thanks!
According to http://spring.io/guides/gs/managing-transactions/
#EnableTransactionManagement activates Spring’s seamless transaction features, which makes #Transactional function
so it started to work after you had added #EnableTransactionManagement
Related
Using Spring-Boot 2.1.4.RELEASE. I have PCF bound Redis cache. In CacheSevice i have #Cacheable method that i call to get configurations.
Code works and unit test also works without validateMockitoUsage()
However if i right-click and run "All-tests" or try to deploy using maven-plugin. Test passes but breaks the first test of the next test class. with "Could not load Application Context"
With validateMockitoUsage() only the cacheService test breaks with: "org.mockito.exceptions.misusing.UnfinishedVerificationException:
I saw a discussion on a very similar issue on this thread (in the comments): https://stackoverflow.com/a/24229350/5680752
but no follow up
#RunWith(SpringRunner.class)
#SpringBootTest
#DirtiesContext
public class RediTests {
#Autowired
private CacheService cacheService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#After
public void validate() {
validateMockitoUsage();
}
#Configuration
#EnableCaching
static class Config {
#Bean
CacheService cacheService() {
return Mockito.mock(CacheService.class);
}
#Bean
#SuppressWarnings("unchecked")
public RedisSerializer<Object> defaultRedisSerializer() {
return Mockito.mock(RedisSerializer.class);
}
#Bean
#SuppressWarnings("unchecked")
public RedisTemplate<String, Object> defaultTemplate() {
return Mockito.mock(RedisTemplate.class);
}
#Bean
public JedisConnectionFactory connectionFactory() {
JedisConnectionFactory factory = Mockito.mock(JedisConnectionFactory.class);
RedisConnection connection = Mockito.mock(RedisConnection.class);
Mockito.when(factory.getConnection()).thenReturn(connection);
return factory;
}
#Bean
CacheManager cacheManager() {
return new ConcurrentMapCacheManager("my-cache");
}
}
#Test
public void testCache_CallsCacheableTwice_WithSameArgument_SkipsMethodCall() throws JSONException {
Configuration mockConfiguration1 = new Configuration ();
mockConfiguration1.setName("mockConfig1");
Configuration mockConfiguration2 = new Configuration ();
mockConfiguration1.setName("mockConfig2");
when(cacheService.getConfigurations(ArgumentMatchers.any())).thenReturn(mockConfiguration1, mockConfiguration2);
Configuration firstCall = cacheService.getConfigurations("1");
assertEquals(firstCall, mockConfiguration1);
Configuration secondCall = cacheService.getConfigurations("1");
assertEquals(secondCall, mockConfiguration1);
Mockito.verify(cacheService, Mockito.times(1)).getConfigurations("1");
Configuration thirdCall = cacheService.getConfigurations("2");
assertEquals(thirdCall, mockConfiguration2);
}
}
[1]: https://stackoverflow.com/a/24229350
I am trying to write and test an application that used spring-cloud with azure functions following this tutorial.
https://github.com/markusgulden/aws-tutorials/tree/master/spring-cloud-function/spring-cloud-function-azure/src/main/java/de/margul/awstutorials/springcloudfunction/azure
I am tryign to write a testcase and override the bean.
Here is the application class having function and handler Bean function.
#SpringBootApplication
#ComponentScan(basePackages = { "com.package" })
public class DataFunctions extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, Data> {
#FunctionName("addData")
public HttpResponseMessage addDataRun(
#HttpTrigger(name = "add", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws JsonParseException, JsonMappingException, IOException {
context.getLogger().info("Java HTTP trigger processed a POST request.");
try {
handleRequest(new GenericMessage<Optional<String>>(request.getBody()), context);
} catch (ServiceException ex) {
ErrorMessage em = new ErrorMessage();
return request.createResponseBuilder(handleException(ex, em)).body(em).build();
}
return request.createResponseBuilder(HttpStatus.CREATED).build();
}
#Autowired
MyService mService;
#Bean
public Consumer<GenericMessage<Optional<String>>> addData() {
ObjectMapper mapper = new ObjectMapper();
return req -> {
SomeModel fp = null;
try {
fp = mapper.readValue(req.getPayload().get(), SomeModel.class);
} catch (Exception e) {
throw new ServiceException(e);
}
mService.addData(fp);
};
}
}
I want to test by overriding the above bean.
Cosmosdb spring configuration
#Configuration
#EnableDocumentDbRepositories
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
#Value("${cosmosdb.collection.endpoint}")
private String uri;
#Value("${cosmosdb.collection.key}")
private String key;
#Value("${cosmosdb.collection.dbname}")
private String dbName;
#Value("${cosmosdb.connect.directly}")
private Boolean connectDirectly;
#Override
public DocumentDBConfig getConfig() {
ConnectionPolicy cp = ConnectionPolicy.GetDefault();
if (connectDirectly) {
cp.setConnectionMode(ConnectionMode.DirectHttps);
} else {
cp.setConnectionMode(ConnectionMode.Gateway);
}
return DocumentDBConfig.builder(uri, key, dbName).connectionPolicy(cp).build();
}
}
Here is the configuration
#TestConfiguration
#PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
#Profile("test")
#Import({DataFunctions.class})
public class TestConfig {
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
#Bean
#Primary
public DocumentDBConfig getConfig() {
return Mockito.mock(DocumentDBConfig.class);
}
}
Finally the test class
#RunWith(SpringRunner.class)
//#SpringBootTest //Enabling this gives initialization error.
#ActiveProfiles("test")
public class TempTest {
#InjectMocks
DataFunctions func;
#Mock
MyService mService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
private Optional<String> createRequestString(final String res) throws IOException {
InputStream iStream = TempTest.class.getResourceAsStream(res);
String charset="UTF-8";
try (BufferedReader br = new BufferedReader(new InputStreamReader(iStream, charset))) {
return Optional.of(br.lines().collect(Collectors.joining(System.lineSeparator())));
}
}
#Test
public void testHttpPostTriggerJava() throws Exception {
#SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
final Optional<String> queryBody = createRequestString("/test-data.json");
doNothing().when(mService).addData(Mockito.any(SomeModel.class));
doReturn(queryBody).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
#Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
doReturn("addData").when(context).getFunctionName();
// Invoke
final HttpResponseMessage ret = func.addDataRun(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.CREATED);
}
}
For this case instead of test configuration addData the actual bean is called from DataFunctions class. Also the database connection is also created when it should use the mocked bean from my test configuration. Can somebody please point out what is wrong in my test configuration?
I was able to resolve the first part of cosmos db config loading by marking it with
#Configuration
#EnableDocumentDbRepositories
#Profile("!test")
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
...
}
Also had to mark the repository bean as optional in the service.
public class MyService {
#Autowired(required = false)
private MyRepository myRepo;
}
Didn't use any spring boot configuration other than this.
#ActiveProfiles("test")
public class FunctionTest {
...
}
For the second part of providing mock version of Mock handlers, I simply made the test config file as spring application as below.
#SpringBootApplication
#ComponentScan(basePackages = { "com.boeing.da.helix.utm.traffic" })
#Profile("test")
public class TestConfiguration {
public static void main(final String[] args) {
SpringApplication.run(TestConfiguration.class, args);
}
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
}
and made use of this constructor from azure functions library in spring cloud in my constructor
public class AppFunctions
extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, List<Data>> {
public AppFunctions(Class<?> configurationClass) {
super(configurationClass);
}
}
public AzureSpringBootRequestHandler(Class<?> configurationClass) {
super(configurationClass);
}
Hope it helps someone.
I have following configuration in my spring boot application. and I am throwing RuntimeException from Repository layer and Service layer to see transaction handling. When I throw RuntimeException from repository layer, the transaction is getting rolled back, however, if I throw Runtime exception from Service layer as shown below, the transaction is not getting rolled back and data is saved into the database. Could anybody help me what is wrong with the following configuration?
#Configuration
#SpringBootApplication
#EnableAsync
#EnableScheduling
#ComponentScan(basePackages = { "com.abc.xyz.api.config", Constant.BASE_PACKAGE, com.abc.c4s.util.Constant.BASE_PACKAGE })
#EnableAutoConfiguration(exclude = { JndiConnectionFactoryAutoConfiguration.class, DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, JpaRepositoriesAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#PropertySources({
#PropertySource(value = "classpath:jdbc.properties")
})
#EnableTransactionManagement
public class ProjectManagerApplication {
public static ConfigurableApplicationContext context;
private static final Logger logger = LoggerFactory.getLogger(ProjectManagerApplication.class);
public static ConfigurableApplicationContext startServer() throws Exception {
logger.debug("Starting server");
SpringApplicationBuilder builder = new SpringApplicationBuilder(ProjectManagerApplication.class);
builder.headless(false);
context = builder.run(new String[] {});
logger.debug("Server started - " + (context != null ? context.isRunning() : "context null, starting failed"));
return context;
}
}
And, Following is my controller class configuration
#RestController
public class RWSettingsController {
#Autowired
SettingsService rwSettingsService;
#RequestMapping(value = "/api/settings", method = RequestMethod.POST)
public void createKiosk(#RequestBody SettingsDTO settingDTO) throws ABCInternalException, ABCException {
//try{
settingsService.create(settingDTO);
//}catch (Exception e){
// e.printStackTrace();
// }
}
}
This is my service class.
#Service
#Transactional//(propagation = Propagation.SUPPORTS, readOnly = true)
public class SettingsService {
#Autowired
SettingsRepository settingsRepository;
#Autowired
ModelMapper modelMapper;
private static final Logger logger = LoggerFactory.getLogger(SettingsService.class);
// #Transactional(
// propagation = Propagation.REQUIRED,
// readOnly = false)
public void create(SettingsDTO settingsDTO) throws ButterflyInternalException, ButterflyException {
try{
SettingsModel settingsModel = new SettingsModel ();
settingsModel .setActive(true);
settingsModel .setParameterDescription(settingsDTO.getParameterDescription());
settingsModel .setParameterName(settingsDTO.getParameterName());
settingsModel .setParameterValue(settingsDTO.getParameterValue());
//throw new HibernateException("");
rwsSettingsRepository.create(rwsKioskSettingsBPA);
throw new RuntimeException("");
}catch(ABCException e){
logger.error(e.getMessage(),e);
throw e;
}catch(Exception e){
logger.error(e);
throw e;
}
}
}
And my repository class is as follows
#Repository
//#Transactional//(propagation = Propagation.SUPPORTS, readOnly = true)
public class SettingsRepository {
#Autowired
#Qualifier("sessionFactory")
SessionFactory abcSessionFactory;
#Autowired
ABCUtilityRepository abcUtilityRepository;
private static final Logger logger = LoggerFactory.getLogger(SettingsRepository .class);
// #Transactional(
// propagation = Propagation.REQUIRED,
// readOnly = false)
public void create(RWSKioskSettingsBPA rwsKioskSettings) throws ABCException, ABCInternalException {
abcUtilityRepository.saveEntity(rwsKioskSettings);
//throw new RuntimeException();
}
I found what was the reason behind this behavior. Spring boot rolls back the transaction if application throw runtime exception. In my service layer, I catch the runtime exception and throw the application specific exception which is the reason why spring does not roll back the transaction. So after changing the code to throw the same runtime exception that was originally generated, application successfully rolled back the transaction.
Above code will run if I put this in the service layer
#Transactional(rollbackFor = Exception.class)
We need to track database metrics so we are using datasource-proxy to track this to integrate the same in spring boot project we have created custom datasource
as below
#Component
#Slf4j
#ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceBeanConfig
{
public DataSource actualDataSource()
{
EmbeddedDatabaseBuilder databaseBuilder = new EmbeddedDatabaseBuilder();
return databaseBuilder.setType(EmbeddedDatabaseType.H2).build();
}
#Bean
#Primary
public DataSource dataSource() {
// use pretty formatted query with multiline enabled
PrettyQueryEntryCreator creator = new PrettyQueryEntryCreator();
creator.setMultiline(true);
log.info("Inside Proxy Creation");
SystemOutQueryLoggingListener listener = new SystemOutQueryLoggingListener();
listener.setQueryLogEntryCreator(creator);
return ProxyDataSourceBuilder
.create(actualDataSource())
.countQuery()
.name("MyDS")
.listener(listener)
.build();
}
}
When we run main application datasource-proxy is picked up but when we use #DataJpaTest it is not picking up. How to enable datasource-proxy in JUNIT test cases?
Edit::
Using Spring BeanPostProcessor to configure Proxy DataSource
#Slf4j
#Configuration
public class DataSourceBeanConfig implements BeanPostProcessor
{
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException
{
if (bean instanceof DataSource)
{
System.out.println("AfterInitialization : " + beanName);
// use pretty formatted query with multiline enabled
PrettyQueryEntryCreator creator = new PrettyQueryEntryCreator();
creator.setMultiline(true);
log.info("Inside Proxy Creation");
SystemOutQueryLoggingListener listener = new SystemOutQueryLoggingListener();
listener.setQueryLogEntryCreator(creator);
return ProxyDataSourceBuilder.create((DataSource) bean).countQuery()
.name("MyDS").listener(listener).build();
}
return bean; // you can return any other object as well
}
}
Here is the solution we need to create TestConfiguration to use in #DataJpaTest
#RunWith(SpringRunner.class)
#DataJpaTest
public class DataTestJPA
{
#TestConfiguration
static class ProxyDataSourceConfig implements BeanPostProcessor
{
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException
{
if (bean instanceof DataSource)
{
return ProxyDataSourceBuilder
.create((DataSource) bean)
.countQuery()
.name("MyDS")
.build();
// #formatter:on
}
return bean; // you can return any other object as well
}
}
}
I am new to spring and am working on a rest service written using Spring and Apache CXF with Java Configurations. I have the following rest service.
#Path("/release/")
#Component
#RestService
#Consumes({ MediaType.APPLICATION_JSON })
#Produces({ MediaType.APPLICATION_JSON })
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ReleaseResource extends AbstractService implements IResource {
#Override
#CustomLogger
#GET
public Response get() {
//Some Logic
return Response.ok("Success!!").build();
}
}
I have created an aspect using #AspectJ for logging. However, the aspect is not working on the services written in CXF. I did a bit of searching in net and found that Spring needs proxy beans for the aspects to work. Then I tried few approaches such as
Making the service class implement an interface
Using CGLIB library and scope proxy mode TARGET_CLASS
Extending a class with method
#Override
public void setMessageContext(MessageContext context) {
this.context = context;
}
But none of them worked.
Any idea if it is possible to run the aspect around the services?
If yes, can someone please tell me how to.
I have read that this can be achieved by bytecode weaving the aspectj manually instead of using spring aspectj autoproxy (not sure how to do it though). Can someone tell me if this is a good option and how to do it?
EDIT:
Sorry for the incomplete info provided. Attaching the other classes
#Aspect
#Configuration
public class LoggerAspect {
#Pointcut(value = "execution(* *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(CustomLogger)")
public Object logAction(ProceedingJoinPoint pjp, CustomLogger customLogger) throws Throwable {
//Log Some Info
return pjp.proceed();
}
}
Web Initializer class:
#Configuration
public class WebInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(new ContextLoaderListener(createWebAppContext()));
addApacheCxfServlet(servletContext);
}
private void addApacheCxfServlet(ServletContext servletContext) {
CXFServlet cxfServlet = new CXFServlet();
ServletRegistration.Dynamic appServlet = servletContext.addServlet("CXFServlet", cxfServlet);
appServlet.setLoadOnStartup(1);
appServlet.addMapping("/*");
}
private WebApplicationContext createWebAppContext() {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.register(TestConfig.class);
return appContext;
}
}
Config Class:
#Configuration
#ComponentScan(basePackages = "com.my.package")
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class TestConfig {
private static final String RESOURCES_PACKAGE = "com.my.package";
#ApplicationPath("/")
public class JaxRsApiApplication extends Application {
}
#Bean(destroyMethod = "shutdown")
public SpringBus cxf() {
return new SpringBus();
}
#Bean
public JacksonJsonProvider jacksonJsonProvider() {
return new JacksonJsonProvider();
}
#Bean
public LoggerAspect getLoggerAspect() {
return new LoggerAspect();
}
#Bean
IResource getReleaseResource() {
return new ReleaseResource();
}
#Bean
#DependsOn("cxf")
public Server jaxRsServer(ApplicationContext appContext) {
JAXRSServerFactoryBean factory = RuntimeDelegate.getInstance().createEndpoint(jaxRsApiApplication(),
JAXRSServerFactoryBean.class);
factory.setServiceBeans(restServiceList(appContext));
factory.setProvider(jacksonJsonProvider());
return factory.create();
}
private List<Object> restServiceList(ApplicationContext appContext) {
return RestServiceBeanScanner.scan(appContext, TestConfig.RESOURCES_PACKAGE);
}
#Bean
public JaxRsApiApplication jaxRsApiApplication() {
return new JaxRsApiApplication();
}
}
RestServiceBeanScanner class
public final class RestServiceBeanScanner {
private RestServiceBeanScanner() {
}
public static List<Object> scan(ApplicationContext applicationContext, String... basePackages) {
GenericApplicationContext genericAppContext = new GenericApplicationContext();
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(genericAppContext, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(RestService.class));
scanner.scan(basePackages);
genericAppContext.setParent(applicationContext);
genericAppContext.refresh();
List<Object> restResources = new ArrayList<>(
genericAppContext.getBeansWithAnnotation(RestService.class).values());
return restResources;
}
}