Fix broken links in quarkus docs

Fix broken links in quarkus docs

[[quarkusJavaQuickStart]]
= Quarkus Java quick start
:doctype: book
:imagesdir: ../..
:sectnums:
:icons: font
:quickstartsCloneUrl: https://github.com/kiegroup/optaplanner-quickstarts
:quickstartsArchiveUrl: https://www.optaplanner.org/download/download.html
:quarkusQuickstartUrl: https://github.com/kiegroup/optaplanner-quickstarts/tree/stable/use-cases/school-timetabling

// Keep this in sync with the quarkus repo's copy
// https://github.com/quarkusio/quarkus/blob/main/docs/src/main/asciidoc/optaplanner.adoc
// Keep this also in sync with spring-boot-quickstart.adoc where applicable

This guide walks you through the process of creating a https://quarkus.io/[Quarkus] application
with https://www.optaplanner.org/[OptaPlanner]'s constraint solving Artificial Intelligence (AI).

== What you will build

You will build a REST application that optimizes a school timetable for students and teachers:

image::quickstart/school-timetabling/schoolTimetablingScreenshot.png[]

Your service will assign `Lesson` instances to `Timeslot` and `Room` instances automatically
by using AI to adhere to hard and soft scheduling _constraints_, such as the following examples:

* A room can have at most one lesson at the same time.
* A teacher can teach at most one lesson at the same time.
* A student can attend at most one lesson at the same time.
* A teacher prefers to teach all lessons in the same room.
* A teacher prefers to teach sequential lessons and dislikes gaps between lessons.
* A student dislikes sequential lessons on the same subject.

Mathematically speaking, school timetabling is an _NP-hard_ problem.
This means it is difficult to scale.
Simply brute force iterating through all possible combinations takes millions of years
for a non-trivial dataset, even on a supercomputer.
Luckily, AI constraint solvers such as OptaPlanner have advanced algorithms
that deliver a near-optimal solution in a reasonable amount of time.

== Solution source code

Follow the instructions in the next sections to create the application step by step (recommended).

Alternatively, you can also skip right to the completed example:

. Clone the Git repository:
+
[source,shell,subs=attributes+]
----
$ git clone {quickstartsCloneUrl}
----
+
or download an {quickstartsArchiveUrl}[archive].

. Find the solution in {quarkusQuickstartUrl}[the `use-cases` directory]
and run it (see its README file).

== Prerequisites

To complete this guide, you need:

* https://adoptopenjdk.net/[JDK] {java-version}+ with `JAVA_HOME` configured appropriately
* https://maven.apache.org/download.html[Apache Maven] {maven-version}+ or https://gradle.org/install/[Gradle] 4+
* An IDE, such as https://www.jetbrains.com/idea[IntelliJ IDEA], VSCode or Eclipse

== The build file and the dependencies

Use https://code.quarkus.io/[code.quarkus.io] to generate an application
with the following extensions, for Maven or Gradle:

* RESTEasy JAX-RS (`quarkus-resteasy`)
* RESTEasy Jackson (`quarkus-resteasy-jackson`)
* OptaPlanner (`optaplanner-quarkus`)
* OptaPlanner Jackson (`optaplanner-quarkus-jackson`)

Alternatively, generate it from the command line with Maven:

[source,shell,subs=attributes+]
----
$ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=optaplanner-quickstart \
    -Dextensions="resteasy,resteasy-jackson,optaplanner-quarkus,optaplanner-quarkus-jackson" \
    -DnoExamples
$ cd optaplanner-quickstart
----

In Maven, your `pom.xml` file contains these dependencies:

[source,xml,subs=attributes+]
----
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-universe-bom</artifactId>
        <version>{quarkus-version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-quarkus</artifactId>
    </dependency>
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-quarkus-jackson</artifactId>
    </dependency>

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
----

//On the other hand, in Gradle, your `build.gradle` file contains these dependencies:
//
//[source,groovy,subs=attributes+]
//----
//dependencies {
//    implementation enforcedPlatform("io.quarkus:quarkus-universe-bom:{quarkus-version}")
//    implementation 'io.quarkus:quarkus-resteasy'
//    implementation 'io.quarkus:quarkus-resteasy-jackson'
//    implementation 'org.optaplanner:optaplanner-quarkus'
//    implementation 'org.optaplanner:optaplanner-quarkus-jackson'
//
//    testImplementation 'io.quarkus:quarkus-junit5'
//}
//----

include::../school-timetabling/school-timetabling-model.adoc[leveloffset=+1]
include::../school-timetabling/school-timetabling-constraints.adoc[leveloffset=+1]
include::../school-timetabling/school-timetabling-solution.adoc[leveloffset=+1]

== Create the solver service

Now you are ready to put everything together and create a REST service.
But solving planning problems on REST threads causes HTTP timeout issues.
Therefore, the Quarkus extension injects a `SolverManager` instance,
which runs solvers in a separate thread pool
and can solve multiple datasets in parallel.

Create the `src/main/java/org/acme/schooltimetabling/rest/TimeTableResource.java` class:

[source,java]
----
package org.acme.schooltimetabling.rest;

import java.util.UUID;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

import org.acme.schooltimetabling.domain.TimeTable;
import org.optaplanner.core.api.solver.SolverJob;
import org.optaplanner.core.api.solver.SolverManager;

@Path("/timeTable")
public class TimeTableResource {

    @Inject
    SolverManager<TimeTable, UUID> solverManager;

    @POST
    @Path("/solve")
    public TimeTable solve(TimeTable problem) {
        UUID problemId = UUID.randomUUID();
        // Submit the problem to start solving
        SolverJob<TimeTable, UUID> solverJob = solverManager.solve(problemId, problem);
        TimeTable solution;
        try {
            // Wait until the solving ends
            solution = solverJob.getFinalBestSolution();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Solving failed.", e);
        }
        return solution;
    }

}
----

For simplicity's sake, this initial implementation waits for the solver to finish,
which can still cause an HTTP timeout.
The _complete_ implementation avoids HTTP timeouts much more elegantly.

== Set the termination time

Without a termination setting or a `terminationEarly()` event, the solver runs forever.
To avoid that, limit the solving time to five seconds.
That is short enough to avoid the HTTP timeout.

Create the `src/main/resources/application.properties` file:

[source,properties]
----
# The solver runs only for 5 seconds to avoid a HTTP timeout in this simple implementation.
# It's recommended to run for at least 5 minutes ("5m") otherwise.
quarkus.optaplanner.solver.termination.spent-limit=5s
----

OptaPlanner returns _the best solution_ found in the available termination time.
Due to xref:optimization-algorithms/optimization-algorithms.adoc#doesPlannerFindTheOptimalSolution[the nature of NP-hard problems],
the best solution might not be optimal, especially for larger datasets.
Increase the termination time to potentially find a better solution.

== Run the application

First start the application:

[source,shell]
----
$ mvn compile quarkus:dev
----

=== Try the application

Now that the application is running, you can test the REST service.
You can use any REST client you wish.
The following example uses the Linux command `curl` to send a POST request:

[source,shell]
----
$ curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
----

After about five seconds, according to the termination spent time defined in your `application.properties`,
the service returns an output similar to the following example:

[source]
----
HTTP/1.1 200
Content-Type: application/json
...

{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}
----

Notice that your application assigned all four lessons to one of the two time slots and one of the two rooms.
Also notice that it conforms to all hard constraints.
For example, M. Curie's two lessons are in different time slots.

On the server side, the `info` log shows what OptaPlanner did in those five seconds:

[source,options="nowrap"]
----
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4).
... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398).
... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
----

=== Test the application

A good application includes test coverage.

==== Test the constraints

To test each constraint in isolation, use a `ConstraintVerifier` in unit tests.
It tests each constraint's corner cases in isolation from the other tests,
which lowers maintenance when adding a new constraint with proper test coverage.

Add a `optaplanner-test` dependency in your `pom.xml`:
[source,xml]
----
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-test</artifactId>
      <scope>test</scope>
    </dependency>
----

Create the `src/test/java/org/acme/schooltimetabling/solver/TimeTableConstraintProviderTest.java` class:

[source,java]
----
package org.acme.schooltimetabling.solver;

import java.time.DayOfWeek;
import java.time.LocalTime;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.domain.Timeslot;
import org.junit.jupiter.api.Test;
import org.optaplanner.test.api.score.stream.ConstraintVerifier;

@QuarkusTest
class TimeTableConstraintProviderTest {

    private static final Room ROOM = new Room("Room1");
    private static final Timeslot TIMESLOT1 = new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9,0), LocalTime.NOON);
    private static final Timeslot TIMESLOT2 = new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(9,0), LocalTime.NOON);

    @Inject
    ConstraintVerifier<TimeTableConstraintProvider, TimeTable> constraintVerifier;

    @Test
    void roomConflict() {
        Lesson firstLesson = new Lesson(1, "Subject1", "Teacher1", "Group1");
        Lesson conflictingLesson = new Lesson(2, "Subject2", "Teacher2", "Group2");
        Lesson nonConflictingLesson = new Lesson(3, "Subject3", "Teacher3", "Group3");

        firstLesson.setRoom(ROOM);
        firstLesson.setTimeslot(TIMESLOT1);

        conflictingLesson.setRoom(ROOM);
        conflictingLesson.setTimeslot(TIMESLOT1);

        nonConflictingLesson.setRoom(ROOM);
        nonConflictingLesson.setTimeslot(TIMESLOT2);

        constraintVerifier.verifyThat(TimeTableConstraintProvider::roomConflict)
                .given(firstLesson, conflictingLesson, nonConflictingLesson)
                .penalizesBy(1);
    }

}
----

This test verifies that the constraint `TimeTableConstraintProvider::roomConflict`,
when given three lessons in the same room, where two lessons have the same timeslot,
it penalizes with a  match weight of `1`.
So with a constraint weight of `10hard` it would reduce the score by `-10hard`.

Notice how `ConstraintVerifier` ignores the constraint weight during testing - even
if those constraint weights are hard coded in the `ConstraintProvider` - because
constraints weights change regularly before going into production.
This way, constraint weight tweaking does not break the unit tests.

==== Test the solver

In a JUnit test, generate a test dataset and send it to the `TimeTableResource` to solve.

Create the `src/test/java/org/acme/schooltimetabling/rest/TimeTableResourceTest.java` class:

[source,java]
----
package org.acme.schooltimetabling.rest;

import java.time.DayOfWeek;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.Timeslot;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.rest.TimeTableResource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@QuarkusTest
public class TimeTableResourceTest {

    @Inject
    TimeTableResource timeTableResource;

    @Test
    @Timeout(600_000)
    public void solve() {
        TimeTable problem = generateProblem();
        TimeTable solution = timeTableResource.solve(problem);
        assertFalse(solution.getLessonList().isEmpty());
        for (Lesson lesson : solution.getLessonList()) {
            assertNotNull(lesson.getTimeslot());
            assertNotNull(lesson.getRoom());
        }
        assertTrue(solution.getScore().isFeasible());
    }

    private TimeTable generateProblem() {
        List<Timeslot> timeslotList = new ArrayList<>();
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(8, 30), LocalTime.of(9, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9, 30), LocalTime.of(10, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(10, 30), LocalTime.of(11, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(13, 30), LocalTime.of(14, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(14, 30), LocalTime.of(15, 30)));

        List<Room> roomList = new ArrayList<>();
        roomList.add(new Room("Room A"));
        roomList.add(new Room("Room B"));
        roomList.add(new Room("Room C"));

        List<Lesson> lessonList = new ArrayList<>();
        lessonList.add(new Lesson(101L, "Math", "B. May", "9th grade"));
        lessonList.add(new Lesson(102L, "Physics", "M. Curie", "9th grade"));
        lessonList.add(new Lesson(103L, "Geography", "M. Polo", "9th grade"));
        lessonList.add(new Lesson(104L, "English", "I. Jones", "9th grade"));
        lessonList.add(new Lesson(105L, "Spanish", "P. Cruz", "9th grade"));

        lessonList.add(new Lesson(201L, "Math", "B. May", "10th grade"));
        lessonList.add(new Lesson(202L, "Chemistry", "M. Curie", "10th grade"));
        lessonList.add(new Lesson(203L, "History", "I. Jones", "10th grade"));
        lessonList.add(new Lesson(204L, "English", "P. Cruz", "10th grade"));
        lessonList.add(new Lesson(205L, "French", "M. Curie", "10th grade"));
        return new TimeTable(timeslotList, roomList, lessonList);
    }

}
----

This test verifies that after solving, all lessons are assigned to a time slot and a room.
It also verifies that it found a feasible solution (no hard constraints broken).

Add test properties to the `src/main/resources/application.properties` file:

[source,properties]
----
quarkus.optaplanner.solver.termination.spent-limit=5s

# Effectively disable spent-time termination in favor of the best-score-limit
%test.quarkus.optaplanner.solver.termination.spent-limit=1h
%test.quarkus.optaplanner.solver.termination.best-score-limit=0hard/*soft
----

Normally, the solver finds a feasible solution in less than 200 milliseconds.
Notice how the `application.properties` overwrites the solver termination during tests
to terminate as soon as a feasible solution (`0hard/*soft`) is found.
This avoids hard coding a solver time, because the unit test might run on arbitrary hardware.
This approach ensures that the test runs long enough to find a feasible solution, even on slow machines.
But it does not run a millisecond longer than it strictly must, even on fast machines.

=== Logging

When adding constraints in your `ConstraintProvider`,
keep an eye on the _score calculation speed_ in the `info` log,
after solving for the same amount of time, to assess the performance impact:

[source]
----
... Solving ended: ..., score calculation speed (29455/sec), ...
----

To understand how OptaPlanner is solving your problem internally,
change the logging in the `application.properties` file or with a `-D` system property:

[source,properties]
----
quarkus.log.category."org.optaplanner".level=debug
----

Use `debug` logging to show every _step_:

[source,options="nowrap"]
----
... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
...     CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]).
...     CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]).
...
----

Use `trace` logging to show every _step_ and every _move_ per step.

== Summary

Congratulations!
You have just developed a Quarkus application with https://www.optaplanner.org/[OptaPlanner]!

== Further improvements: Database and UI integration

Now try adding database and UI integration:

. Store `Timeslot`, `Room`, and `Lesson` in the database with https://quarkus.io/guides/hibernate-orm-panache[Hibernate and Panache].

. https://quarkus.io/guides/rest-json[Expose them through REST].

. Adjust the `TimeTableResource` to read and write a `TimeTable` instance in a single transaction
and use those accordingly:
+
[source,java]
----
package org.acme.schooltimetabling.rest;

import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

import io.quarkus.panache.common.Sort;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.domain.Timeslot;
import org.optaplanner.core.api.score.ScoreManager;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.api.solver.SolverStatus;

@Path("/timeTable")
public class TimeTableResource {

    public static final Long SINGLETON_TIME_TABLE_ID = 1L;

    @Inject
    SolverManager<TimeTable, Long> solverManager;
    @Inject
    ScoreManager<TimeTable, HardSoftScore> scoreManager;

    // To try, open http://localhost:8080/timeTable
    @GET
    public TimeTable getTimeTable() {
        // Get the solver status before loading the solution
        // to avoid the race condition that the solver terminates between them
        SolverStatus solverStatus = getSolverStatus();
        TimeTable solution = findById(SINGLETON_TIME_TABLE_ID);
        scoreManager.updateScore(solution); // Sets the score
        solution.setSolverStatus(solverStatus);
        return solution;
    }

    @POST
    @Path("/solve")
    public void solve() {
        solverManager.solveAndListen(SINGLETON_TIME_TABLE_ID,
                this::findById,
                this::save);
    }

    public SolverStatus getSolverStatus() {
        return solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID);
    }

    @POST
    @Path("/stopSolving")
    public void stopSolving() {
        solverManager.terminateEarly(SINGLETON_TIME_TABLE_ID);
    }

    @Transactional
    protected TimeTable findById(Long id) {
        if (!SINGLETON_TIME_TABLE_ID.equals(id)) {
            throw new IllegalStateException("There is no timeTable with id (" + id + ").");
        }
        // Occurs in a single transaction, so each initialized lesson references the same timeslot/room instance
        // that is contained by the timeTable's timeslotList/roomList.
        return new TimeTable(
                Timeslot.listAll(Sort.by("dayOfWeek").and("startTime").and("endTime").and("id")),
                Room.listAll(Sort.by("name").and("id")),
                Lesson.listAll(Sort.by("subject").and("teacher").and("studentGroup").and("id")));
    }

    @Transactional
    protected void save(TimeTable timeTable) {
        for (Lesson lesson : timeTable.getLessonList()) {
            // TODO this is awfully naive: optimistic locking causes issues if called by the SolverManager
            Lesson attachedLesson = Lesson.findById(lesson.getId());
            attachedLesson.setTimeslot(lesson.getTimeslot());
            attachedLesson.setRoom(lesson.getRoom());
        }
    }

}
----
+
For simplicity's sake, this code handles only one `TimeTable` instance,
but it is straightforward to enable multi-tenancy and handle multiple `TimeTable` instances of different high schools in parallel.
+
The `getTimeTable()` method returns the latest timetable from the database.
It uses the `ScoreManager` (which is automatically injected)
to calculate the score of that timetable, so the UI can show the score.
+
The `solve()` method starts a job to solve the current timetable and store the time slot and room assignments in the database.
It uses the `SolverManager.solveAndListen()` method to listen to intermediate best solutions
and update the database accordingly.
This enables the UI to show progress while the backend is still solving.

. Adjust the `TimeTableResourceTest` instance accordingly, now that the `solve()` method returns immediately.
Poll for the latest solution until the solver finishes solving:
+
[source,java]
----
package org.acme.schooltimetabling.rest;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.TimeTable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.optaplanner.core.api.solver.SolverStatus;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@QuarkusTest
public class TimeTableResourceTest {

    @Inject
    TimeTableResource timeTableResource;

    @Test
    @Timeout(600_000)
    public void solveDemoDataUntilFeasible() throws InterruptedException {
        timeTableResource.solve();
        TimeTable timeTable = timeTableResource.getTimeTable();
        while (timeTable.getSolverStatus() != SolverStatus.NOT_SOLVING) {
            // Quick polling (not a Test Thread Sleep anti-pattern)
            // Test is still fast on fast machines and doesn't randomly fail on slow machines.
            Thread.sleep(20L);
            timeTable = timeTableResource.getTimeTable();
        }
        assertFalse(timeTable.getLessonList().isEmpty());
        for (Lesson lesson : timeTable.getLessonList()) {
            assertNotNull(lesson.getTimeslot());
            assertNotNull(lesson.getRoom());
        }
        assertTrue(timeTable.getScore().isFeasible());
    }

}
----

. Build an attractive web UI on top of these REST methods to visualize the timetable.

Take a look at {quarkusQuickstartUrl}[the quickstart source code] to see how this all turns out.

[[quarkusJavaQuickStart]]
= Quarkus Java quick start
:doctype: book
:imagesdir: ../..
:sectnums:
:icons: font
:quickstartsCloneUrl: https://github.com/kiegroup/optaplanner-quickstarts
:quickstartsArchiveUrl: https://www.optaplanner.org/download/download.html
:quarkusQuickstartUrl: https://github.com/kiegroup/optaplanner-quickstarts/tree/stable/use-cases/school-timetabling

// Keep this in sync with the quarkus repo's copy
// https://github.com/quarkusio/quarkus/blob/main/docs/src/main/asciidoc/optaplanner.adoc
// Keep this also in sync with spring-boot-quickstart.adoc where applicable

This guide walks you through the process of creating a https://quarkus.io/[Quarkus] application
with https://www.optaplanner.org/[OptaPlanner]'s constraint solving Artificial Intelligence (AI).

== What you will build

You will build a REST application that optimizes a school timetable for students and teachers:

image::quickstart/school-timetabling/schoolTimetablingScreenshot.png[]

Your service will assign `Lesson` instances to `Timeslot` and `Room` instances automatically
by using AI to adhere to hard and soft scheduling _constraints_, such as the following examples:

* A room can have at most one lesson at the same time.
* A teacher can teach at most one lesson at the same time.
* A student can attend at most one lesson at the same time.
* A teacher prefers to teach all lessons in the same room.
* A teacher prefers to teach sequential lessons and dislikes gaps between lessons.
* A student dislikes sequential lessons on the same subject.

Mathematically speaking, school timetabling is an _NP-hard_ problem.
This means it is difficult to scale.
Simply brute force iterating through all possible combinations takes millions of years
for a non-trivial dataset, even on a supercomputer.
Luckily, AI constraint solvers such as OptaPlanner have advanced algorithms
that deliver a near-optimal solution in a reasonable amount of time.

== Solution source code

Follow the instructions in the next sections to create the application step by step (recommended).

Alternatively, you can also skip right to the completed example:

. Clone the Git repository:
+
[source,shell,subs=attributes+]
----
$ git clone {quickstartsCloneUrl}
----
+
or download an {quickstartsArchiveUrl}[archive].

. Find the solution in {quarkusQuickstartUrl}[the `use-cases` directory]
and run it (see its README file).

== Prerequisites

To complete this guide, you need:

* https://adoptopenjdk.net/[JDK] {java-version}+ with `JAVA_HOME` configured appropriately
* https://maven.apache.org/download.html[Apache Maven] {maven-version}+ or https://gradle.org/install/[Gradle] 4+
* An IDE, such as https://www.jetbrains.com/idea[IntelliJ IDEA], VSCode or Eclipse

== The build file and the dependencies

Use https://code.quarkus.io/[code.quarkus.io] to generate an application
with the following extensions, for Maven or Gradle:

* RESTEasy JAX-RS (`quarkus-resteasy`)
* RESTEasy Jackson (`quarkus-resteasy-jackson`)
* OptaPlanner (`optaplanner-quarkus`)
* OptaPlanner Jackson (`optaplanner-quarkus-jackson`)

Alternatively, generate it from the command line with Maven:

[source,shell,subs=attributes+]
----
$ mvn io.quarkus:quarkus-maven-plugin:{quarkus-version}:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=optaplanner-quickstart \
    -Dextensions="resteasy,resteasy-jackson,optaplanner-quarkus,optaplanner-quarkus-jackson" \
    -DnoExamples
$ cd optaplanner-quickstart
----

In Maven, your `pom.xml` file contains these dependencies:

[source,xml,subs=attributes+]
----
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-universe-bom</artifactId>
        <version>{quarkus-version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-resteasy-jackson</artifactId>
    </dependency>
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-quarkus</artifactId>
    </dependency>
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-quarkus-jackson</artifactId>
    </dependency>

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-junit5</artifactId>
      <scope>test</scope>
    </dependency>
  </dependencies>
----

//On the other hand, in Gradle, your `build.gradle` file contains these dependencies:
//
//[source,groovy,subs=attributes+]
//----
//dependencies {
//    implementation enforcedPlatform("io.quarkus:quarkus-universe-bom:{quarkus-version}")
//    implementation 'io.quarkus:quarkus-resteasy'
//    implementation 'io.quarkus:quarkus-resteasy-jackson'
//    implementation 'org.optaplanner:optaplanner-quarkus'
//    implementation 'org.optaplanner:optaplanner-quarkus-jackson'
//
//    testImplementation 'io.quarkus:quarkus-junit5'
//}
//----

include::../school-timetabling/school-timetabling-model.adoc[leveloffset=+1]
include::../school-timetabling/school-timetabling-constraints.adoc[leveloffset=+1]
include::../school-timetabling/school-timetabling-solution.adoc[leveloffset=+1]

== Create the solver service

Now you are ready to put everything together and create a REST service.
But solving planning problems on REST threads causes HTTP timeout issues.
Therefore, the Quarkus extension injects a `SolverManager` instance,
which runs solvers in a separate thread pool
and can solve multiple datasets in parallel.

Create the `src/main/java/org/acme/schooltimetabling/rest/TimeTableResource.java` class:

[source,java]
----
package org.acme.schooltimetabling.rest;

import java.util.UUID;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

import org.acme.schooltimetabling.domain.TimeTable;
import org.optaplanner.core.api.solver.SolverJob;
import org.optaplanner.core.api.solver.SolverManager;

@Path("/timeTable")
public class TimeTableResource {

    @Inject
    SolverManager<TimeTable, UUID> solverManager;

    @POST
    @Path("/solve")
    public TimeTable solve(TimeTable problem) {
        UUID problemId = UUID.randomUUID();
        // Submit the problem to start solving
        SolverJob<TimeTable, UUID> solverJob = solverManager.solve(problemId, problem);
        TimeTable solution;
        try {
            // Wait until the solving ends
            solution = solverJob.getFinalBestSolution();
        } catch (InterruptedException | ExecutionException e) {
            throw new IllegalStateException("Solving failed.", e);
        }
        return solution;
    }

}
----

For simplicity's sake, this initial implementation waits for the solver to finish,
which can still cause an HTTP timeout.
The _complete_ implementation avoids HTTP timeouts much more elegantly.

== Set the termination time

Without a termination setting or a `terminationEarly()` event, the solver runs forever.
To avoid that, limit the solving time to five seconds.
That is short enough to avoid the HTTP timeout.

Create the `src/main/resources/application.properties` file:

[source,properties]
----
# The solver runs only for 5 seconds to avoid a HTTP timeout in this simple implementation.
# It's recommended to run for at least 5 minutes ("5m") otherwise.
quarkus.optaplanner.solver.termination.spent-limit=5s
----

OptaPlanner returns _the best solution_ found in the available termination time.
Due to xref:optimization-algorithms/optimization-algorithms.adoc#doesPlannerFindTheOptimalSolution[the nature of NP-hard problems],
the best solution might not be optimal, especially for larger datasets.
Increase the termination time to potentially find a better solution.

== Run the application

First start the application:

[source,shell]
----
$ mvn compile quarkus:dev
----

=== Try the application

Now that the application is running, you can test the REST service.
You can use any REST client you wish.
The following example uses the Linux command `curl` to send a POST request:

[source,shell]
----
$ curl -i -X POST http://localhost:8080/timeTable/solve -H "Content-Type:application/json" -d '{"timeslotList":[{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"}],"roomList":[{"name":"Room A"},{"name":"Room B"}],"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade"},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade"},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade"},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade"}]}'
----

After about five seconds, according to the termination spent time defined in your `application.properties`,
the service returns an output similar to the following example:

[source]
----
HTTP/1.1 200
Content-Type: application/json
...

{"timeslotList":...,"roomList":...,"lessonList":[{"id":1,"subject":"Math","teacher":"A. Turing","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room A"}},{"id":2,"subject":"Chemistry","teacher":"M. Curie","studentGroup":"9th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room A"}},{"id":3,"subject":"French","teacher":"M. Curie","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"08:30:00","endTime":"09:30:00"},"room":{"name":"Room B"}},{"id":4,"subject":"History","teacher":"I. Jones","studentGroup":"10th grade","timeslot":{"dayOfWeek":"MONDAY","startTime":"09:30:00","endTime":"10:30:00"},"room":{"name":"Room B"}}],"score":"0hard/0soft"}
----

Notice that your application assigned all four lessons to one of the two time slots and one of the two rooms.
Also notice that it conforms to all hard constraints.
For example, M. Curie's two lessons are in different time slots.

On the server side, the `info` log shows what OptaPlanner did in those five seconds:

[source,options="nowrap"]
----
... Solving started: time spent (33), best score (-8init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
... Construction Heuristic phase (0) ended: time spent (73), best score (0hard/0soft), score calculation speed (459/sec), step total (4).
... Local Search phase (1) ended: time spent (5000), best score (0hard/0soft), score calculation speed (28949/sec), step total (28398).
... Solving ended: time spent (5000), best score (0hard/0soft), score calculation speed (28524/sec), phase total (2), environment mode (REPRODUCIBLE).
----

=== Test the application

A good application includes test coverage.

==== Test the constraints

To test each constraint in isolation, use a `ConstraintVerifier` in unit tests.
It tests each constraint's corner cases in isolation from the other tests,
which lowers maintenance when adding a new constraint with proper test coverage.

Add a `optaplanner-test` dependency in your `pom.xml`:
[source,xml]
----
    <dependency>
      <groupId>org.optaplanner</groupId>
      <artifactId>optaplanner-test</artifactId>
      <scope>test</scope>
    </dependency>
----

Create the `src/test/java/org/acme/schooltimetabling/solver/TimeTableConstraintProviderTest.java` class:

[source,java]
----
package org.acme.schooltimetabling.solver;

import java.time.DayOfWeek;
import java.time.LocalTime;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.domain.Timeslot;
import org.junit.jupiter.api.Test;
import org.optaplanner.test.api.score.stream.ConstraintVerifier;

@QuarkusTest
class TimeTableConstraintProviderTest {

    private static final Room ROOM = new Room("Room1");
    private static final Timeslot TIMESLOT1 = new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9,0), LocalTime.NOON);
    private static final Timeslot TIMESLOT2 = new Timeslot(DayOfWeek.TUESDAY, LocalTime.of(9,0), LocalTime.NOON);

    @Inject
    ConstraintVerifier<TimeTableConstraintProvider, TimeTable> constraintVerifier;

    @Test
    void roomConflict() {
        Lesson firstLesson = new Lesson(1, "Subject1", "Teacher1", "Group1");
        Lesson conflictingLesson = new Lesson(2, "Subject2", "Teacher2", "Group2");
        Lesson nonConflictingLesson = new Lesson(3, "Subject3", "Teacher3", "Group3");

        firstLesson.setRoom(ROOM);
        firstLesson.setTimeslot(TIMESLOT1);

        conflictingLesson.setRoom(ROOM);
        conflictingLesson.setTimeslot(TIMESLOT1);

        nonConflictingLesson.setRoom(ROOM);
        nonConflictingLesson.setTimeslot(TIMESLOT2);

        constraintVerifier.verifyThat(TimeTableConstraintProvider::roomConflict)
                .given(firstLesson, conflictingLesson, nonConflictingLesson)
                .penalizesBy(1);
    }

}
----

This test verifies that the constraint `TimeTableConstraintProvider::roomConflict`,
when given three lessons in the same room, where two lessons have the same timeslot,
it penalizes with a  match weight of `1`.
So with a constraint weight of `10hard` it would reduce the score by `-10hard`.

Notice how `ConstraintVerifier` ignores the constraint weight during testing - even
if those constraint weights are hard coded in the `ConstraintProvider` - because
constraints weights change regularly before going into production.
This way, constraint weight tweaking does not break the unit tests.

==== Test the solver

In a JUnit test, generate a test dataset and send it to the `TimeTableResource` to solve.

Create the `src/test/java/org/acme/schooltimetabling/rest/TimeTableResourceTest.java` class:

[source,java]
----
package org.acme.schooltimetabling.rest;

import java.time.DayOfWeek;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.Timeslot;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.rest.TimeTableResource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@QuarkusTest
public class TimeTableResourceTest {

    @Inject
    TimeTableResource timeTableResource;

    @Test
    @Timeout(600_000)
    public void solve() {
        TimeTable problem = generateProblem();
        TimeTable solution = timeTableResource.solve(problem);
        assertFalse(solution.getLessonList().isEmpty());
        for (Lesson lesson : solution.getLessonList()) {
            assertNotNull(lesson.getTimeslot());
            assertNotNull(lesson.getRoom());
        }
        assertTrue(solution.getScore().isFeasible());
    }

    private TimeTable generateProblem() {
        List<Timeslot> timeslotList = new ArrayList<>();
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(8, 30), LocalTime.of(9, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(9, 30), LocalTime.of(10, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(10, 30), LocalTime.of(11, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(13, 30), LocalTime.of(14, 30)));
        timeslotList.add(new Timeslot(DayOfWeek.MONDAY, LocalTime.of(14, 30), LocalTime.of(15, 30)));

        List<Room> roomList = new ArrayList<>();
        roomList.add(new Room("Room A"));
        roomList.add(new Room("Room B"));
        roomList.add(new Room("Room C"));

        List<Lesson> lessonList = new ArrayList<>();
        lessonList.add(new Lesson(101L, "Math", "B. May", "9th grade"));
        lessonList.add(new Lesson(102L, "Physics", "M. Curie", "9th grade"));
        lessonList.add(new Lesson(103L, "Geography", "M. Polo", "9th grade"));
        lessonList.add(new Lesson(104L, "English", "I. Jones", "9th grade"));
        lessonList.add(new Lesson(105L, "Spanish", "P. Cruz", "9th grade"));

        lessonList.add(new Lesson(201L, "Math", "B. May", "10th grade"));
        lessonList.add(new Lesson(202L, "Chemistry", "M. Curie", "10th grade"));
        lessonList.add(new Lesson(203L, "History", "I. Jones", "10th grade"));
        lessonList.add(new Lesson(204L, "English", "P. Cruz", "10th grade"));
        lessonList.add(new Lesson(205L, "French", "M. Curie", "10th grade"));
        return new TimeTable(timeslotList, roomList, lessonList);
    }

}
----

This test verifies that after solving, all lessons are assigned to a time slot and a room.
It also verifies that it found a feasible solution (no hard constraints broken).

Add test properties to the `src/main/resources/application.properties` file:

[source,properties]
----
quarkus.optaplanner.solver.termination.spent-limit=5s

# Effectively disable spent-time termination in favor of the best-score-limit
%test.quarkus.optaplanner.solver.termination.spent-limit=1h
%test.quarkus.optaplanner.solver.termination.best-score-limit=0hard/*soft
----

Normally, the solver finds a feasible solution in less than 200 milliseconds.
Notice how the `application.properties` overwrites the solver termination during tests
to terminate as soon as a feasible solution (`0hard/*soft`) is found.
This avoids hard coding a solver time, because the unit test might run on arbitrary hardware.
This approach ensures that the test runs long enough to find a feasible solution, even on slow machines.
But it does not run a millisecond longer than it strictly must, even on fast machines.

=== Logging

When adding constraints in your `ConstraintProvider`,
keep an eye on the _score calculation speed_ in the `info` log,
after solving for the same amount of time, to assess the performance impact:

[source]
----
... Solving ended: ..., score calculation speed (29455/sec), ...
----

To understand how OptaPlanner is solving your problem internally,
change the logging in the `application.properties` file or with a `-D` system property:

[source,properties]
----
quarkus.log.category."org.optaplanner".level=debug
----

Use `debug` logging to show every _step_:

[source,options="nowrap"]
----
... Solving started: time spent (67), best score (-20init/0hard/0soft), environment mode (REPRODUCIBLE), random (JDK with seed 0).
...     CH step (0), time spent (128), score (-18init/0hard/0soft), selected move count (15), picked move ([Math(101) {null -> Room A}, Math(101) {null -> MONDAY 08:30}]).
...     CH step (1), time spent (145), score (-16init/0hard/0soft), selected move count (15), picked move ([Physics(102) {null -> Room A}, Physics(102) {null -> MONDAY 09:30}]).
...
----

Use `trace` logging to show every _step_ and every _move_ per step.

== Summary

Congratulations!
You have just developed a Quarkus application with https://www.optaplanner.org/[OptaPlanner]!

== Further improvements: Database and UI integration

Now try adding database and UI integration:

. Store `Timeslot`, `Room`, and `Lesson` in the database with link:hibernate-orm-panache[Hibernate and Panache].

. link:rest-json[Expose them through REST].

. Adjust the `TimeTableResource` to read and write a `TimeTable` instance in a single transaction
and use those accordingly:
+
[source,java]
----
package org.acme.schooltimetabling.rest;

import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;

import io.quarkus.panache.common.Sort;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.Room;
import org.acme.schooltimetabling.domain.TimeTable;
import org.acme.schooltimetabling.domain.Timeslot;
import org.optaplanner.core.api.score.ScoreManager;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScore;
import org.optaplanner.core.api.solver.SolverManager;
import org.optaplanner.core.api.solver.SolverStatus;

@Path("/timeTable")
public class TimeTableResource {

    public static final Long SINGLETON_TIME_TABLE_ID = 1L;

    @Inject
    SolverManager<TimeTable, Long> solverManager;
    @Inject
    ScoreManager<TimeTable, HardSoftScore> scoreManager;

    // To try, open http://localhost:8080/timeTable
    @GET
    public TimeTable getTimeTable() {
        // Get the solver status before loading the solution
        // to avoid the race condition that the solver terminates between them
        SolverStatus solverStatus = getSolverStatus();
        TimeTable solution = findById(SINGLETON_TIME_TABLE_ID);
        scoreManager.updateScore(solution); // Sets the score
        solution.setSolverStatus(solverStatus);
        return solution;
    }

    @POST
    @Path("/solve")
    public void solve() {
        solverManager.solveAndListen(SINGLETON_TIME_TABLE_ID,
                this::findById,
                this::save);
    }

    public SolverStatus getSolverStatus() {
        return solverManager.getSolverStatus(SINGLETON_TIME_TABLE_ID);
    }

    @POST
    @Path("/stopSolving")
    public void stopSolving() {
        solverManager.terminateEarly(SINGLETON_TIME_TABLE_ID);
    }

    @Transactional
    protected TimeTable findById(Long id) {
        if (!SINGLETON_TIME_TABLE_ID.equals(id)) {
            throw new IllegalStateException("There is no timeTable with id (" + id + ").");
        }
        // Occurs in a single transaction, so each initialized lesson references the same timeslot/room instance
        // that is contained by the timeTable's timeslotList/roomList.
        return new TimeTable(
                Timeslot.listAll(Sort.by("dayOfWeek").and("startTime").and("endTime").and("id")),
                Room.listAll(Sort.by("name").and("id")),
                Lesson.listAll(Sort.by("subject").and("teacher").and("studentGroup").and("id")));
    }

    @Transactional
    protected void save(TimeTable timeTable) {
        for (Lesson lesson : timeTable.getLessonList()) {
            // TODO this is awfully naive: optimistic locking causes issues if called by the SolverManager
            Lesson attachedLesson = Lesson.findById(lesson.getId());
            attachedLesson.setTimeslot(lesson.getTimeslot());
            attachedLesson.setRoom(lesson.getRoom());
        }
    }

}
----
+
For simplicity's sake, this code handles only one `TimeTable` instance,
but it is straightforward to enable multi-tenancy and handle multiple `TimeTable` instances of different high schools in parallel.
+
The `getTimeTable()` method returns the latest timetable from the database.
It uses the `ScoreManager` (which is automatically injected)
to calculate the score of that timetable, so the UI can show the score.
+
The `solve()` method starts a job to solve the current timetable and store the time slot and room assignments in the database.
It uses the `SolverManager.solveAndListen()` method to listen to intermediate best solutions
and update the database accordingly.
This enables the UI to show progress while the backend is still solving.

. Adjust the `TimeTableResourceTest` instance accordingly, now that the `solve()` method returns immediately.
Poll for the latest solution until the solver finishes solving:
+
[source,java]
----
package org.acme.schooltimetabling.rest;

import javax.inject.Inject;

import io.quarkus.test.junit.QuarkusTest;
import org.acme.schooltimetabling.domain.Lesson;
import org.acme.schooltimetabling.domain.TimeTable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.optaplanner.core.api.solver.SolverStatus;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@QuarkusTest
public class TimeTableResourceTest {

    @Inject
    TimeTableResource timeTableResource;

    @Test
    @Timeout(600_000)
    public void solveDemoDataUntilFeasible() throws InterruptedException {
        timeTableResource.solve();
        TimeTable timeTable = timeTableResource.getTimeTable();
        while (timeTable.getSolverStatus() != SolverStatus.NOT_SOLVING) {
            // Quick polling (not a Test Thread Sleep anti-pattern)
            // Test is still fast on fast machines and doesn't randomly fail on slow machines.
            Thread.sleep(20L);
            timeTable = timeTableResource.getTimeTable();
        }
        assertFalse(timeTable.getLessonList().isEmpty());
        for (Lesson lesson : timeTable.getLessonList()) {
            assertNotNull(lesson.getTimeslot());
            assertNotNull(lesson.getRoom());
        }
        assertTrue(timeTable.getScore().isFeasible());
    }

}
----

. Build an attractive web UI on top of these REST methods to visualize the timetable.

Take a look at {quarkusQuickstartUrl}[the quickstart source code] to see how this all turns out.


Update 2013-05-12-Linux-Notes.adoc

Update 2013-05-12-Linux-Notes.adoc

= Linux Notes
:hp-image: penguin.png
:author: jonny rhea
:doctype: notes
:encoding: utf-8
:lang: en
:toc: left
:published_at: 2013-05-12
:numbered:

[index]
== general commands
=== find
Search the current dir (and below) for a file named sue:
[source,bash]
$ find . –name sue

Search /tmp and /var for files named foo*bar modified in the last week:
[source,bash]
$ find /tmp /var –mtime -7 -name foo\*bar

Search the file system for files modified < 10 mins ago, excluding dirs and other special files:
[source,bash]
$ find / -mmin -10 –type f

=== watch
Use the watch command to monitor a process every .1 secs

[source,bash]
----
watch -e -n .1 'if ps cax | grep -w 'foo'; then exit 0; else exit 1; fi'
----

[index]
== linux system info

report file system disk space usage (human readable):
----
$ df -h
----

processor info:
----
$ more /proc/cpuinfo
----

memory info:
----
$ more /proc/meminfo
----

check to see if Linux is 32 or 64bit:
----
$ getconf LONG_BIT
----

distribution info:
----
$ lsb_release
----

list all packages installed:
----
$ dpkg -l
----

find the 25 largest dirs starting at the current dir:
----
$ du –xk |sort –n |tail -25
----

show what processes are using which ports:
----
$ netstat -tulpn
----

[index]
== linux ipc
=== ipcs
*provides information on system inter process communication.*

----
$ ipcs

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x6112703c 32769        me         666        15600      2

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

----

[TIP]
====
use the following options to filter the information displayed:

* m => shared memory
* q => message queues
* s => semaphores
====

to find out what process is attached to shared memory use the -p option:

----
$ ipcs -mp
------ Shared Memory Creator/Last-op --------
shmid      owner      cpid       lpid
32769      me         3265       3290

$ ps -ef | grep 3290
 3290  3289  1 17:28 ?        00:00:12 /usr/local/java/bin/java -jar ~/foo/foo.jar bar
 11587 3290  3 17:41 ?        00:00:00 ~/foo/bar
----

=== ipcrm
*use this command to remove shared memory, message queue, or semaphore.*

remove shared memory by id
---
$ ipcs -m <shmid>
---

remove shared memory by key
---
$ ipcs -M <key>
---

[index]
== linux system configuration
check and repair filesystem:
----
$ fsck /dev/sda1
----

change the frequency of filesys checks on startup:
----
$ tune2fs –c <some number> /dev/sda1
----

to change the login manager edit:
****
*/etc/X11/default-display-manager*
****

if your mac address changes edit or delete:
****
*/etc/udev/rules.d/70-persistent-net.rules*
****

disable filesys checks by changing the last number in line to 0:
****
*/etc/fstab*
----
# <file system> <mount point>   <type>  <options>     <dump> <pass>
# performs filesys check every 30 startups
/dev/sda1        /mnt/foo        ext4   defaults 0 30
# filesys checks disabled
/dev/sdb1        /mnt/bar        ext4   defaults 0 0
----
****

To grant passwordless rsh:
****
*hosts.equiv* +
----
<computer name> <user name>
----
****
Create ssh key (rsa):
----
$ cd ~/.ssh
$ ssh-keygen –t rsa
$ ssh-add <file>
----
[index]
== linux binutils
=== objdump
*display info from an object file(s).*

basic syntax:
----
$ objdump <options> file
----

display the contents of the overall file header:
----
$ objdump –f foo

foo:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0804be34
----

display object format specific file header contents:
----
$ objdump -p foo

foo:     file format elf32-i386

Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x00000120 memsz 0x00000120 flags r-x
  INTERP off    0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
(...)
Dynamic Section:
  NEEDED               libc.so.6
  INIT                 0x080494a0
  FINI                 0x0805a02c
  GNU_HASH             0x080481ac
  STRTAB               0x080489b8
(...)
Version References:
  required from libc.so.6:
    0x09691f73 0x00 10 GLIBC_2.1.3
    0x0d696912 0x00 09 GLIBC_2.2
    0x09691a73 0x00 07 GLIBC_2.2.3
(...)
----

=== nm
*provides info on the symbols used in an object file or executable. by default, nm shows the symbol: value, type and name.*

basic syntax:
----
$ nm
----

[NOTE]
====
if no file is provided, nm will inspect a.out
====

display default information on executable (or object file):
----
$ nm foo
----

display all the symbols in an executable:
----
$ nm -n foo
----

display information about the size of a particular symbol, bar:
----
$ nm -n foo -S | grep bar
----

only display information on external symbols:
----
$ nm -g foo
----

[index]
== other utils
=== file
*get basic file information on a file(s).*

basic syntax:
----
$ file file
----

example:
----
$ file foo

foo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped
----

= Linux Notes
:author: jonny rhea
:doctype: notes
:encoding: utf-8
:lang: en
:toc: left
:published_at: 2013-05-12
:numbered:

[index]
== general commands
=== find
Search the current dir (and below) for a file named sue:
[source,bash]
$ find . –name sue

Search /tmp and /var for files named foo*bar modified in the last week:
[source,bash]
$ find /tmp /var –mtime -7 -name foo\*bar

Search the file system for files modified < 10 mins ago, excluding dirs and other special files:
[source,bash]
$ find / -mmin -10 –type f

=== watch
Use the watch command to monitor a process every .1 secs

[source,bash]
----
watch -e -n .1 'if ps cax | grep -w 'foo'; then exit 0; else exit 1; fi'
----

[index]
== linux system info

report file system disk space usage (human readable):
----
$ df -h
----

processor info:
----
$ more /proc/cpuinfo
----

memory info:
----
$ more /proc/meminfo
----

check to see if Linux is 32 or 64bit:
----
$ getconf LONG_BIT
----

distribution info:
----
$ lsb_release
----

list all packages installed:
----
$ dpkg -l
----

find the 25 largest dirs starting at the current dir:
----
$ du –xk |sort –n |tail -25
----

show what processes are using which ports:
----
$ netstat -tulpn
----

[index]
== linux ipc
=== ipcs
*provides information on system inter process communication.*

----
$ ipcs

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x6112703c 32769        me         666        15600      2

------ Semaphore Arrays --------
key        semid      owner      perms      nsems

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

----

[TIP]
====
use the following options to filter the information displayed:

* m => shared memory
* q => message queues
* s => semaphores
====

to find out what process is attached to shared memory use the -p option:

----
$ ipcs -mp
------ Shared Memory Creator/Last-op --------
shmid      owner      cpid       lpid
32769      me         3265       3290

$ ps -ef | grep 3290
 3290  3289  1 17:28 ?        00:00:12 /usr/local/java/bin/java -jar ~/foo/foo.jar bar
 11587 3290  3 17:41 ?        00:00:00 ~/foo/bar
----

=== ipcrm
*use this command to remove shared memory, message queue, or semaphore.*

remove shared memory by id
---
$ ipcs -m <shmid>
---

remove shared memory by key
---
$ ipcs -M <key>
---

[index]
== linux system configuration
check and repair filesystem:
----
$ fsck /dev/sda1
----

change the frequency of filesys checks on startup:
----
$ tune2fs –c <some number> /dev/sda1
----

to change the login manager edit:
****
*/etc/X11/default-display-manager*
****

if your mac address changes edit or delete:
****
*/etc/udev/rules.d/70-persistent-net.rules*
****

disable filesys checks by changing the last number in line to 0:
****
*/etc/fstab*
----
# <file system> <mount point>   <type>  <options>     <dump> <pass>
# performs filesys check every 30 startups
/dev/sda1        /mnt/foo        ext4   defaults 0 30
# filesys checks disabled
/dev/sdb1        /mnt/bar        ext4   defaults 0 0
----
****

To grant passwordless rsh:
****
*hosts.equiv* +
----
<computer name> <user name>
----
****
Create ssh key (rsa):
----
$ cd ~/.ssh
$ ssh-keygen –t rsa
$ ssh-add <file>
----
[index]
== linux binutils
=== objdump
*display info from an object file(s).*

basic syntax:
----
$ objdump <options> file
----

display the contents of the overall file header:
----
$ objdump –f foo

foo:     file format elf32-i386
architecture: i386, flags 0x00000112:
EXEC_P, HAS_SYMS, D_PAGED
start address 0x0804be34
----

display object format specific file header contents:
----
$ objdump -p foo

foo:     file format elf32-i386

Program Header:
    PHDR off    0x00000034 vaddr 0x08048034 paddr 0x08048034 align 2**2
         filesz 0x00000120 memsz 0x00000120 flags r-x
  INTERP off    0x00000154 vaddr 0x08048154 paddr 0x08048154 align 2**0
         filesz 0x00000013 memsz 0x00000013 flags r--
(...)
Dynamic Section:
  NEEDED               libc.so.6
  INIT                 0x080494a0
  FINI                 0x0805a02c
  GNU_HASH             0x080481ac
  STRTAB               0x080489b8
(...)
Version References:
  required from libc.so.6:
    0x09691f73 0x00 10 GLIBC_2.1.3
    0x0d696912 0x00 09 GLIBC_2.2
    0x09691a73 0x00 07 GLIBC_2.2.3
(...)
----

=== nm
*provides info on the symbols used in an object file or executable. by default, nm shows the symbol: value, type and name.*

basic syntax:
----
$ nm
----

[NOTE]
====
if no file is provided, nm will inspect a.out
====

display default information on executable (or object file):
----
$ nm foo
----

display all the symbols in an executable:
----
$ nm -n foo
----

display information about the size of a particular symbol, bar:
----
$ nm -n foo -S | grep bar
----

only display information on external symbols:
----
$ nm -g foo
----

[index]
== other utils
=== file
*get basic file information on a file(s).*

basic syntax:
----
$ file file
----

example:
----
$ file foo

foo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, stripped
----


Removed out-dated advice about disabling the daemon for ephemeral CI builds.

// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[gradle_daemon]]
= The Gradle Daemon

[quote, Wikipedia]
A daemon is a computer program that runs as a background process, rather than being under the direct control of an interactive user.

Gradle runs on the Java Virtual Machine (JVM) and uses several supporting libraries that require a non-trivial initialization time. As a result, it can sometimes seem a little slow to start. The solution to this problem is the Gradle _Daemon_: a long-lived background process that executes your builds much more quickly than would otherwise be the case. We accomplish this by avoiding the expensive bootstrapping process as well as leveraging caching, by keeping data about your project in memory. Running Gradle builds with the Daemon is no different than without. Simply configure whether you want to use it or not - everything else is handled transparently by Gradle.

[[sec:why_the_daemon]]
== Why the Gradle Daemon is important for performance

The Daemon is a long-lived process, so not only are we able to avoid the cost of JVM startup for every build, but we are able to cache information about project structure, files, tasks, and more in memory.

The reasoning is simple: improve build speed by reusing computations from previous builds. However, the benefits are dramatic: we typically measure build times reduced by 15-75% on subsequent builds. We recommend profiling your build by using `--profile` to get a sense of how much impact the Gradle Daemon can have for you.

The Gradle Daemon is enabled by default starting with Gradle 3.0, so you don't have to do anything to benefit from it.

[[sec:status]]
== Running Daemon Status

To get a list of running Gradle Daemons and their statuses use the `--status` command.

Sample output:
[source]
----
    PID VERSION                 STATUS
  28411 3.0                     IDLE
  34247 3.0                     BUSY
----

Currently, a given Gradle version can only connect to daemons of the same version. This means the status output will only show Daemons for the version of Gradle being invoked and not for any other versions. Future versions of Gradle will lift this constraint and will show the running Daemons for all versions of Gradle.

[[sec:disabling_the_daemon]]
== Disabling the Daemon

The Gradle Daemon is enabled by default, and we recommend always enabling it. There are several ways to disable the Daemon, but the most common one is to add the line

[source]
----
org.gradle.daemon=false
----

to the file `«USER_HOME»/.gradle/gradle.properties`, where `«USER_HOME»` is your home directory. That’s typically one of the following, depending on your platform:

* `C:\Users\<username>` (Windows Vista & 7+)
* `/Users/<username>` (macOS)
* `/home/<username>` (Linux)

If that file doesn’t exist, just create it using a text editor. You can find details of other ways to disable (and enable) the Daemon in <<#daemon_faq,Daemon FAQ>> further down. That section also contains more detailed information on how the Daemon works.

Note that having the Daemon enabled, all your builds will take advantage of the speed boost, regardless of the version of Gradle a particular build uses.

[TIP]
.Continuous integration
====

Since Gradle 3.0, we enable Daemon by default and recommend using it for both developers' machines and Continuous Integration servers. However, if you suspect that Daemon makes your CI builds unstable, you can disable it to use a fresh runtime for each build since the runtime is _completely_ isolated from any previous builds.

====

[[sec:stopping_an_existing_daemon]]
== Stopping an existing Daemon

As mentioned, the Daemon is a background process. You needn’t worry about a build up of Gradle processes on your machine, though. Every Daemon monitors its memory usage compared to total system memory and will stop itself if idle when available system memory is low. If you want to explicitly stop running Daemon processes for any reason, just use the command `gradle --stop`.

This will terminate all Daemon processes that were started with the same version of Gradle used to execute the command. If you have the Java Development Kit (JDK) installed, you can easily verify that a Daemon has stopped by running the `jps` command. You’ll see any running Daemons listed with the name `GradleDaemon`.

[[daemon_faq]]
== FAQ

[[sec:ways_to_disable_gradle_daemon]]
=== How do I disable the Gradle Daemon?

There are two recommended ways to disable the Daemon persistently for an environment:

* Via environment variables: add the flag `-Dorg.gradle.daemon=false` to the `GRADLE_OPTS` environment variable
* Via properties file: add `org.gradle.daemon=false` to the `«GRADLE_USER_HOME»/gradle.properties` file

[NOTE]
====

Note, `«GRADLE_USER_HOME»` defaults to `«USER_HOME»/.gradle`, where `«USER_HOME»` is the home directory of the current user. This location can be configured via the `-g` and `--gradle-user-home` command line switches, as well as by the `GRADLE_USER_HOME` environment variable and `org.gradle.user.home` JVM system property.

====

Both approaches have the same effect. Which one to use is up to personal preference. Most Gradle users choose the second option and add the entry to the user `gradle.properties` file.

On Windows, this command will disable the Daemon for the current user:

[source]
----
(if not exist "%USERPROFILE%/.gradle" mkdir "%USERPROFILE%/.gradle") && (echo. >> "%USERPROFILE%/.gradle/gradle.properties" && echo org.gradle.daemon=false >> "%USERPROFILE%/.gradle/gradle.properties")
----

On UNIX-like operating systems, the following Bash shell command will disable the Daemon for the current user:

[source,bash]
----
mkdir -p ~/.gradle && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
----

Once the Daemon is disabled for a build environment in this way, a Gradle Daemon will not be started unless explicitly requested using the `--daemon` option.

The `--daemon` and `--no-daemon` command line options enable and disable usage of the Daemon for individual build invocations when using the Gradle command line interface. These command line options have the _highest_ precedence when considering the build environment. Typically, it is more convenient to enable the Daemon for an environment (e.g. a user account) so that all builds use the Daemon without requiring to remember to supply the `--daemon` option.

[[sec:why_is_there_more_than_one_daemon_process_on_my_machine]]
=== Why is there more than one Daemon process on my machine?

There are several reasons why Gradle will create a new Daemon, instead of using one that is already running. The basic rule is that Gradle will start a new Daemon if there are no existing idle or compatible Daemons available. Gradle will kill any Daemon that has been idle for 3 hours or more, so you don't have to worry about cleaning them up manually.

idle::
An idle Daemon is one that is not currently executing a build or doing other useful work.
compatible::
A compatible Daemon is one that can (or can be made to) meet the requirements of the requested build environment. The Java runtime used to execute the build is an example aspect of the build environment. Another example is the set of JVM system properties required by the build runtime.

Some aspects of the requested build environment may not be met by an Daemon. If the Daemon is running with a Java 8 runtime, but the requested environment calls for Java 10, then the Daemon is not compatible and another must be started. Moreover, certain properties of a Java runtime cannot be changed once the JVM has started. For example, it is not possible to change the memory allocation (e.g. `-Xmx1024m`), default text encoding, default locale, etc of a running JVM.

The “requested build environment” is typically constructed implicitly from aspects of the build client’s (e.g. Gradle command line client, IDE etc.) environment and explicitly via command line switches and settings. See <<build_environment.adoc#build_environment,Build Environment>> for details on how to specify and control the build environment.

The following JVM system properties are effectively immutable. If the requested build environment requires any of these properties, with a different value than a Daemon’s JVM has for this property, the Daemon is not compatible.

* file.encoding
* user.language
* user.country
* user.variant
* java.io.tmpdir
* javax.net.ssl.keyStore
* javax.net.ssl.keyStorePassword
* javax.net.ssl.keyStoreType
* javax.net.ssl.trustStore
* javax.net.ssl.trustStorePassword
* javax.net.ssl.trustStoreType
* com.sun.management.jmxremote

The following JVM attributes, controlled by startup arguments, are also effectively immutable. The corresponding attributes of the requested build environment and the Daemon’s environment must match exactly in order for a Daemon to be compatible.

* The maximum heap size (i.e. the -Xmx JVM argument)
* The minimum heap size (i.e. the -Xms JVM argument)
* The boot classpath (i.e. the -Xbootclasspath argument)
* The “assertion” status (i.e. the -ea argument)

The required Gradle version is another aspect of the requested build environment. Daemon processes are coupled to a specific Gradle runtime. Working on multiple Gradle projects during a session that use different Gradle versions is a common reason for having more than one running Daemon process.

[[sec:how_much_memory_does_the_daemon_use_and_can_i_give_it_more]]
=== How much memory does the Daemon use and can I give it more?

If the requested build environment does not specify a maximum heap size, the Daemon will use up to 512MB of heap. It will use the JVM's default minimum heap size. 512MB is more than enough for most builds. Larger builds with hundreds of subprojects, lots of configuration, and source code may require, or perform better, with more memory.

To increase the amount of memory the Daemon can use, specify the appropriate flags as part of the requested build environment. Please see <<build_environment.adoc#build_environment,Build Environment>> for details.

[[sec:how_can_i_stop_a_daemon]]
=== How can I stop a Daemon?

Daemon processes will automatically terminate themselves after 3 hours of inactivity or less. If you wish to stop a Daemon process before this, you can either kill the process via your operating system or run the `gradle --stop` command. The `--stop` switch causes Gradle to request that _all_ running Daemon processes, _of the same Gradle version used to run the command_, terminate themselves.

[[sec:what_can_go_wrong_with_daemon]]
=== What can go wrong with Daemon?

Considerable engineering effort has gone into making the Daemon robust, transparent and unobtrusive during day to day development. However, Daemon processes can occasionally be corrupted or exhausted. A Gradle build executes arbitrary code from multiple sources. While Gradle itself is designed for and heavily tested with the Daemon, user build scripts and third party plugins can destabilize the Daemon process through defects such as memory leaks or global state corruption.

It is also possible to destabilize the Daemon (and build environment in general) by running builds that do not release resources correctly. This is a particularly poignant problem when using Microsoft Windows as it is less forgiving of programs that fail to close files after reading or writing.

Gradle actively monitors heap usage and attempts to detect when a leak is starting to exhaust the available heap space in the daemon. When it detects a problem, the Gradle daemon will finish the currently running build and proactively restart the daemon on the next build. This monitoring is enabled by default, but can be disabled by setting the `org.gradle.daemon.performance.enable-monitoring` system property to false.

If it is suspected that the Daemon process has become unstable, it can simply be killed. Recall that the `--no-daemon` switch can be specified for a build to prevent use of the Daemon. This can be useful to diagnose whether or not the Daemon is actually the culprit of a problem.

[[sec:tools_and_ides]]
== Tools & IDEs

The <<embedding.adoc#embedding,Gradle Tooling API>> that is used by IDEs and other tools to integrate with Gradle _always_ uses the Gradle Daemon to execute builds. If you are executing Gradle builds from within your IDE you are using the Gradle Daemon and do not need to enable it for your environment.

[[sec:how_does_the_gradle_daemon_make_builds_faster]]
== How does the Gradle Daemon make builds faster?

The Gradle Daemon is a _long lived_ build process. In between builds it waits idly for the next build. This has the obvious benefit of only requiring Gradle to be loaded into memory once for multiple builds, as opposed to once for each build. This in itself is a significant performance optimization, but that's not where it stops.

A significant part of the story for modern JVM performance is runtime code optimization. For example, HotSpot (the JVM implementation provided by Oracle and used as the basis of OpenJDK) applies optimization to code while it is running. The optimization is progressive and not instantaneous. That is, the code is progressively optimized during execution which means that subsequent builds can be faster purely due to this optimization process. Experiments with HotSpot have shown that it takes somewhere between 5 and 10 builds for optimization to stabilize. The difference in perceived build time between the first build and the 10th for a Daemon can be quite dramatic.

The Daemon also allows more effective in memory caching across builds. For example, the classes needed by the build (e.g. plugins, build scripts) can be held in memory between builds. Similarly, Gradle can maintain in-memory caches of build data such as the hashes of task inputs and outputs, used for incremental building.

// Copyright 2017 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[gradle_daemon]]
= The Gradle Daemon

[quote, Wikipedia]
A daemon is a computer program that runs as a background process, rather than being under the direct control of an interactive user.

Gradle runs on the Java Virtual Machine (JVM) and uses several supporting libraries that require a non-trivial initialization time. As a result, it can sometimes seem a little slow to start. The solution to this problem is the Gradle _Daemon_: a long-lived background process that executes your builds much more quickly than would otherwise be the case. We accomplish this by avoiding the expensive bootstrapping process as well as leveraging caching, by keeping data about your project in memory. Running Gradle builds with the Daemon is no different than without. Simply configure whether you want to use it or not - everything else is handled transparently by Gradle.

[[sec:why_the_daemon]]
== Why the Gradle Daemon is important for performance

The Daemon is a long-lived process, so not only are we able to avoid the cost of JVM startup for every build, but we are able to cache information about project structure, files, tasks, and more in memory.

The reasoning is simple: improve build speed by reusing computations from previous builds. However, the benefits are dramatic: we typically measure build times reduced by 15-75% on subsequent builds. We recommend profiling your build by using `--profile` to get a sense of how much impact the Gradle Daemon can have for you.

The Gradle Daemon is enabled by default starting with Gradle 3.0, so you don't have to do anything to benefit from it.

If you run CI builds in ephemeral environments (such as containers) that do not reuse any processes, use of the Daemon will slightly decrease performance (due to caching additional information) for no benefit, and may be disabled.

[[sec:status]]
== Running Daemon Status

To get a list of running Gradle Daemons and their statuses use the `--status` command.

Sample output:
[source]
----
    PID VERSION                 STATUS
  28411 3.0                     IDLE
  34247 3.0                     BUSY
----

Currently, a given Gradle version can only connect to daemons of the same version. This means the status output will only show Daemons for the version of Gradle being invoked and not for any other versions. Future versions of Gradle will lift this constraint and will show the running Daemons for all versions of Gradle.

[[sec:disabling_the_daemon]]
== Disabling the Daemon

The Gradle Daemon is enabled by default, and we recommend always enabling it. There are several ways to disable the Daemon, but the most common one is to add the line

[source]
----
org.gradle.daemon=false
----

to the file `«USER_HOME»/.gradle/gradle.properties`, where `«USER_HOME»` is your home directory. That’s typically one of the following, depending on your platform:

* `C:\Users\<username>` (Windows Vista & 7+)
* `/Users/<username>` (macOS)
* `/home/<username>` (Linux)

If that file doesn’t exist, just create it using a text editor. You can find details of other ways to disable (and enable) the Daemon in <<#daemon_faq,Daemon FAQ>> further down. That section also contains more detailed information on how the Daemon works.

Note that having the Daemon enabled, all your builds will take advantage of the speed boost, regardless of the version of Gradle a particular build uses.

[TIP]
.Continuous integration
====

Since Gradle 3.0, we enable Daemon by default and recommend using it for both developers' machines and Continuous Integration servers. However, if you suspect that Daemon makes your CI builds unstable, you can disable it to use a fresh runtime for each build since the runtime is _completely_ isolated from any previous builds.

====

[[sec:stopping_an_existing_daemon]]
== Stopping an existing Daemon

As mentioned, the Daemon is a background process. You needn’t worry about a build up of Gradle processes on your machine, though. Every Daemon monitors its memory usage compared to total system memory and will stop itself if idle when available system memory is low. If you want to explicitly stop running Daemon processes for any reason, just use the command `gradle --stop`.

This will terminate all Daemon processes that were started with the same version of Gradle used to execute the command. If you have the Java Development Kit (JDK) installed, you can easily verify that a Daemon has stopped by running the `jps` command. You’ll see any running Daemons listed with the name `GradleDaemon`.

[[daemon_faq]]
== FAQ

[[sec:ways_to_disable_gradle_daemon]]
=== How do I disable the Gradle Daemon?

There are two recommended ways to disable the Daemon persistently for an environment:

* Via environment variables: add the flag `-Dorg.gradle.daemon=false` to the `GRADLE_OPTS` environment variable
* Via properties file: add `org.gradle.daemon=false` to the `«GRADLE_USER_HOME»/gradle.properties` file

[NOTE]
====

Note, `«GRADLE_USER_HOME»` defaults to `«USER_HOME»/.gradle`, where `«USER_HOME»` is the home directory of the current user. This location can be configured via the `-g` and `--gradle-user-home` command line switches, as well as by the `GRADLE_USER_HOME` environment variable and `org.gradle.user.home` JVM system property.

====

Both approaches have the same effect. Which one to use is up to personal preference. Most Gradle users choose the second option and add the entry to the user `gradle.properties` file.

On Windows, this command will disable the Daemon for the current user:

[source]
----
(if not exist "%USERPROFILE%/.gradle" mkdir "%USERPROFILE%/.gradle") && (echo. >> "%USERPROFILE%/.gradle/gradle.properties" && echo org.gradle.daemon=false >> "%USERPROFILE%/.gradle/gradle.properties")
----

On UNIX-like operating systems, the following Bash shell command will disable the Daemon for the current user:

[source,bash]
----
mkdir -p ~/.gradle && echo "org.gradle.daemon=false" >> ~/.gradle/gradle.properties
----

Once the Daemon is disabled for a build environment in this way, a Gradle Daemon will not be started unless explicitly requested using the `--daemon` option.

The `--daemon` and `--no-daemon` command line options enable and disable usage of the Daemon for individual build invocations when using the Gradle command line interface. These command line options have the _highest_ precedence when considering the build environment. Typically, it is more convenient to enable the Daemon for an environment (e.g. a user account) so that all builds use the Daemon without requiring to remember to supply the `--daemon` option.

[[sec:why_is_there_more_than_one_daemon_process_on_my_machine]]
=== Why is there more than one Daemon process on my machine?

There are several reasons why Gradle will create a new Daemon, instead of using one that is already running. The basic rule is that Gradle will start a new Daemon if there are no existing idle or compatible Daemons available. Gradle will kill any Daemon that has been idle for 3 hours or more, so you don't have to worry about cleaning them up manually.

idle::
An idle Daemon is one that is not currently executing a build or doing other useful work.
compatible::
A compatible Daemon is one that can (or can be made to) meet the requirements of the requested build environment. The Java runtime used to execute the build is an example aspect of the build environment. Another example is the set of JVM system properties required by the build runtime.

Some aspects of the requested build environment may not be met by an Daemon. If the Daemon is running with a Java 8 runtime, but the requested environment calls for Java 10, then the Daemon is not compatible and another must be started. Moreover, certain properties of a Java runtime cannot be changed once the JVM has started. For example, it is not possible to change the memory allocation (e.g. `-Xmx1024m`), default text encoding, default locale, etc of a running JVM.

The “requested build environment” is typically constructed implicitly from aspects of the build client’s (e.g. Gradle command line client, IDE etc.) environment and explicitly via command line switches and settings. See <<build_environment.adoc#build_environment,Build Environment>> for details on how to specify and control the build environment.

The following JVM system properties are effectively immutable. If the requested build environment requires any of these properties, with a different value than a Daemon’s JVM has for this property, the Daemon is not compatible.

* file.encoding
* user.language
* user.country
* user.variant
* java.io.tmpdir
* javax.net.ssl.keyStore
* javax.net.ssl.keyStorePassword
* javax.net.ssl.keyStoreType
* javax.net.ssl.trustStore
* javax.net.ssl.trustStorePassword
* javax.net.ssl.trustStoreType
* com.sun.management.jmxremote

The following JVM attributes, controlled by startup arguments, are also effectively immutable. The corresponding attributes of the requested build environment and the Daemon’s environment must match exactly in order for a Daemon to be compatible.

* The maximum heap size (i.e. the -Xmx JVM argument)
* The minimum heap size (i.e. the -Xms JVM argument)
* The boot classpath (i.e. the -Xbootclasspath argument)
* The “assertion” status (i.e. the -ea argument)

The required Gradle version is another aspect of the requested build environment. Daemon processes are coupled to a specific Gradle runtime. Working on multiple Gradle projects during a session that use different Gradle versions is a common reason for having more than one running Daemon process.

[[sec:how_much_memory_does_the_daemon_use_and_can_i_give_it_more]]
=== How much memory does the Daemon use and can I give it more?

If the requested build environment does not specify a maximum heap size, the Daemon will use up to 512MB of heap. It will use the JVM's default minimum heap size. 512MB is more than enough for most builds. Larger builds with hundreds of subprojects, lots of configuration, and source code may require, or perform better, with more memory.

To increase the amount of memory the Daemon can use, specify the appropriate flags as part of the requested build environment. Please see <<build_environment.adoc#build_environment,Build Environment>> for details.

[[sec:how_can_i_stop_a_daemon]]
=== How can I stop a Daemon?

Daemon processes will automatically terminate themselves after 3 hours of inactivity or less. If you wish to stop a Daemon process before this, you can either kill the process via your operating system or run the `gradle --stop` command. The `--stop` switch causes Gradle to request that _all_ running Daemon processes, _of the same Gradle version used to run the command_, terminate themselves.

[[sec:what_can_go_wrong_with_daemon]]
=== What can go wrong with Daemon?

Considerable engineering effort has gone into making the Daemon robust, transparent and unobtrusive during day to day development. However, Daemon processes can occasionally be corrupted or exhausted. A Gradle build executes arbitrary code from multiple sources. While Gradle itself is designed for and heavily tested with the Daemon, user build scripts and third party plugins can destabilize the Daemon process through defects such as memory leaks or global state corruption.

It is also possible to destabilize the Daemon (and build environment in general) by running builds that do not release resources correctly. This is a particularly poignant problem when using Microsoft Windows as it is less forgiving of programs that fail to close files after reading or writing.

Gradle actively monitors heap usage and attempts to detect when a leak is starting to exhaust the available heap space in the daemon. When it detects a problem, the Gradle daemon will finish the currently running build and proactively restart the daemon on the next build. This monitoring is enabled by default, but can be disabled by setting the `org.gradle.daemon.performance.enable-monitoring` system property to false.

If it is suspected that the Daemon process has become unstable, it can simply be killed. Recall that the `--no-daemon` switch can be specified for a build to prevent use of the Daemon. This can be useful to diagnose whether or not the Daemon is actually the culprit of a problem.

[[sec:tools_and_ides]]
== Tools & IDEs

The <<embedding.adoc#embedding,Gradle Tooling API>> that is used by IDEs and other tools to integrate with Gradle _always_ uses the Gradle Daemon to execute builds. If you are executing Gradle builds from within your IDE you are using the Gradle Daemon and do not need to enable it for your environment.

[[sec:how_does_the_gradle_daemon_make_builds_faster]]
== How does the Gradle Daemon make builds faster?

The Gradle Daemon is a _long lived_ build process. In between builds it waits idly for the next build. This has the obvious benefit of only requiring Gradle to be loaded into memory once for multiple builds, as opposed to once for each build. This in itself is a significant performance optimization, but that's not where it stops.

A significant part of the story for modern JVM performance is runtime code optimization. For example, HotSpot (the JVM implementation provided by Oracle and used as the basis of OpenJDK) applies optimization to code while it is running. The optimization is progressive and not instantaneous. That is, the code is progressively optimized during execution which means that subsequent builds can be faster purely due to this optimization process. Experiments with HotSpot have shown that it takes somewhere between 5 and 10 builds for optimization to stabilize. The difference in perceived build time between the first build and the 10th for a Daemon can be quite dramatic.

The Daemon also allows more effective in memory caching across builds. For example, the classes needed by the build (e.g. plugins, build scripts) can be held in memory between builds. Similarly, Gradle can maintain in-memory caches of build data such as the hashes of task inputs and outputs, used for incremental building.