ch.puzzle.itc.mobiliar.business.resourcerelation.control.ResourceRelationService.java Source code

Java tutorial

Introduction

Here is the source code for ch.puzzle.itc.mobiliar.business.resourcerelation.control.ResourceRelationService.java

Source

/*
 * AMW - Automated Middleware allows you to manage the configurations of
 * your Java EE applications on an unlimited number of different environments
 * with various versions, including the automated deployment of those apps.
 * Copyright (C) 2013-2016 by Puzzle ITC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * To change this license header, choose License Headers in Project Properties. To change this template file,
 * choose Tools | Templates and open the template in the editor.
 */
package ch.puzzle.itc.mobiliar.business.resourcerelation.control;

import ch.puzzle.itc.mobiliar.business.auditview.control.AuditService;
import ch.puzzle.itc.mobiliar.business.domain.commons.CommonDomainService;
import ch.puzzle.itc.mobiliar.business.foreignable.control.ForeignableService;
import ch.puzzle.itc.mobiliar.business.foreignable.entity.ForeignableOwner;
import ch.puzzle.itc.mobiliar.business.generator.control.extracted.ResourceDependencyResolverService;
import ch.puzzle.itc.mobiliar.business.property.entity.ResourceEditRelation;
import ch.puzzle.itc.mobiliar.business.resourcegroup.control.ResourceEntityService;
import ch.puzzle.itc.mobiliar.business.resourcegroup.control.ResourceTypeProvider;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.Application;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ApplicationServer;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceEntity;
import ch.puzzle.itc.mobiliar.business.resourcegroup.entity.ResourceGroupEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.AbstractResourceRelationEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ConsumedResourceRelationEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ProvidedResourceRelationEntity;
import ch.puzzle.itc.mobiliar.business.resourcerelation.entity.ResourceRelationTypeEntity;
import ch.puzzle.itc.mobiliar.business.security.control.PermissionService;
import ch.puzzle.itc.mobiliar.business.security.entity.Action;
import ch.puzzle.itc.mobiliar.business.security.entity.Permission;
import ch.puzzle.itc.mobiliar.common.exception.ElementAlreadyExistsException;
import ch.puzzle.itc.mobiliar.common.exception.ResourceNotFoundException;
import ch.puzzle.itc.mobiliar.common.util.DefaultResourceTypeDefinition;
import org.apache.commons.lang.StringUtils;

import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.io.Serializable;
import java.util.*;
import java.util.logging.Logger;

/**
 * @author oschmid
 */
@Stateless
public class ResourceRelationService implements Serializable {

    @Inject
    EntityManager entityManager;

    @Inject
    ResourceTypeProvider resourceTypeProvider;

    @Inject
    ResourceEntityService resourceEntityService;

    @Inject
    CommonDomainService commonService;

    @Inject
    ForeignableService foreignableService;

    @Inject
    PermissionService permissionService;

    @Inject
    ResourceDependencyResolverService resourceDependencyResolverService;

    @Inject
    AuditService auditService;

    @Inject
    public Logger log;

    /**
     * Returns the resource relation entity based on its id. ATTENTION: This method assumes, that the ids
     * between the two instances are exclusive! This is currently achieved by using the same sequence for id
     * generation (defined on the abstract class).
     * 
     * @param resourceRelationId
     * @return
     */
    public AbstractResourceRelationEntity getResourceRelation(Integer resourceRelationId) {
        AbstractResourceRelationEntity result = getConsumedResourceRelation(resourceRelationId);
        if (result == null) {
            // If the resource relation id is not found within the consumed resource relations, it is
            // probably a provided relation (otherwise it is a wrong id and we return null)!
            result = entityManager.find(ProvidedResourceRelationEntity.class, resourceRelationId);
        }
        return result;
    }

    public ConsumedResourceRelationEntity getConsumedResourceRelation(Integer consumedResourceRelationId) {
        return entityManager.find(ConsumedResourceRelationEntity.class, consumedResourceRelationId);
    }

    /**
     * @return
     */
    public ResourceRelationTypeEntity getResourceTypeRelation(Integer resourceTypeRelationId) {
        return entityManager.find(ResourceRelationTypeEntity.class, resourceTypeRelationId);
    }

    /**
     * Adds a relation for all releases existing for slave resource
     * 
     * @param masterId
     * @param slaveGroupId
     * @param provided
     * @param relationName
     * @param typeIdentifier
     * @throws ElementAlreadyExistsException
     * @throws ResourceNotFoundException
     */
    public void addRelationByGroup(Integer masterId, Integer slaveGroupId, boolean provided, String relationName,
            String typeIdentifier, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException, ResourceNotFoundException {
        ResourceEntity master = entityManager.find(ResourceEntity.class, masterId);
        ResourceGroupEntity slaveGroup = entityManager.find(ResourceGroupEntity.class, slaveGroupId);
        auditService.setResourceIdInThreadLocal(master.getId());

        String slaveResourceType = slaveGroup.getResourceType().getName();
        if (DefaultResourceTypeDefinition.NODE.name().equals(slaveResourceType)) {
            permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                    master.getResourceGroup(), null, null);
            addNodeByGroup(masterId, slaveGroupId, provided, relationName, typeIdentifier, changingOwner);
        } else if (DefaultResourceTypeDefinition.APPLICATION.name().equals(master.getResourceType().getName())) {
            permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                    master.getResourceGroup(), null, null);
            addRelationInEditInstanceApplicationByGroup(masterId, slaveGroupId, provided, relationName,
                    typeIdentifier, changingOwner);
        } else if (slaveGroup.getResourceType().isRuntimeType()) {
            resourceEntityService.setRuntime(master, slaveGroup, changingOwner);
        } else {
            permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                    master.getResourceGroup(), null, null);
            doAddResourceRelationForAllReleases(masterId, slaveGroupId, provided, relationName, typeIdentifier,
                    changingOwner);
            // If an application is added to the applicationserver, it has to be removed from the
            // applications without applicationserver container
            if (DefaultResourceTypeDefinition.APPLICATIONSERVER.name().equals(master.getResourceType().getName())) {
                if (DefaultResourceTypeDefinition.APPLICATION.name().equals(slaveResourceType)) {
                    removeAppFromContainerGroupAndRemoveGroupIfEmpty(slaveGroup);
                }
            }
        }
    }

    /**
     * Entfernt Application aus ApplicationServer. Falls ApplicationServerContainer nur die soeben
     * verschobene Applikation enthlt, so soll diese Gruppe gelscht werden.
     * 
     * @throws ResourceNotFoundException
     */
    public void removeAppFromContainerGroupAndRemoveGroupIfEmpty(ResourceGroupEntity applicationGroup)
            throws ResourceNotFoundException {
        if (applicationGroup != null) {

            ApplicationServer applicationCollectorGroup = commonService.createOrGetApplicationCollectorServer();
            List<Application> applications = applicationCollectorGroup.getApplications();
            for (Application app : applications) {
                if (applicationGroup.getResources().contains(app.getEntity())) {
                    List<AbstractResourceRelationEntity> relations = getConsumedRelationsByMasterAndSlave(
                            applicationCollectorGroup.getEntity(), applicationGroup.getResources(), null);
                    if (!relations.isEmpty()) {
                        doRemoveResourceRelationForAllReleases(relations.get(0).getId());
                    }
                }
            }
            // Falls ApplicationServerContainer nur die soeben verschobene
            // Applikation enthlt, so soll diese Gruppe gelscht werden!
            // (sofern es sich nicht um den "Applications without application server" handelt)
            if (applicationCollectorGroup.getEntity().isDeletable() && (applications.size() == 0
                    || applications.size() == applicationGroup.getResources().size())) {
                entityManager.remove(applicationCollectorGroup.getEntity());
                log.info("ApplicationServerContainer removed");
            }
        }
    }

    /**
     * Adds a relation for all releases existing for slave resource<br>
     * Only for users with permission to add related resource
     * 
     * @param masterId
     * @param slaveGroupId
     * @param provided
     * @param relationName
     * @param typeIdentifier
     * @throws ElementAlreadyExistsException
     * @throws ResourceNotFoundException
     */
    private void addRelationInEditInstanceApplicationByGroup(Integer masterId, Integer slaveGroupId,
            boolean provided, String relationName, String typeIdentifier, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException, ResourceNotFoundException {
        doAddResourceRelationForAllReleases(masterId, slaveGroupId, provided, relationName, typeIdentifier,
                changingOwner);
    }

    /**
     * Adds a relation for all releases existing for slave resource<br>
     * Only for users with permission to add related resource
     * 
     * @param masterId
     * @param slaveGroupId
     * @param provided
     * @param relationName
     * @param typeIdentifier
     * @throws ElementAlreadyExistsException
     * @throws ResourceNotFoundException
     */
    private void addNodeByGroup(Integer masterId, Integer slaveGroupId, boolean provided, String relationName,
            String typeIdentifier, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException, ResourceNotFoundException {
        doAddResourceRelationForAllReleases(masterId, slaveGroupId, provided, relationName, typeIdentifier,
                changingOwner);
    }

    /**
     * Adds a relation for all releases existing for slave resource<br>
     * Only for users with permission to add every related resource
     *
     * @param masterId
     * @param slaveGroupId
     * @param provided
     * @param relationName
     * @param typeIdentifier
     * @param changingOwner
     * @throws ElementAlreadyExistsException
     * @throws ResourceNotFoundException
     */
    public void doAddResourceRelationForAllReleases(Integer masterId, Integer slaveGroupId, boolean provided,
            String relationName, String typeIdentifier, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException, ResourceNotFoundException {
        addResourceRelation(masterId, slaveGroupId, provided, relationName, typeIdentifier, null, changingOwner);

    }

    /**
     * Adds a relation for a specific release
     *
     * @param masterId
     * @param slaveGroupId
     * @param provided
     * @param relationName
     * @param typeIdentifier
     * @param releaseId
     * @param changingOwner
     * @throws ElementAlreadyExistsException
     */
    public void doAddResourceRelationForSpecificRelease(Integer masterId, Integer slaveGroupId, boolean provided,
            String relationName, String typeIdentifier, Integer releaseId, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException, ResourceNotFoundException {
        addResourceRelation(masterId, slaveGroupId, provided, relationName, typeIdentifier, releaseId,
                changingOwner);
    }

    private void addResourceRelation(Integer masterId, Integer slaveGroupId, boolean provided, String relationName,
            String typeIdentifier, Integer releaseId, ForeignableOwner changingOwner)
            throws ElementAlreadyExistsException {
        ResourceEntity master = entityManager.find(ResourceEntity.class, masterId);
        ResourceGroupEntity slaveGroup = entityManager.find(ResourceGroupEntity.class, slaveGroupId);
        ResourceRelationTypeEntity resourceRelationType = resourceTypeProvider
                .getOrCreateResourceRelationTypeIncludingParents(master.getResourceType(),
                        slaveGroup.getResources().iterator().next().getResourceType(), typeIdentifier);

        for (ResourceEntity slave : slaveGroup.getResources()) {
            if (releaseId == null || slave.getRelease().getId().equals(releaseId)) {

                if (provided) {
                    master.addProvidedResourceRelation(slave, resourceRelationType, changingOwner);
                } else {
                    master.addConsumedResourceRelation(slave, resourceRelationType, relationName, changingOwner);
                }
                log.info("Relation between resourceId: " + master.getId() + " and resourceId: " + slave.getId()
                        + " added");
            }
        }

        entityManager.persist(master);

    }

    public void deleteRelation(AbstractResourceRelationEntity relation) {
        if (relation != null) {
            entityManager.remove(relation);
        }
    }

    /**
     * Removes all relations for all releases of the slave resource
     * 
     * @throws ResourceNotFoundException
     * @throws ElementAlreadyExistsException
     */
    public void removeRelation(AbstractResourceRelationEntity relation)
            throws ResourceNotFoundException, ElementAlreadyExistsException {
        auditService.storeIdInThreadLocalForAuditLog(relation);
        Integer relationId = relation.getId();
        ResourceEntity master = entityManager.find(ResourceEntity.class, relation.getMasterResource().getId());
        String slaveResourceType = relation.getSlaveResource().getResourceType().getName();
        if (DefaultResourceTypeDefinition.NODE.name().equals(slaveResourceType)
                || DefaultResourceTypeDefinition.APPLICATION.name().equals(master.getResourceType().getName())) {
            permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                    master.getResourceGroup(), null, null);
            doRemoveResourceRelationForAllReleases(relationId);
        }
        // If an application is removed from the applicationserver
        // and application is not related to the applicationserver in an other release,
        // it has to be added to the applications without applicationserver container (otherwise removed)
        else if (DefaultResourceTypeDefinition.APPLICATIONSERVER.name().equals(master.getResourceType().getName())
                && DefaultResourceTypeDefinition.APPLICATION.name().equals(slaveResourceType)) {

            // list all appServers consuming slave
            List<ResourceEntity> appServersConsumingSlave = relation.getSlaveResource()
                    .getMasterResourcesOfConsumedSlaveRelationByResourceType(
                            DefaultResourceTypeDefinition.APPLICATIONSERVER);

            for (Iterator<ResourceEntity> it = appServersConsumingSlave.iterator(); it.hasNext();) {
                ResourceEntity next = it.next();
                if (next.getId().equals(master.getId())) {
                    // remove appServer from relation we are currently editing
                    it.remove();
                }
            }
            if (appServersConsumingSlave.size() <= 0) {
                // there is no other relation to appServer, therefore add to applications without
                // applicationserver collector
                ApplicationServer applicationCollectorServer = commonService
                        .createOrGetApplicationCollectorServer();
                // This has to be done for the whole resource group...
                for (ResourceEntity r : relation.getSlaveResource().getResourceGroup().getResources()) {
                    r.changeResourceRelation(master, applicationCollectorServer.getEntity(),
                            relation.getResourceRelationType());
                }
            } else {
                permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                        master.getResourceGroup(), null, null);
                // there are still relations to the appServer in other releases, therefore remove this one
                doRemoveResourceRelationForAllReleases(relationId);
            }

        } else {
            permissionService.checkPermissionAndFireException(Permission.RESOURCE, null, Action.UPDATE,
                    master.getResourceGroup(), null, null);
            doRemoveResourceRelationForAllReleases(relationId);
        }
    }

    private void doRemoveResourceRelationForAllReleases(Integer relationId) throws ResourceNotFoundException {

        // check for Consumed and ProvidedRelations
        AbstractResourceRelationEntity relation = getResourceRelation(relationId);

        if (relation != null) {
            boolean provided = (relation instanceof ProvidedResourceRelationEntity);
            Set<ResourceEntity> slaveResources = relation.getSlaveResource().getResourceGroup().getResources();

            List<AbstractResourceRelationEntity> relations;
            if (provided) {
                relations = getProvidedRelationsByMasterAndSlave(relation.getMasterResource(), slaveResources,
                        relation.getIdentifier());
            } else {
                relations = getConsumedRelationsByMasterAndSlave(relation.getMasterResource(), slaveResources,
                        relation.getIdentifier());
            }

            boolean hasResourceIdentifier = !StringUtils.isEmpty(relation.getIdentifier());
            boolean hasResourceTypeIdentifier = !StringUtils
                    .isEmpty(relation.getResourceRelationType().getIdentifier());

            for (AbstractResourceRelationEntity rel : relations) {
                if (isMatchingRelation(relation, hasResourceIdentifier, hasResourceTypeIdentifier, rel)) {
                    relation.getMasterResource().removeRelation(rel);
                    relation.getSlaveResource().removeSlaveRelation(rel);
                    entityManager.remove(rel);
                    log.info("Relation between resourceId: " + relation.getMasterResource().getId()
                            + " and resourceId: " + rel.getSlaveResource().getId() + " removed");
                }
            }
        } else {
            throw new ResourceNotFoundException("Relation not found");
        }
    }

    private boolean isMatchingRelation(AbstractResourceRelationEntity relation, boolean hasResourceIdentifier,
            boolean hasResourceTypeIdentifier, AbstractResourceRelationEntity rel) {
        if (hasResourceIdentifier && relation.getIdentifier().equals(rel.getIdentifier())) {
            return true;
        } else if (hasResourceTypeIdentifier && rel.getResourceRelationType() != null
                && relation.getResourceRelationType().getIdentifierOrTypeBName()
                        .equals(rel.getResourceRelationType().getIdentifierOrTypeBName())) {
            return true;
        } else if (rel.getIdentifier() == null && (rel.getResourceRelationType() == null
                || rel.getResourceRelationType().getIdentifier() == null)) {
            return true;
        }
        return false;
    }

    protected List<AbstractResourceRelationEntity> getConsumedRelationsByMasterAndSlave(
            ResourceEntity masterResource, Set<ResourceEntity> slaveResources, String identifier) {
        List<AbstractResourceRelationEntity> consumed = new ArrayList<>();
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<ConsumedResourceRelationEntity> q = cb.createQuery(ConsumedResourceRelationEntity.class);
        Root<ConsumedResourceRelationEntity> r = q.from(ConsumedResourceRelationEntity.class);
        Predicate masterResourceIs = cb.equal(r.<ResourceEntity>get("masterResource"), masterResource);
        Predicate slaveResourceIn = r.<ResourceEntity>get("slaveResource").in(slaveResources);

        if (!StringUtils.isEmpty(identifier)) {
            Predicate identifierIs = cb.equal(r.<String>get("identifier"), identifier);
            q.where(cb.and(masterResourceIs, slaveResourceIn, identifierIs));
        } else {
            q.where(cb.and(masterResourceIs, slaveResourceIn));
        }

        TypedQuery<ConsumedResourceRelationEntity> query = entityManager.createQuery(q);
        List<ConsumedResourceRelationEntity> result = query.getResultList();
        for (ConsumedResourceRelationEntity rel : result) {
            consumed.add(rel);
        }
        return consumed;
    }

    protected List<AbstractResourceRelationEntity> getProvidedRelationsByMasterAndSlave(
            ResourceEntity masterResource, Set<ResourceEntity> slaveResources, String identifier) {
        List<AbstractResourceRelationEntity> provided = new ArrayList<>();
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<ProvidedResourceRelationEntity> q = cb.createQuery(ProvidedResourceRelationEntity.class);
        Root<ProvidedResourceRelationEntity> r = q.from(ProvidedResourceRelationEntity.class);
        Predicate masterResourceIs = cb.equal(r.<ResourceEntity>get("masterResource"), masterResource);
        Predicate slaveResourceIn = r.<ResourceEntity>get("slaveResource").in(slaveResources);

        if (!StringUtils.isEmpty(identifier)) {
            Predicate identifierIs = cb.equal(r.<String>get("identifier"), identifier);
            q.where(cb.and(masterResourceIs, slaveResourceIn, identifierIs));
        } else {
            q.where(cb.and(masterResourceIs, slaveResourceIn));
        }

        TypedQuery<ProvidedResourceRelationEntity> query = entityManager.createQuery(q);
        List<ProvidedResourceRelationEntity> result = query.getResultList();
        for (ProvidedResourceRelationEntity rel : result) {
            provided.add(rel);
        }
        return provided;
    }

    public List<ConsumedResourceRelationEntity> getConsumedSlaveRelations(ResourceEntity slaveResource) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<ConsumedResourceRelationEntity> q = cb.createQuery(ConsumedResourceRelationEntity.class);
        Root<ConsumedResourceRelationEntity> r = q.from(ConsumedResourceRelationEntity.class);
        Join<ConsumedResourceRelationEntity, ResourceEntity> slaveResourceJoin = r.join("slaveResource");
        q.where(cb.equal(slaveResourceJoin.get("id"), slaveResource.getId()));

        TypedQuery<ConsumedResourceRelationEntity> query = entityManager.createQuery(q);
        return query.getResultList();
    }

    public List<ProvidedResourceRelationEntity> getProvidedSlaveRelations(ResourceEntity slaveResource) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<ProvidedResourceRelationEntity> q = cb.createQuery(ProvidedResourceRelationEntity.class);
        Root<ProvidedResourceRelationEntity> r = q.from(ProvidedResourceRelationEntity.class);
        Join<ProvidedResourceRelationEntity, ResourceEntity> slaveResourceJoin = r.join("slaveResource");
        q.where(cb.equal(slaveResourceJoin.get("id"), slaveResource.getId()));

        TypedQuery<ProvidedResourceRelationEntity> query = entityManager.createQuery(q);
        return query.getResultList();
    }

    /**
     * Find best matching release relation for selected resource in release
     * @param releaseRelations
     * @param releaseResource
     * @return
     */
    public ResourceEditRelation getBestMatchingRelationRelease(List<ResourceEditRelation> releaseRelations,
            ResourceEntity releaseResource) {
        long currentTime = releaseResource != null && releaseResource.getRelease() != null
                && releaseResource.getRelease().getInstallationInProductionAt() != null
                        ? releaseResource.getRelease().getInstallationInProductionAt().getTime()
                        : (new Date()).getTime();

        ResourceEditRelation bestMatch = findBestMatchingPastRelease(releaseRelations, currentTime);

        if (bestMatch == null) {
            bestMatch = findBestMatchingFutureRelease(releaseRelations, currentTime);
        }

        return bestMatch;
    }

    private ResourceEditRelation findBestMatchingPastRelease(List<ResourceEditRelation> releaseRelations,
            long currentTime) {
        ResourceEditRelation bestMatch = null;
        for (ResourceEditRelation relation : releaseRelations) {
            if (relation != null) {
                long releaseInstallationTime = relation.getSlaveReleaseDate().getTime();
                Long bestMatchingReleaseTime = bestMatch != null ? bestMatch.getSlaveReleaseDate().getTime() : null;

                if (resourceDependencyResolverService.isBestMatchingPastReleaseTime(bestMatchingReleaseTime,
                        releaseInstallationTime, currentTime)) {
                    bestMatch = relation;
                }
            }
        }
        return bestMatch;
    }

    private ResourceEditRelation findBestMatchingFutureRelease(List<ResourceEditRelation> releaseRelations,
            long currentTime) {
        ResourceEditRelation bestMatch = null;
        for (ResourceEditRelation relation : releaseRelations) {
            if (relation != null) {
                long releaseInstallationTime = relation.getSlaveReleaseDate().getTime();
                Long bestMatchingReleaseTime = bestMatch != null ? bestMatch.getSlaveReleaseDate().getTime() : null;

                if (resourceDependencyResolverService.isBestMatchingFutureReleaseTime(bestMatchingReleaseTime,
                        releaseInstallationTime, currentTime)) {
                    bestMatch = relation;
                }
            }
        }
        return bestMatch;
    }

}