Skip to content

joeltadeu/tus-oop2-assignment-1

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“š Library Management Service

A Spring Boot 3 REST API for managing library operations. It enables librarians to manage publications (books and journals) and their cover images, while allowing members to borrow and return items. The system also provides advanced statistical endpoints for analyzing borrowing trends. The system demonstrates solid object-oriented design, modern Java 25 features, and an in-memory persistence layer β€” no external database required.


🧭 Project Overview

🎯 Purpose

The Library Management Service provides RESTful endpoints for managing loans, members, publications, and library analytics. It allows:

  • Librarians to manage the publication catalog (Books and Journals), including creating, updating, deleting, and uploading cover images.
  • Members to borrow one or multiple available items, return selected or all items, and view loan details.
  • System Analysts to view statistical reports, such as top borrowed items, overdue lists, and rolling monthly activity trends.

βš™οΈ Main Features

  • Publication Management: Full CRUD operations for books and journals with support for localized titles.
  • Image Handling: Upload and serve cover images using NIO.2 with strict validation and path traversal protection.
  • Loan Processing: Checkout items with concurrent availability checks and track due dates.
  • Statistical Reporting: Dedicated endpoints for analytics, including top borrowers, yearly trends, and rolling averages using Gatherers.
  • Concurrency: Parallel processing during checkout to verify item availability efficiently.
  • Localization: Internationalization (i18n) support for error messages and publication titles.
  • In-Memory Storage: Uses thread-safe maps for persistence, eliminating the need for an external database.
  • Auto-initialized Data: Preloaded sample data for members, publications, and historical loans.

πŸ—οΈ Architecture Overview

This API follows a three-layer architecture:

Alt text

Each layer has a clear responsibility:

  • Controller: Handles HTTP requests and responses.
  • Service: Contains business logic for borrowing and returning items.
  • Repository: Manages in-memory storage using static maps.

🧩 UML Diagrams

🧱 Class Diagram

Alt text


πŸ” Sequence Diagram

Create Publication Flow

Alt text

Borrow Flow

Alt text

Return Flow

Alt text


🌐 API Documentation

πŸ”— Swagger OpenAPI Documentation

The application provides interactive API documentation using SpringDoc OpenAPI 3.

πŸ“ Access Points

βš™οΈ Configuration

The OpenAPI documentation is configured in application.yml:

springdoc:
  api-docs:
    path: /api-docs
    groups:
      enabled: true
  swagger-ui:
    path: /swagger-ui.html
    operationsSorter: method
    tagsSorter: alpha
    disable-swagger-default-url: true

πŸ“‹ Features

  • Interactive Testing: Execute API calls directly from the browser
  • Schema Documentation: Complete request/response model documentation
  • Error Responses: All possible HTTP status codes and error formats
  • Parameter Documentation: Detailed parameter descriptions and examples

πŸš€ API Endpoints

πŸ“€ Loan

1️⃣ Checkout Items

POST /v1/members/{memberId}/loans

Create a new loan for a member by borrowing one or more available items.

Request Example

{
  "items": [1, 2]
}

Success Response

{
  "id": 1,
  "memberId": 1,
  "loanDate": "2025-10-25",
  "expectedReturnDate": "2025-11-08",
  "items": [
    { "id": 1, "title": "Clean Code", "type": "BOOK" },
    { "id": 2, "title": "Nature Neuroscience", "type": "JOURNAL" }
  ],
  "status": "OPEN"
}

Error Example

{
  "timestamp": "2025-10-25T15:30:00",
  "status": 409,
  "error": "Conflict",
  "message": "Item 'Clean Code' is currently loaned out",
  "path": "/v1/members/1/loans"
}

2️⃣ List Member Loans

GET /v1/members/{memberId}/loans

Retrieve a summary of all loans for a member.

Success Response

[
  {
    "loanId": 1,
    "loanDate": "2025-10-25",
    "expectedReturnDate": "2025-11-08",
    "status": "OPEN"
  }
]

3️⃣ Get Loan Details

GET /v1/loans/{loanId}

Fetch detailed information about a specific loan and its items.

Success Response

{
  "id": 1,
  "memberId": 1,
  "loanDate": "2025-10-25",
  "expectedReturnDate": "2025-11-08",
  "items": [
    { "id": 1, "title": "Clean Code", "type": "BOOK" }
  ],
  "status": "OPEN"
}

4️⃣ Return Items

POST /v1/loans/{loanId}/returns

Return one or more items, or all if no body is provided.

Request Example (specific items)

{
  "items": [1]
}

Response Example (loan closed)

{
  "id": 1,
  "memberId": 1,
  "loanDate": "2025-10-25",
  "expectedReturnDate": "2025-11-08",
  "items": [
    { "id": 1, "title": "Clean Code", "type": "BOOK", "returnedDate": "2025-10-30" }
  ],
  "status": "CLOSED"
}

πŸ“– Publication

1️⃣ Create Publication

POST /v1/publications

Create a new book or journal entry.

Request Example (Book)

{
  "type": "BOOK",
  "title": "Domain-Driven Design",
  "author": "Eric Evans",
  "publicationDate": "2003-09-30",
  "category": "TECHNOLOGY",
  "isbn": "9780321125217",
  "genre": "Software Architecture",
  "pageCount": 560
}

Success Response (201 Created)

{
  "id": 100,
  "type": "BOOK",
  "title": "Domain-Driven Design",
  "author": "Eric Evans",
  "available": true,
  "isbn": "9780321125217",
  "genre": "Software Architecture",
  "pageCount": 560
}

2️⃣ List Publications

GET /v1/publications

Retrieve all publications. Supports optional filtering by category.

Query Parameters

  • category (optional): Filter by category (e.g., TECHNOLOGY, SCIENCE).

Success Response

[
  {
    "id": 1,
    "type": "BOOK",
    "title": "Clean Code",
    "author": "Robert C. Martin",
    "available": true
  }
]

️3️⃣ Get Publication by ID

GET /v1/publications/{id}

Retrieve detailed information about a specific publication.

Success Response

{
  "id": 1,
  "type": "BOOK",
  "title": "Clean Code",
  "author": "Robert C. Martin",
  "publicationDate": "2008-08-01",
  "category": "TECHNOLOGY",
  "available": true,
  "isbn": "9780132350884",
  "genre": "Programming",
  "pageCount": 464
}

4️⃣️ Publication: Update Publication

PUT /v1/publications/{id}

Update an existing publication. The type in the request must match the stored type.

Request Example

{
  "type": "BOOK",
  "title": "Clean Code: A Handbook of Agile Software Craftsmanship",
  "author": "Robert C. Martin",
  "publicationDate": "2008-08-01",
  "category": "TECHNOLOGY",
  "isbn": "9780132350884",
  "genre": "Software Engineering",
  "pageCount": 464
}

5️⃣ Delete Publication

DELETE /v1/publications/{id}

Delete a publication. Returns 204 No Content. Fails with 409 Conflict if the item is currently on loan.


6️⃣ Upload Cover Image

POST /v1/publications/{id}/image

Upload or replace the cover image for a publication.

Request

  • Content-Type: multipart/form-data
  • Parameter: file (File)

Success Response

{
  "id": 1,
  "coverImageFileName": "cover_1_1714123200000.jpg",
  ...
}

7️⃣ Get Cover Image

GET /v1/publications/{id}/image

Retrieve the cover image file.

Response

  • Content-Type: image/jpeg, image/png, or image/webp.
  • Body: Raw image bytes.

πŸ“ˆ Statistics

1️⃣ Distinct Borrowers

GET /v1/statistics/members/borrowers

Returns a sorted list of all unique member names who have ever borrowed an item.

Success Response

[
  "Alice Johnson",
  "Bob Williams",
  "Charlie Davis"
]

2️⃣ Top Borrowed Items

GET /v1/statistics/items/top-borrowed

Returns the most frequently borrowed items across all time.

Query Parameters

  • topN (optional): Number of results (default 10).

Success Response

[
  { "itemId": 1, "title": "Clean Code", "type": "BOOK", "loanCount": 15 }
]

3️⃣ Loan Count by Category

GET /v1/statistics/categories/loan-counts

Returns categories ranked by how many times their items were borrowed.

Success Response

[
  { "category": "TECHNOLOGY", "loanCount": 50 },
  { "category": "SCIENCE", "loanCount": 30 }
]

4️⃣ Top Borrowers by Year

GET /v1/statistics/members/top-borrowers/year/{year}

Returns members ranked by items borrowed in the given year, most active first.

Path Parameters

  • year: The year to analyze (e.g., 2024).

Success Response

[
  {
    "memberId": 1,
    "firstName": "Alice",
    "lastName": "Johnson",
    "email": "alice@example.com",
    "totalLoans": 12,
    "totalItems": 25
  }
]

5️⃣ Items Borrowed by Year

GET /v1/statistics/items/borrowed/year/{year}

Returns a lightweight summary of items borrowed in a given year.

Success Response

[
  { "itemId": 1, "title": "Clean Code", "type": "BOOK", "loanCount": 8 }
]

6️⃣ Yearly Loan Trend

GET /v1/statistics/loans/yearly-trend

Returns total items loaned per calendar year across all history.

Success Response

[
  { "year": 2024, "loanCount": 120 },
  { "year": 2025, "loanCount": 85 }
]

7️⃣ Overdue Items

GET /v1/statistics/loans/overdue

Returns all unreturned items past their due date, sorted by days overdue descending.

Success Response

[
  {
    "loanId": 50,
    "memberId": 2,
    "memberName": "Bob Williams",
    "itemId": 5,
    "itemTitle": "Effective Java",
    "expectedReturnDate": "2025-11-01",
    "daysOverdue": 15
  }
]

8️⃣ Average Items Per Loan

GET /v1/statistics/loans/avg-items

Returns the all-time average number of items per loan transaction.

Success Response

2.5

9️⃣ Member Active Loans Check

GET /v1/statistics/members/{memberId}/has-active-loans

Returns true if the specified member currently has at least one open loan.

Success Response

true

1️⃣0️⃣ Rolling Monthly Activity

GET /v1/statistics/members/monthly-activity

Returns per-member monthly loan counts with a sliding-window rolling average.

Query Parameters

  • windowSize (optional): Number of months for the rolling window (default 3).

Success Response

{
    "1": [
       { "year": 2025, "month": 10, "loanCount": 2, "rollingAvg": 2.0 },
       { "year": 2025, "month": 11, "loanCount": 4, "rollingAvg": 3.0 }
    ]
}  

πŸ“š Generating Javadoc

The project includes comprehensive Javadoc documentation for all classes, methods, and records.

πŸ› οΈ Generate Documentation

# Generate Javadoc
mvn javadoc:javadoc

# View generated documentation
open target/site/apidocs/index.html

πŸ“– Access Javadoc

After generation, open target/site/apidocs/index.html in your browser to view:

  • Class Documentation: Detailed descriptions of all entities, services, and controllers
  • Method Documentation: Complete parameter and return value documentation
  • Record Documentation: DTO field descriptions and usage examples
  • Exception Documentation: When and why each exception is thrown
  • Inheritance Trees: Class hierarchy and implementation relationships

πŸ“‹ Javadoc Features

  • Complete Coverage: All public and protected methods documented
  • Cross-References: @see tags linking related classes and methods
  • Parameter Validation: Documentation of constraints and validation rules
  • Exception Handling: Detailed @throws documentation
  • Return Value Descriptions: Clear explanations of what each method returns

🧠 Object-Oriented Design Explanation

Principle Description Example
Encapsulation Data hidden behind getters/setters LibraryItem fields are private
Inheritance Common behavior in base classes Book and Journal extend LibraryItem
Polymorphism Shared interface with different implementations getType() returns "BOOK" or "JOURNAL"
Abstraction Abstract parent defines contract LibraryItem is an abstract sealed class
Interfaces Business contracts LoanService defines loan operations

β˜• Fundamental Java Features

Feature Example Description
Sorting .sorted(Comparator.comparingInt( LoanStreakStats::streak).reversed()) Sorting collections using Comparator references
Lambdas Callable<LoanItem> task = () -> { ... } Usage of functional interfaces (Callable, Function)
Streams (Intermediate) .filter(...), .map(...), .flatMap(...), .distinct(), .limit(...) Intermediate operations to transform or filter data streams
Streams (Terminal) .collect(...), .anyMatch(...), .max(...), .forEach(...), findFirst(), count() Terminal operations to produce results or side-effects
Collectors Collectors.toMap(...), Collectors.groupingBy(...) Mutable reduction operations for maps and grouping
Switch Expressions Used for modern control flow Simplified and expressive switch syntax
Sealed Classes public sealed abstract class LibraryItem permits Book, Journal {} Restricts subclassing for type safety
Records public record LoanResponse(...) {} Compact immutable data transfer obj ects
Date/Time API plusDays(...), ChronoUnit.DAYS.between(...) Modern date/time handling

πŸ› οΈ Advanced Java Features

Feature Example Description
Concurrency ExecutorService executor = Executors.newFixedThreadPool(threads); Managing concurrent tasks using thread pools and Callables
NIO.2 Files.createDirectories(...), Files.copy(...), Path.resolve(...) Modern non-blocking I/O operations for robust file handling
Localization ReloadableResourceBundleMessageSource, LocaleResolver Internationalization (i18n) support via message bundles
Gatherers .gather(Gatherer.ofSequential(...)) Custom intermediate operations for stateful stream processing

πŸš€ Java 25 Features Utilized

JEP Feature Example in Code
JEP 481 Scoped Values CorrelationIdFilter uses ScopedValue for request context propagation
JEP 485 Stream Gatherers StatisticsServiceImpl uses custom gatherers for rolling averages
JEP 513 Flexible Constructor Bodies Book class constructor with validation before super()
JEP 512 Compact Source Files and Instance Main Application class with simplified main() method

πŸ”§ Code Examples

JEP 481: Scoped Values

// CorrelationIdFilter.java
ScopedValue.where(RequestContext.CORRELATION_ID, finalCorrelationId)
    .run(() -> LoggingContext.runWithLoggingContext(() -> { ... }));

JEP 513: Flexible Constructor Bodies

public class Book extends LibraryItem {
    public Book(String title, String author, LocalDate publicationDate, String isbn, String genre, int pageCount) {
        super(title, author, publicationDate);
        if (pageCount <= 0) {
            throw new IllegalArgumentException("Page count must be positive");
        }
        this.isbn = isbn;
        this.genre = genre;
        this.pageCount = pageCount;
    }
}

JEP 512: Compact Source Files and Instance Main

@SpringBootApplication
public class LibraryManagementApplication {
    public static void main(String[] args) {
        SpringApplication.run(LibraryManagementApplication.class, args);
    }
}

πŸ’Ύ In-Memory Data Management

This project replaces JPA/H2 with a thread-safe in-memory approach:

  • Uses ConcurrentHashMap as storage for each repository.
  • Uses AtomicLong to auto-generate IDs.
  • Loads mock data via @PostConstruct in LibraryBootstrap:
    • 3 members: Alice, Bob, Charlie
    • Multiple publications: Books and Journals across various categories (Tech, Science, Fiction).
    • Historical Loans: Pre-seeded loans across multiple years (2024-2025) to support statistical analysis.

Example repository initialization:

@PostConstruct
void seed() {
  memberData.getMembers().forEach(memberRepository::save);
  itemData.getBooks().forEach(itemRepository::save);
  // ...
}

πŸš€ Running the Application

Prerequisites

  • Java 25+
  • Maven 3.9+

Run Command

mvn spring-boot:run

Default Endpoint

http://localhost:8080/v1

βœ… Tests

This project includes both unit tests and integration tests using Karate DSL to ensure correctness and reliability of the Library Management Service.


πŸ§ͺ Unit Tests

Unit tests validate the behavior of individual components such as services and repositories. They are written using JUnit 5 and cover core business logic like loan creation, item return, and exception handling.

▢️ Run Unit Tests

mvn test

This command will execute all unit tests located in the src/test/java directory. Results will be displayed in the console and stored in the target/surefire-reports folder.


πŸ₯‹ Integration Tests with Karate DSL

Karate is a powerful DSL for testing REST APIs. It allows you to write expressive, scenario-based tests that validate the full request/response lifecycle.

πŸ“Œ Goals of Karate Tests

  • Verify end-to-end flows for borrowing and returning items.
  • Ensure the API behaves correctly under different conditions.
  • Simulate real-world usage with dynamic data and assertions.

🧾 Covered Scenarios

  • Full Borrow and Return Flow Borrow items, save loan ID, return items, assert loan is closed and returnedDate is set.
  • Partial Return Flow Borrow items 3 and 5, return item 3, then item 5 β€” validating intermediate loan states.

▢️ Run Karate Tests

mvn -Dtest=com.lms.library.karate.BorrowAndReturnBooksTest test

This command runs the Karate feature file located at classpath:karate/BorrowAndReturnBooks.feature.

πŸ“Š View Test Report

After execution, a detailed HTML report is generated at:

target/karate-reports/karate-summary.html

This report includes scenario results, request/response logs, and assertion outcomes. Alt text

🧾 License

This project is for educational and demonstration purposes β€” showing clean architecture, Java 25 features, and OOP principles in a Spring Boot REST API.

About

Spring Boot 3 REST API for library management with books, journals, loans, cover images, and analytics. Demonstrates modern Java 25, concurrency, and in-memory storage.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors