Skip to content

ghazalab96/programming2_java

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

Project Checklist

Overall Learning Outcomes

  • Built a REST API in Java
  • Practiced MVC-based project structure
  • Implemented CRUD operations
  • Worked with HTTP methods, paths, headers, and status codes
  • Added a service layer for business logic
  • Used Maven for dependency management
  • Used Gson for JSON parsing
  • Used Java Streams for filtering data
  • Added unit tests with JUnit
  • Integrated H2 database persistence
  • Created a repository layer for database access
  • Used prepared statements for safer SQL operations
  • Added custom exception handling
  • Used Mockito for testing with mocked dependencies
  • Refactored the project using SOLID principles
  • Discussed and applied design patterns
  • Documented the development process and technical decisions

Exercise 1: REST API, Controller and Model

Base Template and API Understanding

  • Reviewed the base template and understood the application structure
  • Identified the available API endpoints
  • Prepared sample requests with cURL or Postman
  • Explained the role of HTTP methods such as GET, POST, PUT, and DELETE
  • Explained what HTTP headers are and where they are set in the application
  • Understood the role of controller classes in handling HTTP requests

Model Layer

  • Created a models package
  • Created the Movie model class
  • Added private fields: id, title, genre, and releaseYear
  • Added a default constructor with automatic UUID generation
  • Added a constructor with title, genre, and releaseYear
  • Added getters and setters
  • Overrode the toString() method
  • Created generateDummyMovies() to generate sample movie data

Movie Controller and CRUD Endpoints

  • Created the MovieController class inside the controllers package
  • Implemented the HttpHandler interface
  • Added endpoint handling logic inside the handle() method
  • Returned 404 Path not found for unknown endpoints
  • Stored movie data in a movie list generated by Movie.generateDummyMovies()
  • Implemented GET /api/movies/getAll
  • Implemented POST /api/movies/add
  • Implemented DELETE /api/movies/delete
  • Implemented PUT /api/movies/update
  • Returned correct success responses for CRUD operations
  • Handled 400 Bad Request for invalid input
  • Handled 404 Not Found for missing movies
  • Handled 405 Method Not Allowed for wrong HTTP methods

Exercise 2: Maven, Libraries and Unit Testing

Search Endpoint with Query Parameters

  • Added GET /api/movies/search
  • Created parseQueryParams() inside ApiUtils
  • Extracted query parameters from the URL
  • Supported searching by title, genre, and releaseYear
  • Made search case-insensitive
  • Supported partial string search
  • Returned matching movies as JSON

Service Layer

  • Created a services package
  • Created the MovieService class
  • Moved business logic from MovieController into MovieService
  • Injected the movie list through the MovieService constructor
  • Refactored filtering logic to use Java Streams and lambda expressions
  • Updated MovieController to call MovieService methods
  • Improved separation of concerns between controller and service layer

Maven and Gson

  • Added Maven support
  • Added the Gson dependency
  • Replaced manual JSON parsing with Gson
  • Used Gson to parse request bodies into Movie objects
  • Used Gson to generate JSON responses

Unit Testing

  • Added JUnit dependency
  • Added Maven Surefire plugin
  • Created unit tests for MovieService
  • Used @BeforeEach to prepare test data
  • Tested addMovie
  • Tested deleteMovie
  • Tested updateMovie
  • Tested searchMovies
  • Tested edge cases such as duplicate movies
  • Tested partial search behavior
  • Ran tests using mvn test

Exercise 3: Exception Handling and Persistence

H2 Database Integration

  • Added H2 database dependency
  • Created DatabaseUtil
  • Implemented getConnection()
  • Added database schema initialization
  • Created the movies table if it does not already exist
  • Used UUID as the primary key
  • Stored movie data in the database instead of only in memory

Repository Layer

  • Created the MovieRepository class
  • Moved direct database access into the repository layer
  • Implemented add(Movie movie)
  • Implemented findAll()
  • Implemented delete(Movie movie)
  • Implemented update(Movie movie)
  • Used SQL statements for CRUD operations
  • Used Connection.prepareStatement() for database queries
  • Improved persistence and database separation

Service Layer Refactoring

  • Refactored MovieService to use MovieRepository
  • Injected MovieRepository through the MovieService constructor
  • Updated CRUD methods to delegate database operations to the repository
  • Verified database integration manually by adding, reading, updating, and deleting movies

Custom Exception Handling

  • Created MovieNotFoundException
  • Created DatabaseException
  • Threw MovieNotFoundException when a movie could not be found
  • Threw DatabaseException for SQL or database connection problems
  • Propagated exceptions from repository to service layer
  • Propagated exceptions from service layer to controller layer
  • Converted exceptions into meaningful HTTP responses
  • Mapped MovieNotFoundException to 404 Not Found
  • Mapped DatabaseException to 500 Internal Server Error
  • Mapped malformed JSON errors to 400 Bad Request
  • Kept the application stable during runtime errors

Mockito and Updated Unit Tests

  • Added Mockito dependency
  • Mocked MovieRepository in MovieServiceTest
  • Updated service tests to work with the repository layer
  • Tested successful service behavior with mocked repository data
  • Tested MovieNotFoundException
  • Tested DatabaseException
  • Used assertThrows() for exception tests
  • Verified that exceptions are correctly propagated

Exercise 4: Design Patterns and SOLID Principles

SOLID Principles

  • Reviewed the application based on the SOLID principles
  • Identified areas where the design could be improved
  • Improved Single Responsibility Principle by keeping controller, service, repository, and model responsibilities separate
  • Improved Open/Closed Principle by making parts of the application easier to extend without changing existing code
  • Improved Liskov Substitution Principle by using abstractions where possible
  • Improved Interface Segregation Principle by avoiding unnecessary dependencies between classes
  • Improved Dependency Inversion Principle by injecting dependencies instead of creating them directly inside classes
  • Explained why each refactoring improves maintainability
  • Prepared explanations for the exercise demonstration

Creational Design Pattern

  • Introduced or discussed a creational design pattern
  • Used or discussed a factory-style approach for creating objects where useful
  • Explained how the pattern helps object creation become cleaner and more flexible

Structural Design Pattern

  • Introduced or discussed a structural design pattern
  • Used or discussed repository-style separation as a structural improvement
  • Explained how this improves the structure between service and database logic

Behavioral Design Pattern

  • Introduced or discussed a behavioral design pattern
  • Used or discussed a strategy-style approach for search or filtering logic
  • Explained how this would make search behavior easier to extend

Documentation and Presentation

  • Documented the SOLID improvements
  • Documented the design patterns used or discussed
  • Explained why the selected patterns are meaningful in this application
  • Prepared to justify design decisions during the lecture demonstration
  • Prepared to discuss possible alternatives and future improvements
# Movie REST API

A Java-based Movie REST API built step by step as part of the Programming 2 exercises.

The project started as a simple REST API for managing movies and gradually evolved into a more structured backend application with a controller layer, service layer, repository layer, H2 database persistence, exception handling, unit testing, mocking, and design pattern improvements.

## Project Overview

This application manages movies through a simple REST API. It supports creating, reading, updating, deleting, and searching movies.

The current architecture follows a layered structure:

```text
API Request

Controller Layer

Service Layer

Repository Layer

H2 Database

The goal of the project was not only to make the API work, but also to improve the design step by step by applying clean architecture ideas, separation of concerns, exception propagation, testing strategies, SOLID principles, and design patterns.

Features

  • REST API for movie management
  • MVC-inspired structure
  • Controller layer for HTTP request handling
  • Service layer for business logic
  • Repository layer for database access
  • H2 database integration
  • Automatic database table initialization
  • JSON parsing with Gson
  • CRUD operations for movies
  • Search endpoint with query parameters
  • Prepared SQL statements
  • Custom exception handling
  • HTTP response mapping for errors
  • Unit testing with JUnit
  • Mocking with Mockito
  • SOLID-based refactoring
  • Design pattern improvements

Tech Stack

  • Java
  • Maven
  • H2 Database
  • Gson
  • JUnit
  • Mockito
  • Java HTTP Server
  • IntelliJ IDEA

API Endpoints

Get All Movies

GET /api/movies/getAll

Returns all movies stored in the database.

Example request:

curl -X GET http://localhost:8080/api/movies/getAll

Example response:

[
  {
    "id": "200ce2c9-765d-4486-bdb1-bf68963d8142",
    "title": "Inception",
    "genre": "Sci-Fi",
    "releaseYear": 2010
  },
  {
    "id": "de122389-b7f5-4c61-8588-5b651fb31382",
    "title": "The Silent Orbit",
    "genre": "Sci-Fi",
    "releaseYear": 2021
  }
]

Add Movie

POST /api/movies/add

Adds a new movie to the database.

Example request:

curl -X POST http://localhost:8080/api/movies/add \
  -H "Content-Type: application/json" \
  -d '{"title":"Inception","genre":"Sci-Fi","releaseYear":2010}'

Example response:

{
  "message": "Movie added successfully"
}

Delete Movie

DELETE /api/movies/delete

Deletes a movie based on title, genre, and release year.

Example request:

curl -X DELETE http://localhost:8080/api/movies/delete \
  -H "Content-Type: application/json" \
  -d '{"title":"Inception","genre":"Sci-Fi","releaseYear":2010}'

Example response:

{
  "message": "Movie deleted successfully"
}

If the movie does not exist:

{
  "error": "Movie not found for deletion"
}

Update Movie

PUT /api/movies/update

Updates a movie using its ID.

Example request:

curl -X PUT http://localhost:8080/api/movies/update \
  -H "Content-Type: application/json" \
  -d '{
    "id":"200ce2c9-765d-4486-bdb1-bf68963d8142",
    "title":"Inception Updated",
    "genre":"Sci-Fi",
    "releaseYear":2010
  }'

Example response:

{
  "message": "Movie updated successfully"
}

Search Movies

GET /api/movies/search

Searches movies using query parameters.

Supported query parameters:

  • title
  • genre
  • releaseYear

The search supports partial string matches and ignores upper/lower case.

Example requests:

curl "http://localhost:8080/api/movies/search?title=incep"
curl "http://localhost:8080/api/movies/search?genre=Sci-Fi"
curl "http://localhost:8080/api/movies/search?title=Inception&releaseYear=2010&genre=Sci-Fi"

Example response:

[
  {
    "id": "200ce2c9-765d-4486-bdb1-bf68963d8142",
    "title": "Inception",
    "genre": "Sci-Fi",
    "releaseYear": 2010
  }
]

Database

The application uses an H2 database.

The database table is initialized automatically when the application starts.

CREATE TABLE IF NOT EXISTS movies (
    id UUID PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    genre VARCHAR(100) NOT NULL,
    release_year INT NOT NULL
);

The repository layer uses prepared statements for all database operations.

Example:

String sql = "INSERT INTO movies (id, title, genre, release_year) VALUES (?, ?, ?, ?)";
PreparedStatement statement = connection.prepareStatement(sql);

statement.setObject(1, movie.getId());
statement.setString(2, movie.getTitle());
statement.setString(3, movie.getGenre());
statement.setInt(4, movie.getReleaseYear());

Prepared statements are used because they:

  • Separate SQL logic from user input
  • Help prevent SQL injection
  • Handle special characters correctly
  • Make database code cleaner and safer

Error Handling

The application uses custom exceptions to keep error handling clear and meaningful.

Custom exceptions:

  • MovieNotFoundException
  • DatabaseException

The controller catches exceptions and converts them into proper HTTP responses.

Exception HTTP Response
MovieNotFoundException 404 Not Found
DatabaseException 500 Internal Server Error
JsonSyntaxException 400 Bad Request
Unknown Exception 500 Internal Server Error

Example error response:

{
  "error": "Movie not found for deletion"
}

Testing

The project includes unit tests for the service layer.

Testing tools:

  • JUnit
  • Mockito

After adding the repository layer, the service tests use a mocked MovieRepository instead of connecting to the real database.

This keeps the tests focused on service behavior and avoids depending on the database during unit tests.

Example:

when(movieRepository.delete(movieToDelete))
        .thenThrow(new DatabaseException("Database connection error"));

assertThrows(DatabaseException.class, () -> {
    movieService.deleteMovie(movieToDelete);
});

SOLID Principles

The project was also analyzed and improved using the SOLID principles.

Single Responsibility Principle

Each class has one main responsibility.

  • MovieController handles HTTP requests and responses.
  • MovieService contains business logic.
  • MovieRepository handles database operations.
  • DatabaseUtil manages database connection and initialization.
  • Custom exception classes represent specific error cases.

This makes the code easier to understand, test, and maintain.

Open/Closed Principle

The application can be extended without heavily modifying existing code.

For example, the service and repository structure makes it easier to add new operations later, such as filtering by director, rating, or runtime.

New behavior can be added in the service or repository without putting all logic inside the controller.

Liskov Substitution Principle

The application can be improved by depending on abstractions instead of concrete classes.

For example, a MovieRepositoryInterface could be introduced:

public interface MovieRepositoryInterface {
    void add(Movie movie) throws DatabaseException;
    List<Movie> findAll() throws DatabaseException;
    boolean delete(Movie movie) throws DatabaseException, MovieNotFoundException;
    boolean update(Movie movie) throws DatabaseException, MovieNotFoundException;
}

Then MovieRepository can implement this interface.

This would make it possible to replace the database repository with another implementation, such as an in-memory repository for testing.

Interface Segregation Principle

The repository and service interfaces should stay focused.

Instead of creating one large interface with many unrelated methods, the application should keep interfaces small and meaningful.

For this project, the movie-related methods belong together because they all work with the Movie model.

Dependency Inversion Principle

The service layer should not be tightly coupled to a concrete repository implementation.

The improved design is to inject the repository dependency into the service constructor.

Example:

public MovieService(MovieRepository movieRepository) {
    this.movieRepository = movieRepository;
}

This makes the service easier to test because a mock repository can be injected during unit tests.

Design Patterns

The project can be explained using several design patterns.

Creational Pattern: Factory Pattern

A factory can be used to create service or repository objects in one central place.

For example, instead of creating dependencies directly in the controller, an application factory could create and wire them together.

Example idea:

public class AppFactory {
    public static MovieService createMovieService() {
        MovieRepository repository = new MovieRepository();
        return new MovieService(repository);
    }
}

This helps centralize object creation and keeps the controller cleaner.

Structural Pattern: Repository Pattern

The repository pattern is used to separate database access from business logic.

MovieRepository hides the SQL details from the rest of the application.

The service does not need to know how SQL works. It only calls methods such as:

movieRepository.findAll();
movieRepository.add(movie);
movieRepository.delete(movie);
movieRepository.update(movie);

This improves maintainability because database logic is isolated in one layer.

Behavioral Pattern: Strategy Pattern

The search behavior can be improved with the Strategy Pattern.

For example, different search strategies could be created for:

  • Search by title
  • Search by genre
  • Search by release year
  • Combined search

Instead of putting all search conditions in one method, each strategy could handle one type of filtering.

Example idea:

public interface MovieSearchStrategy {
    boolean matches(Movie movie, String value);
}

This would make search logic easier to extend and test.

Running the Project

Clone the repository:

git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY_NAME.git
cd YOUR_REPOSITORY_NAME

Build the project:

mvn clean install

Run the application from the Main class in IntelliJ.

The server runs on:

http://localhost:8080

Test the API:

curl -X GET http://localhost:8080/api/movies/getAll

Running Tests

Run all tests with Maven:

mvn test

Or run the test class directly in IntelliJ.

Notes About H2 and IntelliJ

If you use H2 as a file-based database, avoid opening the database in IntelliJ's Database window while the Java server is running.

The database file can become locked.

Recommended workflow:

Testing API:
1. Disconnect IntelliJ Database
2. Run Java server
3. Use browser, Postman, or curl

Inspecting database:
1. Stop Java server
2. Open IntelliJ Database window
3. Refresh the H2 database
4. Check the MOVIES table

Project Structure

Example structure:

src
└── main
    └── java
        └── at.ac.fhcampuswien
            ├── Main.java
            ├── ApiUtils.java
            ├── controllers
            │   └── MovieController.java
            ├── database
            │   └── DatabaseUtil.java
            ├── exceptions
            │   ├── DatabaseException.java
            │   └── MovieNotFoundException.java
            ├── models
            │   └── Movie.java
            ├── repositories
            │   └── MovieRepository.java
            └── services
                └── MovieService.java

What I Learned

Through this project, I practiced:

  • Building REST endpoints in Java
  • Structuring a backend application
  • Working with controller, service, and repository layers
  • Using Gson for JSON parsing
  • Connecting Java to an H2 database
  • Writing SQL queries with prepared statements
  • Handling checked exceptions
  • Mapping application errors to HTTP responses
  • Writing unit tests with JUnit
  • Mocking dependencies with Mockito
  • Applying SOLID principles
  • Thinking about design patterns and software maintainability

Author

Ghazal Arbabzadeh

About

implementing advanced java features

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Java 100.0%