Testing RestTemplate in SpringBoot - spring

I am trying to test the class bellow which is a Spring Batch reader.
My test returns a null when response.getBody() is invoked from the test in test.
My java class :
#Value("${rest.basepath}")
private String apiURI;
private int nextEmailIndex;
private RestTemplate restTemplate;
private List<EmailEntity> EmailEntityIterator;
private static final Logger logger = Logger.getLogger(EmailItemReader.class.getName());
/**
* Constructor
* #param restTemplate
* The RestTemplate class allows us to call a
* rest end point within the batch
*/
EmailItemReader(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
nextEmailIndex=0;
}
/**
* Checks if the list of emails is empty
* Populates it by fetching unread emails
* #return EmailEntity
*/
#Override
public EmailEntity read(){
if (isEmailEntityInitialised()) {
EmailEntityIterator = fetchEmails();
}
EmailEntity nextEmail = null;
if (nextEmailIndex < EmailEntityIterator.size() && nextEmailIndex < 50) {
nextEmail = EmailEntityIterator.get(nextEmailIndex);
nextEmailIndex++;
}
System.out.println("The email index :- " + nextEmailIndex);
return nextEmail;
}
/**
* Returns a list of unread emails
* #return List<EmailEntity>
*/
private List<EmailEntity> fetchEmails() {
restTemplate.setMessageConverters(getMessageConverters());
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON_UTF8));
HttpEntity<String> entity = new HttpEntity<>(httpHeaders);
ResponseEntity<EmailEntity[]> response =
restTemplate.exchange(apiURI, HttpMethod.GET, entity, EmailEntity[].class, 100);
EmailEntity[] partialEmailEntity = response.getBody();
List<EmailEntity> partialEmailEntityList = Arrays.asList(partialEmailEntity);
List<EmailEntity> EmailEntityList = new ArrayList<>();
for (EmailEntity EmailEntity: partialEmailEntityList) {
logger.info("The UUID :" + EmailEntity.getUuid() );
ResponseEntity<EmailEntity> fullResponce =
restTemplate.exchange(apiURI+EmailEntity.getUuid(), HttpMethod.GET, entity, EmailEntity.class, 100);
EmailEntity fullEmail = fullResponce.getBody();
EmailEntityList.add(fullEmail);
logger.info("The full email : "+ fullEmail);
}
printEmailsToLogs(EmailEntityList);
return EmailEntityList;
}
** Please see my test bellow, I have tried to mock my restTemplate but the response still returns will a null pointer**
#ContextConfiguration
#TestExecutionListeners( { DependencyInjectionTestExecutionListener.class,
StepScopeTestExecutionListener.class })
#RunWith(SpringJUnit4ClassRunner.class)
public class EmailItemReaderTest {
#Mock
private RestTemplate restTemplate;
#InjectMocks
private EmailItemReader reader;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
}
#Test
public void read() {
PlainContent plainContent = new PlainContent();
plainContent.setData("test");
RawEmail rawEmail = new RawEmail();
rawEmail.setData("Raw email");
EmailEntity mockEntity1 = new EmailEntity();
mockEntity1.setBody(plainContent);
mockEntity1.setContactNumberRequired(true);
mockEntity1.setContactNumber("0000000000");
mockEntity1.setStructured(true);
mockEntity1.setSubjectActive(true);
mockEntity1.setConfigEmailSubjectId(1);
mockEntity1.setSubjectLine("subject");
mockEntity1.setRawEmail(rawEmail);
EmailEntity[] testArray = new EmailEntity[1];
testArray[0] = mockEntity1;
ResponseEntity<EmailEntity[]> mockEntity = Mockito.spy(new ResponseEntity(HttpStatus.OK));
Mockito.doReturn(mockEntity).when(restTemplate).exchange(
Mockito.any(URI.class),
Mockito.any(HttpMethod.class),
Mockito.any(HttpEntity.class),
Mockito.eq(Class[].class)
);
reader.read();
Mockito.verify(restTemplate).exchange(Mockito.any(URI.class),Mockito.any(HttpMethod.class),Mockito.any(HttpEntity.class), Mockito.eq(EmailEntity[].class));
}
}
Please assist with any information or suggestions that can help. Thank in advance.

Finaly solved the issues : My response was not null, but my response body was null. So I threw in mock data for the response to return.
So I replaced this:
EmailEntity[] testArray = new EmailEntity[1];
testArray[0] = mockEntity1;
ResponseEntity<EmailEntity[]> mockEntity = Mockito.spy(new ResponseEntity(HttpStatus.OK));
with this :
EmailEntity[] testArray = new EmailEntity[1];
testArray[0] = mockEntity1;
ResponseEntity<EmailEntity[]> mockPartialEntity = Mockito.spy(new ResponseEntity<>(emailEntities, HttpStatus.OK));

Related

WebFlux API-Layer Test returns 404

I'm trying to get started with Spring WebFlux with Spring Boot 3.0
I'm Building a Person API with an open api generator.
The Application runs and gives the expected results when it is tested manually.
But I'm not able to get the API layer unit tested.
This is my Test Class
#WebFluxTest(controllers = {PersonApiController.class})
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class PersonRouterTest {
#MockBean
private PersonService personService;
#Autowired
private WebTestClient client;
#ParameterizedTest
#CsvSource({"1234, Max Mustermann", "5678, Erika Musterfrau"})
void retrieve_a_name(String id, String name) {
when(personService.getPersonDataByID(1234)).thenReturn(Mono.just(new PersonData(1234, "Max Mustermann")));
when(personService.getPersonDataByID(5678)).thenReturn(Mono.just(new PersonData(5678, "Erika Musterfrau")));
client.get()
.uri(uriBuilder -> uriBuilder
.path("/persons/{id}")
.build(id))
.accept(MediaType.ALL)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.jsonPath("$.name").isEqualTo(name);
}
This is my Controller Class
#Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2022-12-
09T09:14:36.692713900+01:00[Europe/Vienna]")
#Controller
#RequestMapping("${openapi.openAPIDefinition.base-path:}")
public class PersonApiController implements PersonApi {
private final PersonApiDelegate delegate;
public PersonApiController(#Autowired(required = false) PersonApiDelegate delegate) {
this.delegate = Optional.ofNullable(delegate).orElse(new PersonApiDelegate() {});
}
#Override
public PersonApiDelegate getDelegate() {
return delegate;
}
}
The API interface:
#Tag(
name = "Person",
description = "the Person API"
)
public interface PersonApi {
default PersonApiDelegate getDelegate() {
return new PersonApiDelegate() {
};
}
#Operation(
operationId = "findPersonById",
summary = "Find Person by ID",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operation",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.GET},
value = {"/persons/{id}"},
produces = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> findPersonById(#Parameter(name = "id",description = "Person ID",required = true) #PathVariable("id") Integer id, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().findPersonById(id, exchange);
}
#Operation(
operationId = "savePerson",
summary = "Creates a new Person",
tags = {"Person"},
responses = {#ApiResponse(
responseCode = "200",
description = "successful operatoin",
content = {#Content(
mediaType = "application/json",
schema = #Schema(
implementation = PersonData.class
)
)}
)}
)
#RequestMapping(
method = {RequestMethod.POST},
value = {"/persons"},
produces = {"application/json"},
consumes = {"application/json"}
)
default Mono<ResponseEntity<PersonData>> savePerson(#Parameter(name = "PersonData",description = "") #RequestBody(required = false) Mono<PersonData> personData, #Parameter(hidden = true) final ServerWebExchange exchange) {
return this.getDelegate().savePerson(personData, exchange);
}
}
and finally my delegate impl:
#Service
public class PersonDelegateImpl implements PersonApiDelegate {
public static final Mono<ResponseEntity<?>> RESPONSE_ENTITY_MONO = Mono.just(ResponseEntity.notFound().build());
private final PersonService service;
private final PersonMapper mapper;
public PersonDelegateImpl(PersonService service, PersonMapper mapper) {
this.service = service;
this.mapper = mapper;
}
public static <T> Mono<ResponseEntity<T>> toResponseEntity(Mono<T> mono) {
return mono.flatMap(t -> Mono.just(ResponseEntity.ok(t)))
.onErrorResume(t -> Mono.just(ResponseEntity.internalServerError().build()));
}
#Override
public Mono<ResponseEntity<PersonData>> findPersonById(Integer id, ServerWebExchange exchange) {
Mono<com.ebcont.talenttoolbackend.person.PersonData> personDataByID = service.getPersonDataByID(id);
return toResponseEntity(personDataByID.map(mapper::map));
}
#Override
public Mono<ResponseEntity<PersonData>> savePerson(Mono<PersonData> personData, ServerWebExchange exchange) {
return PersonApiDelegate.super.savePerson(personData, exchange);
If I run the test class I always get:
< 404 NOT_FOUND Not Found
< Content-Type: [application/json]
< Content-Length: [139]
{"timestamp":"2022-12-09T08:45:41.278+00:00","path":"/persons/1234","status":404,"error":"Not Found","message":null,"requestId":"4805b8b8"}
I have tried to change the Context Configuration but I did not get it to work.
I found the Problem, changing the Test Config to :
#WebFluxTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {PersonMapperImpl.class, H2PersonRepository.class, PersonRepository.class, PersonApiController.class, PersonDelegateImpl.class})
#DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
Solved my Problem.
The Controller bean was not recognized. I had to add PersonApiCrontroller and PersonDelegateImpl to the Context Config. i then removed the PersonApiController from the #WebFluxTest annotation.

unable to get the response entity from an api with mockmvc

I have an issue when I try to run my controller's unit test class. I get always a empty body in the response and I don't manage to find why.
I put here the code. Maybe someone with an external vision will be able to see the reason.
the controller:
#ResponseBody
#PostMapping(path = "/upload", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE }, produces = { MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<Object> uploadFile(#RequestParam("file") MultipartFile multipartFileData, #RequestParam(name="jobId", required = false) String jobId) {
JobStatus result;
try {
result = this.fileService.uploadFileChunk(multipartFileData, 1, 1, jobId);
}catch (ExecutionException|InterruptedException|IOException ex){
Thread.currentThread().interrupt();
return new ResponseEntity<>(ex,HttpStatus.INTERNAL_SERVER_ERROR);
}
return new ResponseEntity<>(result,HttpStatus.OK);
}
the unit test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes= FileUploadServiceRestController.class)
public class FileUploadServiceControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#MockBean
private FileUploadServiceImpl fileService;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Test
public void testUploadFile()
throws Exception {
MockMultipartFile file
= new MockMultipartFile(
"file",
"hello.txt",
MediaType.TEXT_PLAIN_VALUE,
"Hello, World!".getBytes()
);
JobStatus job = new JobStatus("uuid", ConstantUtil.JOB_STARTED);
when(fileService.uploadFileChunk(Mockito.any(MultipartFile.class),Mockito.eq(1),Mockito.eq(1),Mockito.isNull())).thenReturn(job);
mockMvc.perform(MockMvcRequestBuilders.multipart("/file/upload").file(file))
.andDo(MockMvcResultHandlers.print())
.andExpect(status().isOk());
}
}
and the object which will be transfered:
public class JobStatus implements Serializable {
private static final long serialVersionUID = -4405865740177389860L;
private String jobId;
private String status;
public JobStatus() {
}
public JobStatus(String jobId, String status) {
this.jobId = jobId;
this.status = status;
}
public String getJobId() {
return jobId;
}
public void setJobId(String jobId) {
this.jobId = jobId;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
For information, this controller works well when I call it from the client. I can see that the mock is well returned when I put a breakpoint at the end of the controller, but the response body stay empty.
I add here the result of print if it could help:
MockHttpServletRequest:
HTTP Method = POST
Request URI = /file/upload
Parameters = {}
Headers = [Content-Type:"multipart/form-data"]
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = org.iso.fileservice.controller.FileUploadServiceRestController
Method = org.iso.fileservice.controller.FileUploadServiceRestController#uploadFile(MultipartFile, String)
Async:
Async started = false
Async result = null
Resolved Exception:
Type = org.springframework.http.converter.HttpMessageNotWritableException
ModelAndView:
View name = null
View = null
Model = null
FlashMap:
Attributes = null
MockHttpServletResponse:
Status = 500
Error message = null
Headers = []
Content type = null
Body =
Forwarded URL = null
Redirected URL = null
Cookies = []
thanks, Mathieu
Just ran into this today.
I found the answer on Why MockMvc request retrieve empty responseBody while test succeed?
I just added My restController with #Autowired instead of #InjectMocks, after that I started to retrieve the Response Entity instead of a 500 status response

Optional int parameter 'movieId' is present but cannot be translated into a null value

I am working on Maven multi module project so this might build error?
I am trying to perform unit test of delete method from controller and I quite don't understand why it is happening here because methods similar to this one works.
Exception speaks for itself - cannot convert int to null. But why there is null value where it looks like curl looks correct?
{"timestamp":"2019-12-16T11:47:35.450+0000","path":"/movie-composite/1","status":500,"error":"Internal Server Error","message":"Optional int parameter 'movieId' is present but cannot be translated into a null value due to being declared as a primitive type. Consider declaring it as object wrapper for the corresponding primitive type."}
Ho mapping looks like:
/**
* Sample usage: curl $HOST:$PORT/movie-composite/1
*
* #param movieId
*/
#ApiOperation(
value = "${api.movie-composite.delete-composite-movie.description}",
notes = "${api.movie-composite.delete-composite-movie.notes}")
#ApiResponses(value = {
#ApiResponse(code = 400, message = "Bad Request, invalid format of the request. See response message for more information."),
#ApiResponse(code = 422, message = "Unprocessable entity, input parameters caused the processing to fails. See response message for more information.")
})
#DeleteMapping(
value = "/movie-composite/{movieId}",
produces = "application/json")
void deleteCompositeMovie(#PathVariable int movieId);
its implementation:
#Override
public void deleteCompositeMovie(int movieId) {
log.debug("deleteCompositeMovie will delete Movie, Reviews, Recommendations belonging to Movie with id: {}", movieId);
movieCompositeIntegration.deleteMovie(movieId);
movieCompositeIntegration.deleteReviews(movieId);
movieCompositeIntegration.deleteRecommendations(movieId);
log.debug("deleteCompositeMovie deleted Movie, Reviews, Recommendations belonging to Movie with id: {}", movieId);
}
And finally test that won't pass:
#Test
void deleteCompositeMovie() {
int given = 1;
deleteAndVerify(given, HttpStatus.OK);
verify(baseMovieCompositeService, times(1)).deleteCompositeMovie(given);
}
where deleteAndVerify(given, HttpStatus.OK) looks like:
private void deleteAndVerify(int id, HttpStatus httpStatus) {
webTestClient.delete()
.uri("/movie-composite/" + id)
.exchange()
.expectStatus().isEqualTo(httpStatus);
}
complete test file looks like:
#ExtendWith(SpringExtension.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
public class MovieCompositeServiceApplicationTests {
public static final String FAKE_ADDRESS = "Fake address";
public static final String FAKE_GENRE = "Fake genre";
public static final String FAKE_TITLE = "Fake title";
#Autowired
WebTestClient webTestClient;
#MockBean
MovieCompositeIntegration movieCompositeIntegration;
#MockBean
BaseMovieCompositeService baseMovieCompositeService;
#MockBean
ServiceUtil serviceUtil;
#Test
void createMovie() {
int movieId = 1;
MovieAggregate movieAggregate = MovieAggregate.builder()
.movieId(movieId)
.genre(FAKE_GENRE)
.title(FAKE_TITLE)
.recommendations(getRecommendationSummaries())
.reviews(getReviewSummaries())
.serviceAddresses(null)
.build();
postAndVerify(movieAggregate);
}
#Test
void getMovieById() {
int given = 1;
Movie movie = getMovies(given);
Mockito.when(serviceUtil.getServiceAddress()).thenReturn("Fake service address");
List<Recommendation> recommendations = getRecommendations(movie);
List<Review> reviews = getReviews(movie);
Mockito.when(movieCompositeIntegration.getMovie(given)).thenReturn(movie);
Mockito.when(movieCompositeIntegration.getRecommendations(movie.getMovieId())).thenReturn(recommendations);
Mockito.when(movieCompositeIntegration.getReviews(movie.getMovieId())).thenReturn(reviews);
getAndVerifyMovie(given, HttpStatus.OK)
.jsonPath("$.movieId").isEqualTo(given)
.jsonPath("$.recommendations.length()").isEqualTo(3)
.jsonPath("$.reviews.length()").isEqualTo(3);
}
#Test
void getMovieByIdThrowsNotFoundException() {
int given = 1;
Mockito.when(movieCompositeIntegration.getMovie(given)).thenThrow(NotFoundException.class);
getAndVerifyMovie(given, HttpStatus.NOT_FOUND)
.jsonPath("$.path").isEqualTo("/movie-composite/" + given);
}
#Test
void getMovieByIdThrowsInvalidInputException() {
int given = 1;
Mockito.when(movieCompositeIntegration.getMovie(given)).thenThrow(InvalidInputException.class);
getAndVerifyMovie(given, HttpStatus.UNPROCESSABLE_ENTITY)
.jsonPath("$.path").isEqualTo("/movie-composite/" + given);
}
#Test
void deleteCompositeMovie() {
int given = 1;
deleteAndVerify(given, HttpStatus.OK);
verify(baseMovieCompositeService, times(1)).deleteCompositeMovie(given);
}
private WebTestClient.BodyContentSpec getAndVerifyMovie(int id, HttpStatus status) {
return webTestClient.get()
.uri("/movie-composite/" + id)
.accept(MediaType.APPLICATION_JSON_UTF8)
.exchange()
.expectStatus().isEqualTo(status)
.expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
.expectBody();
}
private WebTestClient.BodyContentSpec postAndVerify(MovieAggregate movieAggregate) {
return webTestClient.post()
.uri("/movie-composite")
.body(just(movieAggregate), MovieAggregate.class)
.exchange()
.expectStatus().isEqualTo(HttpStatus.OK)
.expectBody();
}
private void deleteAndVerify(int id, HttpStatus httpStatus) {
webTestClient.delete()
.uri("/movie-composite/" + id)
.exchange()
.expectStatus().isEqualTo(httpStatus);
}
private List<ReviewSummary> getReviewSummaries() {
return Collections.singletonList(ReviewSummary.builder().reviewId(1).subject("s").author("a").content("c").build());
}
private List<RecommendationSummary> getRecommendationSummaries() {
return Collections.singletonList(RecommendationSummary.builder().recommendationId(1).author("a").content("c").rate(1).build());
}
private Movie getMovies(int given) {
return Movie.builder().movieId(given).address(FAKE_ADDRESS).genre(FAKE_GENRE).title(FAKE_TITLE).build();
}
private List<Review> getReviews(Movie movie) {
return Arrays.asList(
Review.builder().movieId(movie.getMovieId()).reviewId(1).author("Author 1").subject("Subject 1").content("Content 1").serviceAddress(serviceUtil.getServiceAddress()).build(),
Review.builder().movieId(movie.getMovieId()).reviewId(2).author("Author 2").subject("Subject 2").content("Content 2").serviceAddress(serviceUtil.getServiceAddress()).build(),
Review.builder().movieId(movie.getMovieId()).reviewId(3).author("Author 2").subject("Subject 3").content("Content 3").serviceAddress(serviceUtil.getServiceAddress()).build()
);
}
private List<Recommendation> getRecommendations(Movie movie) {
return Arrays.asList(
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(1).author("Author 1").rate(1).content("Content 1").serviceAddress(serviceUtil.getServiceAddress()).build(),
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(2).author("Author 2").rate(2).content("Content 2").serviceAddress(serviceUtil.getServiceAddress()).build(),
Recommendation.builder().movieId(movie.getMovieId()).recommendationId(3).author("Author 3").rate(3).content("Content 3").serviceAddress(serviceUtil.getServiceAddress()).build()
);
}
}
Why it won't pass where getMovieById() looks very similar when it comes to input and url and it passes?

camelContext attribute discriminator for tenant resolver , using jpa multitenant and camel routeId

i ask you how can use camelContext to get the name of route fired by an event, more in details, how can I use any kind of discriminator attribute x in camelContext for predicate decision (if x =1 then .. else ..)
For example:
I have this kind of route:
//this route use the forst database
from("direct:csvprocessor1")
.routeId("tenant1")
.from("file:src/main/resources/data/1?move=OUT&moveFailed=REFUSED")
.unmarshal(csv)
.to("bean:myCsvHandler?method=doHandleCsvData")
.setBody(constant("OK VB"))
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.setHeader(Exchange.CONTENT_TYPE, constant("text/html"));
and this other route:
//this route use tenant2, the second database
from("direct:csvprocessor1")
.routeId("tenant2")
.from("file:src/main/resources/data/2?move=OUT&moveFailed=REFUSED")
.unmarshal(csv)
.to("bean:myCsvHandler?method=doHandleCsvData")
.setBody(constant("OK 2"))
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.setHeader(Exchange.CONTENT_TYPE, constant("text/html"));
when i pick up file in 1 folder the first route named "tenant1" starts, the same happen when pick up file in 2, the second route tenant2 starts.It reads csv content and the content must be write using jpa on the right tenantX (database)
I have to retrieve routeid name in another class but this class instanced before the camel Context start so i can't inject context (because this class "BatchCurrentTenantIdentifierResolverImpl " belong to Spring database initializator). I try to add method "of" to set camelContext but i get tenant1 only, also when route 2 starts, so can't switch from tenant to another tenant (tenant is database, i have two database):
#Component
public class BatchTenantContext {
private static final Logger log = LoggerFactory.getLogger(BatchTenantContext.class);
// don't Inject, use method Of because injecton was null
CamelContext cctx;
public BatchTenantContext(){getInstance();}
private final static BatchTenantContext instance = new BatchTenantContext();
public static BatchTenantContext getInstance(){
return instance;
}
public synchronized String get() {
if (cctx != null){
Route val = cctx.getRoute("tenant1");
if (val == null){
val = cctx.getRoute("tenant2");
if (val == null){
return "";
}
else {
return "tenant_2";
}
}
else return "tenant_1";
}
return "";
}
public synchronized void of(CamelContext ctx){
cctx = ctx;
}
public CamelContext getCamelContext()
{
return cctx;
}
}
//multitenant approach, switch from one database to another
//based on BatchTenantContext resolution..
public class BatchCurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
static final Logger log = LoggerFactory.getLogger(BatchCurrentTenantIdentifierResolverImpl.class);
#Override
public String resolveCurrentTenantIdentifier() {
String val = BatchTenantContext.getInstance().get();
log.info("*** get tenant " + val);
return val;
}
#Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
So, how to know how route fire? Note thaht the class above is singleton..I'm in a right way?
I use jpa whitin hibernate provider, configured using rhe multitenant configuration like this post: http://tech.asimio.net/2017/01/17/Multitenant-applications-using-Spring-Boot-JPA-Hibernate-and-Postgres.html
The application work in spring-boot Runtime environment or with Tomcat app server.
Any ideas about all?
Thanks so much!
roby
I add this code:
#Configuration
#EnableConfigurationProperties({ MultiTenantAfSissProperties.class, JpaProperties.class })
#ImportResource(locations = { "classpath:applicationContent.xml" })
#EnableTransactionManagement
public class MultiTenantJpaConfiguration {
static final Logger logger = LoggerFactory.getLogger(MultiTenantJpaConfiguration.class);
#Inject
private JpaProperties jpaProperties;
#Inject
MultiTenantAFSISSProperties multiTenantAFSISSProperties; //lista dei datasources collegati ai tenant
#Bean
public Map<String, DataSource> dataSourceRetrieval(){
Map<String, DataSource> result = new HashMap<>();
for (DataSourceProperties dsProperties : this.multiTenantAFSISSProperties.getDataSources()) {
DataSourceBuilder factory = DataSourceBuilder
.create()
.url(dsProperties.getUrl())
.username(dsProperties.getUsername())
.password(dsProperties.getPassword())
.driverClassName(dsProperties.getDriverClassName());
result.put(dsProperties.getTenantId(), factory.build());
}
return result;
}
/**
*
* #return
*/
#Bean
public MultiTenantConnectionProvider multiTenantConnectionProvider(){
return new AfsissMultiTenantConnectionProviderImpl();
}
/**
*
* #return
*/
#Bean
public CurrentTenantIdentifierResolver currentTenantIdentifierResolver(){
return new BatchCurrentTenantIdentifierResolverImpl();
}
/**
*
* #param multiTenantConnectionProvider
* #param currentTenantIdentifierResolver
* #return
*/
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(MultiTenantConnectionProvider multiTenantConnectionProvider,
CurrentTenantIdentifierResolver currentTenantIdentifierResolver) {
Map<String, Object> hibernateProps = new LinkedHashMap<>();
hibernateProps.putAll(this.jpaProperties.getProperties());
Map<String,String> all = this.jpaProperties.getProperties();
for ( Map.Entry<String, String> prop : all.entrySet()){
System.out.println(" " + prop.getKey() + " = " + prop.getValue());
}
hibernateProps.put(Environment.MULTI_TENANT, MultiTenancyStrategy.DATABASE);
hibernateProps.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
hibernateProps.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolver);
// No dataSource is set to resulting entityManagerFactoryBean
LocalContainerEntityManagerFactoryBean result = new LocalContainerEntityManagerFactoryBean();
result.setPackagesToScan(new String[] { AfFileEntity.class.getPackage().getName() });
result.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
result.setJpaPropertyMap(hibernateProps);
return result;
}
/**
* crea la factory per ricavare l'entity manager
* #param entityManagerFactoryBean
* #return
*/
#Bean
public EntityManagerFactory entityManagerFactory(LocalContainerEntityManagerFactoryBean entityManagerFactoryBean) {
return entityManagerFactoryBean.getObject();
}
/**
* get transaction manager
* #param entityManagerFactory
* #return
*/
#Bean
public PlatformTransactionManager txManager(EntityManagerFactory entityManagerFactory) {
SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
HibernateTransactionManager result = new HibernateTransactionManager();
result.setAutodetectDataSource(false);
result.setSessionFactory(sessionFactory);
return result;
}
}
In applicationContent.xml:
<jpa:repositories base-package="com.xxx.dao" transaction-manager-ref="txManager" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true" />
The class BatchCurrentTenantIdentifierResolverImpl is called in currentTenantIdentifierResolver() method above by spring transaction manager every time i use entity manager and transaction manager in csvHanlder :
#Component
#Transactional(propagation = Propagation.REQUIRED)
public class MyCsvHandler {
#Inject
AFMOVCrudRepository _entitymanagerMov; //it extends JpaRepository
#Inject
AFVINCCrudRepository _entityManagerVINC;//it extends JpaRepository
#Inject
AFFileCrudRepository _entityManagerAfFile;//it extends JpaRepository
static final Logger logger = LoggerFactory.getLogger(MyCsvHandler.class);
//save csv data on the right table on the right tenant
public void doHandleCsvData(List<List<String>> csvData) throws FileNotEvaluableException
{
//System.out.println("stampo..");
boolean status = true;
if (csvData.size() > 0){
AfFileEntity afbean = new AfFileEntity();
afbean.setNomeFile("test");
afbean.setDataImport(new java.sql.Timestamp(System.currentTimeMillis()));
afbean.setTipoFile("M");
afbean.setAfStatoFlusso("I");
_entityManagerAfFile.save(afbean);
long pkfile = afbean.getId();
logger.info("pkfile: " + pkfile);
int i = 1;
logger.info("file size:" + csvData.size());
for (List<String> rows : csvData){
//for (int j = 0; i < rows.size(); j++){
if (rows.get(2).trim().equalsIgnoreCase(...)){
MovEntity mbean = new MovEntity();
setMovFields(mbean, rows);
mbean.setAfFileId(afbean);
logger.info(String.valueOf((i++)) + " " + mbean);
_entitymanagerMov.save(mbean);
}
else if (rows.get(2).trim().equalsIgnoreCase(..) || rows.get(2).trim().equalsIgnoreCase(..) ) {
VincEntity vincBean = new VincEntity();
setVincFields(vincBean, rows);
vincBean.setAfFileId(afbean);
logger.info(String.valueOf((i++)) + " " + vincBean);
_entityManagerVINC.save(vincBean);
}
else {
status = false;
break;
}
}
if (!status) throw new FileNotEvaluableException("error file format");
}
}
private void setVincFields(VincEntity vincBean, List<String> rows) {
vincBean.setXXX().. and others methods
}
private void setMovFields(MovEntity mbean, List<String> rows) {
mbean.setStxxx() and other .. methods
}
return new
Something like this in your routes
from("direct:csvprocessor1").routeId("tenant2").process((Exchange e)-> {
BatchCurrentTenantIdentifierResolverImpl.tenant.set("tenant_1");
})
.from("file:src/main/resources/data/2?move=OUT&moveFailed=REFUSED")
.unmarshal().csv()
.to("bean:myCsvHandler?method=doHandleCsvData")
.setBody(constant("OK 2"))
.setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200))
.setHeader(Exchange.CONTENT_TYPE, constant("text/html"));
And in your BatchCurrentTenantIdentifierResolverImpl implement it aspublic
class BatchCurrentTenantIdentifierResolverImpl {
public static ThreadLocal<String> tenant = new ThreadLocal<String>();
static final Logger log = LoggerFactory.getLogger(BatchCurrentTenantIdentifierResolverImpl.class);
#Override
public String resolveCurrentTenantIdentifier() {
String val = tenant.get();
log.info("*** get tenant " + val);
return val;
}
#Override
public boolean validateExistingCurrentSessions() {
return true;
}
}

406 error always happening with spring rest controller

I'd like to have your opinion on a error always throwed in my spring boot rest controller. I got the following first controller accepting reading requests :
#RequestMapping(value="/read/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity myFunction(#PathVariable("file") String file) {
String[] parts = file.split("\\.");
String extension = parts[1];
List<SousBloc> resWord;
List<SousBloc> resPdf;
List<CvAvecBlocs> resExcel;
RestTemplate rt = new RestTemplate();
rt.getMessageConverters().add(new StringHttpMessageConverter());
if(extension.equals("xlsx")){
resExcel = rt.getForObject("http://localhost:8080/readExcel/"+file, List.class, 200);
return new ResponseEntity<>(resExcel, HttpStatus.OK);
}
else if(extension.equals("pdf")){
resPdf = rt.getForObject("http://localhost:8080/readPdf/"+file, List.class, 200);
return new ResponseEntity<>(resPdf, HttpStatus.OK);
}
else if(extension.equals("docx")){
resWord = rt.getForObject("http://localhost:8080/readWord/"+file, List.class, 200);
return new ResponseEntity<>(resWord, HttpStatus.OK);
}
return null;
}
There is my Reading Word Controller :
#Controller
public class ReadWordController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadWord readWord;
#RequestMapping(value="/readWord/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingWord(#PathVariable("file") String file) throws IOException {
String path = UPLOADED_FOLDER+file;
List<SousBloc> sousBlocs = readWord.extract(path);
return new ResponseEntity<>(sousBlocs, HttpStatus.OK);
}
}
Well this controller works fine and does the job.
Now there is my Reading Pdf Controller :
#Controller
public class ReadPdfController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadPdf readPdf;
#RequestMapping(value="/readPdf/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingPdf(#PathVariable("file") String file) throws IOException {
String path = UPLOADED_FOLDER+file;
List<SousBloc> blocs = readPdf.extract(path);
return new ResponseEntity<>(blocs, HttpStatus.OK);
}
}
It is contructed on the same model of the Reading Word Controller but it does not work. In debug, the program works fine until the return new ResponseEntity<>(blocs, HttpStatus.OK); that throws a 406 error null...
Do you know why ?
EDIT: I tried something strange and it worked... I put the following code :
#Controller
public class ReadWordController {
private static String UPLOADED_FOLDER = "C:\\cvsUploades\\";
#Autowired
ReadWord readWord;
#Autowired
ReadPdf readPdf;
#RequestMapping(value="/readWord/{file:.+}" , method = RequestMethod.GET)
public ResponseEntity readingWord(#PathVariable("file") String file) throws IOException {
/*String path = UPLOADED_FOLDER+file;
List<SousBloc> sousBlocs = readWord.extract(path);
return new ResponseEntity<>(sousBlocs, HttpStatus.OK);*/
String path = "C:\\cvsUploades\\file.pdf";
List<SousBloc> blocs = readPdf.extract(path);
return new ResponseEntity<>(blocs, HttpStatus.OK);
}
}

Resources