/* Copyright 2014 Danish Maritime Authority. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.maritimecloud.serviceregistry.command.organization; import java.util.HashMap; import java.util.Map; import net.maritimecloud.portal.application.ApplicationServiceRegistry; import net.maritimecloud.serviceregistry.command.api.AddOrganizationAlias; import net.maritimecloud.serviceregistry.command.api.AddServiceInstanceAlias; import net.maritimecloud.serviceregistry.command.api.OrganizationNameAndSummaryChanged; import net.maritimecloud.serviceregistry.command.api.OrganizationCreated; import net.maritimecloud.serviceregistry.command.api.CreateOrganization; import net.maritimecloud.serviceregistry.command.api.ChangeOrganizationNameAndSummary; import net.maritimecloud.serviceregistry.command.api.ChangeOrganizationWebsiteUrl; import net.maritimecloud.serviceregistry.command.api.OrganizationAliasAdded; import net.maritimecloud.serviceregistry.command.api.OrganizationAliasRemoved; import net.maritimecloud.serviceregistry.command.api.OrganizationPrimaryAliasAdded; import net.maritimecloud.serviceregistry.command.api.OrganizationWebsiteUrlChanged; import net.maritimecloud.serviceregistry.command.api.RemoveOrganizationAlias; import net.maritimecloud.serviceregistry.command.api.RemoveServiceInstanceAlias; import net.maritimecloud.serviceregistry.command.api.ServiceInstanceAliasAdded; import net.maritimecloud.serviceregistry.command.api.ServiceInstanceAliasRegistrationDenied; import net.maritimecloud.serviceregistry.command.api.ServiceInstanceAliasRemoved; import net.maritimecloud.serviceregistry.command.api.ServiceInstancePrimaryAliasAdded; import net.maritimecloud.serviceregistry.command.serviceinstance.Coverage; import net.maritimecloud.serviceregistry.command.serviceinstance.ServiceInstance; import net.maritimecloud.serviceregistry.command.serviceinstance.ServiceInstanceId; import net.maritimecloud.serviceregistry.command.servicespecification.ServiceSpecification; import net.maritimecloud.serviceregistry.command.servicespecification.ServiceSpecificationId; import net.maritimecloud.serviceregistry.command.servicespecification.ServiceType; import net.maritimecloud.serviceregistry.domain.service.AliasGroups; import net.maritimecloud.serviceregistry.domain.service.AliasService; import org.axonframework.commandhandling.annotation.CommandHandler; import org.axonframework.eventsourcing.annotation.AbstractAnnotatedAggregateRoot; import org.axonframework.eventsourcing.annotation.AggregateIdentifier; import org.axonframework.eventsourcing.annotation.EventSourcingHandler; import org.springframework.stereotype.Component; /** * Responsibilities (in the ServiceRegistry context): * <p> * Owns ServiceSpecifications and provides ServiceInstances * <p> * Makes sure that ServiceSpecifications and ServiceInstances has a unique identity within the owning organization * <p> * Maintains the lists of ServiceSpecifications and ServiceInstances held by an organization * <p> * @author Christoffer Børrild */ @Component public class Organization extends AbstractAnnotatedAggregateRoot<OrganizationId> { @AggregateIdentifier private OrganizationId organizationId; private String name; private String summary; private Map<String, ServiceInstanceId> aliases; private Map<String, ServiceInstanceId> serviceInstanceAliases; protected Organization() { serviceInstanceAliases = new HashMap<>(); } @CommandHandler public Organization(CreateOrganization command) { this(); apply(new OrganizationCreated(command.getOrganizationId(), command.getPrimaryAlias(), command.getName(), command.getSummary(), command.getUrl())); } @CommandHandler public void handle(ChangeOrganizationNameAndSummary command) { apply(new OrganizationNameAndSummaryChanged(command.getOrganizationId(), command.getName(), command.getSummary())); } @CommandHandler public void handle(ChangeOrganizationWebsiteUrl command) { apply(new OrganizationWebsiteUrlChanged(command.getOrganizationId(), command.getUrl())); } @CommandHandler public void handle(AddOrganizationAlias command) { AliasService aliasService = ApplicationServiceRegistry.aliasService(); if (aliasService.isDefined(AliasGroups.USERS_AND_ORGANIZATIONS.name(), command.getAlias())) { if (aliasService.isIdentical(AliasGroups.USERS_AND_ORGANIZATIONS.name(), command.getAlias(), command.getOrganizationId().identifier())) { // idempotent, ok to register same alias and instance twice return; } // alias already in use by other target -> action denied //apply(new OrganizationAliasRegistrationDenied(command.getOrganizationId(), command.getServiceInstanceId(), command.getAlias())); return; } if (aliasService.hasTarget(AliasGroups.USERS_AND_ORGANIZATIONS.name(), command.getOrganizationId().identifier())) { apply(new OrganizationAliasAdded(command.getOrganizationId(), command.getAlias())); } else { // there's a first time for everything apply(new OrganizationPrimaryAliasAdded(command.getOrganizationId(), command.getAlias())); } } @CommandHandler public void handle(RemoveOrganizationAlias command) { AliasService aliasService = ApplicationServiceRegistry.aliasService(); if (aliasService.isDefined(AliasGroups.USERS_AND_ORGANIZATIONS.name(), command.getAlias())) { if (aliasService.isIdentical(AliasGroups.USERS_AND_ORGANIZATIONS.name(), command.getAlias(), command.getOrganizationId().identifier())) { apply(new OrganizationAliasRemoved(command.getOrganizationId(), command.getAlias())); } } } @CommandHandler public void handle(AddServiceInstanceAlias command) { ServiceInstanceId serviceInstanceId = serviceInstanceAliases.get(command.getAlias()); if (serviceInstanceId != null) { if (serviceInstanceId != command.getServiceInstanceId()) { // alias already in use by other target -> action denied apply(new ServiceInstanceAliasRegistrationDenied(command.getOrganizationId(), command.getServiceInstanceId(), command.getAlias())); } // idempotent, ok to register same alias and instance twice return; } if (serviceInstanceAliases.containsValue(command.getServiceInstanceId())) { apply(new ServiceInstanceAliasAdded(command.getOrganizationId(), command.getServiceInstanceId(), command.getAlias())); } else { // there's a first time for everything apply(new ServiceInstancePrimaryAliasAdded(command.getOrganizationId(), command.getServiceInstanceId(), command.getAlias())); } } @CommandHandler public void handle(RemoveServiceInstanceAlias command) { ServiceInstanceId serviceInstanceId = serviceInstanceAliases.get(command.getAlias()); if (serviceInstanceId != null) { apply(new ServiceInstanceAliasRemoved(command.getOrganizationId(), command.getAlias())); } } // Remove alias should ignore/fail on remove of last alias, as this one is locked for removal (primary alias)! @EventSourcingHandler public void on(OrganizationCreated event) { this.organizationId = event.getOrganizationId(); } @EventSourcingHandler public void on(ServiceInstanceAliasAdded event) { serviceInstanceAliases.put(event.getAlias(), event.getServiceInstanceId()); } @EventSourcingHandler public void on(ServiceInstanceAliasRemoved event) { serviceInstanceAliases.remove(event.getAlias()); } /** * Factory for creating a ServiceSpecification in "prepare"/"draft" mode. * <p> * ( Even thought this factory makes the CommandHandler for this use case somewhat cumbersome to test it serves the DDD valid purpose of * guarding the invariant that a ServiceSpecification cannot be created for a non-existing or deleted Organization ) */ public ServiceSpecification prepareServiceSpecification(ServiceSpecificationId serviceSpecificationId, ServiceType serviceType, String name, String summary) { return new ServiceSpecification(organizationId, serviceSpecificationId, serviceType, name, summary); } /** * Factory for creating a ServiceInstance to be published by the Organization. * <p> * ( Even thought this factory makes the CommandHandler for this use case somewhat cumbersome to test it serves the DDD valid purpose of * guarding the invariant that a ServiceInstance cannot be created for a non-existing or deleted Organization ) */ public ServiceInstance provideServiceInstance( ServiceSpecification specification, ServiceInstanceId serviceInstanceId, String name, String summary, Coverage coverage) { return specification.materialize(organizationId, serviceInstanceId, name, summary, coverage); } }