cz.cesnet.shongo.controller.api.rpc.ReservationServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for cz.cesnet.shongo.controller.api.rpc.ReservationServiceImpl.java

Source

package cz.cesnet.shongo.controller.api.rpc;

import cz.cesnet.shongo.Technology;
import cz.cesnet.shongo.TodoImplementException;
import cz.cesnet.shongo.api.ClassHelper;
import cz.cesnet.shongo.controller.*;
import cz.cesnet.shongo.controller.AclIdentityType;
import cz.cesnet.shongo.controller.acl.AclObjectClass;
import cz.cesnet.shongo.controller.api.*;
import cz.cesnet.shongo.controller.api.request.AvailabilityCheckRequest;
import cz.cesnet.shongo.controller.api.request.ListResponse;
import cz.cesnet.shongo.controller.api.request.ReservationListRequest;
import cz.cesnet.shongo.controller.api.request.ReservationRequestListRequest;
import cz.cesnet.shongo.controller.authorization.Authorization;
import cz.cesnet.shongo.controller.authorization.AuthorizationManager;
import cz.cesnet.shongo.controller.booking.Allocation;
import cz.cesnet.shongo.controller.booking.ObjectIdentifier;
import cz.cesnet.shongo.controller.booking.executable.Executable;
import cz.cesnet.shongo.controller.booking.request.ReservationRequest;
import cz.cesnet.shongo.controller.booking.request.ReservationRequestManager;
import cz.cesnet.shongo.controller.booking.reservation.ReservationManager;
import cz.cesnet.shongo.controller.cache.Cache;
import cz.cesnet.shongo.controller.scheduler.*;
import cz.cesnet.shongo.controller.util.NativeQuery;
import cz.cesnet.shongo.controller.util.QueryFilter;
import cz.cesnet.shongo.report.Report;
import org.joda.time.DateTime;
import org.joda.time.Interval;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import java.util.*;

/**
 * Implementation of {@link ReservationService}.
 *
 * @author Martin Srom <martin.srom@cesnet.cz>
 */
public class ReservationServiceImpl extends AbstractServiceImpl
        implements ReservationService, Component.EntityManagerFactoryAware, Component.AuthorizationAware {
    /**
     * @see cz.cesnet.shongo.controller.cache.Cache
     */
    private Cache cache;

    /**
     * @see javax.persistence.EntityManagerFactory
     */
    private EntityManagerFactory entityManagerFactory;

    /**
     * @see cz.cesnet.shongo.controller.authorization.Authorization
     */
    private Authorization authorization;

    /**
     * Constructor.
     */
    public ReservationServiceImpl(Cache cache) {
        this.cache = cache;
    }

    @Override
    public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) {
        this.entityManagerFactory = entityManagerFactory;
    }

    @Override
    public void setAuthorization(Authorization authorization) {
        this.authorization = authorization;
    }

    @Override
    public void init(ControllerConfiguration configuration) {
        checkDependency(entityManagerFactory, EntityManagerFactory.class);
        checkDependency(authorization, Authorization.class);
        super.init(configuration);
    }

    @Override
    public String getServiceName() {
        return "Reservation";
    }

    @Override
    public Object checkAvailability(AvailabilityCheckRequest request) {
        checkNotNull("request", request);
        SecurityToken securityToken = request.getSecurityToken();
        authorization.validate(securityToken);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        try {
            // We must check only the future (because scheduler allocates only in future)
            DateTime minimumDateTime = DateTime.now();
            Interval slot = request.getSlot();
            if (slot.getEnd().isBefore(minimumDateTime)) {
                throw new ControllerReportSet.ReservationRequestEmptyDurationException();
            }
            if (slot.getStart().isBefore(minimumDateTime)) {
                slot = slot.withStart(minimumDateTime);
            }

            Specification specificationApi = request.getSpecification();
            cz.cesnet.shongo.controller.booking.specification.Specification specification = null;
            Interval allocationSlot = slot;
            if (specificationApi != null) {
                specification = cz.cesnet.shongo.controller.booking.specification.Specification
                        .createFromApi(specificationApi, entityManager);
                if (specification instanceof SpecificationIntervalUpdater) {
                    SpecificationIntervalUpdater intervalUpdater = (SpecificationIntervalUpdater) specification;
                    allocationSlot = intervalUpdater.updateInterval(allocationSlot, minimumDateTime);
                }
            }

            // Create scheduler context
            SchedulerContext schedulerContext = new SchedulerContext(DateTime.now(), cache, entityManager,
                    new AuthorizationManager(entityManager, authorization));
            schedulerContext.setPurpose(request.getPurpose());

            // Ignore reservations for already allocated reservation request
            SchedulerContextState schedulerContextState = schedulerContext.getState();
            String ignoredReservationRequestId = request.getIgnoredReservationRequestId();
            if (ignoredReservationRequestId != null) {
                ObjectIdentifier objectId = ObjectIdentifier.parse(ignoredReservationRequestId,
                        ObjectType.RESERVATION_REQUEST);
                cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest ignoredReservationRequest = reservationRequestManager
                        .get(objectId.getPersistenceId());
                for (cz.cesnet.shongo.controller.booking.reservation.Reservation reservation : ignoredReservationRequest
                        .getAllocation().getReservations()) {
                    if (allocationSlot.overlaps(reservation.getSlot())) {
                        schedulerContextState.addAvailableReservation(reservation,
                                AvailableReservation.Type.REALLOCATABLE);
                    }
                }

                for (cz.cesnet.shongo.controller.booking.request.ReservationRequest childReservationRequest : ignoredReservationRequest
                        .getAllocation().getChildReservationRequests()) {
                    for (cz.cesnet.shongo.controller.booking.reservation.Reservation reservation : childReservationRequest
                            .getAllocation().getReservations()) {
                        if (reservation.getSlot().overlaps(slot)) {
                            schedulerContextState.addAvailableReservation(reservation,
                                    AvailableReservation.Type.REALLOCATABLE);
                        }
                    }
                }
            }

            try {
                // Check reservation request reusability
                String reservationRequestId = request.getReservationRequestId();
                if (reservationRequestId != null) {
                    ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId,
                            ObjectType.RESERVATION_REQUEST);
                    cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                            .get(objectId.getPersistenceId());
                    schedulerContext.setReusableAllocation(reservationRequest.getAllocation(), slot);
                }

                // Check specification availability
                if (specification != null) {
                    if (specification instanceof ReservationTaskProvider) {
                        try {
                            entityManager.getTransaction().begin();
                            ReservationTaskProvider reservationTaskProvider = (ReservationTaskProvider) specification;
                            ReservationTask reservationTask = reservationTaskProvider
                                    .createReservationTask(schedulerContext, slot);
                            reservationTask.perform();
                        } finally {
                            entityManager.getTransaction().rollback();
                        }
                    } else {
                        throw new SchedulerReportSet.SpecificationNotAllocatableException(specification);
                    }
                }
            } catch (SchedulerException exception) {
                // Specification cannot be allocated or reservation request cannot be reused in requested time slot
                return exception.getReport().toAllocationStateReport(
                        authorization.isAdministrator(securityToken) ? Report.UserType.DOMAIN_ADMIN
                                : Report.UserType.USER);
            }

            // Request is available
            return Boolean.TRUE;
        } finally {
            entityManager.close();
        }
    }

    @Override
    public String createReservationRequest(SecurityToken securityToken,
            cz.cesnet.shongo.controller.api.AbstractReservationRequest reservationRequestApi) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequest", reservationRequestApi);

        // Check whether user can create reservation requests
        if (!authorization.hasSystemPermission(securityToken, SystemPermission.RESERVATION)) {
            ControllerReportSetHelper.throwSecurityNotAuthorizedFault("create reservation request");
        }

        // Change user id (only root can do that)
        String userId = securityToken.getUserId();
        if (reservationRequestApi.getUserId() != null && authorization.isAdministrator(securityToken)) {
            userId = reservationRequestApi.getUserId();
        }

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        AuthorizationManager authorizationManager = new AuthorizationManager(entityManager, authorization);
        try {
            // Check permission for reused reservation request
            String reusedReservationRequestId = reservationRequestApi.getReusedReservationRequestId();
            if (reusedReservationRequestId != null) {
                checkReusedReservationRequest(securityToken, reusedReservationRequestId, reservationRequestManager);
            }

            authorizationManager.beginTransaction();
            entityManager.getTransaction().begin();

            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest
                    .createFromApi(reservationRequestApi, entityManager);
            reservationRequest.setCreatedBy(userId);
            reservationRequest.setUpdatedBy(userId);

            reservationRequestManager.create(reservationRequest);

            reservationRequest.getSpecification().updateTechnologies(entityManager);

            authorizationManager.createAclEntry(AclIdentityType.USER, userId, reservationRequest, ObjectRole.OWNER);

            Allocation reusedAllocation = reservationRequest.getReusedAllocation();
            if (reusedAllocation != null) {
                ReservationRequest reusedReservationRequest = (ReservationRequest) reusedAllocation
                        .getReservationRequest();
                if (reusedReservationRequest.getReusement().equals(ReservationRequestReusement.OWNED)) {
                    authorizationManager.createAclEntriesForChildEntity(reusedReservationRequest,
                            reservationRequest);
                }
            }

            entityManager.getTransaction().commit();
            authorizationManager.commitTransaction();

            return ObjectIdentifier.formatId(reservationRequest);
        } finally {
            if (authorizationManager.isTransactionActive()) {
                authorizationManager.rollbackTransaction();
            }
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            entityManager.close();
        }

    }

    @Override
    public String modifyReservationRequest(SecurityToken securityToken,
            cz.cesnet.shongo.controller.api.AbstractReservationRequest reservationRequestApi) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequest", reservationRequestApi);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        AuthorizationManager authorizationManager = new AuthorizationManager(entityManager, authorization);
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        String reservationRequestId = reservationRequestApi.getId();
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);

        try {
            // Get old reservation request and check permissions and restrictions for modification
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest oldReservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());
            if (!authorization.hasObjectPermission(securityToken, oldReservationRequest, ObjectPermission.WRITE)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("modify reservation request %s",
                        objectId);
            }
            switch (oldReservationRequest.getState()) {
            case MODIFIED:
                throw new ControllerReportSet.ReservationRequestAlreadyModifiedException(objectId.toId());
            case DELETED:
                throw new ControllerReportSet.ReservationRequestDeletedException(objectId.toId());
            }
            if (!isReservationRequestModifiable(oldReservationRequest)) {
                throw new ControllerReportSet.ReservationRequestNotModifiableException(objectId.toId());
            }

            // Change user id (only root can do that)
            String userId = securityToken.getUserId();
            if (reservationRequestApi.getUserId() != null && authorization.isAdministrator(securityToken)) {
                userId = reservationRequestApi.getUserId();
            }

            // Check permission for reused reservation request
            String reusedReservationRequestId = reservationRequestApi.getReusedReservationRequestId();
            if (reusedReservationRequestId != null) {
                checkReusedReservationRequest(securityToken, reusedReservationRequestId, reservationRequestManager);
            }

            // Check if modified reservation request is of the same class
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest newReservationRequest;
            Class<? extends cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest> reservationRequestClass = cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest
                    .getClassFromApi(reservationRequestApi.getClass());
            if (reservationRequestClass.isInstance(oldReservationRequest)) {
                // Update old detached reservation request (the changes will not be serialized to database)
                oldReservationRequest.fromApi(reservationRequestApi, entityManager);
                // Create new reservation request by cloning old reservation request
                newReservationRequest = oldReservationRequest.clone(entityManager);
            } else {
                // Create new reservation request
                newReservationRequest = ClassHelper.createInstanceFromClass(reservationRequestClass);
                newReservationRequest.synchronizeFrom(oldReservationRequest, entityManager);
                newReservationRequest.fromApi(reservationRequestApi, entityManager);
            }
            newReservationRequest.setCreatedBy(userId);
            newReservationRequest.setUpdatedBy(userId);

            // Revert changes to old reservation request
            entityManager.clear();

            authorizationManager.beginTransaction();
            entityManager.getTransaction().begin();

            oldReservationRequest = reservationRequestManager.get(objectId.getPersistenceId());
            oldReservationRequest.setUpdatedBy(userId);

            // Create new reservation request and update old reservation request
            reservationRequestManager.modify(oldReservationRequest, newReservationRequest);
            entityManager.flush();
            entityManager.refresh(newReservationRequest);

            // Update ACL entries by reused reservation requests
            Allocation oldReusedAllocation = oldReservationRequest.getReusedAllocation();
            Allocation newReusedAllocation = newReservationRequest.getReusedAllocation();
            if (oldReusedAllocation != newReusedAllocation) {
                // Remove ACL entries from old reused reservation request
                if (oldReusedAllocation != null) {
                    ReservationRequest oldReusedReservationRequest = (ReservationRequest) oldReusedAllocation
                            .getReservationRequest();
                    if (oldReusedReservationRequest.getReusement().equals(ReservationRequestReusement.OWNED)) {
                        // TODO: consider removing ACL entries from parent object
                        // But some ACL entries (at least for the user who performs modification) must be
                        // preserved
                    }
                }
                // Create ACL entries from new reused reservation request
                if (newReusedAllocation != null) {
                    ReservationRequest newReusedReservationRequest = (ReservationRequest) newReusedAllocation
                            .getReservationRequest();
                    if (newReusedReservationRequest.getReusement().equals(ReservationRequestReusement.OWNED)) {
                        authorizationManager.createAclEntriesForChildEntity(newReusedReservationRequest,
                                newReservationRequest);
                    }
                }
            }

            entityManager.getTransaction().commit();
            authorizationManager.commitTransaction();

            return ObjectIdentifier.formatId(newReservationRequest);
        } finally {
            if (authorizationManager.isTransactionActive()) {
                authorizationManager.rollbackTransaction();
            }
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            entityManager.close();
        }
    }

    @Override
    public String revertReservationRequest(SecurityToken securityToken, String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        AuthorizationManager authorizationManager = new AuthorizationManager(entityManager, authorization);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            authorizationManager.beginTransaction();
            entityManager.getTransaction().begin();

            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest abstractReservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!abstractReservationRequest.getState()
                    .equals(cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest.State.ACTIVE)) {
                throw new ControllerReportSet.ReservationRequestNotRevertibleException(
                        ObjectIdentifier.formatId(abstractReservationRequest));
            }

            if (abstractReservationRequest instanceof ReservationRequest) {
                ReservationRequest reservationRequest = (ReservationRequest) abstractReservationRequest;
                if (reservationRequest.getAllocationState().equals(ReservationRequest.AllocationState.ALLOCATED)) {
                    throw new ControllerReportSet.ReservationRequestNotRevertibleException(
                            ObjectIdentifier.formatId(abstractReservationRequest));
                }
            }

            if (!authorization.hasObjectPermission(securityToken, abstractReservationRequest,
                    ObjectPermission.WRITE)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("revert reservation request %s",
                        objectId);
            }

            // Set modified reservation request as ACTIVE
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest modifiedReservationRequest = abstractReservationRequest
                    .getModifiedReservationRequest();
            modifiedReservationRequest
                    .setState(cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest.State.ACTIVE);
            modifiedReservationRequest.getAllocation().setReservationRequest(modifiedReservationRequest);
            reservationRequestManager.update(modifiedReservationRequest);

            // Revert the modification
            reservationRequestManager.delete(abstractReservationRequest, authorizationManager, true);

            entityManager.getTransaction().commit();
            authorizationManager.commitTransaction();

            return ObjectIdentifier.formatId(modifiedReservationRequest);
        } finally {
            if (authorizationManager.isTransactionActive()) {
                authorizationManager.rollbackTransaction();
            }
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            entityManager.close();
        }
    }

    @Override
    public void deleteReservationRequest(SecurityToken securityToken, String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        AuthorizationManager authorizationManager = new AuthorizationManager(entityManager, authorization);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            authorizationManager.beginTransaction();
            entityManager.getTransaction().begin();

            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, reservationRequest, ObjectPermission.WRITE)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("delete reservation request %s",
                        objectId);
            }
            switch (reservationRequest.getState()) {
            case MODIFIED:
                throw new ControllerReportSet.ReservationRequestNotDeletableException(objectId.toId());
            case DELETED:
                throw new ControllerReportSet.ReservationRequestDeletedException(objectId.toId());
            }
            ReservationManager reservationManager = new ReservationManager(entityManager);
            if (!isReservationRequestDeletable(reservationRequest, reservationManager)) {
                throw new ControllerReportSet.ReservationRequestNotDeletableException(
                        ObjectIdentifier.formatId(reservationRequest));
            }

            reservationRequest.setUpdatedBy(securityToken.getUserId());
            reservationRequestManager.softDelete(reservationRequest, authorizationManager);

            entityManager.getTransaction().commit();
            authorizationManager.commitTransaction();
        } finally {
            if (authorizationManager.isTransactionActive()) {
                authorizationManager.rollbackTransaction();
            }
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            entityManager.close();
        }
    }

    @Override
    public void updateReservationRequest(SecurityToken securityToken, String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        AuthorizationManager authorizationManager = new AuthorizationManager(entityManager, authorization);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            authorizationManager.beginTransaction();
            entityManager.getTransaction().begin();

            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest abstractReservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, abstractReservationRequest,
                    ObjectPermission.WRITE)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("update reservation request %s",
                        objectId);
            }

            // Update reservation requests
            if (abstractReservationRequest instanceof ReservationRequest) {
                ReservationRequest reservationRequest = (ReservationRequest) abstractReservationRequest;
                switch (reservationRequest.getAllocationState()) {
                case ALLOCATION_FAILED: {
                    // Reservation request was modified, so we must clear it's state
                    reservationRequest.clearState();
                    // Update state
                    reservationRequest.updateStateBySpecification();
                }
                }
            }

            // Update child reservation requests
            for (ReservationRequest reservationRequest : abstractReservationRequest.getAllocation()
                    .getChildReservationRequests()) {
                switch (reservationRequest.getAllocationState()) {
                case ALLOCATION_FAILED: {
                    // Reservation request was modified, so we must clear it's state
                    reservationRequest.clearState();
                    // Update state
                    reservationRequest.updateStateBySpecification();
                }
                }
            }

            entityManager.getTransaction().commit();
            authorizationManager.commitTransaction();
        } finally {
            if (authorizationManager.isTransactionActive()) {
                authorizationManager.rollbackTransaction();
            }
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            entityManager.close();
        }
    }

    @Override
    public ListResponse<ReservationRequestSummary> listReservationRequests(ReservationRequestListRequest request) {
        checkNotNull("request", request);
        SecurityToken securityToken = request.getSecurityToken();
        authorization.validate(securityToken);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            QueryFilter queryFilter = new QueryFilter("reservation_request_summary", true);

            // List only reservation requests which is current user permitted to read
            queryFilter.addFilterId("allocation_id", authorization, securityToken, Allocation.class,
                    ObjectPermission.READ);

            // List only reservation requests which are requested (but latest versions of them)
            if (request.getReservationRequestIds().size() > 0) {
                queryFilter.addFilter("reservation_request_summary.id IN ("
                        + " SELECT allocation.abstract_reservation_request_id"
                        + " FROM abstract_reservation_request"
                        + " LEFT JOIN allocation ON allocation.id = abstract_reservation_request.allocation_id"
                        + " WHERE abstract_reservation_request.id IN(:reservationRequestIds)" + ")");
                Set<Long> reservationRequestIds = new HashSet<Long>();
                for (String reservationRequestId : request.getReservationRequestIds()) {
                    reservationRequestIds
                            .add(ObjectIdentifier.parseId(reservationRequestId, ObjectType.RESERVATION_REQUEST));
                }
                queryFilter.addFilterParameter("reservationRequestIds", reservationRequestIds);
            }
            // Else other filters
            else {
                // List only latest versions of a reservation requests (no it's modifications or deleted requests)
                queryFilter.addFilter("reservation_request_summary.state = 'ACTIVE'");

                // List only child reservation requests for specified parent reservation request
                String parentReservationRequestId = request.getParentReservationRequestId();
                if (parentReservationRequestId != null) {
                    queryFilter.addFilter("reservation_request.parent_allocation_id IN("
                            + " SELECT DISTINCT abstract_reservation_request.allocation_id"
                            + " FROM abstract_reservation_request "
                            + " WHERE abstract_reservation_request.id = :parentReservationRequestId)");
                    queryFilter.addFilterParameter("parentReservationRequestId",
                            ObjectIdentifier.parseId(parentReservationRequestId, ObjectType.RESERVATION_REQUEST));
                } else {
                    // List only top reservation requests (no child requests created for a set of reservation requests)
                    queryFilter.addFilter("reservation_request.parent_allocation_id IS NULL");
                }

                // List only reservation requests which specifies given technologies
                if (request.getSpecificationTechnologies().size() > 0) {
                    queryFilter.addFilter("reservation_request_summary.id IN ("
                            + "  SELECT DISTINCT abstract_reservation_request.id"
                            + "  FROM abstract_reservation_request"
                            + "  LEFT JOIN specification_technologies ON specification_technologies.specification_id = "
                            + "            abstract_reservation_request.specification_id"
                            + "  WHERE specification_technologies.technologies IN(:technologies))");
                    queryFilter.addFilterParameter("technologies", request.getSpecificationTechnologies());
                }

                // List only reservation requests which has specification of given classes
                if (request.getSpecificationTypes().size() > 0) {
                    StringBuilder specificationTypes = new StringBuilder();
                    for (ReservationRequestSummary.SpecificationType type : request.getSpecificationTypes()) {
                        if (specificationTypes.length() > 0) {
                            specificationTypes.append(",");
                        }
                        specificationTypes.append("'");
                        specificationTypes.append(type);
                        specificationTypes.append("'");
                    }
                    queryFilter.addFilter("specification_summary.type IN(" + specificationTypes.toString() + ")");
                }

                // Filter specification resource id
                String specificationResourceId = request.getSpecificationResourceId();
                if (specificationResourceId != null) {
                    queryFilter.addFilter("specification_summary.resource_id = :resource_id");
                    queryFilter.addFilterParameter("resource_id",
                            ObjectIdentifier.parseId(specificationResourceId, ObjectType.RESOURCE));
                }

                String reusedReservationRequestId = request.getReusedReservationRequestId();
                if (reusedReservationRequestId != null) {
                    if (reusedReservationRequestId.equals(ReservationRequestListRequest.FILTER_EMPTY)) {
                        // List only reservation requests which hasn't reused any reservation request
                        queryFilter.addFilter("reservation_request_summary.reused_reservation_request_id IS NULL");
                    } else if (reusedReservationRequestId.equals(ReservationRequestListRequest.FILTER_NOT_EMPTY)) {
                        // List only reservation requests which reuse any reservation request
                        queryFilter
                                .addFilter("reservation_request_summary.reused_reservation_request_id IS NOT NULL");
                    } else {
                        // List only reservation requests which reuse given reservation request
                        Long persistenceId = ObjectIdentifier.parseId(reusedReservationRequestId,
                                ObjectType.RESERVATION_REQUEST);
                        queryFilter.addFilter("reservation_request_summary.reused_reservation_request_id = "
                                + ":reusedReservationRequestId");
                        queryFilter.addFilterParameter("reusedReservationRequestId", persistenceId);
                    }
                }

                AllocationState allocationState = request.getAllocationState();
                if (allocationState != null) {
                    queryFilter.addFilter("reservation_request_summary.allocation_state = :allocationState");
                    queryFilter.addFilterParameter("allocationState", allocationState.toString());
                }

                Interval interval = request.getInterval();
                if (interval != null) {
                    queryFilter.addFilter("reservation_request_summary.slot_start < :intervalEnd");
                    queryFilter.addFilter("reservation_request_summary.slot_end > :intervalStart");
                    queryFilter.addFilterParameter("intervalStart", interval.getStart().toDate());
                    queryFilter.addFilterParameter("intervalEnd", interval.getEnd().toDate());
                }

                String userId = request.getUserId();
                if (userId != null) {
                    queryFilter.addFilter("reservation_request_summary.created_by = :userId"
                            + " OR reservation_request_summary.updated_by = :userId"
                            + " OR reservation_request_summary.allocation_id IN("
                            + "   SELECT acl_object_identity.object_id FROM acl_entry"
                            + "   LEFT JOIN acl_identity ON acl_identity.id = acl_entry.acl_identity_id"
                            + "   LEFT JOIN acl_object_identity ON acl_object_identity.id = acl_entry.acl_object_identity_id"
                            + "   LEFT JOIN acl_object_class ON acl_object_class.id = acl_object_identity.acl_object_class_id"
                            + "   WHERE acl_object_class.class = 'RESERVATION_REQUEST' AND acl_identity.principal_id = :userId)");
                    queryFilter.addFilterParameter("userId", userId);
                }

                String participantUserId = request.getParticipantUserId();
                if (participantUserId != null) {
                    queryFilter.addFilter("reservation_request_summary.allocation_id IN("
                            + " SELECT reservation.allocation_id FROM reservation"
                            + " LEFT JOIN room_endpoint_participants ON room_endpoint_participants.room_endpoint_id = reservation.executable_id"
                            + " LEFT JOIN person_participant ON person_participant.id = abstract_participant_id"
                            + " LEFT JOIN person ON person.id = person_participant.person_id"
                            + " WHERE person.user_id = :participantUserId)");
                    queryFilter.addFilterParameter("participantUserId", participantUserId);
                }

                String search = request.getSearch();
                if (search != null) {
                    queryFilter.addFilter("LOWER(reservation_request_summary.description) LIKE :search"
                            + " OR LOWER(specification_summary.alias_room_name) LIKE :search"
                            + " OR LOWER(reused_specification_summary.alias_room_name) LIKE :search");
                    queryFilter.addFilterParameter("search", "%" + search.toLowerCase() + "%");
                }
            }

            // Query order by
            String queryOrderBy;
            ReservationRequestListRequest.Sort sort = request.getSort();
            if (sort != null) {
                switch (sort) {
                case ALIAS_ROOM_NAME:
                    queryOrderBy = "specification_summary.alias_room_name";
                    break;
                case DATETIME:
                    queryOrderBy = "reservation_request_summary.created_at";
                    break;
                case REUSED_RESERVATION_REQUEST:
                    queryOrderBy = "reservation_request_summary.reused_reservation_request_id IS NOT NULL";
                    break;
                case ROOM_PARTICIPANT_COUNT:
                    queryOrderBy = "specification_summary.room_participant_count";
                    break;
                case SLOT:
                    queryOrderBy = "reservation_request_summary.slot_end";
                    break;
                case SLOT_NEAREST:
                    queryOrderBy = "reservation_request_summary.slot_nearness_priority, reservation_request_summary.slot_nearness_value";
                    break;
                case STATE:
                    queryOrderBy = "reservation_request_summary.allocation_state, reservation_request_summary.executable_state";
                    break;
                case TECHNOLOGY:
                    queryOrderBy = "specification_summary.technologies";
                    break;
                case TYPE:
                    queryOrderBy = "specification_summary.type";
                    break;
                case USER:
                    queryOrderBy = "reservation_request_summary.created_by";
                    break;
                default:
                    throw new TodoImplementException(sort);
                }
            } else {
                queryOrderBy = "reservation_request_summary.id";
            }
            Boolean sortDescending = request.getSortDescending();
            sortDescending = (sortDescending != null ? sortDescending : false);
            if (sortDescending) {
                queryOrderBy = queryOrderBy + " DESC";
            }

            Map<String, String> parameters = new HashMap<String, String>();
            parameters.put("filter", queryFilter.toQueryWhere());
            parameters.put("order", queryOrderBy);
            String query = NativeQuery.getNativeQuery(NativeQuery.RESERVATION_REQUEST_LIST, parameters);

            ListResponse<ReservationRequestSummary> response = new ListResponse<ReservationRequestSummary>();
            List<Object[]> records = performNativeListRequest(query, queryFilter, request, response, entityManager);
            for (Object[] record : records) {
                ReservationRequestSummary reservationRequestSummary = getReservationRequestSummary(record);
                response.addItem(reservationRequestSummary);
            }
            return response;
        } finally {
            entityManager.close();
        }
    }

    @Override
    public cz.cesnet.shongo.controller.api.AbstractReservationRequest getReservationRequest(
            SecurityToken securityToken, String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, reservationRequest, ObjectPermission.READ)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation request %s", objectId);
            }

            return reservationRequest.toApi(authorization.isOperator(securityToken));
        } finally {
            entityManager.close();
        }
    }

    @Override
    public List<ReservationRequestSummary> getReservationRequestHistory(SecurityToken securityToken,
            String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, reservationRequest, ObjectPermission.READ)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation request %s", objectId);
            }

            String historyQuery = NativeQuery.getNativeQuery(NativeQuery.RESERVATION_REQUEST_HISTORY);

            List history = entityManager.createNativeQuery(historyQuery)
                    .setParameter("reservationRequestId", objectId.getPersistenceId()).getResultList();

            List<ReservationRequestSummary> reservationRequestSummaries = new LinkedList<ReservationRequestSummary>();
            for (Object historyItem : history) {
                Object[] historyItemData = (Object[]) historyItem;
                ReservationRequestSummary reservationRequestSummary = getReservationRequestSummary(historyItemData);
                reservationRequestSummaries.add(reservationRequestSummary);
            }

            return reservationRequestSummaries;
        } finally {
            entityManager.close();
        }
    }

    @Override
    public List<Reservation> getReservationRequestReservations(SecurityToken securityToken,
            String reservationRequestId) {
        authorization.validate(securityToken);
        checkNotNull("reservationRequestId", reservationRequestId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationRequestManager reservationRequestManager = new ReservationRequestManager(entityManager);
        ReservationManager reservationManager = new ReservationManager(entityManager);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationRequestId, ObjectType.RESERVATION_REQUEST);
        try {
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, reservationRequest, ObjectPermission.READ)) {
                ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation request %s", objectId);
            }

            List<Reservation> reservations = new LinkedList<Reservation>();
            for (cz.cesnet.shongo.controller.booking.reservation.Reservation reservation : reservationManager
                    .listByReservationRequest(objectId.getPersistenceId())) {
                reservations.add(reservation.toApi(entityManager, authorization.isOperator(securityToken)));
            }
            return reservations;
        } finally {
            entityManager.close();
        }
    }

    @Override
    public Reservation getReservation(SecurityToken securityToken, String reservationId) {
        authorization.validate(securityToken);
        checkNotNull("reservationId", reservationId);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationManager reservationManager = new ReservationManager(entityManager);
        ObjectIdentifier objectId = ObjectIdentifier.parse(reservationId, ObjectType.RESERVATION);

        try {
            cz.cesnet.shongo.controller.booking.reservation.Reservation reservation = reservationManager
                    .get(objectId.getPersistenceId());

            if (!authorization.hasObjectPermission(securityToken, reservation, ObjectPermission.READ)) {
                cz.cesnet.shongo.controller.booking.resource.Resource resource = reservation.getAllocatedResource();
                if (resource == null || !authorization.hasObjectRole(securityToken, resource, ObjectRole.OWNER)) {
                    ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation %s", objectId);
                }
            }

            return reservation.toApi(entityManager, authorization.isOperator(securityToken));
        } finally {
            entityManager.close();
        }
    }

    @Override
    public List<Reservation> getReservations(SecurityToken securityToken, Collection<String> reservationIds) {
        authorization.validate(securityToken);
        checkNotNull("reservationIds", reservationIds);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        ReservationManager reservationManager = new ReservationManager(entityManager);
        try {
            Set<Long> reservationPersistentIds = new HashSet<Long>();
            for (String reservationId : reservationIds) {
                ObjectIdentifier objectId = ObjectIdentifier.parse(reservationId, ObjectType.RESERVATION);
                cz.cesnet.shongo.controller.booking.reservation.Reservation reservation = reservationManager
                        .get(objectId.getPersistenceId());
                if (!authorization.hasObjectPermission(securityToken, reservation, ObjectPermission.READ)) {
                    ControllerReportSetHelper.throwSecurityNotAuthorizedFault("read reservation %s", objectId);
                }
                reservationPersistentIds.add(objectId.getPersistenceId());
            }
            List<Reservation> reservations = new LinkedList<Reservation>();
            for (cz.cesnet.shongo.controller.booking.reservation.Reservation reservation : reservationManager
                    .listByIds(reservationPersistentIds)) {
                reservations.add(reservation.toApi(entityManager, authorization.isOperator(securityToken)));
            }
            return reservations;
        } finally {
            entityManager.close();
        }
    }

    @Override
    public ListResponse<ReservationSummary> listReservations(ReservationListRequest request) {
        checkNotNull("request", request);
        SecurityToken securityToken = request.getSecurityToken();
        authorization.validate(securityToken);

        EntityManager entityManager = entityManagerFactory.createEntityManager();
        try {
            QueryFilter queryFilter = new QueryFilter("reservation_summary");

            // List only reservations which is current user permitted to read or which allocates resource owned by the user
            Set<Long> readableReservationIds = authorization.getEntitiesWithPermission(securityToken,
                    cz.cesnet.shongo.controller.booking.reservation.Reservation.class, ObjectPermission.READ);
            if (readableReservationIds != null) {
                Set<Long> ownedResourceIds = authorization.getEntitiesWithRole(securityToken,
                        cz.cesnet.shongo.controller.booking.resource.Resource.class, ObjectRole.OWNER);
                StringBuilder filterBuilder = new StringBuilder();
                filterBuilder.append("1=0");
                if (!readableReservationIds.isEmpty()) {
                    filterBuilder.append(" OR reservation_summary.id IN(:readableReservationIds)");
                    queryFilter.addFilterParameter("readableReservationIds", readableReservationIds);
                }
                if (!ownedResourceIds.isEmpty()) {
                    filterBuilder.append(" OR reservation_summary.resource_id IN(:ownedResourceIds)");
                    queryFilter.addFilterParameter("ownedResourceIds", ownedResourceIds);

                }
                queryFilter.addFilter(filterBuilder.toString());
            }

            // List only reservations of requested types
            if (request.getReservationTypes().size() > 0) {
                StringBuilder reservationTypes = new StringBuilder();
                for (ReservationSummary.Type reservationType : request.getReservationTypes()) {
                    if (reservationTypes.length() > 0) {
                        reservationTypes.append(",");
                    }
                    reservationTypes.append("'");
                    reservationTypes.append(reservationType);
                    reservationTypes.append("'");
                }
                queryFilter.addFilter("reservation_summary.type IN(" + reservationTypes.toString() + ")");
            }

            // List only reservations which allocates requested resource
            if (!request.getResourceIds().isEmpty()) {
                queryFilter.addFilter("reservation_summary.resource_id IN(:resourceIds)");
                Set<Long> resourceIds = new HashSet<Long>();
                for (String resourceId : request.getResourceIds()) {
                    resourceIds.add(ObjectIdentifier.parseId(resourceId, ObjectType.RESOURCE));
                }
                queryFilter.addFilterParameter("resourceIds", resourceIds);
            }

            // List only reservations in requested interval
            Interval interval = request.getInterval();
            if (interval != null) {
                queryFilter.addFilter("reservation_summary.slot_start < :slotEnd");
                queryFilter.addFilter("reservation_summary.slot_end > :slotStart");
                queryFilter.addFilterParameter("slotStart", interval.getStart().toDate());
                queryFilter.addFilterParameter("slotEnd", interval.getEnd().toDate());
            }

            // Sort query part
            String queryOrderBy;
            ReservationListRequest.Sort sort = request.getSort();
            if (sort != null) {
                switch (sort) {
                case SLOT:
                    queryOrderBy = "reservation_summary.slot_start";
                    break;
                default:
                    throw new TodoImplementException(sort);
                }
            } else {
                queryOrderBy = "reservation_summary.id";
            }
            Boolean sortDescending = request.getSortDescending();
            sortDescending = (sortDescending != null ? sortDescending : false);
            if (sortDescending) {
                queryOrderBy = queryOrderBy + " DESC";
            }

            Map<String, String> parameters = new HashMap<String, String>();
            parameters.put("filter", queryFilter.toQueryWhere());
            parameters.put("order", queryOrderBy);
            String query = NativeQuery.getNativeQuery(NativeQuery.RESERVATION_LIST, parameters);

            ListResponse<ReservationSummary> response = new ListResponse<ReservationSummary>();
            List<Object[]> records = performNativeListRequest(query, queryFilter, request, response, entityManager);
            for (Object[] record : records) {
                ReservationSummary reservationSummary = getReservationSummary(record);
                response.addItem(reservationSummary);
            }
            return response;
        } finally {
            entityManager.close();
        }
    }

    /**
     * Check whether user with given {@code userId} can provide given {@code reusedReservationRequestId}.
     *
     * @param securityToken
     * @param reusedReservationRequestId
     * @throws ControllerReportSet.SecurityNotAuthorizedException
     *
     */
    private void checkReusedReservationRequest(SecurityToken securityToken, String reusedReservationRequestId,
            ReservationRequestManager reservationRequestManager) {
        ObjectIdentifier objectId = ObjectIdentifier.parse(reusedReservationRequestId);
        cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest reservationRequest = reservationRequestManager
                .get(objectId.getPersistenceId());
        if (reservationRequest.getState()
                .equals(cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest.State.DELETED)) {
            throw new ControllerReportSet.ReservationRequestDeletedException(reusedReservationRequestId);
        }
        if (!authorization.hasObjectPermission(securityToken, reservationRequest,
                ObjectPermission.PROVIDE_RESERVATION_REQUEST)) {
            ControllerReportSetHelper.throwSecurityNotAuthorizedFault("provide reservation request %s", objectId);
        }
    }

    /**
     * Check whether {@code abstractReservationRequest} can be modified.
     *
     * @param abstractReservationRequest
     * @return true when the given {@code abstractReservationRequest} can be modified, otherwise false
     */
    private boolean isReservationRequestModifiable(
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest abstractReservationRequest) {
        Allocation allocation = abstractReservationRequest.getAllocation();

        // Check if reservation request is not created by controller
        if (abstractReservationRequest instanceof ReservationRequest) {
            ReservationRequest reservationRequestImpl = (ReservationRequest) abstractReservationRequest;
            if (reservationRequestImpl.getParentAllocation() != null) {
                return false;
            }
        }

        // Check child reservation requests
        for (ReservationRequest reservationRequestImpl : allocation.getChildReservationRequests()) {
            if (isReservationRequestModifiable(reservationRequestImpl)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Check whether {@code abstractReservationRequest} can be deleted.
     *
     * @param abstractReservationRequest
     * @return true when the given {@code abstractReservationRequest} can be deleted, otherwise false
     */
    private boolean isReservationRequestDeletable(
            cz.cesnet.shongo.controller.booking.request.AbstractReservationRequest abstractReservationRequest,
            ReservationManager reservationManager) {
        Allocation allocation = abstractReservationRequest.getAllocation();

        // Check if reservation request is not created by controller
        if (abstractReservationRequest instanceof ReservationRequest) {
            ReservationRequest reservationRequestImpl = (ReservationRequest) abstractReservationRequest;
            if (reservationRequestImpl.getParentAllocation() != null) {
                return false;
            }
        }

        // Check allocated reservations
        if (reservationManager.isAllocationReused(allocation)) {
            return false;
        }

        // Check child reservation requests
        for (ReservationRequest reservationRequestImpl : allocation.getChildReservationRequests()) {
            if (isReservationRequestDeletable(reservationRequestImpl, reservationManager)) {
                return false;
            }
        }

        return true;
    }

    /**
     * @param record
     * @return {@link ReservationRequestSummary} from given {@code record}
     */
    private ReservationRequestSummary getReservationRequestSummary(Object[] record) {
        ReservationRequestSummary reservationRequestSummary = new ReservationRequestSummary();
        reservationRequestSummary
                .setId(ObjectIdentifier.formatId(ObjectType.RESERVATION_REQUEST, record[0].toString()));
        if (record[1] != null) {
            reservationRequestSummary.setParentReservationRequestId(
                    ObjectIdentifier.formatId(ObjectType.RESERVATION_REQUEST, record[1].toString()));
        }
        reservationRequestSummary.setType(ReservationRequestType.valueOf(record[2].toString().trim()));
        reservationRequestSummary.setDateTime(new DateTime(record[3]));
        reservationRequestSummary.setUserId(record[4].toString());
        reservationRequestSummary.setDescription(record[5] != null ? record[5].toString() : null);
        reservationRequestSummary.setPurpose(ReservationRequestPurpose.valueOf(record[6].toString().trim()));
        reservationRequestSummary.setEarliestSlot(new Interval(new DateTime(record[7]), new DateTime(record[8])));
        if (record[9] != null) {
            reservationRequestSummary.setAllocationState(
                    ReservationRequest.AllocationState.valueOf(record[9].toString().trim()).toApi());
        }
        if (record[10] != null) {
            reservationRequestSummary
                    .setExecutableState(Executable.State.valueOf(record[10].toString().trim()).toApi());
        }
        reservationRequestSummary.setReusedReservationRequestId(record[11] != null
                ? ObjectIdentifier.formatId(ObjectType.RESERVATION_REQUEST, record[11].toString())
                : null);
        if (record[12] != null) {
            reservationRequestSummary
                    .setLastReservationId(ObjectIdentifier.formatId(ObjectType.RESERVATION, record[12].toString()));
        }
        String type = record[13].toString().trim();
        if (type.equals("ALIAS")) {
            reservationRequestSummary.setSpecificationType(ReservationRequestSummary.SpecificationType.ALIAS);
            reservationRequestSummary.setRoomName(record[16] != null ? record[16].toString() : null);
        } else if (type.equals("ROOM")) {
            reservationRequestSummary.setSpecificationType(ReservationRequestSummary.SpecificationType.ROOM);
            reservationRequestSummary
                    .setRoomParticipantCount(record[15] != null ? ((Number) record[15]).intValue() : null);
            reservationRequestSummary.setRoomHasRecordingService(record[16] != null && (Boolean) record[16]);
            reservationRequestSummary.setRoomHasRecordings(record[17] != null && (Boolean) record[17]);
            reservationRequestSummary.setRoomName(record[18] != null ? record[18].toString() : null);
        } else if (type.equals("PERMANENT_ROOM")) {
            reservationRequestSummary
                    .setSpecificationType(ReservationRequestSummary.SpecificationType.PERMANENT_ROOM);
            reservationRequestSummary.setRoomHasRecordingService(record[16] != null && (Boolean) record[16]);
            reservationRequestSummary.setRoomHasRecordings(record[17] != null && (Boolean) record[17]);
            reservationRequestSummary.setRoomName(record[18] != null ? record[18].toString() : null);
        } else if (type.equals("USED_ROOM")) {
            reservationRequestSummary.setSpecificationType(ReservationRequestSummary.SpecificationType.USED_ROOM);
            reservationRequestSummary
                    .setRoomParticipantCount(record[15] != null ? ((Number) record[15]).intValue() : null);
            reservationRequestSummary.setRoomHasRecordingService(record[16] != null && (Boolean) record[16]);
            reservationRequestSummary.setRoomHasRecordings(record[17] != null && (Boolean) record[17]);
            reservationRequestSummary.setRoomName(record[18] != null ? record[18].toString() : null);
        } else if (type.equals("RESOURCE")) {
            reservationRequestSummary.setSpecificationType(ReservationRequestSummary.SpecificationType.RESOURCE);
            reservationRequestSummary.setResourceId(
                    ObjectIdentifier.formatId(ObjectType.RESOURCE, ((Number) record[19]).longValue()));
        } else {
            reservationRequestSummary.setSpecificationType(ReservationRequestSummary.SpecificationType.OTHER);
        }
        if (record[14] != null) {
            String technologies = record[14].toString();
            if (!technologies.isEmpty()) {
                for (String technology : technologies.split(",")) {
                    reservationRequestSummary.addSpecificationTechnology(Technology.valueOf(technology.trim()));
                }
            }
        }
        if (record[20] != null) {
            reservationRequestSummary
                    .setUsageExecutableState(cz.cesnet.shongo.controller.booking.executable.Executable.State
                            .valueOf(record[20].toString().trim()).toApi());
        }
        if (record[21] != null) {
            reservationRequestSummary.setFutureSlotCount(((Number) record[21]).intValue());
        }
        return reservationRequestSummary;
    }

    /**
     * @param record
     * @return {@link ReservationSummary} from given {@code record}
     */
    private ReservationSummary getReservationSummary(Object[] record) {
        ReservationSummary reservationSummary = new ReservationSummary();
        reservationSummary.setId(ObjectIdentifier.formatId(ObjectType.RESERVATION, record[0].toString()));
        reservationSummary.setUserId(record[1] != null ? record[1].toString() : null);
        reservationSummary.setReservationRequestId(
                record[2] != null ? ObjectIdentifier.formatId(ObjectType.RESERVATION_REQUEST, record[2].toString())
                        : null);
        reservationSummary.setType(ReservationSummary.Type.valueOf(record[3].toString().trim()));
        reservationSummary.setSlot(new Interval(new DateTime(record[4]), new DateTime(record[5])));
        if (record[6] != null) {
            reservationSummary.setResourceId(ObjectIdentifier.formatId(ObjectType.RESOURCE, record[6].toString()));
        }
        if (record[7] != null) {
            reservationSummary.setRoomLicenseCount(record[7] != null ? ((Number) record[7]).intValue() : null);
        }
        if (record[8] != null) {
            reservationSummary.setRoomName(record[8] != null ? record[8].toString() : null);
        }
        if (record[9] != null) {
            reservationSummary.setAliasTypes(record[9] != null ? record[9].toString() : null);
        }
        if (record[10] != null) {
            reservationSummary.setValue(record[10] != null ? record[10].toString() : null);
        }
        return reservationSummary;
    }
}