Quantcast
Channel: Sleepless Dev
Viewing all articles
Browse latest Browse all 217

Using QBit to create Java RESTful microservices

$
0
0

QBit Restful Microservices

Before we delve into QBit restful services, let's cover what we get from gradle's application plugin. In order to be a microservice, a service needs to run in a standalone process or a related group of standalone processes.

Gradle application plugin

Building a standalone application with gradle is quite easy. You use the gradle application plug-in.

Gradle build using java and application plugin

apply plugin:'java'
apply plugin:'application'

sourceCompatibility =1.8
version ='1.0'
mainClassName ="io.advantageous.examples.Main"

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
testCompile group:'junit', name:'junit', version:'4.11'
}
To round out this example, let's create a simple Main java class.

Simple main Java to demonstrate Gradle application plugin

packageio.advantageous.examples;

publicclassMain {

publicstaticvoidmain(String... args) {
System.out.println("Hello World!");
}
}
The project structure is as follows:

Project structure

$ tree
.
├── build.gradle
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── restful-qbit.iml
├── settings.gradle
└── src
└── main
└── java
└── io
└── advantageous
└── examples
└── Main.java
We use a standard maven style structure.
To build this application we use the following commands:

Building our application

$ gradle clean build

Output

:clean UP-TO-DATE
:compileJava
:processResources UP-TO-DATE
:classes
:jar
:assemble
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test UP-TO-DATE
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 2.474 secs
To run our application we use the following:

Running our application

$ gradle run

Output

:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:run
Hello World!

BUILD SUCCESSFUL

Total time: 2.202 secs
There are three more commands that we care about:
  • installDist Installs the application into a specified directory
  • distZip Creates ZIP archive including libs and start scripts
  • distTar Creates TAR archive including libs and start scripts
The application plug-in allows you to create scripts to start a process. These scripts work on all operating systems. Microservices run as standalone processes. The gradle application plug-in is a good fit for microservice development.
Let's use the application plugin to create a dist zip file.

Using gradle to create a distribution zip

$ gradle distZip
Let's see where gradle put the zip.

Using find to see where the zip went

$ find . -name "*.zip"
./build/distributions/restful-qbit-1.0.zip
Let's unzip to a directory.

Unzipping to an install directory

$ mkdir /opt/example
$ unzip ./build/distributions/restful-qbit-1.0.zip -d /opt/example/
Archive: ./build/distributions/restful-qbit-1.0.zip
creating: /opt/example/restful-qbit-1.0/
creating: /opt/example/restful-qbit-1.0/lib/
inflating: /opt/example/restful-qbit-1.0/lib/restful-qbit-1.0.jar
creating: /opt/example/restful-qbit-1.0/bin/
inflating: /opt/example/restful-qbit-1.0/bin/restful-qbit
inflating: /opt/example/restful-qbit-1.0/bin/restful-qbit.bat
Now we can run it from the install directory.

Running from install directory

$ /opt/example/restful-qbit-1.0/bin/restful-qbit
Hello World!
Contents of restful-qbit startup script.
$ cat /opt/example/restful-qbit-1.0/bin/restful-qbit
#!/usr/bin/env bash

##############################################################################
##
## restful-qbit start up script for UN*X
##
##############################################################################

# Add default JVM options here. You can also use JAVA_OPTS and RESTFUL_QBIT_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""

APP_NAME="restful-qbit"
APP_BASE_NAME=`basename "$0"`

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"

warn ( ) {
echo"$*"
}

die ( ) {
echo
echo"$*"
echo
exit 1
}

# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case"`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac

# For Cygwin, ensure paths are in UNIX format before anything is touched.
if$cygwin;then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi

# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ;do
ls=`ls -ld "$PRG"`
link=`expr "$ls":'.*-> \(.*\)$'`
if expr "$link":'/.*'> /dev/null;then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd"`dirname \"$PRG\"`/..">&-
APP_HOME="`pwd -P`"
cd"$SAVED">&-

CLASSPATH=$APP_HOME/lib/restful-qbit-1.0.jar

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ;then
if [ -x "$JAVA_HOME/jre/sh/java" ] ;then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ;then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1|| die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi

# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ;then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ;then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ;then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ;then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi

# For Darwin, add options to specify how the application appears in the dock
if$darwin;then
GRADLE_OPTS="$GRADLE_OPTS\"-Xdock:name=$APP_NAME\"\"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi

# For Cygwin, switch paths to Windows format before running java
if$cygwin;then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`

# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
fordirin$ROOTDIRSRAW;do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN"!= "" ] ;then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
forargin"$@";do
CHECK=`echo"$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo"$arg"|egrep -c "^-"`### Determine if an option

if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ;then### Added a condition
eval`echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval`echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case$i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0""$args1" ;;
(3) set -- "$args0""$args1""$args2" ;;
(4) set -- "$args0""$args1""$args2""$args3" ;;
(5) set -- "$args0""$args1""$args2""$args3""$args4" ;;
(6) set -- "$args0""$args1""$args2""$args3""$args4""$args5" ;;
(7) set -- "$args0""$args1""$args2""$args3""$args4""$args5""$args6" ;;
(8) set -- "$args0""$args1""$args2""$args3""$args4""$args5""$args6""$args7" ;;
(9) set -- "$args0""$args1""$args2""$args3""$args4""$args5""$args6""$args7""$args8" ;;
esac
fi

# Split up the JVM_OPTS And RESTFUL_QBIT_OPTS values into an array, following the shell quoting and substitution rules
functionsplitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS$JAVA_OPTS$RESTFUL_QBIT_OPTS


exec"$JAVACMD""${JVM_OPTS[@]}" -classpath "$CLASSPATH" io.advantageous.examples.Main "$@"

Creating a simple RestFul Microservice

Let's create a simple HTTP service that responsds with pong when we send it a ping as follows.

Service that responds to this curl command

$ curl http://localhost:9090/services/pongservice/ping
"pong"
First let's import the qbit lib. We are using the SNAPSHOT but hopefully by the time you read this the release will be available.
Add the following to your gradle build.

Adding QBit to your gradle build

apply plugin:'java'
apply plugin:'application'

sourceCompatibility =1.8
version ='1.0'
mainClassName ="io.advantageous.examples.Main"

repositories {
mavenLocal()
mavenCentral()
}

dependencies {
testCompile group:'junit', name:'junit', version:'4.11'
compile group:'io.advantageous.qbit', name:'qbit-vertx', version:'0.7.3-SNAPSHOT'
}
Define a service as follows:

QBit service

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.RequestMapping;

@RequestMapping
publicclassPongService {


@RequestMapping
publicStringping() {
return"pong";
}

}
The @RequestMapping defines the service as one that responsds to an HTTP call. If you do not specify the path, then the lower case name of the class and the lower case name of the method becomes the path. Thus PongService.ping() becomes /pongservice/ping. To bind this service to a port we use a service server. A service server is a server that hosts services like our pong service.
Change the Main class to use the ServiceServer as follows:

Using main class to bind in service to a port

packageio.advantageous.examples;

importio.advantageous.qbit.server.ServiceServer;
importio.advantageous.qbit.server.ServiceServerBuilder;

publicclassMain {

publicstaticvoidmain(String... args) {
finalServiceServer serviceServer =ServiceServerBuilder
.serviceServerBuilder()
.setPort(9090).build();
serviceServer.initServices(newPongService());
serviceServer.startServer();
}
}
Notice we pass an instance of the PongService to the initServices of the service server. If we want to change the root address from "services" to something else we could do this:

Changing the root URI of the service server

finalServiceServer serviceServer =ServiceServerBuilder
.serviceServerBuilder()
.setUri("/main")
.setPort(9090).build();
serviceServer.initServices(newPongService());
serviceServer.startServer();
Now we can call this using curl as follows:

Use the curl command to invoke service via /main/pongservice/ping

$ curl http://localhost:9090/main/pongservice/ping
"pong"
QBit uses builders to make it easy to integrate QBit with frameworks like Spring or Guice or to just use standalone.

Adding a service that takes request params

Taking request parameters

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestParam;

@RequestMapping("/my/service")
publicclassSimpleService {

@RequestMapping("/add")
publicintadd(@RequestParam("a") inta,
@RequestParam("b") intb) {

return a + b;
}


}
Notice the above uses @RequestParam this allows you to pull the requests params as arguments to the method. If we pass a URL like: http://localhost:9090/main/my/service/add?a=1&b=2. QBit will use 1 for argument a and 2 for argument b.

Adding the new service to Main

packageio.advantageous.examples;

importio.advantageous.qbit.server.ServiceServer;
importio.advantageous.qbit.server.ServiceServerBuilder;

publicclassMain {

publicstaticvoidmain(String... args) {
finalServiceServer serviceServer =ServiceServerBuilder
.serviceServerBuilder()
.setUri("/main")
.setPort(9090).build();
serviceServer.initServices(
newPongService(),
newSimpleService());
serviceServer.startServer();
}
}
When we load this URL:
http://localhost:9090/main/my/service/add?a=1&b=2
We get this response.
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1

3

Working with URI params

Many think that URLs without request parameters are more search engine friendly or you can list things under a context which make it more RESTful. This is open to debate. I don't care about debate, but here is an example of using URI params.

Working with URI params example

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.PathVariable;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestParam;

@RequestMapping("/my/service")
publicclassSimpleService {
...
...
@RequestMapping("/add2/{a}/{b}")
publicintadd2( @PathVariable("a") inta,
@PathVariable("b") intb) {

return a + b;
}


}
Now we can pass arguments which are part of the URL path. We do this by using the@PathVariable annotation. Thus the following URL:
http://localhost:9090/main/my/service/add2/1/4
The 1 is correlates to the "a" argument to the method and the 4 correlates to the "b" arguments.
We would get the following response when we load this URL.

Working with URI params output

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1

5
You can mix and match URI params and request params.

Working with URI params and request params example

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.PathVariable;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestParam;

@RequestMapping("/my/service")
publicclassSimpleService {
...
...
@RequestMapping("/add3/{a}/")
publicintadd3( @PathVariable("a") inta,
@RequestParam("b") intb) {

return a + b;
}

}
This allows us to mix URI params and request params as follows:
http://localhost:9090/main/my/service/add3/1?b=8
Now we get this response:

Working with URI params and request params example

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 1

9

Fuller RESTful example

Let's create a simple Employee / Department listing application.

RESTful Employee and Department listing

packageio.advantageous.examples.employees;


importio.advantageous.qbit.annotation.PathVariable;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestMethod;
importio.advantageous.qbit.annotation.RequestParam;

importjava.util.*;
importjava.util.function.Predicate;

@RequestMapping("/dir")
publicclassEmployeeDirectoryService {


privatefinalList<Department> departmentList =newArrayList<>();


@RequestMapping("/employee/{employeeId}/")
publicEmployeelistEmployee(@PathVariable("employeeId") finallongemployeeId) {

/* Find the department that has the employee. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.employeeList().stream()
.anyMatch(employee -> employee.getId() == employeeId)).findFirst();

/* Find employee in department. */
if (departmentOptional.isPresent()) {
return departmentOptional.get().employeeList()
.stream().filter(employee -> employee.getId() == employeeId)
.findFirst().get();
} else {
returnnull;
}
}



@RequestMapping("/department/{departmentId}/")
publicDepartmentlistDepartment(@PathVariable("departmentId") finallongdepartmentId) {

/* Find the department that has the employee. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findFirst();

/* Find employee in department. */
if (departmentOptional.isPresent()) {
return departmentOptional.get();
} else {
returnnull;
}
}



@RequestMapping(value="/department/", method=RequestMethod.POST)
publicbooleanaddDepartment( @RequestParam("departmentId") finallongdepartmentId,
@RequestParam("name") finalStringname) {
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findAny();
if (departmentOptional.isPresent()) {
thrownewIllegalArgumentException("Department "+ departmentId +" already exists");
}
departmentList.add(newDepartment(departmentId, name));
returntrue;
}


@RequestMapping(value="/department/employee/", method=RequestMethod.POST)
publicbooleanaddEmployee( @RequestParam("departmentId") finallongdepartmentId,
finalEmployeeemployee) {

finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findAny();
if (!departmentOptional.isPresent()) {
thrownewIllegalArgumentException("Department not found");
}


finalboolean alreadyExists = departmentOptional.get().employeeList().stream()
.anyMatch(employeeItem -> employeeItem.getId() == employee.getId());

if (alreadyExists) {
thrownewIllegalArgumentException("Employee with id already exists "+ employee.getId());
}
departmentOptional.get().addEmployee(employee);
returntrue;
}

}
To add three departments Engineering, HR and Sales:

Add Engineering, HR and Sales department with REST

$ curl -X POST http://localhost:9090/main/dir/department/?departmentId=1&name=Engineering
$ curl -X POST http://localhost:9090/main/dir/department/?departmentId=2&name=HR
$ curl -X POST http://localhost:9090/main/dir/department/?departmentId=3&name=Sales
Now let's add some employees into those departments.

Add some employees to the departments

curl -H "Content-Type: application/json" -X POST /
-d '{"firstName":"Rick","lastName":"Hightower", "id": 1}' /
http://localhost:9090/main/dir/departme\
nt/employee/?departmentId=1

curl -H "Content-Type: application/json" -X POST /
-d '{"firstName":"Diana","lastName":"Hightower", "id": 2}' /
http://localhost:9090/main/dir/departm\
ent/employee/?departmentId=2

curl -H "Content-Type: application/json" -X POST /
-d '{"firstName":"Maya","lastName":"Hightower", "id": 3}' /
http://localhost:9090/main/dir/departme\
nt/employee/?departmentId=3

curl -H "Content-Type: application/json" -X POST /
-d '{"firstName":"Paul","lastName":"Hightower", "id": 4}' /
http://localhost:9090/main/dir/departmen\
t/employee/?departmentId=3
Now let's list the employees. We can get the employees with the following curl.

Listing employees with curl

$ curl http://localhost:9090/main/dir/employee/1
{"firstName":"Rick","lastName":"Hightower","id":1}

$ curl http://localhost:9090/main/dir/employee/2
{"firstName":"Diana","lastName":"Hightower","id":2}

$ curl http://localhost:9090/main/dir/employee/3
{"firstName":"Maya","lastName":"Hightower","id":3}

$ curl http://localhost:9090/main/dir/employee/4
{"firstName":"Paul","lastName":"Hightower","id":4}
Now we can list departments with our RESTful API:

Listing departments with curl

$ curl http://localhost:9090/main/dir/department/1
{"id":1,"employees":[{"firstName":"Rick","lastName":"Hightower","id":1}]}

$ curl http://localhost:9090/main/dir/department/2
{"id":2,"employees":[{"firstName":"Diana","lastName":"Hightower","id":2}]}

$ curl http://localhost:9090/main/dir/department/3
{
"id": 3,
"employees": [
{
"firstName": "Maya",
"lastName": "Hightower",
"id": 3
},
{
"firstName": "Paul",
"lastName": "Hightower",
"id": 4
}
]
}
Some feel that is a search engine friendly RESTful interface although it is not. A true RESTful interface would have hyperlinks, but let's leave that off for another discussion lest we bring on the debate akin to vi vs. emacs or Scala vs. Groovy.

Some theory

Generally speaking people prefer the following (subject to much debate):
  • POST to add to a resource
  • PUT to update a resource
  • GET to read a resource
  • DELETE to remove an item from a list
  • End a group in the singular form with a slash as in department/
  • Use the name or id in the URI path to address a resource department/1/ would address the department with id 1.

Adding DELETE verb

You can use any of the HTTP verbs. Typically as mentioned before you use the DELETE verb to delete a resource as follows:

Mapping methods to DELETE verb

    @RequestMapping(value ="/employee", method =RequestMethod.DELETE)
publicboolean removeEmployee(@RequestParam("id") finallong employeeId) {

/* Find the department that has the employee. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.employeeList().stream()
.anyMatch(employee -> employee.getId() == employeeId)).findFirst();

/* Remove employee from department. */
if (departmentOptional.isPresent()) {
departmentOptional.get().removeEmployee(employeeId);
returntrue;
} else {
returnfalse;
}
}


@RequestMapping(value ="/department", method =RequestMethod.DELETE)
publicboolean removeDepartment(@RequestParam("id") finallong departmentId) {

return departmentList.removeIf(department -> departmentId == department.getId());
}
Now let's delete somebody.

Using DELETE from command line

curl -H "Content-Type: application/json" -X DELETE  /
http://localhost:9090/main/dir/employee?id=3

Mixing and matching request params and path variables

You can mix and match @PathVariable and @RequestParam which is a quite common case and one that QBit just started supporitng this last release.

Mixing and matching request params and path variables example

    @RequestMapping(value ="/department/{departmentId}/employee", method =RequestMethod.DELETE)
publicboolean removeEmployeeFromDepartment(
@PathVariable("departmentId") finallong departmentId,
@RequestParam("id") finallong employeeId) {

/* Find the department by id. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findFirst();

/* Remove employee from department. */
if (departmentOptional.isPresent()) {
departmentOptional.get().removeEmployee(employeeId);
returntrue;
} else {
returnfalse;
}
}
In this very common case, you can use the PathVariable to address the department resource and then ask for a specific employee to be deleted only from this department.

Using curl to delete an employee from a specific department

curl -H "Content-Type: application/json" -X DELETE  \
http://localhost:9090/main/dir/department/3/employee?id=4

Full examples:

Listing

$ tree
.
├── addDepartments.sh
├── addEmployees.sh
├── build.gradle
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── removeEmployee.sh
├── restful-qbit.iml
├── settings.gradle
├── showEmployees.sh
└── src
├── main
│   └── java
│   └── io
│   └── advantageous
│   └── examples
│   ├── Main.java
│   ├── PongService.java
│   ├── SimpleService.java
│   └── employees
│   ├── Department.java
│   ├── Employee.java
│   └── EmployeeDirectoryService.java
└── test
└── java
└── io
└── advantageous
└── examples
└── employees
└── EmployeeDirectoryServiceTest.java

Department.java

packageio.advantageous.examples.employees;

importjava.util.ArrayList;
importjava.util.List;

publicclassDepartment {

privateString name;
privatefinallong id;
privatefinalList<Employee> employees =newArrayList();

publicDepartment(longid, Stringname) {
this.id = id;
this.name = name;
}

publicvoidaddEmployee(finalEmployeeemployee) {
employees.add(employee);
}


publicbooleanremoveEmployee(finallongid) {
return employees.removeIf(employee -> employee.getId() == id);
}

publicList<Employee>employeeList() {
return employees;
}


publiclonggetId() {
return id;
}

publicStringgetName() {
return name;
}

publicvoidsetName(Stringname) {
this.name = name;
}
}

Employee

packageio.advantageous.examples.employees;

publicclassEmployee {

privateString firstName;
privateString lastName;
privatefinallong id;

publicEmployee(StringfirstName, StringlastName, longid) {
this.firstName = firstName;
this.lastName = lastName;
this.id = id;
}

publicStringgetFirstName() {
return firstName;
}

publicvoidsetFirstName(StringfirstName) {
this.firstName = firstName;
}

publicStringgetLastName() {
return lastName;
}

publicvoidsetLastName(StringlastName) {
this.lastName = lastName;
}

publiclonggetId() {
return id;
}
}

EmployeeServiceDirectory

packageio.advantageous.examples.employees;


importio.advantageous.qbit.annotation.PathVariable;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestMethod;
importio.advantageous.qbit.annotation.RequestParam;

importjava.util.*;
importjava.util.function.Predicate;

@RequestMapping("/dir")
publicclassEmployeeDirectoryService {


privatefinalList<Department> departmentList =newArrayList<>();


@RequestMapping("/employee/{employeeId}/")
publicEmployeelistEmployee(@PathVariable("employeeId") finallongemployeeId) {

/* Find the department that has the employee. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.employeeList().stream()
.anyMatch(employee -> employee.getId() == employeeId)).findFirst();

/* Find employee in department. */
if (departmentOptional.isPresent()) {
return departmentOptional.get().employeeList()
.stream().filter(employee -> employee.getId() == employeeId)
.findFirst().get();
} else {
returnnull;
}
}



@RequestMapping("/department/{departmentId}/")
publicDepartmentlistDepartment(@PathVariable("departmentId") finallongdepartmentId) {

return departmentList.stream()
.filter(department -> department.getId() == departmentId).findFirst().get();
}



@RequestMapping(value="/department/", method=RequestMethod.POST)
publicbooleanaddDepartment( @RequestParam("departmentId") finallongdepartmentId,
@RequestParam("name") finalStringname) {
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findAny();
if (departmentOptional.isPresent()) {
thrownewIllegalArgumentException("Department "+ departmentId +" already exists");
}
departmentList.add(newDepartment(departmentId, name));
returntrue;
}


@RequestMapping(value="/department/employee/", method=RequestMethod.POST)
publicbooleanaddEmployee( @RequestParam("departmentId") finallongdepartmentId,
finalEmployeeemployee) {

finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findAny();
if (!departmentOptional.isPresent()) {
thrownewIllegalArgumentException("Department not found");
}


finalboolean alreadyExists = departmentOptional.get().employeeList().stream()
.anyMatch(employeeItem -> employeeItem.getId() == employee.getId());

if (alreadyExists) {
thrownewIllegalArgumentException("Employee with id already exists "+ employee.getId());
}
departmentOptional.get().addEmployee(employee);
returntrue;
}




@RequestMapping(value="/employee", method=RequestMethod.DELETE)
publicbooleanremoveEmployee(@RequestParam("id") finallongemployeeId) {

/* Find the department that has the employee. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.employeeList().stream()
.anyMatch(employee -> employee.getId() == employeeId)).findFirst();

/* Remove employee from department. */
if (departmentOptional.isPresent()) {
departmentOptional.get().removeEmployee(employeeId);
returntrue;
} else {
returnfalse;
}
}


@RequestMapping(value="/department", method=RequestMethod.DELETE)
publicbooleanremoveDepartment(@RequestParam("id") finallongdepartmentId) {

return departmentList.removeIf(department -> departmentId == department.getId());
}



@RequestMapping(value="/department/{departmentId}/employee", method=RequestMethod.DELETE)
publicbooleanremoveEmployeeFromDepartment(
@PathVariable("departmentId") finallongdepartmentId,
@RequestParam("id") finallongemployeeId) {

/* Find the department by id. */
finalOptional<Department> departmentOptional = departmentList.stream()
.filter(department -> department.getId() == departmentId).findFirst();

/* Remove employee from department. */
if (departmentOptional.isPresent()) {
departmentOptional.get().removeEmployee(employeeId);
returntrue;
} else {
returnfalse;
}
}

}

PongService.java

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.RequestMapping;

@RequestMapping
publicclassPongService {


@RequestMapping
publicStringping() {
return"pong";
}

}

SimpleService.java

packageio.advantageous.examples;


importio.advantageous.qbit.annotation.PathVariable;
importio.advantageous.qbit.annotation.RequestMapping;
importio.advantageous.qbit.annotation.RequestParam;

@RequestMapping("/my/service")
publicclassSimpleService {

@RequestMapping("/add")
publicintadd(@RequestParam("a") inta,
@RequestParam("b") intb) {

return a + b;
}

@RequestMapping("/add2/{a}/{b}")
publicintadd2( @PathVariable("a") inta,
@PathVariable("b") intb) {

return a + b;
}

@RequestMapping("/add3/{a}/")
publicintadd3( @PathVariable("a") inta,
@RequestParam("b") intb) {

return a + b;
}


}

Main.java

packageio.advantageous.examples;

importio.advantageous.examples.employees.EmployeeDirectoryService;
importio.advantageous.qbit.server.ServiceServer;
importio.advantageous.qbit.server.ServiceServerBuilder;

publicclassMain {

publicstaticvoidmain(String... args) {
finalServiceServer serviceServer =ServiceServerBuilder
.serviceServerBuilder()
.setUri("/main")
.setPort(9090).build();
serviceServer.initServices(
newPongService(),
newSimpleService(),
newEmployeeDirectoryService());
serviceServer.startServer();
}
}

Viewing all articles
Browse latest Browse all 217

Latest Images

Trending Articles



Latest Images