Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7a56a2d
Add repositories to use local libs gpsUtil rewardCentral and tripPric…
KentinTL Aug 26, 2025
7fd33b0
Modifying getTripDeals to have 10 providers in test, obligation to ca…
KentinTL Aug 26, 2025
b5502f0
Allow @Test on test method getTripDeals into TestTourGuideService
KentinTL Aug 26, 2025
5e5bda0
Add rewardsLock variable from ReentrantLock to lock calculateRewards …
KentinTL Aug 26, 2025
bfe9cb8
Remove @Disabled on test nearAllAttractions to pass the test
KentinTL Aug 26, 2025
adb1e70
Remove @Disabled on test nearAllAttractions to pass the test
KentinTL Aug 26, 2025
44439a6
Merge pull request #1 from KentinTL/etape-2
KentinTL Aug 26, 2025
6bc3c72
Create new Class NearByAttrationDto to use it in TourGuideController …
KentinTL Aug 29, 2025
e1db0f4
Create new method findClosestAttraction to dispatch logic and not hav…
KentinTL Aug 29, 2025
ee4291e
Modified getNearByAttractions to use rewardsService.findClosestAttrac…
KentinTL Aug 29, 2025
60b93e1
Modified getNearBytAttractions request method by using tourGuideServi…
KentinTL Aug 29, 2025
6ee9901
Add Mockito into test class and create new test method testGetNearByA…
KentinTL Aug 29, 2025
03a73d5
Add the new test testFindClosestAttractions to test new method into r…
KentinTL Aug 29, 2025
8e11afa
Merge pull request #2
KentinTL Aug 29, 2025
cbd37b3
Change class name to have camelCase
KentinTL Sep 1, 2025
edd6506
Added ExecutorService and Executor to use newFixedThreadPool into my …
KentinTL Sep 1, 2025
3d39876
Added ExecutorService and Executor to use newFixedThreadPool into my …
KentinTL Sep 1, 2025
63bd57d
Change call trackUserLocation by trackAllUsersLocation to simplify th…
KentinTL Sep 1, 2025
c88791e
Adapt test methods
KentinTL Sep 1, 2025
f319ed7
Workflow for Build and testing Application
KentinTL Sep 1, 2025
49c585b
Workflow to compile in .jar and Release the Artefact
KentinTL Sep 1, 2025
790df26
fixed maj missing into jar Name
KentinTL Sep 1, 2025
c935590
remove setup method and @BeforeEach annotation and user local instance
KentinTL Sep 1, 2025
edb445b
Merge pull request #3 from KentinTL/etape-4
KentinTL Sep 1, 2025
0205b56
Create README.md
KentinTL Sep 2, 2025
7a853e7
remove global variable in constructor to use it as it was originally …
KentinTL Sep 5, 2025
530d763
use 10 000 instead of 100 to have better mesure for tests
KentinTL Sep 5, 2025
90dd9c5
Merge remote-tracking branch 'origin/master'
KentinTL Sep 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: TourGuide CI with Maven

on:
push:
branches:
- master
pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Build and testing with Maven
run: mvn -B package --file TourGuide/pom.xml
32 changes: 32 additions & 0 deletions .github/workflows/realease.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: TourGuide Release artefact with Maven

on:
push:
tags:
- 'v*.*.*'

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven

- name: Compile Artefact
run: mvn package --file TourGuide/pom.xml

- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: TourGuide/target/*.jar
tag_name: ${{ github.ref_name }}
name: Release ${{ github.ref_name }}
body: "Publication automatique de la version ${{ github.ref_name }}"
110 changes: 110 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# TourGuide API - Java Project 8

## 1. Project Description

**TourGuide** is a Java and Spring Boot back-end application that simulates an API for a tour guide service. The application's purpose is to track user locations in real-time, suggest nearby tourist attractions, and manage a rewards system.

The main goal of this project was to refactor an existing application to **drastically optimize its performance** to handle a very high load (up to 100,000 users) concurrently and efficiently.

---

## 2. Key Features

* **Real-Time Tracking**: Locates and updates the position of multiple users concurrently.
* **Nearby Attractions**: For a given user, returns the 5 closest tourist attractions, along with the distance and associated reward points.
* **Rewards Calculation**: Automatically calculates and assigns reward points when a user visits an attraction.
* **Trip Deals**: Generates a list of offers from travel partners based on user preferences and accumulated reward points.
* **REST API**: Exposes a set of endpoints to interact with the application's features.

---

## 3. Technologies & Libraries

This project is built with the following technologies, as defined in the `pom.xml` file:

* **Language**: Java 8
* **Main Framework**: Spring Boot (`2.1.0.RELEASE`)
* **Web Server**: Tomcat (embedded with Spring Boot)
* **Build & Dependency Management**: Apache Maven
* **Testing**: JUnit 5, Mockito
* **External Libraries (provided as .jar files)**:
* `gpsUtil`: Simulates calls to a GPS location service.
* `RewardCentral`: Simulates calls to a points calculation service.
* `TripPricer`: Simulates calls to a trip pricing service.

---

## 4. Architecture & Performance Optimizations

To meet the high-performance requirements, the application's architecture was thoroughly refactored.

### a. Asynchronous Processing with CompletableFuture

The main bottleneck was the sequential processing of users. The solution was to implement a multi-threaded architecture.

* **`ExecutorService`**: A thread pool (`newFixedThreadPool`) is used in `TourGuideService` and `RewardsService` to manage tasks in a controlled manner, avoiding the costly creation of threads on the fly.
* **`CompletableFuture`**: The `trackAllUsersLocation` and `calculateAllUsersRewards` methods launch the location tracking and rewards calculation processes completely asynchronously. This allows for the parallel processing of thousands of users. The performance tests leverage these methods to start all tasks and then wait for their global completion using `CompletableFuture.allOf(...).join()`.

### b. Strategic Caching

The second performance issue stemmed from repeated calls to slow external resources.

* **Attractions Cache**: The list of attractions is fetched **only once** at application startup within the `RewardsService` constructor and is stored in memory. This eliminates thousands of redundant and slow calls to the `gpsUtil` library, making the rewards calculation almost instantaneous, as demonstrated by the `highVolumeGetRewards` test.

### c. Thread Safety

With parallel processing, it was crucial to prevent race conditions.

* **Synchronization**: Methods that modify shared data, such as `user.addUserReward()`, have been marked as `synchronized` to ensure that concurrent access is handled safely, without data corruption.

---

## 5. Installation & Setup

### Prerequisites

* Java Development Kit (JDK) 8 or higher
* Apache Maven 3+

### Steps

1. **Clone the repository**:
```bash
git clone [https://github.com/KentinTL/JavaPathENProject8.git](https://github.com/KentinTL/JavaPathENProject8.git)
cd JavaPathENProject8/TourGuide
```

2. **Install local dependencies**:
The project uses local libraries. Make sure they are correctly referenced in the `pom.xml` via `<systemPath>`.

3. **Build the project with Maven**:
This command will download dependencies, compile the code, and run unit tests. Performance tests are excluded from the default build thanks to the `maven-surefire-plugin` configuration.
```bash
mvn clean install
```

4. **Run the application**:
```bash
java -jar target/tourguide-0.0.1-SNAPSHOT.jar
```

The application will start and be accessible at `http://localhost:8080`.

---

## 6. API Endpoints

The API is defined in `TourGuideController.java` and exposes the following endpoints:

| Method | Endpoint | Parameters | Description |
| :--- | :--- | :--- | :--- |
| `GET` | `/` | _None_ | Displays a welcome message. |
| `GET` | `/getLocation` | `userName` (String) | Gets the last known location of the user. |
| `GET` | `/getNearbyAttractions` | `userName` (String) | Returns the 5 nearest attractions for the user. |
| `GET` | `/getRewards` | `userName` (String) | Returns the list of rewards earned by the user. |
| `GET` | `/getAllCurrentLocations`| _None_ | Returns the last known location of all users. |
| `GET` | `/getTripDeals` | `userName` (String) | Gets trip deals for the user. |

**Example usage with cURL:**
```bash
curl "http://localhost:8080/getNearbyAttractions?userName=internalUser1"
22 changes: 17 additions & 5 deletions TourGuide/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
<properties>
<java.version>17</java.version>
</properties>
<repositories>
<repository>
<id>local-libs</id>
<url>file://${project.basedir}/libs</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down Expand Up @@ -48,21 +54,27 @@
</dependency>

<dependency>
<groupId>gpsUtil</groupId>
<groupId>com.local</groupId>
<artifactId>gpsUtil</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/gpsUtil.jar</systemPath>
</dependency>

<dependency>
<groupId>tripPricer</groupId>
<artifactId>tripPricer</artifactId>
<groupId>com.local</groupId>
<artifactId>RewardCentral</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/RewardCentral.jar</systemPath>
</dependency>

<dependency>
<groupId>rewardCentral</groupId>
<artifactId>rewardCentral</artifactId>
<groupId>com.local</groupId>
<artifactId>TripPricer</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/TripPricer.jar</systemPath>
</dependency>
</dependencies>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TourguideApplication {
public class TourGuideApplication {

public static void main(String[] args) {
SpringApplication.run(TourguideApplication.class, args);
SpringApplication.run(TourGuideApplication.class, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import java.util.List;

import com.openclassrooms.tourguide.dto.NearByAttractionDto;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import gpsUtil.location.Attraction;
import gpsUtil.location.VisitedLocation;

import com.openclassrooms.tourguide.service.TourGuideService;
Expand Down Expand Up @@ -41,10 +42,10 @@ public VisitedLocation getLocation(@RequestParam String userName) {
// The distance in miles between the user's location and each of the attractions.
// The reward points for visiting each Attraction.
// Note: Attraction reward points can be gathered from RewardsCentral
@RequestMapping("/getNearbyAttractions")
public List<Attraction> getNearbyAttractions(@RequestParam String userName) {
VisitedLocation visitedLocation = tourGuideService.getUserLocation(getUser(userName));
return tourGuideService.getNearByAttractions(visitedLocation);
@RequestMapping("/getNearbyAttractions")
public List<NearByAttractionDto> getNearbyAttractions(@RequestParam String userName) {
User user = getUser(userName);
return tourGuideService.getNearByAttractionDtos(user);
}

@RequestMapping("/getRewards")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.openclassrooms.tourguide.dto;

import gpsUtil.location.Location;

public class NearByAttractionDto {
public String attractionName;
public Location attractionLocation;
public Location userLocation;
public double distance;
public int rewardPoints;

public NearByAttractionDto(String attractionName, Location attractionLocation,
Location userLocation, double distance, int rewardPoints) {
this.attractionName = attractionName;
this.attractionLocation = attractionLocation;
this.userLocation = userLocation;
this.distance = distance;
this.rewardPoints = rewardPoints;
}
}
Loading