Rick

Rick
Rick

Friday, April 29, 2016

QBit Resourceful RESTful Microservices

You can build resourceful REST URIs using @RequestMapping from QBit microservices lib.
Typically you use HTTP GET to get a list of something and if you are returning more than one then the URI path should end with / as follows:

@RequestMapping("/department/")

    @RequestMapping("/department/")
    public List<Department> getDepartments() {
To add to a list, you would use a PUT or a POST. PUT is generally used for updates, and POST is used to create. If you were editing an object at a give ID you would use a PUT, but if you were adding a new item to a list, you would use a PUT to update a list or a POST to create an item. Dealers choice.

@RequestMapping(value = "/department/{departmentId}/", method = RequestMethod.POST)

@RequestMapping(value = "/department/{departmentId}/", method = RequestMethod.POST)
public boolean addDepartment(@PathVariable("departmentId") Integer departmentId,
                                  final Department department) {
You could easily get into a long drawn out argument about which to use PUT or POST in the above scenario, because you are updating the list (adding an item to it), but you are creating a department. Just remember POST for create, and PUT for update.
To get a single employee, you could use a path param. Some say that path params are nice for people and search engines, I say ok dude.

Using a path param @RequestMapping(value = "/department/{departmentId}/employee/{employeeId}", method = RequestMethod.GET)

    @RequestMapping(value = "/department/{departmentId}/employee/{employeeId}", method = RequestMethod.GET)
    public Employee getEmployee(@PathVariable("departmentId") Integer departmentId,
                                @PathVariable("employeeId") Long employeeId) {
HTTP GET is the default, but you can specify it as we did above.
There are other annotations that work the same way except the HTTP method is in the name of the param.
  • @POST
  • @PUT
  • @GET
You could rewrite the last examples like this.

Using the @POST, @PUT, @GET

    @GET("/department/")
    public List<Department> getDepartments() {

...
    @POST(value = "/department/{departmentId}/")
    public boolean addDepartment(@PathVariable("departmentId") Integer departmentId,
                                  final Department department) {


...


    @GET(value = "/department/{departmentId}/employee/{employeeId}")
    public Employee getEmployee(@PathVariable("departmentId") Integer departmentId,
                                @PathVariable("employeeId") Long employeeId) {
Making the method name also be the annotation name makes things a bit more compact, and a bit easier to read.
Here are some more Resourceful REST examples in QBit to ponder.

Resource based RESTful API for Microservices

package com.mammatustech.hr;



import io.advantageous.qbit.annotation.*;

import java.util.*;

@RequestMapping("/hr")
public class HRService {

    final Map<Integer, Department> departmentMap = new HashMap<>();


    @RequestMapping("/department/")
    public List<Department> getDepartments() {
        return new ArrayList<>(departmentMap.values());
    }

    @RequestMapping(value = "/department/{departmentId}/", method = RequestMethod.POST)
    public boolean addDepartment(@PathVariable("departmentId") Integer departmentId,
                                  final Department department) {

        departmentMap.put(departmentId, department);
        return true;
    }

    @RequestMapping(value = "/department/{departmentId}/employee/", method = RequestMethod.POST)
    public boolean addEmployee(@PathVariable("departmentId") Integer departmentId,
                               final Employee employee) {

        final Department department = departmentMap.get(departmentId);

        if (department ==  null) {
            throw new IllegalArgumentException("Department " + departmentId + " does not exist");
        }

        department.addEmployee(employee);
        return true;
    }

    @RequestMapping(value = "/department/{departmentId}/employee/{employeeId}", method = RequestMethod.GET)
    public Employee getEmployee(@PathVariable("departmentId") Integer departmentId,
                                @PathVariable("employeeId") Long employeeId) {

        final Department department = departmentMap.get(departmentId);

        if (department ==  null) {
            throw new IllegalArgumentException("Department " + departmentId + " does not exist");
        }

        Optional<Employee> employee = department.getEmployeeList().stream().filter(
                employee1 -> employee1.getId() == employeeId).findFirst();

        if (employee.isPresent()){
            return employee.get();
        } else {
            throw new IllegalArgumentException("Employee with id " + employeeId + " Not found ");
        }
    }


    @RequestMapping(value = "/department/{departmentId}/employee/{employeeId}/phoneNumber/",
            method = RequestMethod.POST)
    public boolean addPhoneNumber(@PathVariable("departmentId") Integer departmentId,
                                  @PathVariable("employeeId") Long employeeId,
                                  PhoneNumber phoneNumber) {

        Employee employee = getEmployee(departmentId, employeeId);
        employee.addPhoneNumber(phoneNumber);
        return true;
    }



    @RequestMapping(value = "/department/{departmentId}/employee/{employeeId}/phoneNumber/")
    public List<PhoneNumber> getPhoneNumbers(@PathVariable("departmentId") Integer departmentId,
                                             @PathVariable("employeeId") Long employeeId) {

        Employee employee = getEmployee(departmentId, employeeId);
        return employee.getPhoneNumbers();
    }


    @RequestMapping(value = "/kitchen/{departmentId}/employee/phoneNumber/kitchen/",
            method = RequestMethod.POST)
    public boolean addPhoneNumberKitchenSink(@PathVariable("departmentId") Integer departmentId,
                                  @RequestParam("employeeId") Long employeeId,
                                  @HeaderParam("X-PASS-CODE") String passCode,
                                             PhoneNumber phoneNumber) {

        if ("passcode".equals(passCode)) {
            Employee employee = getEmployee(departmentId, employeeId);
            employee.addPhoneNumber(phoneNumber);
            return true;
        } else {
            return false;
        }
    }



}

Thursday, April 28, 2016

Konf Java configuration with JSON, YAML and JavaScript version 1.1.0.RELEASE


Added getDuration(path)getIntList(path), and the rest of the numbers (double, float, long).

Working with java.time.Duration

  • getDuration(path) get a duration
  • getDurationList(path) get a duration list
Konf supports "10 seconds" style config for duration as well as
having built-in functions and support for ISO-8601. See documentation 
for duration config
for more details.
Konf can reads list of numbers.
  • getIntList reads list of ints
  • getLongList reads list of longs
  • getDoubleList reads list of doubles
  • getFloatList reads list of floats
See documentation list of number configuration 
for more details.

Thanks

Downloads




Working with Durations



Konf, the type safe Java configuration library, has a way to configure java.time.Duration. You use the getDuration(path) and getDurationList(path) method to read a duration from a location.

Different way to configure duration

var config = {
  tenSeconds: seconds(10),
  tenDays: days(10),
  tenMinutes: minutes(10),
  tenHours: hours(10),
  tenMillis: millis(10),
  tenMilliseconds: milliseconds(10),
  fifteenMinutes: "PT15M",
  tenSeconds2: "10 seconds",
  tenMinutes2: "10m",
  tenHours2: "10 h",
  tenDays2: "10 day",
  tenMillis2: "10ms"
};

TypeSafe Config style

Duration can be specified with time (type safe config style).
You can postFix the type value in a string with any of these prefixes.

Typesafe config style postfixes

ns, nano, nanos, nanosecond, nanoseconds
us, micro, micros, microsecond, microseconds
ms, milli, millis, millisecond, milliseconds
s, second, seconds
m, minute, minutes
h, hour, hours
d, day, days

ISO-8601 Duration

Konf also supports ISO-8601 duration format, i.e., PT15M.

Built-in functions for Duration

There are also built-in functions for durations using seconds(5)minutes(5), etc.

Example usage

First we load the config.

Load the config

    private Config config;

    @Before
    public void setUp() throws Exception {
        config = ConfigLoader.load("test-config.js");
    }
Then we use getDuration(path) to read the Duration values.
        assertEquals(Duration.ofMillis(10), 
               config.getDuration("tenMillis"));

        assertEquals(Duration.ofMillis(10), 
                  config.getDuration("tenMilliseconds"));

        assertEquals(Duration.ofSeconds(10), config.getDuration("tenSeconds"));
        assertEquals(Duration.ofMinutes(10), config.getDuration("tenMinutes"));
        assertEquals(Duration.ofHours(10), config.getDuration("tenHours"));
        assertEquals(Duration.ofDays(10), config.getDuration("tenDays"));

        assertEquals(Duration.ofMinutes(15), 
                                 config.getDuration("fifteenMinutes"));

        assertEquals(Duration.ofSeconds(10), config.getDuration("tenSeconds2"));
        assertEquals(Duration.ofMinutes(10), config.getDuration("tenMinutes2"));
        assertEquals(Duration.ofHours(10), config.getDuration("tenHours2"));
        assertEquals(Duration.ofDays(10), config.getDuration("tenDays2"));
        assertEquals(Duration.ofMillis(10), config.getDuration("tenMillis2"));
        assertEquals(Duration.ofMillis(10), config.getDuration("tenMilliseconds2"));

Working with lists of numbers


Konf reads list of numbers.
  • getIntList reads list of ints
  • getLongList reads list of longs
  • getDoubleList reads list of doubles
  • getFloatList reads list of floats
Given this sample configuration file.
test-config.js
var config = {

  floats: [1.0, 2.0, 3.0],
  doubles: [1.0, 2.0, 3.0],
  longs: [1.0, 2.0, 3.0],
  ints: [1, 2, 3],

  intsNull: [1, null, 3],
  intsWrongType: [1, "2", 3]
}
First we load the config file (must be on the class path).

Load config file

    private Config config;

    @Before
    public void setUp() throws Exception {
        config = ConfigLoader.load("test-config.js");
    }
...
Now we can use getDoubleList et al to read values from the config as follows.

Read config items

    @Test
    public void testNumberList() throws Exception {
        assertEquals(asList(1.0, 2.0, 3.0), 
                  config.getDoubleList("doubles"));

        assertEquals(asList(1.0f, 2.0f, 3.0f), 
                  config.getFloatList("floats"));

        assertEquals(asList(1, 2, 3), 
                  config.getIntList("ints"));

        assertEquals(asList(1L, 2L, 3L), 
                  config.getLongList("longs"));
    }
The list of numbers must not contain nulls.

Nulls do not work

    @Test(expected = IllegalArgumentException.class)
    public void listHasNull() {
        assertEquals(asList(1, 2, 3), config.getIntList("intsNull"));
    }
The list of numbers must contain valid numbers.

Wrong types do not work

    @Test(expected = IllegalArgumentException.class)
    public void wrongTypeInList() {
        assertEquals(asList(1, 2, 3), config.getIntList("intsWrongType"));

    }




JMeter vs. Gatling: Fact Checking: SHILL! ASTROTURFING SHILL!