Skip to content

Commit a614f6c

Browse files
author
Shelson Ferrari
committed
Refactor and improve Camel integration with better error handling, logging, and documentation
- Added detailed explanation of Apache Camel integration to the documentation - Refactored CurrencyConversionService to use ProducerTemplate effectively - Improved error handling and logging in CurrencyConversionService - Created ExchangeRateProcessor for handling API responses - Updated CurrencyConversionRoute to use ExchangeRateProcessor - Updated tests for CurrencyConversionService and ExchangeRateProcessor - Added headers and comments to newly created/updated files for consistency - Updated POM file to include necessary dependencies and plugins
1 parent fba56e4 commit a614f6c

8 files changed

Lines changed: 247 additions & 32 deletions

File tree

pom.xml

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,22 @@ Currency Conversion API - POM.xml
1111
This file configures the Maven project for the Currency Conversion API application.
1212
1313
Includes:
14-
- Dependency definitions
15-
- Plugins
16-
- Build profiles
17-
- Project properties
14+
- Dependency definitions for various libraries and frameworks such as Apache Camel, Spring Boot,
15+
and Hibernate.
16+
- Plugins for building, testing, and documenting the project.
17+
- Build profiles for different environments and use cases.
18+
- Project properties including Java version and encoding settings.
1819
1920
Manages:
20-
- Spring Boot dependencies
21-
- Testing tools
22-
- Validation
23-
- JSON handling
21+
- Spring Boot dependencies for web and data access.
22+
- Testing tools including JUnit, Mockito, and AssertJ.
23+
- Validation using Hibernate Validator.
24+
- JSON handling with Jackson and other libraries.
2425
2526
Configures:
26-
- Plugins for code style checking
27-
- Rule enforcement
28-
- Javadoc generation
27+
- Plugins for code style checking (Checkstyle) to ensure consistent code quality.
28+
- Rule enforcement using the Maven Enforcer Plugin.
29+
- Javadoc generation for creating project documentation.
2930
-->
3031

3132
<!-- Maven project definition -->
@@ -84,6 +85,22 @@ Configures:
8485
<version>3.15.0</version>
8586
</dependency>
8687

88+
<!-- Apache Camel dependencies para testes -->
89+
<dependency>
90+
<groupId>org.apache.camel</groupId>
91+
<artifactId>camel-test-spring-junit5</artifactId>
92+
<version>4.0.2</version>
93+
<scope>test</scope>
94+
</dependency>
95+
96+
<!-- Dependência do WireMock para simular respostas de API -->
97+
<dependency>
98+
<groupId>com.github.tomakehurst</groupId>
99+
<artifactId>wiremock-jre8</artifactId>
100+
<version>2.31.0</version>
101+
<scope>test</scope>
102+
</dependency>
103+
87104
<!-- Apache Camel dependencies -->
88105
<dependency>
89106
<groupId>org.apache.camel</groupId>
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright (c) 2024, Shelson Ferrari
3+
*
4+
* Licensed under the MIT License and the Apache License, Version 2.0 (the "Licenses"); you may not use this file except in
5+
* compliance with the Licenses. You may obtain a copy of the Licenses at
6+
*
7+
* MIT License:
8+
* https://opensource.org/licenses/MIT
9+
*
10+
* Apache License, Version 2.0:
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software distributed under the Licenses is
14+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
15+
* the Licenses for the specific language governing permissions and limitations under the Licenses.
16+
*/
17+
18+
package com.shelson.application.processors;
19+
20+
import java.util.Map;
21+
22+
import org.apache.camel.Exchange;
23+
import org.apache.camel.Processor;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.springframework.stereotype.Component;
27+
28+
import com.fasterxml.jackson.core.type.TypeReference;
29+
import com.fasterxml.jackson.databind.ObjectMapper;
30+
31+
/**
32+
* Processor to handle exchange rates.
33+
* This processor reads the exchange rate data from the message body,
34+
* parses it, and sets the parsed rates back into the message body.
35+
*/
36+
@Component
37+
public class ExchangeRateProcessor implements Processor {
38+
private static final Logger logger = LoggerFactory.getLogger(ExchangeRateProcessor.class);
39+
private final ObjectMapper objectMapper = new ObjectMapper();
40+
41+
/**
42+
* Processes the exchange to extract and set exchange rates.
43+
*
44+
* @param exchange The Camel exchange containing the message
45+
* @throws Exception If an error occurs during processing
46+
*/
47+
@Override
48+
public void process(Exchange exchange) throws Exception {
49+
logger.info("Processing exchange to extract rates");
50+
String response = exchange.getIn().getBody(String.class);
51+
Map<String, Map<String, Double>> ratesResponse = objectMapper.readValue(response, new TypeReference<Map<String, Map<String, Double>>>() {});
52+
Map<String, Double> rates = ratesResponse.get("rates");
53+
exchange.getIn().setBody(rates);
54+
logger.info("Exchange rates extracted and set in the message body");
55+
}
56+
}

src/main/java/com/shelson/application/routes/CurrencyConversionRoute.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,19 @@
1414
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
1515
* the Licenses for the specific language governing permissions and limitations under the Licenses.
1616
*/
17+
1718
package com.shelson.application.routes;
1819

1920
import org.apache.camel.builder.RouteBuilder;
2021
import org.springframework.beans.factory.annotation.Autowired;
2122
import org.springframework.stereotype.Component;
2223

2324
import com.shelson.application.dto.CurrencyConversionDTO;
25+
import com.shelson.application.processors.ExchangeRateProcessor;
2426
import com.shelson.application.service.CurrencyConversionService;
2527
import com.shelson.domain.model.Currency;
28+
import org.slf4j.Logger;
29+
import org.slf4j.LoggerFactory;
2630

2731
/**
2832
* Camel route for handling currency conversion requests.
@@ -32,20 +36,32 @@
3236
@Component
3337
public class CurrencyConversionRoute extends RouteBuilder {
3438

39+
private static final Logger logger = LoggerFactory.getLogger(CurrencyConversionRoute.class);
40+
3541
@Autowired
3642
private CurrencyConversionService currencyConversionService;
3743

44+
@Autowired
45+
private ExchangeRateProcessor exchangeRateProcessor;
46+
47+
/**
48+
* Configures the Camel route for currency conversion.
49+
*
50+
* @throws Exception if an error occurs during configuration
51+
*/
3852
@Override
3953
public void configure() throws Exception {
4054
from("direct:convertCurrency")
4155
.routeId("currencyConversionRoute")
4256
.log("Processing currency conversion: source=${header.source}, target=${header.target}, amount=${header.amount}")
57+
.process(exchangeRateProcessor)
4358
.process(exchange -> {
4459
String source = exchange.getIn().getHeader("source", String.class);
4560
String target = exchange.getIn().getHeader("target", String.class);
4661
Double amount = exchange.getIn().getHeader("amount", Double.class);
4762

4863
if (source == null || target == null || amount == null) {
64+
logger.error("Source, target, and amount must be provided");
4965
throw new IllegalArgumentException("Source, target, and amount must be provided");
5066
}
5167

@@ -54,7 +70,8 @@ public void configure() throws Exception {
5470

5571
CurrencyConversionDTO conversionDTO = currencyConversionService.convertCurrency(sourceCurrency, targetCurrency, amount);
5672
exchange.getIn().setBody(conversionDTO);
57-
})
58-
.log("Currency conversion completed: ${body}");
73+
74+
logger.info("Currency conversion completed: {}", conversionDTO);
75+
});
5976
}
6077
}

src/main/java/com/shelson/application/service/CurrencyConversionService.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@
1010
* Apache License, Version 2.0:
1111
* http://www.apache.org/licenses/LICENSE-2.0
1212
*
13-
* Unless required by applicable law or agreed to in writing, software distributed under the Licenses is
14-
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
15-
* the Licenses for the specific language governing permissions and limitations under the Licenses.
13+
* Unless required by applicable law or agreed to in writing, software distributed under the Licenses é
14+
* distribuído em uma base "COMO ESTÁ", SEM GARANTIAS OU CONDIÇÕES DE QUALQUER TIPO, expressas ou implícitas. Veja
15+
* os Licenses para a linguagem específica que rege permissões e limitações sob os Licenses.
1616
*/
17+
1718
package com.shelson.application.service;
1819

1920
import java.time.LocalDateTime;
@@ -83,7 +84,7 @@ public CurrencyConversionDTO convertCurrency(Currency sourceCurrency, Currency t
8384
}
8485
} catch (Exception ex) {
8586
logger.error("Error fetching exchange rates from API: {}", ex.getMessage());
86-
throw new ResourceNotFoundException("Error fetching exchange rates from API: " + ex.getMessage());
87+
throw new ResourceNotFoundException("Error fetching exchange rates from API", ex);
8788
}
8889

8990
if (rates == null || !rates.containsKey(targetCurrency.getCode())) {

src/main/java/com/shelson/infrastructure/exception/ResourceNotFoundException.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,32 @@
1414
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
1515
* the Licenses for the specific language governing permissions and limitations under the Licenses.
1616
*/
17-
package com.shelson.infrastructure.exception;
1817

19-
import org.springframework.http.HttpStatus;
20-
import org.springframework.web.bind.annotation.ResponseStatus;
18+
package com.shelson.infrastructure.exception;
2119

2220
/**
2321
* Exception thrown when a requested resource is not found.
24-
* <p>
25-
* This exception is used to indicate that a specific resource was not found.
26-
* It is annotated with {@link ResponseStatus} to return a 404 Not Found HTTP status
27-
* code when the exception is thrown.
28-
* </p>
2922
*/
30-
@ResponseStatus(code = HttpStatus.NOT_FOUND)
3123
public class ResourceNotFoundException extends RuntimeException {
32-
24+
3325
private static final long serialVersionUID = 1L;
3426

3527
/**
36-
* Constructor that accepts an error message.
28+
* Constructs a new ResourceNotFoundException with the specified detail message.
3729
*
38-
* @param message The error message detailing the cause of the exception.
30+
* @param message the detail message
3931
*/
4032
public ResourceNotFoundException(String message) {
4133
super(message);
4234
}
35+
36+
/**
37+
* Constructs a new ResourceNotFoundException with the specified detail message and cause.
38+
*
39+
* @param message the detail message
40+
* @param cause the cause
41+
*/
42+
public ResourceNotFoundException(String message, Throwable cause) {
43+
super(message, cause);
44+
}
4345
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2024, Shelson Ferrari
3+
*
4+
* Licensed under the MIT License and the Apache License, Version 2.0 (the "Licenses"); you may not use this file except in
5+
* compliance with the Licenses. You may obtain a copy of the Licenses at
6+
*
7+
* MIT License:
8+
* https://opensource.org/licenses/MIT
9+
*
10+
* Apache License, Version 2.0:
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software distributed under the Licenses is
14+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
15+
* the Licenses for the specific language governing permissions and limitations under the Licenses.
16+
*/
17+
18+
package com.shelson.application.processors;
19+
20+
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNotNull;
22+
23+
import java.util.Map;
24+
25+
import org.apache.camel.Exchange;
26+
import org.apache.camel.impl.DefaultCamelContext;
27+
import org.apache.camel.support.DefaultExchange;
28+
import org.junit.jupiter.api.BeforeEach;
29+
import org.junit.jupiter.api.Test;
30+
import org.slf4j.Logger;
31+
import org.slf4j.LoggerFactory;
32+
33+
/**
34+
* Unit tests for the {@link ExchangeRateProcessor} class.
35+
*/
36+
public class ExchangeRateProcessorTest {
37+
38+
private static final Logger logger = LoggerFactory.getLogger(ExchangeRateProcessorTest.class);
39+
40+
private ExchangeRateProcessor processor;
41+
private Exchange exchange;
42+
43+
/**
44+
* Sets up the test environment.
45+
*/
46+
@BeforeEach
47+
public void setUp() {
48+
processor = new ExchangeRateProcessor();
49+
exchange = new DefaultExchange(new DefaultCamelContext());
50+
logger.info("Test environment set up");
51+
}
52+
53+
/**
54+
* Tests the {@link ExchangeRateProcessor#process(Exchange)} method.
55+
*
56+
* @throws Exception if any error occurs during processing
57+
*/
58+
@Test
59+
public void testProcess() throws Exception {
60+
// Correctly formatted JSON string
61+
String json = "{\"rates\":{\"USD\":1.0,\"EUR\":0.85}}";
62+
exchange.getIn().setBody(json);
63+
64+
logger.info("Processing exchange with body: {}", json);
65+
processor.process(exchange);
66+
67+
@SuppressWarnings("unchecked")
68+
Map<String, Double> rates = exchange.getIn().getBody(Map.class);
69+
70+
assertNotNull(rates, "Rates should not be null");
71+
assertEquals(1.0, rates.get("USD"), "USD rate should be 1.0");
72+
assertEquals(0.85, rates.get("EUR"), "EUR rate should be 0.85");
73+
74+
logger.info("Processed rates: {}", rates);
75+
}
76+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.shelson.application.routes;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertNotNull;
5+
6+
import java.util.HashMap;
7+
import java.util.Map;
8+
9+
import org.apache.camel.CamelContext;
10+
import org.apache.camel.ProducerTemplate;
11+
import org.apache.camel.test.spring.junit5.CamelSpringBootTest;
12+
import org.junit.jupiter.api.Test;
13+
import org.springframework.beans.factory.annotation.Autowired;
14+
import org.springframework.boot.test.context.SpringBootTest;
15+
import org.springframework.test.annotation.DirtiesContext;
16+
17+
import com.shelson.application.dto.CurrencyConversionDTO;
18+
19+
@CamelSpringBootTest
20+
@SpringBootTest
21+
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
22+
public class CurrencyConversionRouteTest {
23+
24+
@Autowired
25+
private ProducerTemplate producerTemplate;
26+
27+
@Autowired
28+
private CamelContext camelContext;
29+
30+
@Test
31+
public void testCurrencyConversionRoute() throws Exception {
32+
camelContext.start();
33+
34+
Map<String, Object> headers = new HashMap<>();
35+
headers.put("source", "USD");
36+
headers.put("target", "EUR");
37+
headers.put("amount", 100.0);
38+
39+
CurrencyConversionDTO result = producerTemplate.requestBodyAndHeaders("direct:convertCurrency", null, headers, CurrencyConversionDTO.class);
40+
assertNotNull(result);
41+
assertEquals("USD", result.getSourceCurrency().getCode());
42+
assertEquals("EUR", result.getTargetCurrency().getCode());
43+
44+
camelContext.stop();
45+
}
46+
}

src/test/java/com/shelson/application/service/CurrencyConversionServiceTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
* Apache License, Version 2.0:
1111
* http://www.apache.org/licenses/LICENSE-2.0
1212
*
13-
* Unless required by applicable law or agreed to in writing, software distributed under the Licenses is
14-
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
15-
* the Licenses for the specific language governing permissions and limitations under the Licenses.
13+
* Unless required by applicable law or agreed to in writing, software distribuído sob os Licenses é
14+
* distribuído em uma base "COMO ESTÁ", SEM GARANTIAS OU CONDIÇÕES DE QUALQUER TIPO, expressas ou implícitas. Veja
15+
* os Licenses para a linguagem específica que rege permissões e limitações sob os Licenses.
1616
*/
1717

1818
package com.shelson.application.service;

0 commit comments

Comments
 (0)