In Spring/Hibernate project, in my entity class I have:
package klab.finance.main.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import klab.backend.entity.postgres.base.BaseEntity;
import klab.backend.utils.DateTimeUtils;
import klab.backend.utils.JsonDateTime;
import klab.backend.utils.json.LocalDateTimeToTimestampDeserializer;
import klab.backend.utils.json.TimestampToLocalDateTimeSerializer;
import org.hibernate.annotations.Where;
import org.joda.time.LocalDateTime;
import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;
#Entity
#Table(name="transactions")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Transaction extends BaseEntity {
private Long bankTransactionDate;
#Column(name="bank_transaction_date")
#JsonSerialize(using = TimestampToLocalDateTimeSerializer.class)
public Long getBankTransactionDate(){
return bankTransactionDate;
}
#JsonDeserialize(using = LocalDateTimeToTimestampDeserializer.class)
public void setBankTransactionDate(Long bankTransactionDate){
this.bankTransactionDate=bankTransactionDate;
}
}
My serializer class:
package klab.backend.utils.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import klab.backend.entity.postgres.base.BaseEntity;
import klab.backend.utils.DateTimeUtils;
import org.joda.time.LocalDateTime;
import java.io.IOException;
public class TimestampToLocalDateTimeSerializer extends JsonSerializer<Long> {
#Override
public void serialize(Long value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeString("blabla");
}
}
I expect to get JSON with "bank_transaction_date": "blabla".
Instead, I get it with "bank_transaction_date": null. I also notice, that if I set a breakpoint in my serializer, debugger is never stopping there, so it is probably not used.
What's wrong?
The answer was very simple: bankTransactionDate was null, and it turns out, that for null values serializer is not called :)
Once I filled database with values, serializer started to work and gave me "bank_transaction_date": "blabla".
Related
Hi I am new to spring and have created an Export csv application with two controllers which can give all the data as well as selective columns data and want to write junits for the same but it is quite difficult for me to do the same as I am not aware how i should do it some help would be really great
Main class:
package com.example;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.example.model.Example;
import com.example.repository.ExampleRepository;
import net.bytebuddy.implementation.bind.annotation.Super;
#SpringBootApplication
public class ExampleApplication implements CommandLineRunner {
#Autowired
ExampleRepository exampleRepository;
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
List<Example> example= new ArrayList<>();
// create dummy
example.add(new Example(1,"abc","sachin","abc","abc"));
example.add(new Example(2,"abc","rahul","abc","abc"));
example.add(new Example(3,"abc","rahul","abc","abc"));
exampleRepository.saveAll(example);
}
}
Entity layer:
package com.example.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="example")
public class Example{
#Column(name="city")
private String city;
#Column(name="name")
private String name;
#Column(name="amount")
private String amount;
#Column(name="country")
private String country;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private long id;
//getter and setters
}
Repository layer is as follows:
package com.example.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.example.model.Example;
#Repository("exampleRepository")
public interface ExampleRepository extends JpaRepository<Example,Long> {
List<Report> findByName(String name);
}
service layer is as follows
package com.example.services;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.model.Example;
import com.example.repository.ExampleRepository;
#Transactional
#Service
public class ExampleService {
#Autowired
ExampleRepository exampleRepository;
public List<Example> fetchAll() {
return exampleRepository.findAll();
}
public List<Example> findByName(String name){
return exampleRepository.findByName(name);
}
}
I have 2 controllers one to get all details and one to get selective columns and here we are generating csv files based on records that match the name
COntroller 1:
package com.example.controllers;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.example.model.Example;
import com.example.services.ExampleService;
#RestController
#RequestMapping("/demo")
public class Controller1 {
#Autowired
ExampleService exampleService;
#GetMapping("/result")
public void exportCSV(#RequestParam(name="name") String name ,HttpServletResponse response) throws Exception {
// set file name and content type
String filename = "names.csv";
response.setContentType("text/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
// Configure the CSV writer builder
StatefulBeanToCsvBuilder<Example> builder = new StatefulBeanToCsvBuilder<Example>(response.getWriter()).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER).withSeparator(CSVWriter.DEFAULT_SEPARATOR).withOrderedResults(false);
// create a csv writer
StatefulBeanToCsv<Example> writer = builder.build();
// write all employees to csv file
writer.write(examplesService.findByName(name));
}
}
Controller 2:
package com.reports.controllers;
import java.util.Arrays;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.opencsv.CSVWriter;
import com.opencsv.bean.StatefulBeanToCsv;
import com.opencsv.bean.StatefulBeanToCsvBuilder;
import com.example.model.Example;
import com.example.services.ExampleService;
#RestController
#RequestMapping("/example")
public class Controller2 {
#Autowired
ExampleService exampleService;
#GetMapping("/output")
public void exportCSV(#RequestParam(name="name") String name ,HttpServletResponse response) throws Exception {
// set file name and content type
String filename = "details.csv";
response.setContentType("text/csv");
response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"");
// Configure the CSV writer builder
StatefulBeanToCsvBuilder<Example> builder = new StatefulBeanToCsvBuilder<Example>(response.getWriter()).withQuotechar(CSVWriter.NO_QUOTE_CHARACTER).withSeparator(CSVWriter.DEFAULT_SEPARATOR).withOrderedResults(false);
// Ignore any field except the `id` and `amount` ones
Arrays.stream(Example.class.getDeclaredFields())
.filter(field -> !("id".equals(field.getName()) || "amount".equals(field.getName())
))
.forEach(field -> builder.withIgnoreField(Report.class, field));
// create a csv writer
StatefulBeanToCsv<Example> writer = builder.build();
// write all employees to csv file
writer.write(exampleService.findByname(name));
}
}
Try this approach :
generate a file in your /src/test/resources path and then test if it contains the generated data you expected
use combine it with a file system mocking to do it
You will :
test your ResController
mock your Service class
Also use Contructor Injection for you dependency
I have an application based on Jersey JAX-RS. I need to refactor the event handler and therefore also write a test for it.
I'm trying to do this with the JerseyTest Framework. I created a configuration to extend ResourceConfig, but when I use the target () call the handler is not called.
I will present the situation using code.
Here is an example Resource class:
package com.my.page;
import org.glassfish.hk2.api.messaging.Topic;
import com.my.core.entity.Link;
import com.my.core.location.LinkHitLocationFactory;
import com.my.core.service.LinkService;
import com.my.core.service.link.LinkFinder;
import com.my.core.service.link.LinkFinderFactory;
import com.my.event.LinkHitEvent;
import com.my.exception.FragmentNotFoundException;
import javax.annotation.security.PermitAll;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.Response;
#PermitAll
#Path("/")
public class LinkResource {
#Inject
private LinkService linkService;
#Inject
private Topic<LinkHitEvent> linkHitPublisher;
#Inject
private LinkFinderFactory linkFinderFactory;
#Inject
private LinkHitLocationFactory linkHitLocationFactory;
#GET
#Path("/{fragment:[^ ]{1,32}}")
public Response redirect(
#PathParam("fragment") String fragment,
#HeaderParam("Range") String range,
#HeaderParam("User-Agent") String userAgent,
#Context HttpHeaders headers) throws Exception {
LinkFinder linkFinder = linkFinderFactory.getLinkFinder(fragment);
Link link = linkFinder.getLink(fragment);
if (link.isExpired()) {
throw new FragmentNotFoundException(fragment);
}
linkService.insertHit();
linkHitPublisher.publish(new LinkHitEvent(link));
return handlerFactory.getHandler(link).handleGet(link, range).build();
}
}
Event test:
package com.my.page;
import org.glassfish.hk2.extras.events.internal.TopicDistributionModule;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import pl.comvision.hk2.events.ThreadedEventDistributorService;
import com.my.client.CallbackTargetBuilder;
import com.my.core.entity.Link;
import com.my.core.mapper.LinkMapper;
import com.my.core.service.LinkService;
import com.my.page.resource.LinkResource;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Response;
import static javax.ws.rs.core.Response.Status.TEMPORARY_REDIRECT;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;
#RunWith(MockitoJUnitRunner.class)
public class CallbackEventTest extends JerseyTest {
#Mock
private LinkMapper linkMapper;
#Mock
private LinkService linkService;
private CallbackTargetBuilder callbackTargetBuilder;
private final String callbackUrl = "";
#Override
protected Application configure() {
this.callbackTargetBuilder = spy(new CallbackTargetBuilder(this.callbackUrl));
ResourceConfig config = new ResourceConfig(LinkResource.class);
config.register(new TopicDistributionModule());
config.register(new AbstractBinder() {
#Override
protected void configure() {
addActiveDescriptor(ThreadedEventDistributorService.class).setRanking(100);
}
});
config.register(new EventsContainerListener(CallbackEventHandler.class));
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(linkMapper).to(LinkMapper.class);
bind(linkService).to(LinkService.class);
bind(mock(LinkService.class)).to(LinkService.class);
bind("").to(String.class).named("varPath");
bind("127.0.0.1").to(String.class).named("requestIP");
bind(callbackTargetBuilder).to(CallbackTargetBuilder.class);
}
});
return config;
}
#Test
public void publish_event() {
Link link = mock(Link.class);
when(link.getUrl()).thenReturn("example");
when(link.getName()).thenReturn("test");
when(linkMapper.getByName(anyString())).thenReturn(link);
Response response = target("/testY").property("jersey.config.client.followRedirects", false).request().get();
assertEquals(TEMPORARY_REDIRECT.getStatusCode(), response.getStatus());
verify(callbackTargetBuilder).build();
}
}
For testing purposes, I only injected callbackTargetBuilder into the handler, and called the build method on it to verify the call:
package com.my.page;
import org.glassfish.hk2.api.messaging.MessageReceiver;
import org.glassfish.hk2.api.messaging.SubscribeTo;
import org.jvnet.hk2.annotations.Service;
import com.my.client.CallbackTargetBuilder;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.MediaType;
#Service
#Singleton
#MessageReceiver
public class CallbackEventHandler {
#Named("callbackUrl")
private String url;
#Inject
private CallbackTargetBuilder callbackTargetBuilder;
#MessageReceiver
public void handle(#SubscribeTo LinkHitEvent event) {
Form form = new Form();
form.param("id", event.getLink().getId().toString());
form.param("name", event.getLink().getName());
callbackTargetBuilder.build();
Client client = ClientBuilder.newClient();
client.target(url).request().post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED_TYPE));
}
}
Edit:
I tried to register dependencies differently, but it does not bring satisfactory results. Each time verification fails:
verify (callbackTargetBuilder) .build ();
Looking for information I found that I can configure the DeploymentContext, but I don't know if this is the right direction.
Edit the second:
A quick test shows that I may have some more basic problem with mocking. Because the call:
verify (linkService) .insertHit (anyObject ());
It also fails.
I will write only for posterity that the above code is correct. The problem was a lot of small bugs in the tested code and how to mock it.
I'm trying to persist a cassandra entity but on startup I get:
Caused by: org.springframework.data.mapping.MappingException: Couldn't find PersistentEntity for type class com.service.model.Cart!
This is my entity class:
package com.service.model;
import io.vavr.collection.LinkedHashMap;
import io.vavr.collection.Map;
import lombok.Getter;
import lombok.ToString;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.function.Function;
#ToString
#Table("carts")
public class Cart {
#Getter
#PrimaryKey
private final UUID uuid;
private final Map<CartItemKey, CartItem> items;
public Cart(UUID uuid) {
this(uuid, LinkedHashMap.empty());
}
private Cart(UUID uuid, Map<CartItemKey, CartItem> items) {
this.uuid = Objects.requireNonNull(uuid, "Cart's uuid cannot be null");
this.items = Objects.requireNonNull(items, "Cart's items cannot be null");
}
}
This is my CassandraConfig:
package com.service.configuration;
import com.service.model.Cart;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.cassandra.config.AbstractClusterConfiguration;
import org.springframework.data.cassandra.core.convert.CassandraCustomConversions;
import org.springframework.data.cassandra.core.cql.keyspace.CreateKeyspaceSpecification;
import org.springframework.data.cassandra.core.cql.keyspace.KeyspaceOption;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static java.util.Collections.singletonList;
#Configuration
public class CassandraConfig extends AbstractClusterConfiguration {
#Value("${spring.data.cassandra.keyspace-name}")
private String keyspaceName;
#Value("${spring.data.cassandra.contact-points}")
private String contactPoints;
#Override
protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
return singletonList(
CreateKeyspaceSpecification.createKeyspace(keyspaceName)
.ifNotExists()
.with(KeyspaceOption.DURABLE_WRITES, true)
.withSimpleReplication());
}
#Override
protected boolean getMetricsEnabled() {
return false;
}
#Override
protected String getContactPoints() {
return contactPoints;
}
#Bean
public CassandraCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(new CartWriteConverter());
converters.add(new CartReadConverter());
return new CassandraCustomConversions(converters);
}
static class CartWriteConverter implements Converter<Cart, String> {
public String convert(Cart source) {
try {
return new ObjectMapper().writeValueAsString(source);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
}
static class CartReadConverter implements Converter<String, Cart> {
public Cart convert(String source) {
if (StringUtils.hasText(source)) {
try {
return new ObjectMapper().readValue(source, Cart.class);
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
return null;
}
}
}
And lastly my Application:
package com.service.cart;
import org.axonframework.springboot.autoconfig.AxonServerAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import org.springframework.scheduling.annotation.EnableAsync;
#EnableCaching
#EnableAsync
#EnableFeignClients
#SpringBootApplication
#EnableAutoConfiguration(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
AxonServerAutoConfiguration.class})
#EnableCassandraRepositories(basePackages = "com.service.repository")
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
What seems puzzling to me is that when I remove customConversions() bean it fails with a different error - not being able to map vavr Map, so spring must have scanned and registered this entity so that it got inspected. This is expected while it is not cassandra data type but in my understanding adding my custom conversions should solve this problem.
I also tried experimenting with #EntityScan with the same results.
Any help would be appreciated.
I have implemented a filter as below -
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
#WebFilter("/login.jsf")
public class ServiceFilter implements ContainerRequestFilter, ContainerResponseFilter, Filter {
public ServiceFilter() {}
#PostConstruct
public void init() {
logger.debug("initialized.");
}
#Override
public void destroy() {
logger.debug("destroyed");
}
#Override
public void init(FilterConfig arg0) throws ServletException {
//nothing here
}
...other method overides
}
When I deploy it on Wildfly10, I see the below gets printed
TIMESTAMP DEBUG ServiceFilter:65 - Initialized (ServiceFilter#39df1f0b)
TIMESTAMP DEBUG ServiceFilter:65 - Initialized (ServiceFilter#1a33dd9)
ServiceFilter class is also registered as a root resource in one of the class which implements javax.ws.rs.core.Application. This class acts as an entry point for rest and web resources.
How can I avoid the ServiceFilter class from being initialized twice? Or, is this okay as the filter implements both servlet and rest filters? Or, should I move the implementation in their own class files?
PS: The above stated behavior doesn't actually hinder any functionality offered by my application (I think and could be otherwise) but just wanted to make sure that I'm doing it right.
I have the following hierarchy:
#Validated
public class BaseResource
and
public class DeviceResource extends BaseResource
The #Validated annotation is as follows:
package com.redbend.validation.annotation;
import static java.lang.annotation.ElementType.TYPE;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Scope;
#Scope
#Target(TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface Validated {
}
And I have a Spring Aspect with the following advice:
package com.redbend.validation.aspect;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.NotNull;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.redbend.exceptions.EExceptionMsgID;
import com.redbend.exceptions.runtime.MissingMandatoryParameterException;
import com.redbend.validation.annotation.MandatoryOneOfParams;
import com.redbend.validation.annotation.MandatoryParams;
import com.redbend.validation.annotation.NotEmpty;
import com.redbend.validation.annotation.OneOfParamsForValue;
import com.redbend.validation.annotation.OneOfParamsForValueMap;
import com.redbend.validation.annotation.ParamsForValue;
import com.redbend.validation.annotation.ParamsForValueMap;
#Aspect
#Component
#Order(2)
public class ValidationInterceptor {
private static Logger logger = LoggerFactory.getLogger(ValidationInterceptor.class);
public ValidationInterceptor() {
// TODO Auto-generated constructor stub
}
#Before("within(com.redbend..*) && #within(com.redbend.validation.annotation.Validated) ")
public void validate(JoinPoint joinPoint) throws Exception {
validateParams(joinPoint);
}
When I call a method in DeviceResource, it is not caught by the aspect, even thought it inherits from BaseResource which is annotated with #Validated, and #Validated is annotated with #Inherited.
When I annotate DeviceResource with #Validated it works fine. How can I make the aspect intercept my method in DeviceResource without annotating it with #Validated?
Thanks,
Amir
within(#com.redbend.validation.annotation.Validated)
is incorrect, it should be
#within(com.redbend.validation.annotation.Validated)
I eventually solved it by changing the pointcut expression in my aspect:
#Before("within(com.redbend..*) && within(#com.redbend.validation.annotation.Validated *)")
I still don't know why it didn't work before or why #within(com.redbend.validation.annotation.Validated)
didn't work...