/*******************************************************************************
 * Copyright (c) 2014 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
 * which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * Contributors:
 *      Dmitry Kornilov - Initial implementation
 ******************************************************************************/
package org.eclipse.persistence.jpars.test.server.v2;

import com.sun.jersey.api.client.ClientResponse.Status;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.jpa.rs.exceptions.ErrorResponse;
import org.eclipse.persistence.jpars.test.BaseJparsTest;
import org.eclipse.persistence.jpars.test.model.employee.Employee;
import org.eclipse.persistence.jpars.test.model.employee.EmployeeAddress;
import org.eclipse.persistence.jpars.test.model.employee.EmploymentPeriod;
import org.eclipse.persistence.jpars.test.model.employee.Gender;
import org.eclipse.persistence.jpars.test.model.employee.LargeProject;
import org.eclipse.persistence.jpars.test.model.employee.Office;
import org.eclipse.persistence.jpars.test.model.employee.PhoneNumber;
import org.eclipse.persistence.jpars.test.model.employee.SmallProject;
import org.eclipse.persistence.jpars.test.server.RestCallFailedException;
import org.eclipse.persistence.jpars.test.util.DBUtils;
import org.eclipse.persistence.jpars.test.util.RestUtils;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

import javax.ws.rs.core.MediaType;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

/**
 * ServerEmployeeTest from JPARS 1.0 adapted for version 2.0.
 * {@see ServerEmployeeTest}
 *
 * @author Dmitry Kornilov
 * @since EclipseLink 2.6.0
 */
public class ServerEmployeeV2Test extends BaseJparsTest {

    @BeforeClass
    public static void setup() throws Exception {
        initContext("jpars_employee-static", "v2.0");
    }

    @After
    public void cleanup() throws URISyntaxException {
        RestUtils.restUpdateQuery(context, "Employee.deleteAll", "Employee", null, null, MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test read employee json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeJSON() throws Exception {
        readEmployee(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test read employee xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeXML() throws Exception {
        readEmployee(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test update employee with employment period json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithEmploymentPeriodJSON() throws Exception {
        updateEmployeeWithEmploymentPeriod(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test update employee with employment period xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithEmploymentPeriodXML() throws Exception {
        updateEmployeeWithEmploymentPeriod(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test create employee with phone numbers json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testCreateEmployeeWithPhoneNumbersJSON() throws Exception {
        createEmployeeWithPhoneNumbers();
    }

    /**
     * Test create employee with address json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testCreateEmployeeWithAddressJSON() throws Exception {
        String msg = RestUtils.getJSONMessage("employee-with-address-V2.json");
        String employee = RestUtils.restUpdate(context, msg, Employee.class.getSimpleName(), null, MediaType.APPLICATION_JSON_TYPE);
        assertNotNull(employee);
        String addressLink = "\"address\":{\"links\":[{\"rel\":\"self\",\"href\":\"" + getServerURI() + "/entity/Employee/20130/address\"}";
        assertTrue(employee.contains(addressLink));
        RestUtils.restDelete(context, 20130, Employee.class.getSimpleName(), Employee.class, null, null, MediaType.APPLICATION_JSON_TYPE);
    }

    /**
      * Test update employee with manager json.
      *
      * @throws Exception the exception
      */
    @Test
    public void testUpdateEmployeeWithManagerJSON() throws Exception {
        updateEmployeeWithManager(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test update employee with manager xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithManagerXML() throws Exception {
        updateEmployeeWithManager(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test update employee with project json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithProjectJSON() throws Exception {
        updateEmployeeWithProject(MediaType.APPLICATION_JSON_TYPE, false);
    }

    /**
     * Test update employee with project xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithProjectXML() throws Exception {
        updateEmployeeWithProject(MediaType.APPLICATION_XML_TYPE, false);
    }

    /**
     * Test update employee with project remove all projects json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithProjectRemoveAllProjectsJSON() throws Exception {
        updateEmployeeWithProject(MediaType.APPLICATION_JSON_TYPE, true);
    }

    /**
     * Test update employee with project remove all projects xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testUpdateEmployeeWithProjectRemoveAllProjectsXML() throws Exception {
        updateEmployeeWithProject(MediaType.APPLICATION_XML_TYPE, true);
    }

    /**
     * Test create employee address with binary data json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testCreateEmployeeAddressWithBinaryDataJSON() throws Exception {
        createEmployeeAddressWithBinaryData(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test create employee address with binary data xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testCreateEmployeeAddressWithBinaryDataXML() throws Exception {
        createEmployeeAddressWithBinaryData(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test execute query get all xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testExecuteQueryGetAllXML() throws Exception {
        executeQueryGetAll(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test execute query get all json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testExecuteQueryGetAllJSON() throws Exception {
        executeQueryGetAll(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test execute single result query xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testExecuteSingleResultQueryXML() throws Exception {
        executeSingleResultQuery(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test execute single result query json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testExecuteSingleResultQueryJSON() throws Exception {
        executeSingleResultQuery(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test read employee with responsibilities xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithResponsibilitiesXML() throws Exception {
        readEmployeeWithResponsibilities(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test read employee with responsibilities json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithResponsibilitiesJSON() throws Exception {
        readEmployeeWithResponsibilities(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test multi result query get employee address with simple fields xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryGetEmployeeAddressWithSimpleFieldsXML() throws Exception {
        executeMultiResultQueryGetEmployeeAddressWithSimpleFields(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test multi result query get employee address with simple fields json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryGetEmployeeAddressWithSimpleFieldsJSON() throws Exception {
        executeMultiResultQueryGetEmployeeAddressWithSimpleFields(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test multi result query get employee with domain object json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryGetEmployeeWithDomainObjectJSON() throws Exception {
        executeMultiResultQueryGetEmployeeWithDomainObject(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test multi result query get employee with domain object xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryGetEmployeeWithDomainObjectXML() throws Exception {
        executeMultiResultQueryGetEmployeeWithDomainObject(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test employee address multi result named query with binary data xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmployeeAddressMultiResultNamedQueryWithBinaryDataXML() throws Exception {
        getEmployeeAddressMultiResultNamedQueryWithBinaryData(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test employee address multi result named query with binary data json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmployeeAddressMultiResultNamedQueryWithBinaryDataJSON() throws Exception {
        getEmployeeAddressMultiResultNamedQueryWithBinaryData(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test read employee responsibilities xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeResponsibilitiesXML() throws Exception {
        readEmployeeResponsibilities(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test read employee responsibilities json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeResponsibilitiesJSON() throws Exception {
        readEmployeeResponsibilities(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test single result query get employee with domain object json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryGetEmployeeWithDomainObjectJSON() throws Exception {
        executeSingleResultQueryGetEmployeeWithDomainObject(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test single result query get employee with domain object xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryGetEmployeeWithDomainObjectXML() throws Exception {
        executeSingleResultQueryGetEmployeeWithDomainObject(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test multi result query max xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryMaxXML() throws Exception {
        executeMultiResultQueryMax(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test multi result query max json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryMaxJSON() throws Exception {
        executeMultiResultQueryMax(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test single result query max xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryMaxXML() throws Exception {
        executeSingleResultQueryMax(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test single result query max json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryMaxJSON() throws Exception {
        executeSingleResultQueryMax(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test multi result query count xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryCountXML() throws Exception {
        executeMultiResultQueryCount(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test multi result query count json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testMultiResultQueryCountJSON() throws Exception {
        executeMultiResultQueryCount(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test single result query count xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryCountXML() throws Exception {
        executeSingleResultQueryCount(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test single result query count json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testSingleResultQueryCountJSON() throws Exception {
        executeSingleResultQueryCount(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test employee address single result named query with binary data xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmployeeAddressSingleResultNamedQueryWithBinaryDataXML() throws Exception {
        getEmployeeAddressSingleResultNamedQueryWithBinaryData(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test employee address single result named query with binary data json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmployeeAddressSingleResultNamedQueryWithBinaryDataJSON() throws Exception {
        getEmployeeAddressSingleResultNamedQueryWithBinaryData(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test employee address single result named query with binary data octet stream.
     *
     * @throws Exception the exception
     */
    @Test
    public void testEmployeeAddressSingleResultNamedQueryWithBinaryDataOctetStream() throws Exception {
        getEmployeeAddressSingleResultNamedQueryWithBinaryData(MediaType.APPLICATION_OCTET_STREAM_TYPE);
    }

    /**
     * Test read employee with address lazy fetch one2 one xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithAddressLazyFetchOne2OneXML() throws Exception {
        readEmployeeWithAddressLazyFetchOne2One(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test read employee with address lazy fetch one2 one json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithAddressLazyFetchOne2OneJSON() throws Exception {
        readEmployeeWithAddressLazyFetchOne2One(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test create employee with expertise non idempotent.
     *
     * @throws Exception the exception
     */
    @Test(expected = RestCallFailedException.class)
    public void testCreateEmployeeWithExpertiseNonIdempotent() throws Exception {
        String employee = RestUtils.getJSONMessage("employee-expertiseByValueNoId.json");
        // The expertise object contained by the employee object has generated id field, and create is idempotent.
        // So, create operation on employee with expertise list should fail.
        RestUtils.restCreateWithSequence(context, employee, Employee.class.getSimpleName(), null, MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test create office with employee non idempotent.
     *
     * @throws Exception the exception
     */
    @Test(expected = RestCallFailedException.class)
    public void testCreateOfficeWithEmployeeNonIdempotent() throws Exception {
        String office = RestUtils.getJSONMessage("office-employeeByValueNoId-V2.json");
        RestUtils.restCreateWithSequence(context, office, Office.class.getSimpleName(), null, MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test create employee with office many to one inverse mapping.
     *
     * @throws Exception the exception
     */
    @Test(expected = RestCallFailedException.class)
    public void testCreateEmployeeWithOfficeManyToOneInverseMapping() throws Exception {
        String msg = RestUtils.getJSONMessage("employee-officeByValueNoId-V2.json");

        // In JPARS version 1.0 or below the office should be ignored (inverse mapping) and employee should be created successfully
        // In version 2.0 inverse mappings are not removed, the idempotence exception is expected
        RestUtils.restCreate(context, msg, Employee.class.getSimpleName(), Employee.class, null, MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test query employee find all json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testQueryEmployeeFindAllJSON() throws Exception {
        queryEmployeeFindAll(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test query employee find all xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testQueryEmployeeFindAllXML() throws Exception {
        queryEmployeeFindAll(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test read employee with certifications xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithCertificationsXML() throws Exception {
        readEmployeeWithCertifications(MediaType.APPLICATION_XML_TYPE);
    }

    /**
     * Test read employee with certifications json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testReadEmployeeWithCertificationsJSON() throws Exception {
        readEmployeeWithCertifications(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test database exception json.
     *
     * @throws Exception the exception
     */
    @Test
    public void testDatabaseExceptionJSON() throws Exception {
        databaseException(MediaType.APPLICATION_JSON_TYPE);
    }

    /**
     * Test database exception xml.
     *
     * @throws Exception the exception
     */
    @Test
    public void testDatabaseExceptionXML() throws Exception {
        databaseException(MediaType.APPLICATION_XML_TYPE);
    }

    private void queryEmployeeFindAll(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee1 = new Employee();
        employee1.setFirstName("Billie");
        employee1.setLastName("Holiday");
        employee1 = RestUtils.restUpdate(context, employee1, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee1);

        // create another employee
        Employee employee2 = new Employee();
        employee2.setFirstName("Ella");
        employee2.setLastName("Fitzgerald");
        employee2.setGender(Gender.Female);
        employee2 = RestUtils.restUpdate(context, employee2, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee2);

        // query employee
        String queryResult = RestUtils.restNamedMultiResultQuery(context, "Employee.findAll", null, null, mediaType);
        assertNotNull("Query all employees failed.", queryResult);

        assertTrue(queryResult.contains(employee1.getFirstName()));
        assertTrue(queryResult.contains(employee1.getLastName()));

        assertTrue(queryResult.contains(employee2.getFirstName()));
        assertTrue(queryResult.contains(employee1.getLastName()));

        // delete employees
        RestUtils.restDelete(context, employee1.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        RestUtils.restDelete(context, employee2.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void readEmployeeWithAddressLazyFetchOne2One(MediaType mediaType) throws Exception {
        // address on employee object is annoted to be LAZY fetch
        // Create an employee without any address, and make sure that read employee response doesn't contain address,
        // then add an address to the employee and re-read the employee to make sure that the response contains a link to the address
        // even if it is fetched lazily.

        // create an employee
        Employee employee = new Employee();
        employee.setFirstName("Diana");
        employee.setLastName("Krall");

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, false);
        assertNotNull("Employee create failed.", employee);
        assertTrue(employee.getAddress() == null);

        // create an address for this employee
        EmployeeAddress address = new EmployeeAddress();
        address.setCity("Toronto");
        address.setStreet("Queen street");
        address.setPostalCode("ON");
        address.setCountry("CA");
        address.setPostalCode("H0H 0H0");

        address = RestUtils.restUpdate(context, address, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("Employee address create failed.", address);
        assertTrue("Queen street".equals(address.getStreet()));
        assertTrue("H0H 0H0".equals(address.getPostalCode()));

        // update employee with address
        String result = RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "address", address, mediaType, null, true);
        assertNotNull(result);

        // make sure that response from restUpdateBidirectionalRelationship contains newly added address in employee object
        String addressLinkHref = getServerURI() + "/entity/EmployeeAddress/" + address.getId();
        assertTrue(result.contains(addressLinkHref));

        // read employee with an addresss
        String employeeWithAddress = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), null, mediaType);
        assertNotNull("Employee read failed.", employeeWithAddress);
        // make sure employee has address
        assertTrue(employeeWithAddress.contains(addressLinkHref));

        // delete employee (cascade deletes address)
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void getEmployeeAddressSingleResultNamedQueryWithBinaryData(MediaType mediaType) throws Exception {
        EmployeeAddress address = new EmployeeAddress("Newyork City", "USA", "NY", "10005", "Wall Street");
        byte[] manhattan = RestUtils.convertImageToByteArray("manhattan.png");
        address.setAreaPicture(manhattan);

        address = RestUtils.restUpdate(context, address, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, MediaType.APPLICATION_JSON_TYPE, true);
        assertNotNull("EmployeeAddress create failed.", address);
        assertNotNull("EmployeeAddress area picture is null", address.getAreaPicture());
        assertTrue("Newyork City".equals(address.getCity()));
        assertTrue("USA".equals(address.getCountry()));
        assertTrue("NY".equals(address.getProvince()));
        assertTrue("10005".equals(address.getPostalCode()));
        assertTrue("Wall Street".equals(address.getStreet()));

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("id", address.getId());

        // query
        if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            String expected = "<areaPicture> </areaPicture>";
            String result = RestUtils.restNamedSingleResultQuery(context, "EmployeeAddress.getPicture", parameters, null, mediaType);
            assertNotNull(result);
            assertTrue(result.contains(expected));
        } else if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            String expected = "\"areaPicture\":\" \"";
            String result = RestUtils.restNamedSingleResultQuery(context, "EmployeeAddress.getPicture", parameters, null, mediaType);
            assertNotNull(result);
            assertTrue(result.contains(expected));
        } else if (mediaType == MediaType.APPLICATION_OCTET_STREAM_TYPE) {
            byte[] expectedResult = RestUtils.restNamedSingleResultQueryInByteArray(context, "EmployeeAddress.getPicture", pu, parameters, null, mediaType);
            assertTrue(Arrays.equals(manhattan, expectedResult));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee address
        RestUtils.restDelete(context, address.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, MediaType.APPLICATION_JSON_TYPE);
    }

    private void executeMultiResultQueryMax(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee1 = new Employee();
        employee1.setFirstName("Miles");
        employee1.setLastName("Davis");
        employee1.setGender(Gender.Male);
        employee1.setSalary(20000.0);

        employee1 = RestUtils.restUpdate(context, employee1, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee1 create failed.", employee1);

        // create another employee
        Employee employee2 = new Employee();
        employee2.setFirstName("Charlie");
        employee2.setLastName("Parker");
        employee2.setGender(Gender.Male);
        employee2.setSalary(30000.0);

        employee2 = RestUtils.restUpdate(context, employee2, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee2 create failed.", employee2);

        // query
        String queryResult = RestUtils.restNamedMultiResultQuery(context, "Employee.salaryMax", null, null, mediaType);
        assertNotNull(queryResult);

        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.contains("{\"id\":" + employee2.getId() + ",\"max_salary\":30000.0}"));
            assertTrue(queryResult.contains("{\"id\":" + employee1.getId() + ",\"max_salary\":20000.0}"));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<id>" + employee1.getId() + "</id><max_salary>20000.0</max_salary>"));
            assertTrue(queryResult.contains("<id>" + employee2.getId() + "</id><max_salary>30000.0</max_salary>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employees
        RestUtils.restDelete(context, employee1.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        RestUtils.restDelete(context, employee2.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeSingleResultQueryMax(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setFirstName("Miles");
        employee.setLastName("Davis");
        employee.setGender(Gender.Male);
        employee.setSalary(20000.0);

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        // query
        String queryResult = RestUtils.restNamedSingleResultQuery(context, "Employee.salaryMax", null, null, mediaType);
        assertNotNull(queryResult);

        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.contains("\"id\":" + employee.getId() + ",\"max_salary\":20000.0"));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<id>" + employee.getId() + "</id><max_salary>20000.0</max_salary>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeSingleResultQueryCount(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee1 = new Employee();
        employee1.setFirstName("Miles");
        employee1.setLastName("Davis");
        employee1.setGender(Gender.Male);

        employee1 = RestUtils.restUpdate(context, employee1, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee1 create failed.", employee1);

        // create another employee
        Employee employee2 = new Employee();
        employee2.setFirstName("Charlie");
        employee2.setLastName("Parker");
        employee2.setGender(Gender.Male);

        employee2 = RestUtils.restUpdate(context, employee2, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee2 create failed.", employee2);

        // query
        String queryResult = RestUtils.restNamedSingleResultQuery(context, "Employee.count", null, null, mediaType);
        assertNotNull(queryResult);

        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.contains("\"COUNT\":2"));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<COUNT>2</COUNT>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employees
        RestUtils.restDelete(context, employee1.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        RestUtils.restDelete(context, employee2.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeMultiResultQueryCount(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee1 = new Employee();
        employee1.setFirstName("Miles");
        employee1.setLastName("Davis");
        employee1.setGender(Gender.Male);

        employee1 = RestUtils.restUpdate(context, employee1, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee1 create failed.", employee1);

        // create another employee
        Employee employee2 = new Employee();
        employee2.setFirstName("Charlie");
        employee2.setLastName("Parker");
        employee2.setGender(Gender.Male);

        employee2 = RestUtils.restUpdate(context, employee2, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee2 create failed.", employee2);

        // query
        String queryResult = RestUtils.restNamedMultiResultQuery(context, "Employee.count", null, null, mediaType);
        assertNotNull(queryResult);

        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.equals("{\"items\":[{\"COUNT\":2}],\"links\":[{\"rel\":\"self\",\"href\":\"" +
                    getServerURI() + "/query/Employee.count\"}]}"));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<result><items><COUNT>2</COUNT></items><links rel=\"self\" href=\"" +
                    getServerURI() + "/query/Employee.count\"/></result>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employees
        RestUtils.restDelete(context, employee1.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        RestUtils.restDelete(context, employee2.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeMultiResultQueryGetEmployeeWithDomainObject(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setFirstName("Miles");
        employee.setLastName("Davis");
        employee.setGender(Gender.Male);

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        // create a manager
        Employee manager = new Employee();
        manager.setFirstName("Charlie");
        manager.setLastName("Parker");
        manager.setGender(Gender.Male);

        manager = RestUtils.restUpdate(context, manager, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee manager create failed.", manager);

        // update employee with manager
        RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "manager", manager, mediaType, "managedEmployees", true);

        // read manager and verify that the relationship is set correctly
        manager = RestUtils.restRead(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", manager);
        assertNotNull("Manager's managed employee list is null", manager.getManagedEmployees());
        assertTrue("Manager's managed employee list is empty", manager.getManagedEmployees().size() > 0);

        employee = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", employee);

        for (Employee emp : manager.getManagedEmployees()) {
            assertNotNull("Managed employee's first name is null", emp.getFirstName());
            assertNotNull("Managed employee's last name is null", emp.getLastName());
        }

        // query
        String queryResult = RestUtils.restNamedMultiResultQuery(context, "Employee.getManager", null, null, mediaType);
        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.contains("\"firstName\":\"Miles\""));
            assertTrue(queryResult.contains("\"lastName\":\"Davis\""));
            assertTrue(queryResult.contains("\"rel\":\"self\",\"href\":\"" + getServerURI() + "/entity/Employee/" + manager.getId() + "\""));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<firstName>Miles</firstName>"));
            assertTrue(queryResult.contains("<lastName>Davis</lastName>"));
            assertTrue(queryResult.contains("<links rel=\"self\" href=\"" + getServerURI() + "/entity/Employee/" + manager.getId() + "\"/>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);

        // delete manager
        RestUtils.restDelete(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void readEmployeeWithResponsibilities(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setId(11025);
        employee.setFirstName("Miles");
        employee.setLastName("Davis");

        employee.addResponsibility("team lead");
        employee.addResponsibility("standard lead");
        employee.addResponsibility("er team member");

        employee = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        employee = RestUtils.restRead(context, 11025, Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull(employee.getResponsibilities());
        assertTrue(employee.getResponsibilities().size() == 3);

        // delete employee
        RestUtils.restDelete(context, 11025, Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeQueryGetAll(MediaType mediaType) throws Exception {
        // Create address1
        EmployeeAddress address1 = new EmployeeAddress("Newyork City", "USA", "NY", "10005", "Wall Street");
        address1 = RestUtils.restUpdate(context, address1, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address1);
        assertTrue("Newyork City".equals(address1.getCity()));
        assertTrue("USA".equals(address1.getCountry()));
        assertTrue("NY".equals(address1.getProvince()));
        assertTrue("10005".equals(address1.getPostalCode()));
        assertTrue("Wall Street".equals(address1.getStreet()));

        // Create address2
        EmployeeAddress address2 = new EmployeeAddress("Ottawa", "Canada", "Ontario", "K1Y 6F7", "Main Street");
        address2 = RestUtils.restUpdate(context, address2, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address2);
        assertTrue("Ottawa".equals(address2.getCity()));
        assertTrue("Canada".equals(address2.getCountry()));
        assertTrue("Ontario".equals(address2.getProvince()));
        assertTrue("K1Y 6F7".equals(address2.getPostalCode()));
        assertTrue("Main Street".equals(address2.getStreet()));

        // Query
        final String result = RestUtils.restNamedMultiResultQuery(context, "EmployeeAddress.getAll", null, null, mediaType);

        assertTrue("Address1 is missing in the result", result.contains(getServerURI() + "/entity/EmployeeAddress/" + address1.getId()));
        assertTrue("Address2 is missing in the result", result.contains(getServerURI() + "/entity/EmployeeAddress/" + address2.getId()));

        // Delete employee addresses
        RestUtils.restDelete(context, address1.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
        RestUtils.restDelete(context, address2.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
    }

    private void executeMultiResultQueryGetEmployeeAddressWithSimpleFields(MediaType mediaType) throws Exception {
        // create address1
        EmployeeAddress address1 = new EmployeeAddress("Washington", "USA", "WA", "99999", "Main");
        address1 = RestUtils.restUpdate(context, address1, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address1);
        assertTrue("Washington".equals(address1.getCity()));
        assertTrue("USA".equals(address1.getCountry()));
        assertTrue("WA".equals(address1.getProvince()));
        assertTrue("99999".equals(address1.getPostalCode()));
        assertTrue("Main".equals(address1.getStreet()));

        // create address2
        EmployeeAddress address2 = new EmployeeAddress("Halifax", "Canada", "NS", "K1A5A7", "Queen");
        address2 = RestUtils.restUpdate(context, address2, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address2);
        assertTrue("Halifax".equals(address2.getCity()));
        assertTrue("Canada".equals(address2.getCountry()));
        assertTrue("NS".equals(address2.getProvince()));
        assertTrue("K1A5A7".equals(address2.getPostalCode()));
        assertTrue("Queen".equals(address2.getStreet()));

        // query
        String result = RestUtils.restNamedMultiResultQuery(context, "EmployeeAddress.getRegion", null, null, mediaType);
        assertNotNull(result);
        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(result.contains("{\"postalCode\":\"99999\",\"province\":\"WA\",\"street\":\"Main\"}"));
            assertTrue(result.contains("{\"postalCode\":\"K1A5A7\",\"province\":\"NS\",\"street\":\"Queen\"}"));
            assertTrue(result.contains("\"links\":[{\"rel\":\"self\",\"href\":\"" +
                    getServerURI() + "/query/EmployeeAddress.getRegion\"}]"));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(result.contains("<postalCode>99999</postalCode><province>WA</province><street>Main</street>"));
            assertTrue(result.contains("<postalCode>K1A5A7</postalCode><province>NS</province><street>Queen</street>"));
            assertTrue(result.contains("<links rel=\"self\" href=\"" +
                    getServerURI() + "/query/EmployeeAddress.getRegion\"/>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee address
        RestUtils.restDelete(context, address1.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
        RestUtils.restDelete(context, address2.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
    }

    private void executeSingleResultQuery(MediaType mediaType) throws Exception {
        EmployeeAddress address = new EmployeeAddress("Newyork City", "USA", "NY", "10005", "Wall Street");
        address.setId(9112);
        address.setAreaPicture(RestUtils.convertImageToByteArray("manhattan.png"));

        address = RestUtils.restCreate(context, address, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);

        assertNotNull("EmployeeAddress create failed.", address);
        assertNotNull("EmployeeAddress area picture is null", address.getAreaPicture());
        assertTrue("Newyork City".equals(address.getCity()));
        assertTrue("USA".equals(address.getCountry()));
        assertTrue("NY".equals(address.getProvince()));
        assertTrue("10005".equals(address.getPostalCode()));
        assertTrue("Wall Street".equals(address.getStreet()));
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("id", address.getId());

        String result = RestUtils.restNamedSingleResultQuery(context, "EmployeeAddress.getById", parameters, null, mediaType);
        assertNotNull(result);

        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(result.contains("\"areaPicture\":\" \""));
            assertTrue(result.contains("\"city\":\"Newyork City\""));
            assertTrue(result.contains("\"country\":\"USA\""));
            assertTrue(result.contains("\"id\":9112"));
            assertTrue(result.contains("\"postalCode\":\"10005\""));
            assertTrue(result.contains("\"province\":\"NY\""));
            assertTrue(result.contains("\"street\":\"Wall Street\""));
            assertTrue(result.contains("\"rel\":\"self\",\"href\":\"" + getServerURI() + "/singleResultQuery/EmployeeAddress.getById;id=9112\""));
            assertTrue(result.contains("\"rel\":\"canonical\",\"href\":\"" + getServerURI() + "/entity/EmployeeAddress/9112\""));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(result.contains(
                "<areaPicture>" +
                " " +
                "</areaPicture>"));
            assertTrue(result.contains("<city>Newyork City</city>"));
            assertTrue(result.contains("<country>USA</country>"));
            assertTrue(result.contains("<id>9112</id>"));
            assertTrue(result.contains("<postalCode>10005</postalCode>"));
            assertTrue(result.contains("<province>NY</province>"));
            assertTrue(result.contains("<street>Wall Street</street>"));
            assertTrue(result.contains("<links rel=\"self\" href=\"" + getServerURI() + "/singleResultQuery/EmployeeAddress.getById;id=9112\"/>"));
            assertTrue(result.contains("<links rel=\"canonical\" href=\"" + getServerURI() + "/entity/EmployeeAddress/9112\"/>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee address
        RestUtils.restDelete(context, address.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
    }

    private void createEmployeeAddressWithBinaryData(MediaType mediaType) throws Exception {
        EmployeeAddress address = new EmployeeAddress("Newyork City", "USA", "NY", "10005", "Wall Street");
        address.setAreaPicture(RestUtils.convertImageToByteArray("manhattan.png"));

        address = RestUtils.restUpdate(context, address, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address);
        assertNotNull("EmployeeAddress area picture is null", address.getAreaPicture());
        assertTrue("Newyork City".equals(address.getCity()));
        assertTrue("USA".equals(address.getCountry()));
        assertTrue("NY".equals(address.getProvince()));
        assertTrue("10005".equals(address.getPostalCode()));
        assertTrue("Wall Street".equals(address.getStreet()));

        // delete employee address
        RestUtils.restDelete(context, address.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
    }

    private void updateEmployeeWithProject(MediaType mediaType, boolean removeAllProjects) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setFirstName("Charles");
        employee.setLastName("Mingus");
        employee.setGender(Gender.Male);

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        // create a small project
        SmallProject smallProject = new SmallProject("SmallProject", "This is a small project.");

        smallProject = RestUtils.restUpdate(context, smallProject, SmallProject.class.getSimpleName(), SmallProject.class, null, mediaType, true);
        assertNotNull("SmallProject create failed.", smallProject);

        // update employee with small project
        RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "projects", smallProject,
                mediaType, "teamLeader", true);

        // create a large project
        LargeProject largeProject = new LargeProject();
        largeProject.setName("LargeProject");
        largeProject.setBudget(100000);

        largeProject = RestUtils.restUpdate(context, largeProject, LargeProject.class.getSimpleName(), LargeProject.class, null, mediaType, true);
        assertNotNull("LargeProject create failed.", largeProject);

        // update employee with large project
        RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "projects", largeProject,
                mediaType, "teamLeader", true);

        // read employee and verify that the relationship is set correctly for the projects
        final Employee employeeUpdated = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Employee read failed.", employeeUpdated);
        assertTrue("Incorrect projects number", employeeUpdated.getProjects().size() == 2);
        assertTrue("SmallProject not found.", employeeUpdated.getProjects().get(0).getName().equals("SmallProject") || employeeUpdated.getProjects().get(1).getName().equals("SmallProject"));
        assertTrue("LargeProject not found.", employeeUpdated.getProjects().get(0).getName().equals("LargeProject") || employeeUpdated.getProjects().get(1).getName().equals("LargeProject"));

        if (removeAllProjects) {
            // Remove all projects
            RestUtils.restRemoveBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "projects", mediaType, "teamLeader", null);

        } else {
            // Remove projects one by one

            // Disassociate large project from the employee
            RestUtils.restRemoveBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "projects", mediaType, "teamLeader", String.valueOf(largeProject.getId()));

            // Read projects
            String projects = RestUtils.restFindAttribute(context, employee.getId(), Employee.class.getSimpleName(), "projects", null, mediaType);

            // Verify that only SmallProject is there
            assertTrue("Projects verification failed.", projects.contains("SmallProject") || !projects.contains("LargeProject"));

            // Disassociate small project from the employee
            RestUtils.restRemoveBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "projects", mediaType, "teamLeader", String.valueOf(smallProject.getId()));
        }

        // Read projects
        String projects = RestUtils.restFindAttribute(context, employee.getId(), Employee.class.getSimpleName(), "projects", null, mediaType);

        // Verify that there are no projects
        assertTrue("Projects verification failed.", !projects.contains("SmallProject") || !projects.contains("LargeProject"));

        // Delete projects
        RestUtils.restDelete(context, largeProject.getId(), LargeProject.class.getSimpleName(), LargeProject.class, null, null, mediaType);
        RestUtils.restDelete(context, smallProject.getId(), SmallProject.class.getSimpleName(), SmallProject.class, null, null, mediaType);

        // Delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void updateEmployeeWithManager(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setId(90888);
        employee.setFirstName("Miles");
        employee.setLastName("Davis");
        employee.setGender(Gender.Male);

        employee = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        // create a manager
        Employee manager = new Employee();
        manager.setId(11910);
        manager.setFirstName("Charlie");
        manager.setLastName("Parker");
        manager.setGender(Gender.Male);

        manager = RestUtils.restCreate(context, manager, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee manager create failed.", manager);

        // update employee with manager
        RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "manager", manager, mediaType, "managedEmployees", true);

        // read manager and verify that the relationship is set correctly
        manager = RestUtils.restRead(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", manager);
        assertNotNull("Manager's managed employee list is null", manager.getManagedEmployees());
        assertTrue("Manager's managed employee list is empty", manager.getManagedEmployees().size() > 0);

        employee = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", employee);

        for (Employee emp : manager.getManagedEmployees()) {
            assertNotNull("Managed employee's first name is null", emp.getFirstName());
            assertNotNull("Managed employee's last name is null", emp.getLastName());
        }

        // delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);

        // delete manager
        RestUtils.restDelete(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void createEmployeeWithPhoneNumbers() throws Exception {
        String msg = RestUtils.getJSONMessage("employee-with-phoneNumber-V2.json");
        Employee employee = RestUtils.restCreate(context, msg, Employee.class.getSimpleName(), Employee.class, null, MediaType.APPLICATION_JSON_TYPE);
        assertNotNull(employee);
        assertTrue(employee.getId() == 743627);
        List<PhoneNumber> phoneNumbers = employee.getPhoneNumbers();
        assertNotNull(phoneNumbers);
        assertTrue(phoneNumbers.size() == 1);
        assertTrue("613".equals(phoneNumbers.get(0).getAreaCode()));

        // update employee
        employee.setSalary(20000);
        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, MediaType.APPLICATION_JSON_TYPE, true);
        assertNotNull(employee);
        assertTrue(employee.getId() == 743627);
        assertTrue(employee.getSalary() == 20000);

        // delete
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, MediaType.APPLICATION_JSON_TYPE);
    }

    private void updateEmployeeWithEmploymentPeriod(MediaType mediaType) throws Exception {
        Employee employee = new Employee();
        employee.setId(10234);
        employee.setFirstName("John");
        employee.setLastName("Travolta");
        EmploymentPeriod employmentPeriod = new EmploymentPeriod();
        Calendar now = GregorianCalendar.getInstance();
        employmentPeriod.setStartDate(now);
        employee.setPeriod(employmentPeriod);

        employee = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        Calendar threeYearsLater = GregorianCalendar.getInstance();
        long end = (now.getTimeInMillis() + 94608000000L); // + 3 years
        threeYearsLater.setTimeInMillis(end);

        employmentPeriod.setEndDate(threeYearsLater);
        employee.setPeriod(employmentPeriod);

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);

        assertNotNull("Employee update failed.", employee);
        assertNotNull("Employee's employment period update failed", employee.getPeriod());
        assertNotNull("Employee's employment period end date is null", employee.getPeriod().getEndDate());
        assertTrue("Incorrect end date for employee", employee.getPeriod().getEndDate().getTimeInMillis() == threeYearsLater.getTimeInMillis());

        RestUtils.restDelete(context, 10234, Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void readEmployee(MediaType mediaType) throws Exception {
        Employee employee = new Employee();
        employee.setId(18234);
        employee.setFirstName("Pat");
        employee.setLastName("Metheny");
        EmploymentPeriod employmentPeriod = new EmploymentPeriod();
        employmentPeriod.setStartDate(GregorianCalendar.getInstance());
        employee.setPeriod(employmentPeriod);

        Employee employeeCreated = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        Employee employeeRead = RestUtils.restRead(context, 18234, Employee.class.getSimpleName(), Employee.class, null, mediaType);

        assertNotNull("Employee create failed.", employeeCreated);
        assertNotNull("Employee read failed.", employeeRead);
        assertTrue("Employee created and employee read is different", employeeCreated.getLastName().equals(employeeRead.getLastName()));
        RestUtils.restDelete(context, 18234, Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        Employee emp = DBUtils.dbRead(18234, Employee.class, context.getEmf().createEntityManager());
        assertNull("Employee could not be deleted", emp);
    }

    private void getEmployeeAddressMultiResultNamedQueryWithBinaryData(MediaType mediaType) throws Exception {
        EmployeeAddress address = new EmployeeAddress("Newyork City", "USA", "NY", "10005", "Wall Street");
        address.setAreaPicture(RestUtils.convertImageToByteArray("manhattan.png"));

        address = RestUtils.restUpdate(context, address, EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, mediaType, true);
        assertNotNull("EmployeeAddress create failed.", address);
        assertNotNull("EmployeeAddress area picture is null", address.getAreaPicture());
        assertTrue("Newyork City".equals(address.getCity()));
        assertTrue("USA".equals(address.getCountry()));
        assertTrue("NY".equals(address.getProvince()));
        assertTrue("10005".equals(address.getPostalCode()));
        assertTrue("Wall Street".equals(address.getStreet()));

        Map<String, Object> parameters = new HashMap<>();
        parameters.put("id", address.getId());

        final String expected;
        if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            expected = "<result><items><areaPicture> </areaPicture></items>";
        } else if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            expected = "[{\"areaPicture\":\" \"}]";
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // query
        String result = RestUtils.restNamedMultiResultQuery(context, "EmployeeAddress.getPicture", parameters, null, mediaType);
        assertNotNull(result);
        assertTrue(result.contains(expected));

        // delete employee address
        RestUtils.restDelete(context, address.getId(), EmployeeAddress.class.getSimpleName(), EmployeeAddress.class, null, null, mediaType);
    }

    private void readEmployeeResponsibilities(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setId(921025);
        employee.setFirstName("Johannes");
        employee.setLastName("Brahms");

        employee.addResponsibility("musician");
        employee.addResponsibility("architect");
        employee.addResponsibility("conductor");

        employee = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        employee = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull(employee.getResponsibilities());
        assertTrue(employee.getResponsibilities().size() == 3);
        List<String> responsibilities = employee.getResponsibilities();
        assertTrue(responsibilities.contains("musician"));
        assertTrue(responsibilities.contains("architect"));
        assertTrue(responsibilities.contains("conductor"));

        // read responsibilities
        final String result = RestUtils.restFindAttribute(context, employee.getId(), Employee.class.getSimpleName(), "responsibilities", null, mediaType);
        assertNotNull(result);
        assertTrue(result.contains("musician"));
        assertTrue(result.contains("architect"));
        assertTrue(result.contains("conductor"));

        // delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void executeSingleResultQueryGetEmployeeWithDomainObject(MediaType mediaType) throws Exception {
        // create an employee
        Employee employee = new Employee();
        employee.setFirstName("Miles");
        employee.setLastName("Davis");
        employee.setGender(Gender.Male);

        employee = RestUtils.restUpdate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employee);

        // create a manager
        Employee manager = new Employee();
        manager.setFirstName("Charlie");
        manager.setLastName("Parker");
        manager.setGender(Gender.Male);

        manager = RestUtils.restUpdate(context, manager, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee manager create failed.", manager);

        // update employee with manager
        RestUtils.restUpdateBidirectionalRelationship(context, String.valueOf(employee.getId()), Employee.class.getSimpleName(), "manager", manager, mediaType, "managedEmployees", true);

        // read manager and verify that the relationship is set correctly
        manager = RestUtils.restRead(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", manager);
        assertNotNull("Manager's managed employee list is null", manager.getManagedEmployees());
        assertTrue("Manager's managed employee list is empty", manager.getManagedEmployees().size() > 0);

        employee = RestUtils.restRead(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, mediaType);
        assertNotNull("Manager read failed.", employee);

        for (Employee emp : manager.getManagedEmployees()) {
            assertNotNull("Managed employee's first name is null", emp.getFirstName());
            assertNotNull("Managed employee's last name is null", emp.getLastName());
        }

        // single result query
        Map<String, Object> parameters = new HashMap<>();
        parameters.put("id", employee.getId());

        String queryResult = RestUtils.restNamedSingleResultQuery(context, "Employee.getManagerById", parameters, null, mediaType);
        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            assertTrue(queryResult.contains("\"firstName\":\"Miles\""));
            assertTrue(queryResult.contains("\"lastName\":\"Davis\""));
            assertTrue(queryResult.contains("\"rel\":\"self\",\"href\":\"" + getServerURI() + "/entity/Employee/" + manager.getId() + "\""));
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            assertTrue(queryResult.contains("<firstName>Miles</firstName>"));
            assertTrue(queryResult.contains("<lastName>Davis</lastName>"));
            assertTrue(queryResult.contains("<links rel=\"self\" href=\"" + getServerURI() + "/entity/Employee/" + manager.getId() + "\"/>"));
        } else {
            // unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }

        // delete employee
        RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);

        // delete manager
        RestUtils.restDelete(context, manager.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void readEmployeeWithCertifications(MediaType mediaType) throws Exception {
        // Create employee
        final String msg;
        if (mediaType == MediaType.APPLICATION_JSON_TYPE) {
            msg = RestUtils.getJSONMessage("employee-with-certification-V2.json");
        } else if (mediaType == MediaType.APPLICATION_XML_TYPE) {
            msg = RestUtils.getXMLMessage("employee-with-certification-V2.xml");
        } else {
            // Unsupported media type
            throw new RestCallFailedException(Status.BAD_REQUEST);
        }
        RestUtils.restCreate(context, msg, Employee.class.getSimpleName(), Employee.class, null, mediaType);

        // Read employee and check certifications attribute
        final String employee = RestUtils.restRead(context, 201204, Employee.class.getSimpleName(), null, mediaType);
        assertTrue("Certifications link doesn't exist.", employee.contains(getServerURI() + "/entity/Employee/201204/certifications"));

        // Read certifications and check that 'Java' certification exists
        final String certifications = RestUtils.restFindAttribute(context, 201204, Employee.class.getSimpleName(), "certifications", null, mediaType);
        assertTrue("Invalid certifications list.", certifications.contains("Java"));

        // Delete employee
        RestUtils.restDelete(context, 201204, Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
    }

    private void databaseException(MediaType mediaType) throws Exception {
        Employee employee = new Employee();
        employee.setId(19209);
        employee.setFirstName("Pat");
        employee.setLastName("Metheny");
        EmploymentPeriod employmentPeriod = new EmploymentPeriod();
        employmentPeriod.setStartDate(GregorianCalendar.getInstance());
        employee.setPeriod(employmentPeriod);

        Employee employeeCreated = RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        assertNotNull("Employee create failed.", employeeCreated);

        // 2nd create call should fail, because of the violation of the integrity constraints
        try {
            RestUtils.restCreate(context, employee, Employee.class.getSimpleName(), Employee.class, null, mediaType, true);
        } catch (RestCallFailedException ex) {
            ErrorResponse errorResponse = ex.getErrorDetails();
            assertNotNull(errorResponse);
            assertTrue(DatabaseException.class.getName().equals(errorResponse.getTitle()));
        } finally {
            RestUtils.restDelete(context, employee.getId(), Employee.class.getSimpleName(), Employee.class, null, null, mediaType);
        }
    }
}
