A simple process service for an airline scheduling flights and optimizing seat assignments using OptaPlanner.
It uses an event-based subprocess to simulate passengers buying tickets, who the airline security officer must approve via a user task before the passenger is allowed to get a ticket.
The main process waits for a user task to be completed to finalize the passenger list.
It then invokes a custom Java service FlightSeatingSolveService.assignSeats
to optimize the flight's seats following the rules in FlightSeatingConstraintProvider
using OptaPlanner's, followed by a user task to finalize seat assignments.
Based on these two processes (defined using BPMN 2.0 format), the custom data object and custom Java service, a new service is generated that exposes REST operations to create new flights (following the steps as defined in the main and sub-process) and add passengers to flights.
A UI is included in this example, which can be accessed on localhost:8080
when the application is running.
You will need:
- Java 11+ installed
- Environment variable JAVA_HOME set accordingly
- Maven 3.6.2+ installed
When using native image compilation, you will also need:
- GraalVM 19.1.1 installed
- Environment variable GRAALVM_HOME set accordingly
- Note that GraalVM native image compilation typically requires other packages (glibc-devel, zlib-devel and gcc) to be installed too. You also need 'native-image' installed in GraalVM (using 'gu install native-image'). Please refer to GraalVM installation documentation for more details.
mvn clean package quarkus:dev
Note that this requires GRAALVM_HOME to point to a valid GraalVM installation
mvn clean package -Pnative
To run the generated native executable, generated in target/
, execute
./target/jbpm-optaplanner-quarkus-example-{version}-runner
Note: This does not yet work on Windows, GraalVM and Quarkus should be rolling out support for Windows soon
You can take a look at the swagger definition - automatically generated and included in this service - to determine all available operations exposed by this service. For easy readability you can visualize the swagger definition file using a swagger UI like for example available here. In addition, various clients to interact with this service can be easily generated using this swagger definition.
Once the service is up and running, you can access the UI at localhost:8080
or use
the following examples to interact with the service.
Allows to create a new flight with the given data:
curl -d '{ "params": { "origin" : "JFK", "destination": "SFO", "departureDateTime": "2020-01-01T12:00", "seatRowSize": 8, "seatColumnSize": 6 } }' -H "Content-Type: application/json" -X POST http://localhost:8080/rest/flights
or on windows
curl -d "{\"params\": { \"origin\" : \"JFK\", \"destination\": \"SFO\", \"departureDateTime\": \"2020-01-01T12:00\", \"seatRowSize\": 8, \"seatColumnSize\": 6 }}" -H "Content-Type: application/json" -X POST http://localhost:8080/rest/flights
As response the created flight is returned (in field "flight"). Example response:
{
"id":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"flight":{
"flightInfo":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":{...},
"seatRowSize":1,
"seatColumnSize":1
},
"seatList":[{"name":"1A","row":0,"column":0,"seatType":"WINDOW","emergencyExitRow":false}],
"passengerList":[],
"score":null,
"origin":"YYZ",
"seatColumnSize":6,
"seatRowSize":4,
"destination":"KRND",
"departureDateTime":{...}
},
"isSolving":true,
"processId":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"params":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":"2020-03-06T20:19:49.240",
"seatRowSize":4,
"seatColumnSize":6
},
"isPassengerListFinalized":false
}
Returns list of flights currently being scheduled:
curl -X GET http://localhost:8080/rest/flights
As response an array of flights is returned.
Returns flight with given id (if being scheduled):
# Replace {id} with the process id
curl -X GET http://localhost:8080/rest/flights/{id}
As response a single flight is returned if found, otherwise no content (204) is returned.
Cancels flight with given id
# Replace {id} with the process id
curl -X DELETE http://localhost:8080/rest/flights/{id}
Get user tasks that currently require action for a flight (with task id's as keys, and task types as values).
# Replace {id} with the process id
curl -X GET http://localhost:8080/rest/flights/{id}/tasks
Example response:
{
"66c11e3e-c211-4cee-9a07-848b5e861bc5": "finalizePassengerList",
"a2c11e3e-c211-4cee-9a07-848b5e861bc5": "finalizeSeatAssignment",
"f4c11e3e-c211-4cee-9a07-848b5e861bc5": "approveDenyPassenger"
}
Create a new Ticket request for a passenger, who must be approved by security.
# Replace {id} with the process id
curl -d '{ "passenger": { "name": "Amy Cole", "seatTypePreference": "WINDOW", "emergencyExitRowCapable": true, "paidForSeat": true, "seat": "3A" } }' -X POST http://localhost:8080/rest/flights/{id}/newPassengerRequest
or on Windows:
rem Replace {id} with the process id
curl -d "{ \"passenger\": { \"name\": \"Amy Cole\", \"seatTypePreference\": \"WINDOW\", \"emergencyExitRowCapable\": true, \"paidForSeat\": true, \"seat\": \"3A\" } }" -X POST http://localhost:8080/rest/flights/{id}/newPassengerRequest
Get the passenger that need to be denied or approved for the given "approveDenyPassenger" task.
# Replace {id} with the process id and {taskId} with the id of the task to complete
curl -X GET http://localhost:8080/rest/flights/{id}/approveDenyPassenger/{taskId}
Example response:
{
"passenger": {
"name": "Amy Cole",
"seatTypePreference": "WINDOW",
"emergencyExitRowCapable": true,
"paidForSeat": true,
"seat": "3A"
}
}
Approves an passenger and add them to the flight if "isPassengerApproved" is true, otherwise cancels their ticket.
# Replace {id} with the process id and {taskId} with the id of the task to complete
curl -d '{ "isPassengerApproved": true }' -X POST http://localhost:8080/rest/flights/{id}/approveDenyPassenger/{taskId}
or in Windows
rem Replace {id} with the process id and {taskId} with the id of the task to complete
curl -d "{ \"isPassengerApproved\": true }" -X POST http://localhost:8080/rest/flights/{id}/approveDenyPassenger/{taskId}
Example Response:
{
"id":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"flight":{
"flightInfo":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":{...},
"seatRowSize":1,
"seatColumnSize":1
},
"seatList":[{"name":"1A","row":0,"column":0,"seatType":"WINDOW","emergencyExitRow":false}],
"passengerList":[{
"id": 0,
"name": "Amy Cole",
"seatTypePreference": "WINDOW",
"emergencyExitRowCapable": true,
"paidForSeat": false,
"seat":null
}],
"score":null,
"origin":"YYZ",
"seatColumnSize":6,
"seatRowSize":4,
"destination":"KRND",
"departureDateTime":{...}
},
"isSolving":true,
"processId":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"params":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":"2020-03-06T20:19:49.240",
"seatRowSize":4,
"seatColumnSize":6
},
"isPassengerListFinalized":false
}
Finalize the passenger list for the flight.
# Replace {id} with the process id and {taskId} with the id of the task to complete
curl -d '{}' -X POST http://localhost:8080/rest/flights/{id}/finalizePassengerList/{taskId}
Example response:
{
"id":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"flight":{
"flightInfo":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":{...},
"seatRowSize":1,
"seatColumnSize":1
},
"seatList":[{"name":"1A","row":0,"column":0,"seatType":"WINDOW","emergencyExitRow":false}],
"passengerList":[{
"id": 0,
"name": "Amy Cole",
"seatTypePreference": "WINDOW",
"emergencyExitRowCapable": true,
"paidForSeat": false,
"seat":null
}],
"score":null,
"origin":"YYZ",
"seatColumnSize":6,
"seatRowSize":4,
"destination":"KRND",
"departureDateTime":{...}
},
"isSolving":true,
"processId":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"params":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":"2020-03-06T20:19:49.240",
"seatRowSize":4,
"seatColumnSize":6
},
"isPassengerListFinalized":true
}
Finalize seat assignments for the flight.
# Replace {id} with the process id and {taskId} with the id of the task to complete
curl -d '{}' -X POST http://localhost:8080/rest/flights/{id}/finalizeSeatAssignment/{taskId}
Example response:
{
"id":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"flight":{
"flightInfo":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":{...},
"seatRowSize":1,
"seatColumnSize":1
},
"seatList":[{"name":"1A","row":0,"column":0,"seatType":"WINDOW","emergencyExitRow":false}],
"passengerList":[{
"id": 0,
"name": "Amy Cole",
"seatTypePreference": "WINDOW",
"emergencyExitRowCapable": true,
"paidForSeat": false,
"seat":{"name":"1A","row":0,"column":0,"seatType":"WINDOW","emergencyExitRow":false}
}],
"score":null,
"origin":"YYZ",
"seatColumnSize":6,
"seatRowSize":4,
"destination":"KRND",
"departureDateTime":{...}
},
"isSolving":false,
"processId":"7f24831f-9dc6-44c7-8dec-9b4a696506b5",
"params":{
"origin":"YYZ",
"destination":"KRND",
"departureDateTime":"2020-03-06T20:19:49.240",
"seatRowSize":4,
"seatColumnSize":6
},
"isPassengerListFinalized":true
}