diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..6c2a39d Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index b1cccfd..0761768 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,383 @@ -## Submission Instructions - -To submit your Oracle JAVA Spring Boot Maven project as a solution, please follow these steps: - -### Step 1: Install git on your PC -- Install "git" as shown in this tutorial: [How to install git](https://youtu.be/iYkLrXobBbA?si=_l0haibv_X9NpIjJ) -- Open command prompt and run - ```bash - git version - ``` -- If you see the version, then git is successfully installed. - -### Step 2: Fork the Repository -- Navigate to [this repository](https://github.com/CodelineAtyab/oraclequantapi) provided by Codeline. -- Click on the "Fork" button at the top-right corner of the page to create a copy of the repository under your own GitHub account. - -### Step 3: Clone the Forked Repository -- Open your terminal or command prompt. -- Clone the forked repository to your local machine using the following command: - ```bash - git clone https://github.com/your-username/repo-name.git - ``` - -### Step 4: Create a new branch -- Navigate to the cloned repository directory - ```bash - cd repo-name - ``` -- Create a new branch for your code submissions (Replace your-name with your name in your-name-submission-branch): - ```bash - git checkout -b your-name-submission-branch - ``` - - -### Step 5: Add Your Code -- Implement the API - -### Step 6: Commit your changes -- Run the following commands in order to commit your changes: - ```bash - git add * - git commit -m "Meaningful commit message here" - ``` - -### Step 7: Push Your Branch to GitHub -- Run the following commands to upload the changes to the forked github repository (Replace your-name with your name in your-name-submission-branch): - ```bash - git push origin your-name-submission-branch - ``` - -### Step 8: Create a Pull Request -- Go to your forked repository on GitHub. -- You should see a prompt to create a pull request. Click on "Compare & pull request". -- Provide a title and description for your pull request, then click "Create pull request". - -### Step 9: Notify Codeline -- Notify on slack that you have created a PR for your solution. - -## Note: If you face any issues in the process above, Please do the following: -- Watch [this youtube tutorial](https://www.youtube.com/watch?v=a_FLqX3vGR4) -- Contact Ikhlas or Atyab. +# Oracle Quant API + +## Project Overview + +Oracle Quant API is a Spring Boot backend application that processes encoded measurement strings and converts them into readable numerical package totals. + +The project was designed using clean layered architecture and follows object-oriented programming principles such as abstraction, encapsulation, inheritance, polymorphism, and separation of responsibilities. + +The application supports: + +* Encoded measurement parsing +* REST API communication +* Validation and centralized exception handling +* Logging with rolling log files +* Oracle database persistence +* Measurement history retrieval and management + +The application uses Oracle Database XE and persists all successful conversion requests. + +--- + +## Technologies Used + +* Java 17 +* Spring Boot +* Maven +* Spring Data JPA +* Hibernate +* Oracle Database XE +* Oracle Linux +* Docker +* SLF4J Logging +* Logback + +--- + +## Features + +* Encoded measurement parsing +* REST API architecture +* Oracle database persistence +* Measurement history retrieval +* Measurement history deletion +* Centralized exception handling +* Rolling application logs +* Oracle Linux deployment support + +--- + +## Design Principles + +The application follows: + +* Single Responsibility Principle (SRP) +* Encapsulation +* Abstraction +* Interface-driven development +* Layered architecture +* Separation of concerns + +--- + +## Project Structure + +```text +controller/ +service/ +service/impl/ +repository/ +entity/ +parser/ +exception/ +``` + +--- + +## Oracle Database Configuration + +Database configuration is located inside: + +```text +src/main/resources/application.properties +``` + +Example configuration: + +```properties +spring.datasource.url=jdbc:oracle:thin:@localhost:1521/XE +spring.datasource.username=system +spring.datasource.password=oracle +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +spring.jpa.hibernate.ddl-auto=update +spring.jpa.show-sql=true +``` + +--- + +## Oracle Database Setup + +Oracle Database XE was configured using Docker. + +Start the Oracle XE container: + +```bash +docker run -d \ +--name oracle-db \ +-p 1521:1521 \ +-e ORACLE_PASSWORD=oracle \ +gvenzl/oracle-xe:21-slim +``` + +Verify the running container: + +```bash +docker ps +``` + +Access the Oracle container: + +```bash +docker exec -it oracle-db bash +``` + +Connect to SQL Plus: + +```bash +sqlplus system/oracle@localhost:1521/XE +``` + +Verify stored records: + +```sql +SELECT * FROM measurement_record; +``` + +--- + +## Build the Application + +Run the following command inside the project directory: + +```bash +mvn clean package -DskipTests +``` + +After a successful build, the JAR file will be generated inside: + +```text +target/oraclequantapi-0.0.1-SNAPSHOT.jar +``` + +--- + +## Run the Application + +Start the Spring Boot application using: + +```bash +java -jar target/oraclequantapi-0.0.1-SNAPSHOT.jar +``` + +The application runs on: + +```text +http://localhost:8080 +``` + +--- + +## REST API Endpoints + +### Convert Measurements + +```http +GET /convert-measurements?input=dz_a_aazzzaaa +``` + +Example: + +```bash +curl "http://localhost:8080/convert-measurements?input=dz_a_aazzzaaa" +``` + +Example Response: + +```json +[28,53,1] +``` + +--- + +### Get All Measurement History + +```http +GET /measurement-history +``` + +Example: + +```bash +curl "http://localhost:8080/measurement-history" +``` + +--- + +### Get Measurement History By ID + +```http +GET /measurement-history/{id} +``` + +Example: + +```bash +curl "http://localhost:8080/measurement-history/1" +``` + +--- + +### Update Measurement History + +```http +PUT /measurement-history/{id} +``` + +Example: + +```bash +curl -X PUT "http://localhost:8080/measurement-history/1" \ +-H "Content-Type: application/json" \ +-d '{"input":"updated_input","output":"[10,20]"}' +``` + +--- + +### Delete Measurement History + +```http +DELETE /measurement-history/{id} +``` + +Example: + +```bash +curl -X DELETE "http://localhost:8080/measurement-history/1" +``` + +Example verification: + +```bash +curl "http://localhost:8080/measurement-history" +``` + +--- + +## Logging + +Application logs are written both to the console and rolling log files. + +Log files are stored inside: + +```text +logs/application.log +``` + +Rolling log history is configured for one week retention using Logback rolling policies. + +Example configuration: + +```properties +logging.file.name=logs/application.log +logging.logback.rollingpolicy.max-file-size=10MB +logging.logback.rollingpolicy.max-history=7 +``` + +--- + +## Oracle Linux Deployment + +This project was packaged into a deployable Spring Boot JAR file and tested successfully on Oracle Linux. + +### Connect to Oracle Linux via SSH + +```bash +ssh username@server-ip +``` + +--- + +### Clone the Repository + +```bash +git clone +``` + +Navigate into the project: + +```bash +cd oraclequantapi +``` + +--- + +### Build the Project on Oracle Linux + +```bash +mvn clean package -DskipTests +``` + +--- + +### Run the Application on Oracle Linux + +```bash +java -jar target/oraclequantapi-0.0.1-SNAPSHOT.jar +``` + +--- + +### Verify the API + +```bash +curl "http://localhost:8080/convert-measurements?input=dz_a_aazzzaaa" +``` + +Expected response: + +```json +[28,53,1] +``` + +--- + +## Exception Handling + +The application uses centralized exception handling for invalid measurement inputs and unexpected server errors. + +Custom exceptions are located inside: + +```text +exception/ +``` + +--- + +## Version + +Current Version: + +```text +v1.0.0 +``` + +### Features Included + +* Measurement conversion endpoint +* Oracle database persistence +* Measurement history endpoints +* Logging support +* Oracle Linux deployment +* REST API architecture +* Centralized exception handling + +--- + +## Changelog + +### v1.0.0 + +* Initial project release +* Implemented encoded measurement conversion +* Added Oracle XE database persistence +* Added measurement history endpoints +* Added rolling logging support +* Added Oracle Linux deployment support +* Added centralized exception handling + +--- + +## Development Environment + +Developed using Java 17, Spring Boot, Oracle XE Database, and Oracle Linux deployment environment. \ No newline at end of file diff --git a/logs/application.log b/logs/application.log new file mode 100644 index 0000000..36fb284 --- /dev/null +++ b/logs/application.log @@ -0,0 +1,26 @@ +2026-05-25 11:14:12 [INFO] c.o.o.OraclequantapiApplicationTests - Starting OraclequantapiApplicationTests using Java 25.0.2 with PID 49186 (started by saif in /Users/saif/Desktop/measurement-api/oraclequantapi) +2026-05-25 11:14:12 [INFO] c.o.o.OraclequantapiApplicationTests - No active profile set, falling back to 1 default profile: "default" +2026-05-25 11:14:12 [INFO] o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode. +2026-05-25 11:14:13 [INFO] o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 44 ms. Found 1 JPA repository interface. +2026-05-25 11:14:13 [INFO] o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default] +2026-05-25 11:14:13 [INFO] org.hibernate.Version - HHH000412: Hibernate ORM core version 6.6.49.Final +2026-05-25 11:14:13 [INFO] o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled +2026-05-25 11:14:13 [INFO] o.s.o.j.p.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer +2026-05-25 11:14:13 [INFO] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting... +2026-05-25 11:14:14 [INFO] com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection oracle.jdbc.driver.T4CConnection@45796b2a +2026-05-25 11:14:14 [INFO] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed. +2026-05-25 11:14:15 [INFO] o.hibernate.orm.connections.pooling - HHH10001005: Database info: + Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)'] + Database driver: undefined/unknown + Database version: 21.3 + Autocommit mode: undefined/unknown + Isolation level: undefined/unknown + Minimum pool size: undefined/unknown + Maximum pool size: undefined/unknown +2026-05-25 11:14:15 [INFO] o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration) +2026-05-25 11:14:20 [INFO] o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default' +2026-05-25 11:14:21 [WARN] o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning +2026-05-25 11:14:21 [INFO] c.o.o.OraclequantapiApplicationTests - Started OraclequantapiApplicationTests in 9.811 seconds (process running for 10.569) +2026-05-25 11:14:22 [INFO] o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default' +2026-05-25 11:14:22 [INFO] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated... +2026-05-25 11:14:23 [INFO] com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed. diff --git a/logs/application.log.2026-05-23.0.gz b/logs/application.log.2026-05-23.0.gz new file mode 100644 index 0000000..eab91bb Binary files /dev/null and b/logs/application.log.2026-05-23.0.gz differ diff --git a/logs/application.log.2026-05-24.0.gz b/logs/application.log.2026-05-24.0.gz new file mode 100644 index 0000000..0239669 Binary files /dev/null and b/logs/application.log.2026-05-24.0.gz differ diff --git a/pom.xml b/pom.xml index 20909d2..9650a47 100644 --- a/pom.xml +++ b/pom.xml @@ -35,6 +35,28 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-validation + + + + com.oracle.database.jdbc + ojdbc11 + runtime + + + + org.projectlombok + lombok + true + + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplication.java b/src/main/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplication.java index 5e28689..f54eba4 100644 --- a/src/main/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplication.java +++ b/src/main/java/com/oraclequantapi/oraclequantapi/OraclequantapiApplication.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication + public class OraclequantapiApplication { public static void main(String[] args) { SpringApplication.run(OraclequantapiApplication.class, args); } - } diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java b/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java new file mode 100644 index 0000000..bef0dab --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/controller/MeasurementController.java @@ -0,0 +1,73 @@ +package com.oraclequantapi.oraclequantapi.controller; + +import com.oraclequantapi.oraclequantapi.service.MeasurementService; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import com.oraclequantapi.oraclequantapi.entity.MeasurementRecord; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.DeleteMapping; +import jakarta.servlet.http.HttpServletRequest; + +import java.util.List; + +@RestController +public class MeasurementController { + + private final MeasurementService measurementService; + + public MeasurementController( + MeasurementService measurementService + ) { + this.measurementService = measurementService; + } + + @GetMapping("/convert-measurements") + public List convertMeasurements( + @RequestParam String input, + HttpServletRequest request + + ) { + + return measurementService.convertMeasurements( + input, + request.getRemoteAddr() + ); + } + + @GetMapping("/measurement-history") + public List getMeasurementHistory() { + + return measurementService.getMeasurementHistory(); + + } + + @GetMapping("/measurement-history/{id}") + public MeasurementRecord getMeasurementById( + @PathVariable Long id + ) { + return measurementService.getMeasurementById(id); + } + + @PutMapping("/measurement-history/{id}") + public MeasurementRecord updateMeasurementRecord( + @PathVariable Long id, + @RequestBody MeasurementRecord updatedRecord + + ) { + return measurementService.updateMeasurementRecord( + id, + updatedRecord + ); + } + + @DeleteMapping("/measurement-history/{id}") + public void deleteMeasurementRecord( + @PathVariable Long id +) { + + measurementService.deleteMeasurementRecord(id); + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/entity/MeasurementRecord.java b/src/main/java/com/oraclequantapi/oraclequantapi/entity/MeasurementRecord.java new file mode 100644 index 0000000..de99eaa --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/entity/MeasurementRecord.java @@ -0,0 +1,67 @@ +package com.oraclequantapi.oraclequantapi.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import java.time.LocalDateTime; + +@Entity +public class MeasurementRecord { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private LocalDateTime timestamp; + + private String sourceIpAddress; + + private String input; + + private String output; + + public MeasurementRecord() { + + } + + public MeasurementRecord( + String input, + String output, + String sourceIpAddress + + ) { + this.input = input; + this.output = output; + this.sourceIpAddress = sourceIpAddress; + this.timestamp = LocalDateTime.now(); + } + + public Long getId() { + return id; + } + + public LocalDateTime getTimestamp() { + return timestamp; + } + + public String getSourceIpAddress() { + return sourceIpAddress; + } + + public String getInput() { + return input; + } + + public String getOutput() { + return output; + } + + public void setInput(String input) { + this.input = input; + } + + public void setOutput(String output) { + this.output = output; + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/exception/GlobalExceptionHandler.java b/src/main/java/com/oraclequantapi/oraclequantapi/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..396e314 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/exception/GlobalExceptionHandler.java @@ -0,0 +1,29 @@ +package com.oraclequantapi.oraclequantapi.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.util.Map; + +@ControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(InvalidMeasurementInputException.class) + public ResponseEntity> + handleInvalidMeasurementInputException( + InvalidMeasurementInputException exception + + ) { + + return ResponseEntity + .status(HttpStatus.BAD_REQUEST) + .body( + Map.of( + "error", + exception.getMessage() + ) + ); + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/exception/InvalidMeasurementInputException.java b/src/main/java/com/oraclequantapi/oraclequantapi/exception/InvalidMeasurementInputException.java new file mode 100644 index 0000000..6fb5a7b --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/exception/InvalidMeasurementInputException.java @@ -0,0 +1,8 @@ +package com.oraclequantapi.oraclequantapi.exception; + +public class InvalidMeasurementInputException extends RuntimeException { + + public InvalidMeasurementInputException(String message) { + super(message); + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/parser/DecodedNumber.java b/src/main/java/com/oraclequantapi/oraclequantapi/parser/DecodedNumber.java new file mode 100644 index 0000000..604aa71 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/parser/DecodedNumber.java @@ -0,0 +1,21 @@ +package com.oraclequantapi.oraclequantapi.parser; + +public class DecodedNumber { + + private final int value; + + private final int nextIndex; + + public DecodedNumber(int value, int nextIndex) { + this.value = value; + this.nextIndex = nextIndex; + } + + public int getValue() { + return value; + } + + public int getNextIndex() { + return nextIndex; + } +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/parser/MeasurementParser.java b/src/main/java/com/oraclequantapi/oraclequantapi/parser/MeasurementParser.java new file mode 100644 index 0000000..abc2409 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/parser/MeasurementParser.java @@ -0,0 +1,95 @@ +package com.oraclequantapi.oraclequantapi.parser; + +import com.oraclequantapi.oraclequantapi.exception.InvalidMeasurementInputException; + +import java.util.ArrayList; +import java.util.List; + +public class MeasurementParser { + + public List parseMeasurements(String input) { + + List results = new ArrayList<>(); + + int currentIndex = 0; + + while (currentIndex < input.length()) { + + DecodedNumber packageCountResult = + readEncodedNumber(input, currentIndex); + + int packageCount = packageCountResult.getValue(); + + currentIndex = packageCountResult.getNextIndex(); + + int packageTotal = 0; + int valuesRead = 0; + + while (valuesRead < packageCount + && currentIndex < input.length()) { + + DecodedNumber valueResult = + readEncodedNumber(input, currentIndex); + + packageTotal += valueResult.getValue(); + + currentIndex = valueResult.getNextIndex(); + + valuesRead++; + } + results.add(packageTotal); + } + return results; + } + + private DecodedNumber readEncodedNumber( + String input, + int currentIndex + + ) { + + char currentCharacter = input.charAt(currentIndex); + + if (!(currentCharacter == '_' + || (currentCharacter >= 'a' + && currentCharacter <= 'z'))) { + + throw new InvalidMeasurementInputException( + "Invalid character in measurement input" + ); + } + + int value = 0; + + if (currentCharacter == 'z') { + + while (currentIndex < input.length() + && input.charAt(currentIndex) == 'z') { + + value += 26; + currentIndex++; + + } + + if (currentIndex < input.length()) { + char terminatingCharacter = input.charAt(currentIndex); + + if (terminatingCharacter != '_') { + value += (terminatingCharacter - 'a') + 1; + } + currentIndex++; + } + } + + else { + if (currentCharacter == '_') { + value = 0; + + } else { + value = (currentCharacter - 'a') + 1; + } + currentIndex++; + } + return new DecodedNumber(value, currentIndex); + } +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java b/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java new file mode 100644 index 0000000..94fc9e2 --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/repository/MeasurementRecordRepository.java @@ -0,0 +1,11 @@ +package com.oraclequantapi.oraclequantapi.repository; + +import com.oraclequantapi.oraclequantapi.entity.MeasurementRecord; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MeasurementRecordRepository + + extends JpaRepository { + +} diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementService.java b/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementService.java new file mode 100644 index 0000000..15b412c --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/service/MeasurementService.java @@ -0,0 +1,22 @@ +package com.oraclequantapi.oraclequantapi.service; + +import com.oraclequantapi.oraclequantapi.entity.MeasurementRecord; +import java.util.List; + +public interface MeasurementService { + List convertMeasurements( + String input, + String sourceIpAddress + ); + + List getMeasurementHistory(); + + MeasurementRecord getMeasurementById(Long id); + + void deleteMeasurementRecord(Long id); + + MeasurementRecord updateMeasurementRecord( + Long id, + MeasurementRecord updatedRecord + ); +} \ No newline at end of file diff --git a/src/main/java/com/oraclequantapi/oraclequantapi/service/impl/MeasurementServiceImpl.java b/src/main/java/com/oraclequantapi/oraclequantapi/service/impl/MeasurementServiceImpl.java new file mode 100644 index 0000000..0fde35d --- /dev/null +++ b/src/main/java/com/oraclequantapi/oraclequantapi/service/impl/MeasurementServiceImpl.java @@ -0,0 +1,122 @@ +package com.oraclequantapi.oraclequantapi.service.impl; + +import com.oraclequantapi.oraclequantapi.parser.MeasurementParser; +import com.oraclequantapi.oraclequantapi.service.MeasurementService; +import java.util.List; +import org.springframework.stereotype.Service; +import com.oraclequantapi.oraclequantapi.entity.MeasurementRecord; +import com.oraclequantapi.oraclequantapi.repository.MeasurementRecordRepository; +import com.oraclequantapi.oraclequantapi.exception.InvalidMeasurementInputException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Service +public class MeasurementServiceImpl implements MeasurementService { + + private static final Logger logger = + LoggerFactory.getLogger(MeasurementServiceImpl.class); + + private final MeasurementRecordRepository + measurementRecordRepository; + + private final MeasurementParser measurementParser = + new MeasurementParser(); + + public MeasurementServiceImpl( + MeasurementRecordRepository measurementRecordRepository + ) { + this.measurementRecordRepository = + measurementRecordRepository; + } + + @Override + public List convertMeasurements( + String input, + String sourceIpAddress + + ) { + + logger.info("Received measurement conversion request"); + + if (input == null || input.isBlank()) { + logger.warn("Received invalid blank measurement input"); + + throw new InvalidMeasurementInputException( + "Input parameter is required" + ); + } + + List results = + measurementParser.parseMeasurements(input); + + MeasurementRecord measurementRecord = + new MeasurementRecord( + input, + results.toString(), + sourceIpAddress + ); + + measurementRecordRepository.save(measurementRecord); + + logger.info("Successfully converted measurement input"); + + return results; + } + + @Override + public List getMeasurementHistory() { + logger.info("Fetching measurement history"); + return measurementRecordRepository.findAll(); + } + + @Override + public MeasurementRecord getMeasurementById(Long id) { + + logger.info("Fetching measurement record by id"); + + return measurementRecordRepository.findById(id) + .orElseThrow(() -> + new InvalidMeasurementInputException( + "Measurement record not found" + ) + ); + } + + @Override + public void deleteMeasurementRecord(Long id) { + + logger.info("Deleting measurement record"); + + if (!measurementRecordRepository.existsById(id)) { + + throw new InvalidMeasurementInputException( + "Measurement record not found" + ); + } + + measurementRecordRepository.deleteById(id); + } + + @Override + public MeasurementRecord updateMeasurementRecord( + Long id, + MeasurementRecord updatedRecord + ) { + + logger.info("Updating measurement record"); + + MeasurementRecord existingRecord = + measurementRecordRepository.findById(id) + .orElseThrow(() -> + new InvalidMeasurementInputException( + "Measurement record not found" + ) + ); + + existingRecord.setInput(updatedRecord.getInput()); + existingRecord.setOutput(updatedRecord.getOutput()); + + return measurementRecordRepository.save(existingRecord); + } +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 99d0060..967b60c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1 +1,23 @@ spring.application.name=oraclequantapi + +logging.file.name=logs/application.log + +logging.logback.rollingpolicy.max-file-size=10MB + +logging.logback.rollingpolicy.max-history=7 + +logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n + +logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%level] %logger{36} - %msg%n + +spring.datasource.url=jdbc:oracle:thin:@192.168.100.42:1521/XE + +spring.datasource.username=system + +spring.datasource.password=oracle + +spring.datasource.driver-class-name=oracle.jdbc.OracleDriver + +spring.jpa.hibernate.ddl-auto=update + +spring.jpa.show-sql=true diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..1376219 --- /dev/null +++ b/version.txt @@ -0,0 +1,94 @@ +v0.1.0 - Project creation/setup milestone. + +----------------------------------------------- + +v0.2.0 - Dependencies + architecture established. + +----------------------------------------------- + +v0.3.0 - Core decoding engine completed: + +* continuation mode +* parser state movement +* DecodedNumber + +----------------------------------------------- + +v0.4.0 - Package workflow foundation: + +* package count decoding +* accumulation variables +* nested parsing structure + +----------------------------------------------- + +v0.5.0 - Completed encoded measurement parser engine + +----------------------------------------------- + +v0.6.0 - Implemented Spring Boot REST API integration + +----------------------------------------------- + +v0.7.0 - Added API validation and centralized exception handling + +----------------------------------------------- + +v0.8.0 - Added rolling application logging infrastructure + +----------------------------------------------- + +v0.9.0 - Added Oracle database and JPA datasource configuration + +----------------------------------------------- + +v1.0.0 - Implemented Oracle database persistence for measurement history + +----------------------------------------------- + +v1.1.0 - Added measurement history retrieval endpoint + +----------------------------------------------- + +v1.2.0 - Added measurement history CRUD operations: + +* GET measurement history by id +* UPDATE measurement history via PUT +* DELETE measurement history records + +----------------------------------------------- + +v1.3.0 - Added request metadata persistence: + +* timestamp tracking +* source IP address tracking +* Oracle XE history persistence improvements + +----------------------------------------------- + +v1.4.0 - Improved parser validation and exception handling: + +* custom InvalidMeasurementInputException +* invalid character validation +* centralized error responses + +----------------------------------------------- + +v1.5.0 - Production deployment completed: + +* Maven runnable fat JAR packaging +* Oracle Linux deployment +* SSH/SCP deployment workflow +* REST API validation via curl + +----------------------------------------------- + +v2.0.0 - Final production-ready evaluation release + +* Full Oracle XE persistence support +* Complete measurement history CRUD functionality +* Oracle Linux deployment verified +* Logging and rolling file retention enabled +* README and deployment documentation finalized + +----------------------------------------------- \ No newline at end of file