Java tutorial
/******************************************************************************* * * Copyright FUJITSU LIMITED 2016 * * Creation Date: 2009-02-05 * *******************************************************************************/ package org.oscm.subscriptionservice.bean; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.annotation.Resource; import javax.annotation.security.RolesAllowed; import javax.ejb.EJB; import javax.ejb.Local; import javax.ejb.Remote; import javax.ejb.SessionContext; import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.inject.Inject; import javax.interceptor.Interceptors; import org.apache.commons.lang3.StringUtils; import org.oscm.accountservice.assembler.BillingContactAssembler; import org.oscm.accountservice.assembler.OrganizationAssembler; import org.oscm.accountservice.assembler.PaymentInfoAssembler; import org.oscm.accountservice.assembler.UdaAssembler; import org.oscm.accountservice.dataaccess.UdaAccess; import org.oscm.accountservice.local.AccountServiceLocal; import org.oscm.applicationservice.local.ApplicationServiceLocal; import org.oscm.communicationservice.local.CommunicationServiceLocal; import org.oscm.configurationservice.local.ConfigurationServiceLocal; import org.oscm.converter.ParameterizedTypes; import org.oscm.dataservice.local.DataService; import org.oscm.domobjects.BillingContact; import org.oscm.domobjects.CatalogEntry; import org.oscm.domobjects.Marketplace; import org.oscm.domobjects.OnBehalfUserReference; import org.oscm.domobjects.OperationParameter; import org.oscm.domobjects.OperationRecord; import org.oscm.domobjects.Organization; import org.oscm.domobjects.OrganizationRefToPaymentType; import org.oscm.domobjects.OrganizationReference; import org.oscm.domobjects.OrganizationRole; import org.oscm.domobjects.OrganizationToRole; import org.oscm.domobjects.Parameter; import org.oscm.domobjects.ParameterDefinition; import org.oscm.domobjects.ParameterSet; import org.oscm.domobjects.PaymentInfo; import org.oscm.domobjects.PaymentType; import org.oscm.domobjects.PlatformUser; import org.oscm.domobjects.PriceModel; import org.oscm.domobjects.Product; import org.oscm.domobjects.RoleDefinition; import org.oscm.domobjects.Session; import org.oscm.domobjects.Subscription; import org.oscm.domobjects.TechnicalProduct; import org.oscm.domobjects.TechnicalProductOperation; import org.oscm.domobjects.TriggerDefinition; import org.oscm.domobjects.TriggerProcess; import org.oscm.domobjects.TriggerProcessIdentifier; import org.oscm.domobjects.TriggerProcessParameter; import org.oscm.domobjects.Uda; import org.oscm.domobjects.UsageLicense; import org.oscm.domobjects.UserGroup; import org.oscm.domobjects.enums.LocalizedObjectTypes; import org.oscm.domobjects.enums.ModifiedEntityType; import org.oscm.domobjects.enums.OrganizationReferenceType; import org.oscm.i18nservice.bean.LocalizerFacade; import org.oscm.i18nservice.local.LocalizerServiceLocal; import org.oscm.id.IdGenerator; import org.oscm.identityservice.assembler.UserDataAssembler; import org.oscm.identityservice.local.IdentityServiceLocal; import org.oscm.interceptor.AuditLogDataInterceptor; import org.oscm.interceptor.DateFactory; import org.oscm.interceptor.ExceptionMapper; import org.oscm.interceptor.InvocationDateContainer; import org.oscm.internal.intf.SubscriptionSearchService; import org.oscm.internal.intf.SubscriptionService; import org.oscm.internal.types.enumtypes.ConfigurationKey; import org.oscm.internal.types.enumtypes.OperationStatus; import org.oscm.internal.types.enumtypes.OrganizationRoleType; import org.oscm.internal.types.enumtypes.ParameterModificationType; import org.oscm.internal.types.enumtypes.ParameterType; import org.oscm.internal.types.enumtypes.PerformanceHint; import org.oscm.internal.types.enumtypes.ServiceAccessType; import org.oscm.internal.types.enumtypes.ServiceStatus; import org.oscm.internal.types.enumtypes.ServiceType; import org.oscm.internal.types.enumtypes.SubscriptionStatus; import org.oscm.internal.types.enumtypes.TriggerType; import org.oscm.internal.types.enumtypes.UserRoleType; import org.oscm.internal.types.exception.ConcurrentModificationException; import org.oscm.internal.types.exception.DomainObjectException; import org.oscm.internal.types.exception.IllegalArgumentException; import org.oscm.internal.types.exception.InvalidPhraseException; import org.oscm.internal.types.exception.MailOperationException; import org.oscm.internal.types.exception.MandatoryCustomerUdaMissingException; import org.oscm.internal.types.exception.MandatoryUdaMissingException; import org.oscm.internal.types.exception.NonUniqueBusinessKeyException; import org.oscm.internal.types.exception.ObjectNotFoundException; import org.oscm.internal.types.exception.OperationNotPermittedException; import org.oscm.internal.types.exception.OperationPendingException; import org.oscm.internal.types.exception.OperationPendingException.ReasonEnum; import org.oscm.internal.types.exception.OperationStateException; import org.oscm.internal.types.exception.OrganizationAuthoritiesException; import org.oscm.internal.types.exception.PaymentDataException; import org.oscm.internal.types.exception.PaymentInformationException; import org.oscm.internal.types.exception.PriceModelException; import org.oscm.internal.types.exception.SaaSApplicationException; import org.oscm.internal.types.exception.SaaSSystemException; import org.oscm.internal.types.exception.ServiceChangedException; import org.oscm.internal.types.exception.ServiceParameterException; import org.oscm.internal.types.exception.SubscriptionAlreadyExistsException; import org.oscm.internal.types.exception.SubscriptionMigrationException; import org.oscm.internal.types.exception.SubscriptionMigrationException.Reason; import org.oscm.internal.types.exception.SubscriptionStateException; import org.oscm.internal.types.exception.SubscriptionStillActiveException; import org.oscm.internal.types.exception.TechnicalServiceNotAliveException; import org.oscm.internal.types.exception.TechnicalServiceOperationException; import org.oscm.internal.types.exception.ValidationException; import org.oscm.internal.vo.VOBillingContact; import org.oscm.internal.vo.VOInstanceInfo; import org.oscm.internal.vo.VOLocalizedText; import org.oscm.internal.vo.VOOrganization; import org.oscm.internal.vo.VOParameter; import org.oscm.internal.vo.VOPaymentInfo; import org.oscm.internal.vo.VORoleDefinition; import org.oscm.internal.vo.VOService; import org.oscm.internal.vo.VOServiceOperationParameter; import org.oscm.internal.vo.VOServiceOperationParameterValues; import org.oscm.internal.vo.VOSubscription; import org.oscm.internal.vo.VOSubscriptionDetails; import org.oscm.internal.vo.VOSubscriptionIdAndOrganizations; import org.oscm.internal.vo.VOTechnicalServiceOperation; import org.oscm.internal.vo.VOUda; import org.oscm.internal.vo.VOUsageLicense; import org.oscm.internal.vo.VOUser; import org.oscm.internal.vo.VOUserSubscription; import org.oscm.logging.Log4jLogger; import org.oscm.logging.LoggerFactory; import org.oscm.notification.vo.VONotification; import org.oscm.notification.vo.VOProperty; import org.oscm.operation.data.OperationResult; import org.oscm.paginator.Pagination; import org.oscm.paginator.PaginationFullTextFilter; import org.oscm.permission.PermissionCheck; import org.oscm.provisioning.data.User; import org.oscm.serviceprovisioningservice.assembler.ProductAssembler; import org.oscm.serviceprovisioningservice.assembler.RoleAssembler; import org.oscm.serviceprovisioningservice.assembler.TechServiceOperationParameterAssembler; import org.oscm.sessionservice.local.SessionServiceLocal; import org.oscm.string.Strings; import org.oscm.subscriptionservice.assembler.SubscriptionAssembler; import org.oscm.subscriptionservice.auditlog.SubscriptionAuditLogCollector; import org.oscm.subscriptionservice.dao.BillingContactDao; import org.oscm.subscriptionservice.dao.MarketplaceDao; import org.oscm.subscriptionservice.dao.ModifiedEntityDao; import org.oscm.subscriptionservice.dao.OrganizationDao; import org.oscm.subscriptionservice.dao.ProductDao; import org.oscm.subscriptionservice.dao.SessionDao; import org.oscm.subscriptionservice.dao.SubscriptionDao; import org.oscm.subscriptionservice.dao.SubscriptionHistoryDao; import org.oscm.subscriptionservice.dao.UsageLicenseDao; import org.oscm.subscriptionservice.local.SubscriptionListServiceLocal; import org.oscm.subscriptionservice.local.SubscriptionServiceLocal; import org.oscm.taskhandling.local.TaskMessage; import org.oscm.taskhandling.local.TaskQueueServiceLocal; import org.oscm.taskhandling.operations.NotifyProvisioningServiceHandler; import org.oscm.taskhandling.operations.SendMailHandler; import org.oscm.taskhandling.payloads.NotifyProvisioningServicePayload; import org.oscm.taskhandling.payloads.SendMailPayload; import org.oscm.techproductoperation.bean.OperationRecordServiceLocalBean; import org.oscm.tenantprovisioningservice.bean.TenantProvisioningServiceBean; import org.oscm.tenantprovisioningservice.vo.TenantProvisioningResult; import org.oscm.triggerservice.bean.TriggerProcessIdentifiers; import org.oscm.triggerservice.local.TriggerMessage; import org.oscm.triggerservice.local.TriggerProcessMessageData; import org.oscm.triggerservice.local.TriggerQueueServiceLocal; import org.oscm.triggerservice.notification.VONotificationBuilder; import org.oscm.triggerservice.validator.TriggerProcessValidator; import org.oscm.types.constants.Configuration; import org.oscm.types.enumtypes.EmailType; import org.oscm.types.enumtypes.LogMessageIdentifier; import org.oscm.types.enumtypes.PlatformParameterIdentifiers; import org.oscm.types.enumtypes.ProvisioningType; import org.oscm.types.enumtypes.TriggerProcessIdentifierName; import org.oscm.types.enumtypes.TriggerProcessParameterName; import org.oscm.types.enumtypes.UdaTargetType; import org.oscm.types.exceptions.UserAlreadyAssignedException; import org.oscm.types.exceptions.UserNotAssignedException; import org.oscm.usergroupservice.bean.UserGroupServiceLocalBean; import org.oscm.validation.ArgumentValidator; import org.oscm.validation.PaymentDataValidator; import org.oscm.validator.ADMValidator; import org.oscm.validator.BLValidator; import org.oscm.vo.BaseAssembler; /** * Session Bean implementation class of SubscriptionService (Remote IF) and * SubscriptionServiceLocal (Local IF) */ @Stateless @Remote(SubscriptionService.class) @Local(SubscriptionServiceLocal.class) @Interceptors({ InvocationDateContainer.class, ExceptionMapper.class, AuditLogDataInterceptor.class }) public class SubscriptionServiceBean implements SubscriptionService, SubscriptionServiceLocal { public static final String KEY_PAIR_NAME = "Key pair name"; public static final String AMAZONAWS_COM = "amazonaws.com"; private static final int PAYMENTTYPE_INVOICE = 3; private static final Log4jLogger LOG = LoggerFactory.getLogger(SubscriptionServiceBean.class); @EJB(beanInterface = ApplicationServiceLocal.class) protected ApplicationServiceLocal appManager; @EJB(beanInterface = SessionServiceLocal.class) protected SessionServiceLocal prodSessionMgmt; @EJB protected DataService dataManager; @EJB protected IdentityServiceLocal idManager; @EJB protected TenantProvisioningServiceBean tenantProvisioning; @EJB protected CommunicationServiceLocal commService; @EJB(beanInterface = LocalizerServiceLocal.class) protected LocalizerServiceLocal localizer; @EJB(beanInterface = ConfigurationServiceLocal.class) ConfigurationServiceLocal cfgService; @EJB(beanInterface = SubscriptionListServiceLocal.class) SubscriptionListServiceLocal subscriptionListService; @EJB(beanInterface = TriggerQueueServiceLocal.class) protected TriggerQueueServiceLocal triggerQS; @EJB(beanInterface = TaskQueueServiceLocal.class) public TaskQueueServiceLocal tqs; @EJB(beanInterface = AccountServiceLocal.class) public AccountServiceLocal accountService; @EJB public SubscriptionSearchService subscriptionSearchService; @EJB SubscriptionAuditLogCollector audit; @Resource protected SessionContext sessionCtx; @Inject public TerminateSubscriptionBean terminateBean; @Inject public ManageSubscriptionBean manageBean; @Inject public ValidateSubscriptionStateBean stateValidator; @Inject public ModifyAndUpgradeSubscriptionBean modUpgBean; @Inject public OperationRecordServiceLocalBean operationRecordBean; @Inject UserGroupServiceLocalBean userGroupService; private static final List<SubscriptionStatus> VISIBLE_SUBSCRIPTION_STATUS = Arrays.asList( SubscriptionStatus.ACTIVE, SubscriptionStatus.EXPIRED, SubscriptionStatus.PENDING, SubscriptionStatus.PENDING_UPD, SubscriptionStatus.SUSPENDED, SubscriptionStatus.SUSPENDED_UPD); @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscription subscribeToService(VOSubscription subscription, VOService service, List<VOUsageLicense> users, VOPaymentInfo paymentInfo, VOBillingContact billingContact, List<VOUda> udas) throws ObjectNotFoundException, NonUniqueBusinessKeyException, ValidationException, PaymentInformationException, ServiceParameterException, ServiceChangedException, PriceModelException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationNotPermittedException, SubscriptionAlreadyExistsException, OperationPendingException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException, ConcurrentModificationException, SubscriptionStateException { ArgumentValidator.notNull("subscription", subscription); ArgumentValidator.notNull("service", service); Subscription sub; PlatformUser currentUser = dataManager.getCurrentUser(); Product prod = dataManager.getReference(Product.class, service.getKey()); checkIfServiceAvailable(service.getKey(), service.getServiceId(), currentUser); checkIfSubscriptionAlreadyExists(prod); verifyIdAndKeyUniqueness(currentUser, subscription); if (isPaymentInfoHidden() && prod.getPriceModel().isChargeable()) { if (billingContact == null) { billingContact = createBillingContactForOrganization(currentUser); } if (paymentInfo == null) { Organization organization = currentUser.getOrganization(); paymentInfo = createPaymentInfoForOrganization(organization); } } validateSettingsForSubscribing(subscription, service, paymentInfo, billingContact); validateUserAssignmentForSubscribing(service, users); validateTriggerProcessForCreateSubscription(subscription); TriggerProcess triggerProcess = createTriggerProcessForCreateSubscription(subscription, service, users, paymentInfo, billingContact, udas); VOSubscription voSub = null; TriggerDefinition triggerDefinition = triggerProcess.getTriggerDefinition(); if (triggerDefinition == null) { try { sub = subscribeToServiceInt(triggerProcess); voSub = SubscriptionAssembler.toVOSubscription(sub, new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale())); autoAssignUser(prod, sub); } catch (ObjectNotFoundException | ValidationException | ServiceChangedException | PriceModelException | PaymentInformationException | NonUniqueBusinessKeyException | TechnicalServiceNotAliveException | TechnicalServiceOperationException | ServiceParameterException | ConcurrentModificationException | MandatoryUdaMissingException | SubscriptionStateException | MandatoryCustomerUdaMissingException e) { sessionCtx.setRollbackOnly(); throw e; } } else if (triggerDefinition.isSuspendProcess()) { triggerProcess.setTriggerProcessIdentifiers(TriggerProcessIdentifiers.createUnsubscribeFromService( dataManager, TriggerType.SUBSCRIBE_TO_SERVICE, subscription.getSubscriptionId())); dataManager.merge(triggerProcess); } return voSub; } @Override public boolean isPaymentInfoHidden() { return !cfgService.isPaymentInfoAvailable(); } private VOBillingContact createBillingContactForOrganization(PlatformUser user) throws NonUniqueBusinessKeyException { Organization organization = user.getOrganization(); BillingContact orgBillingContact = new BillingContact(); String email = organization.getEmail() == null ? " " : organization.getEmail(); String address = organization.getAddress() == null ? " " : organization.getAddress(); List<BillingContact> billingContacts = getBillingContactDao() .getBillingContactsForOrganization(organization.getKey(), email, address); if (!billingContacts.isEmpty()) { orgBillingContact = billingContacts.get(0); } else { orgBillingContact.setAddress(address); orgBillingContact.setCompanyName(organization.getName()); orgBillingContact.setOrganization_tkey(organization.getKey()); orgBillingContact.setOrgAddressUsed(true); orgBillingContact.setEmail(email); String organizationId = organization.getName() == null ? user.getUserId() : organization.getName(); orgBillingContact.setBillingContactId(organizationId + DateFactory.getInstance().getTransactionTime()); orgBillingContact.setOrganization(organization); dataManager.persist(orgBillingContact); } return BillingContactAssembler.toVOBillingContact(orgBillingContact); } private VOPaymentInfo createPaymentInfoForOrganization(Organization organization) throws NonUniqueBusinessKeyException { PaymentInfo paInfo = new PaymentInfo(DateFactory.getInstance().getTransactionTime()); paInfo.setOrganization_tkey(organization.getKey()); PaymentType paymentType = new PaymentType(); paymentType.setPaymentTypeId(PaymentType.INVOICE); paymentType = (PaymentType) dataManager.find(paymentType); paInfo.setOrganization(organization); paInfo.setPaymentType(paymentType); LocalizerFacade localizerFacade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); paInfo.setPaymentInfoId( localizerFacade.getText(PAYMENTTYPE_INVOICE, LocalizedObjectTypes.PAYMENT_TYPE_NAME)); try { paInfo = (PaymentInfo) dataManager.getReferenceByBusinessKey(paInfo); } catch (ObjectNotFoundException onfe) { dataManager.persist(paInfo); } return PaymentInfoAssembler.toVOPaymentInfo(paInfo, localizerFacade); } private void autoAssignUser(Product prod, Subscription sub) throws ObjectNotFoundException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationNotPermittedException, ConcurrentModificationException { TechnicalProduct techProd = prod.getTechnicalProduct(); if (ProvisioningType.SYNCHRONOUS.equals(techProd.getProvisioningType()) && prod.isAutoAssignUserEnabled() != null && prod.isAutoAssignUserEnabled().booleanValue()) { VOService svc = new VOService(); svc.setKey(prod.getKey()); assignUsersForSubscription(sub.getSubscriptionId(), svc); } } void assignUsersForSubscription(String subscriptionId, VOService service) throws ObjectNotFoundException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationNotPermittedException, ConcurrentModificationException { PlatformUser currentUser = dataManager.getCurrentUser(); assignUsersForSubscription(subscriptionId, service, currentUser); } void assignUsersForSubscription(String subscriptionId, VOService service, PlatformUser userToAssign) throws ObjectNotFoundException, OperationNotPermittedException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, ConcurrentModificationException { List<VORoleDefinition> serviceRoles = getServiceRolesForService(service); // If the service roles are defined for technical service, // assign the first role to user to avoid assignment failure. // If service roles are not defined, set default role to null. List<VOUsageLicense> usersToBeAdded = new ArrayList<>(); VOUsageLicense lic = new VOUsageLicense(); lic.setUser(UserDataAssembler.toVOUserDetails(userToAssign)); lic.setRoleDefinition((serviceRoles == null || serviceRoles.isEmpty()) ? null : serviceRoles.get(0)); usersToBeAdded.add(lic); TriggerProcess proc = new TriggerProcess(); proc.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscriptionId); proc.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscriptionId); proc.addTriggerProcessParameter(TriggerProcessParameterName.USERS_TO_ADD, usersToBeAdded); proc.addTriggerProcessParameter(TriggerProcessParameterName.USERS_TO_REVOKE, null); addRevokeUserInt(proc); } /** * @param subscription * @return * @throws OperationPendingException */ private void validateTriggerProcessForCreateSubscription(VOSubscription subscription) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); String subscriptionId = subscription.getSubscriptionId(); if (validator.isSubscribeOrUnsubscribeServicePending(subscriptionId)) { OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to create a subscription or unsubscribe from the subscription with identifier '%s'", subscriptionId), ReasonEnum.SUBSCRIBE_TO_SERVICE, new Object[] { subscriptionId }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_SUBSCRIBE_TO_SERVICE_FAILED_DUE_TO_TRIGGER_CONFLICT, subscriptionId); throw ope; } } private TriggerProcess createTriggerProcessForCreateSubscription(VOSubscription subscription, VOService service, List<VOUsageLicense> users, VOPaymentInfo paymentInfo, VOBillingContact billingContact, List<VOUda> udas) { TriggerMessage message = new TriggerMessage(TriggerType.SUBSCRIBE_TO_SERVICE); List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message)); TriggerProcess triggerProcess = list.get(0).getTrigger(); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscription.getSubscriptionId()); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscription); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.PRODUCT, service); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.USERS, users); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.PAYMENTINFO, paymentInfo); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.BILLING_CONTACT, billingContact); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.UDAS, udas); return triggerProcess; } void saveUdasForSubscription(List<VOUda> udas, Subscription subscription) throws ObjectNotFoundException, NonUniqueBusinessKeyException, ValidationException, OperationNotPermittedException, ConcurrentModificationException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException { Organization supplier = subscription.getProduct().getSupplierOrResellerTemplate().getVendor(); UdaAccess udaAccess = new UdaAccess(dataManager, sessionCtx); udaAccess.saveUdasForSubscription(udas, supplier, subscription); } /** * Checks if the technical product the marketing service belongs to has * service roles defined and if the roles defined for the user assignments * are part of this set and defined. * * @param product * the product to subscribe to * @param users * the users to assign * @throws ObjectNotFoundException * if the product or a role wasn't found * @throws OperationNotPermittedException * if the role to assign doesn't belong to the products * technical product or if no role is set when using service * roles. */ private void validateUserAssignmentForSubscribing(VOService product, List<VOUsageLicense> users) throws ObjectNotFoundException, OperationNotPermittedException { Product prod = dataManager.getReference(Product.class, product.getKey()); if (users == null) { return; } for (VOUsageLicense lic : users) { getAndCheckServiceRole(lic, prod); } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public Subscription subscribeToServiceInt(TriggerProcess tp) throws ObjectNotFoundException, ValidationException, OperationNotPermittedException, ServiceChangedException, PriceModelException, PaymentInformationException, NonUniqueBusinessKeyException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, ServiceParameterException, SubscriptionAlreadyExistsException, ConcurrentModificationException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException, SubscriptionStateException { PlatformUser currentUser = dataManager.getCurrentUser(); Organization organization = currentUser.getOrganization(); // read parameters from trigger process VOSubscription subscription = tp.getParamValueForName(TriggerProcessParameterName.SUBSCRIPTION) .getValue(VOSubscription.class); VOService product = tp.getParamValueForName(TriggerProcessParameterName.PRODUCT).getValue(VOService.class); VOPaymentInfo voPaymentInfo = tp.getParamValueForName(TriggerProcessParameterName.PAYMENTINFO) .getValue(VOPaymentInfo.class); VOBillingContact voBillingContact = tp.getParamValueForName(TriggerProcessParameterName.BILLING_CONTACT) .getValue(VOBillingContact.class); List<?> udas = tp.getParamValueForName(TriggerProcessParameterName.UDAS).getValue(List.class); PlatformUser owner = dataManager.getReference(PlatformUser.class, tp.getUser().getKey()); Product productTemplate = dataManager.getReference(Product.class, product.getKey()); checkIfSubscriptionAlreadyExists(productTemplate); UserGroup unit = getUnit(subscription.getUnitKey(), subscription.getUnitName(), organization.getKey()); validateSettingsForSubscribing(subscription, product, voPaymentInfo, voBillingContact); Organization vendor = productTemplate.getVendor(); Organization supplier = dataManager.getReference(Product.class, product.getKey()) .getSupplierOrResellerTemplate().getVendor(); List<VOUda> originalCustomerUdas = getUdasForCustomer("CUSTOMER", dataManager.getCurrentUser().getOrganization().getKey(), supplier); OrganizationReference refVendorCust = null; if (!organization.getVendorsOfCustomer().contains(vendor)) { refVendorCust = new OrganizationReference(vendor, organization, OrganizationReferenceType.getOrgRefTypeForSourceRoles(vendor.getGrantedRoleTypes())); dataManager.persist(refVendorCust); } if (vendor.getGrantedRoleTypes().contains(OrganizationRoleType.BROKER) && !organization.getVendorsOfCustomer().contains(productTemplate.getTemplate().getVendor())) { refVendorCust = new OrganizationReference(productTemplate.getTemplate().getVendor(), organization, OrganizationReferenceType.SUPPLIER_TO_CUSTOMER); dataManager.persist(refVendorCust); } if (refVendorCust != null) { enableDefaultPaymentsForCustomer(refVendorCust); } // Look for the marketplace where the service is published Marketplace mp = null; Product publishedService = productTemplate.getType() == ServiceType.CUSTOMER_TEMPLATE ? productTemplate.getTemplate() : productTemplate; List<Marketplace> mps = getMarketplaceDao().getMarketplaceByService(publishedService); for (Marketplace m : mps) { mp = m; // current assumption is that there's only one marketplace break; } // Create a new subscription object Subscription newSub = new Subscription(); Long creationTime = Long.valueOf(DateFactory.getInstance().getTransactionTime()); newSub.setCreationDate(creationTime); newSub.setStatus(SubscriptionStatus.PENDING); // set default cut-off day (db unique constrain) newSub.setCutOffDay(1); newSub.setSubscriptionId(subscription.getSubscriptionId().trim()); newSub.setPurchaseOrderNumber(subscription.getPurchaseOrderNumber()); newSub.setOrganization(organization); // for subscribing service, set the current user as subscription owner newSub.setOwner(owner); verifyUnitAndRoles(currentUser, unit, newSub); Product theProduct = productTemplate.copyForSubscription(productTemplate.getTargetCustomer(), newSub); if (theProduct.getPriceModel() != null) { // FIXME LG clean // The first target pricemodel version is created when subscription // is still in PENDING, but must be fitered for billing. Set the // indicating flag before persisting. theProduct.getPriceModel().setProvisioningCompleted(false); if (theProduct.getPriceModel().isExternal()) { newSub.setExternal(true); } } // to avoid id conflicts in high load scenarios add customer // organization hash theProduct.setProductId(theProduct.getProductId() + organization.hashCode()); theProduct.setOwningSubscription(null); // subscription copies do not have/need a CatalogEntry theProduct.setCatalogEntries(new ArrayList<CatalogEntry>()); try { dataManager.persist(theProduct); } catch (NonUniqueBusinessKeyException e) { SaaSSystemException sse = new SaaSSystemException("The product copy for product '" + product.getKey() + "' cannot be stored, as the business key already exists.", e); LOG.logError(Log4jLogger.SYSTEM_LOG, sse, LogMessageIdentifier.ERROR_CREATE_CUSTOMER_FOR_SPECIFIC_PRICEMODEL_FAILED, Long.toString(dataManager.getCurrentUser().getKey())); throw sse; } copyLocalizedPricemodelValues(theProduct, productTemplate); // update the subscription's configurable parameter List<Parameter> modifiedParametersForLog = updateConfiguredParameterValues(theProduct, product.getParameters(), null); // now bind the product and the price model to the subscription: newSub.bindToProduct(theProduct); // register the marketplace the subscription was coming from newSub.setMarketplace(mp); // Link the passed payment information to this subscription if (voPaymentInfo != null) { PaymentInfo paymentInfo = dataManager.getReference(PaymentInfo.class, voPaymentInfo.getKey()); newSub.setPaymentInfo(paymentInfo); } if (voBillingContact != null) { BillingContact bc = dataManager.getReference(BillingContact.class, voBillingContact.getKey()); newSub.setBillingContact(bc); } // persist the subscription. This is essential to ensure the // subscription exists and also eliminates all potential problems // with a subsequent call to the application. dataManager.persist(newSub); // save subscription attributes before provisioning call, to have them // available at the API saveUdasForSubscription(ParameterizedTypes.list(udas, VOUda.class), newSub); dataManager.flush(); // send customer udas to corresponding app appManager.saveAttributes(newSub); theProduct.setOwningSubscription(newSub); createAllowOnBehalfActingReference(newSub); TenantProvisioningResult provisioningResult = createInstanceAndAddUsersToSubscription(tp, newSub); newSub.setSuccessMessage(provisioningResult.getResultMesage()); dataManager.flush(); triggerQS.sendAllNonSuspendingMessages( TriggerMessage.create(TriggerType.SUBSCRIPTION_CREATION, tp.getTriggerProcessParameters(), vendor)); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.SUBSCRIBE_TO_SERVICE, tp.getTriggerProcessParameters(), dataManager.getCurrentUser().getOrganization())); sendSubscriptionCreatedMailToAdministrators(newSub, newSub.getProduct().getTechnicalProduct().isAllowingOnBehalfActing()); // Used for Autoassign a service which was supended via trigger // (subscribeToService) autoAssignUserForTriggerProcess(tp, product, owner, newSub); audit.editSubscriptionParameterConfiguration(dataManager, theProduct, modifiedParametersForLog); audit.subscribeToService(dataManager, newSub); logSubscriptionAttributeForCreation(newSub, ParameterizedTypes.list(udas, VOUda.class), originalCustomerUdas); return newSub; } private void verifyUnitAndRoles(PlatformUser currentUser, UserGroup unit, Subscription newSub) throws OperationNotPermittedException { if (!currentUser.isOrganizationAdmin()) { boolean isUnitAdmin = currentUser.isUnitAdmin(); boolean isSubMgr = currentUser.isSubscriptionManager(); boolean isUnitToBeAssigned = unit != null; boolean unitIsMandatory = isUnitAdmin && !isSubMgr; boolean unitIsForbidden = !isUnitAdmin && isSubMgr; if (isUnitToBeAssigned && unitIsForbidden) { throw new OperationNotPermittedException(); } else if (unitIsMandatory && !isUnitToBeAssigned) { throw new OperationNotPermittedException(); } } if (currentUser.isOrganizationAdmin() || currentUser.isUnitAdmin()) { newSub.setUserGroup(unit); } } private void autoAssignUserForTriggerProcess(TriggerProcess tp, VOService product, PlatformUser owner, Subscription newSub) throws ObjectNotFoundException, OperationNotPermittedException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, ConcurrentModificationException { Product prod = dataManager.getReference(Product.class, product.getKey()); TechnicalProduct techProd = prod.getTechnicalProduct(); if (ProvisioningType.SYNCHRONOUS.equals(techProd.getProvisioningType()) && tp.getTriggerDefinition() != null && (prod.isAutoAssignUserEnabled() != null && prod.isAutoAssignUserEnabled().booleanValue()) && newSub.getUsageLicenseForUser(owner) == null) { // TODO 1. assign users only for SYNCHRONOUS case. // 2. extract code to another method (more readability). if (owner != dataManager.getCurrentUser()) { assignUsersForSubscription(newSub.getSubscriptionId(), product, owner); } else { assignUsersForSubscription(newSub.getSubscriptionId(), product); } } } List<VOUda> getUdasForCustomer(String targetType, long targetObjectKey, Organization supplier) throws ValidationException, ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("targetType", targetType); UdaTargetType type = UdaAssembler.toUdaTargetType(targetType); Organization customer = dataManager.getCurrentUser().getOrganization(); UdaAccess udaAccess = new UdaAccess(dataManager, sessionCtx); List<Uda> udas = udaAccess.getUdasForTypeTargetAndCustomer(targetObjectKey, type, supplier, customer); List<VOUda> voUdas = new ArrayList<>(); for (Uda uda : udas) { voUdas.add(UdaAssembler.toVOUda(uda, new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()))); } return voUdas; } void logSubscriptionAttributeForEdit(Subscription sub, List<VOUda> udaList) { Organization customer = dataManager.getCurrentUser().getOrganization(); for (VOUda voUda : udaList) { audit.editSubscriptionAndCustomerAttributeByCustomer(dataManager, customer, sub, voUda.getUdaDefinition().getUdaId(), voUda.getUdaValue(), voUda.getUdaDefinition().getTargetType()); } } void logSubscriptionAttributeForCreation(Subscription sub, List<VOUda> udaList, List<VOUda> customerUdaList) { Map<String, String> customerAttributesMap = new HashMap<>(); for (VOUda voUda : customerUdaList) { String parameterName = voUda.getUdaDefinition().getUdaId(); String parameterValue = voUda.getUdaValue(); customerAttributesMap.put(parameterName, parameterValue); } for (VOUda voUda : udaList) { String parameterName = voUda.getUdaDefinition().getUdaId(); String targetType = voUda.getUdaDefinition().getTargetType(); String parameterValue = voUda.getUdaValue(); String defaultValue = voUda.getUdaDefinition().getDefaultValue(); defaultValue = defaultValue == null ? "" : defaultValue; if (!UdaTargetType.CUSTOMER.toString().equals(targetType)) { if (!parameterValue.equals(defaultValue)) { audit.editSubscriptionAndCustomerAttributeByCustomer(dataManager, null, sub, parameterName, parameterValue, voUda.getUdaDefinition().getTargetType()); } } else { String existingValue = customerAttributesMap.get(voUda.getUdaDefinition().getUdaId()); existingValue = existingValue == null ? "" : existingValue; Organization customer = dataManager.getCurrentUser().getOrganization(); if (!(parameterValue.equals(existingValue) || parameterValue.equals(defaultValue))) { audit.editSubscriptionAndCustomerAttributeByCustomer(dataManager, customer, null, parameterName, parameterValue, voUda.getUdaDefinition().getTargetType()); } } } } void logSubscriptionOwner(Subscription sub, PlatformUser oldOwner) { audit.editSubscriptionOwner(dataManager, sub, oldOwner); } /** * Get the subscription attributes that are changed by user */ List<VOUda> getUpdatedSubscriptionAttributes(List<VOUda> inputUdaList, List<Uda> existingUdas) { Map<String, String> existingAttributesMap = new HashMap<>(); List<VOUda> updatedList = new ArrayList<>(); for (Uda uda : existingUdas) { existingAttributesMap.put(uda.getUdaDefinition().getUdaId(), uda.getUdaValue()); } for (VOUda input : inputUdaList) { String existingValue = existingAttributesMap.get(input.getUdaDefinition().getUdaId()); String defaultValue = input.getUdaDefinition().getDefaultValue() == null ? "" : input.getUdaDefinition().getDefaultValue(); String inputValue = input.getUdaValue() == null ? "" : input.getUdaValue(); if (existingValue == null && !inputValue.equals(defaultValue)) { updatedList.add(input); } if (existingValue != null && !inputValue.equals(existingValue)) { updatedList.add(input); } } return updatedList; } /** * Enables the payment types that are enabled for new customer for the * provided {@link OrganizationReference}. * * @param refSuplCust * the reference to enable the default payment types for */ private void enableDefaultPaymentsForCustomer(OrganizationReference refSuplCust) { Organization supplier = refSuplCust.getSource(); Organization customer = refSuplCust.getTarget(); Set<OrganizationToRole> roles = customer.getGrantedRoles(); OrganizationRole role = null; for (OrganizationToRole organizationToRole : roles) { OrganizationRole tmp = organizationToRole.getOrganizationRole(); if (tmp.getRoleName() == OrganizationRoleType.CUSTOMER) { role = tmp; break; } } // the suppliers default configuration List<OrganizationRefToPaymentType> refs = supplier.getPaymentTypes(true, refSuplCust.getReferenceType() == OrganizationReferenceType.RESELLER_TO_CUSTOMER ? OrganizationRoleType.RESELLER : OrganizationRoleType.SUPPLIER, OrganizationRoleType.PLATFORM_OPERATOR.name()); for (OrganizationRefToPaymentType ref : refs) { OrganizationRefToPaymentType newRef = new OrganizationRefToPaymentType(); newRef.setOrganizationReference(refSuplCust); newRef.setPaymentType(ref.getPaymentType()); newRef.setOrganizationRole(role); try { dataManager.persist(newRef); } catch (NonUniqueBusinessKeyException e) { SaaSSystemException sse = new SaaSSystemException( "Caught NonUniqueBusinessKeyException although there is no business key", e); LOG.logError(Log4jLogger.SYSTEM_LOG, sse, LogMessageIdentifier.ERROR_UNEXPECTED_BK_VIOLATION); throw sse; } refSuplCust.getPaymentTypes().add(newRef); } } /** * Add users to the subscription assign the users to the subscription, but * do not inform the product about it at this time (will be done later) We * assume that the users already exist as platform users ! We start with the * admins: if a user is in both lists, we just can ignore his entry in * "users", as he is already entered from "admins" and has the correct * isAdmin-flag ! We have to ensure that at least 1 user is assigned * * @throws ServiceParameterException * @throws OperationNotPermittedException * @throws ObjectNotFoundException * @throws TechnicalServiceOperationException * @throws TechnicalServiceNotAliveException */ private TenantProvisioningResult createInstanceAndAddUsersToSubscription(TriggerProcess tp, Subscription subscription) throws ServiceParameterException, ObjectNotFoundException, OperationNotPermittedException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { List<PlatformUser> addedUsers = new ArrayList<>(); List<UsageLicense> addedUserLicenses = new ArrayList<>(); List<?> users = tp.getParamValueForName(TriggerProcessParameterName.USERS).getValue(List.class); if (users != null) { for (Object o : users) { VOUsageLicense lic = VOUsageLicense.class.cast(o); PlatformUser usr = idManager.getPlatformUser(lic.getUser().getUserId(), dataManager.getCurrentUser().getTenantId(), true); // not // found? // => // throws // ObjectNotFoundException RoleDefinition role = getAndCheckServiceRole(lic, subscription.getProduct()); try { addUserToSubscription(subscription, usr, role); addedUsers.add(usr); UsageLicense usageLicenseForUser = subscription.getUsageLicenseForUser(usr); // add the user's license to the list for later mail // sending if (usageLicenseForUser != null) { addedUserLicenses.add(usageLicenseForUser); } } catch (UserAlreadyAssignedException e) { // Most probably the user already has been in the users // list (or he/she is in the list twice) // Let's ignore it! // But log this event ! LOG.logWarn(Log4jLogger.SYSTEM_LOG, LogMessageIdentifier.WARN_USER_APPEAR_MORE_THAN_ONCE, Long.toString(usr.getKey()), Long.toString(subscription.getKey())); } } } boolean directLogin = subscription.getProduct().getTechnicalProduct() .getAccessType() == ServiceAccessType.DIRECT; if (!directLogin) { verifyParameterNamedUser(subscription); } // Call tenant provisioning to create instance! Afterwards the // instance is started as well TenantProvisioningResult provisioningResult = tenantProvisioning.createProductInstance(subscription); if (provisioningResult.isAsyncProvisioning()) { return provisioningResult; } PriceModel pm = subscription.getProduct().getPriceModel(); pm.setProvisioningCompleted(true); subscription.setActivationDate(subscription.getCreationDate()); activateSubscriptionFirstTime(subscription); String instanceId = provisioningResult.getProductInstanceId(); subscription.setProductInstanceId(instanceId); subscription.setAccessInfo(provisioningResult.getAccessInfo()); subscription.setBaseURL(provisioningResult.getBaseUrl()); subscription.setLoginPath(provisioningResult.getLoginPath()); // inform the product about the users try { informProductAboutNewUsers(subscription, addedUsers); } catch (SubscriptionStateException e) { // should never be reached because state is set to // active LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_INFORM_PRODUCT_ABOUT_NEW_USER_FAILED); } // Send an email to the organization admin. Is performed // after the save operation to ensure everything worked fine and // no mail is sent if storing fails. sendToSubscriptionAddedMail(subscription, addedUserLicenses); return provisioningResult; } /** * Send the subscription created email to the users assigned to the * subscription. The mail also includes the access information. * * @param subscription * the created subscription to get the id and access info (url or * descriptive text) from * @param usageLicenses * the usage licenses of the users added to the subscription */ private void sendToSubscriptionAddedMail(Subscription subscription, List<UsageLicense> usageLicenses) { if (subscription.getStatus() != SubscriptionStatus.ACTIVE) { return; } EmailType emailType = useAccessInfo(subscription) ? EmailType.SUBSCRIPTION_USER_ADDED_ACCESSTYPE_DIRECT : EmailType.SUBSCRIPTION_USER_ADDED; Long marketplaceKey = null; if (subscription.getMarketplace() != null) { marketplaceKey = Long.valueOf(subscription.getMarketplace().getKey()); } SendMailPayload payload = new SendMailPayload(); for (UsageLicense usageLicense : usageLicenses) { String accessInfo = getAccessInfo(subscription, usageLicense.getUser()); if (isUsableAWSAccessInfo(accessInfo)) { payload.addMailObjectForUser(usageLicense.getUser().getKey(), EmailType.SUBSCRIPTION_USER_ADDED_ACCESSINFO, new Object[] { subscription.getSubscriptionId(), getPublicDNS(accessInfo), getIPAddress(accessInfo), getKeyPairName(accessInfo) }, marketplaceKey); } else { payload.addMailObjectForUser(usageLicense.getUser().getKey(), emailType, new Object[] { subscription.getSubscriptionId(), accessInfo }, marketplaceKey); } } TaskMessage message = new TaskMessage(SendMailHandler.class, payload); tqs.sendAllMessages(Collections.singletonList(message)); } private boolean useAccessInfo(Subscription subscription) { ServiceAccessType accessType = subscription.getProduct().getTechnicalProduct().getAccessType(); return accessType == ServiceAccessType.DIRECT || accessType == ServiceAccessType.USER; } String getAccessInfo(Subscription subscription, PlatformUser user) { String accessInfo; if (useAccessInfo(subscription)) { accessInfo = subscription.getAccessInfo(); if (accessInfo == null) { accessInfo = localizer.getLocalizedTextFromDatabase(user.getLocale(), subscription.getProduct().getTechnicalProduct().getKey(), LocalizedObjectTypes.TEC_PRODUCT_LOGIN_ACCESS_DESC); if (accessInfo != null) { accessInfo = accessInfo.replaceAll("<p>", "\n"); accessInfo = accessInfo.replaceAll("</p>", "\n"); } } } else { accessInfo = getSubscriptionUrl(subscription); } if (accessInfo == null) { accessInfo = ""; } return accessInfo; } /** * Gets the URL to access the subscription. * * @param subscription * the subscription for which we want to know the URL * @return the URL to access the subscription */ String getSubscriptionUrl(Subscription subscription) { StringBuilder url = new StringBuilder(); String baseUrl = cfgService.getBaseURL(); String technicalProductBaseUrl = subscription.getProduct().getTechnicalProduct().getBaseURL(); if (ADMValidator.isHttpsScheme(technicalProductBaseUrl)) { baseUrl = cfgService .getConfigurationSetting(ConfigurationKey.BASE_URL_HTTPS, Configuration.GLOBAL_CONTEXT) .getValue(); } url.append(baseUrl); if (url.length() == 0 || url.charAt(url.length() - 1) != '/') { url.append('/'); } url.append("opt/"); url.append(Long.toHexString(subscription.getKey())); url.append('/'); return url.toString(); } /** * Creates the 'allowing on behalf acting reference' between the technology * provider and the customer's organization. * * @throws NonUniqueBusinessKeyException */ private void createAllowOnBehalfActingReference(Subscription subscription) throws NonUniqueBusinessKeyException { // fetch the technical product TechnicalProduct technicalProduct = subscription.getProduct().getTechnicalProduct(); // if allow on behalf acting is true if (technicalProduct.isAllowingOnBehalfActing()) { // get source (techn. prov.) and target (supplier) organization Organization source = technicalProduct.getOrganization(); Organization target = dataManager.getCurrentUser().getOrganization(); // check if a reference does not already exist if (!isOnBehalfReferenceExisting(source, target)) { // create and persist reference OrganizationReference reference = new OrganizationReference(source, target, OrganizationReferenceType.ON_BEHALF_ACTING); dataManager.persist(reference); } } } private boolean isOnBehalfReferenceExisting(Organization sourceOrganization, Organization targetOrganization) { return getOrganizationReference(sourceOrganization, targetOrganization) != null; } private OrganizationReference getOrganizationReference(Organization sourceOrganization, Organization targetOrganization) { for (OrganizationReference reference : targetOrganization .getSourcesForType(OrganizationReferenceType.ON_BEHALF_ACTING)) { if (reference.getSource().getKey() == sourceOrganization.getKey() && reference.getTargetKey() == targetOrganization.getKey()) { return reference; } } return null; } private void sendSubscriptionCreatedMailToAdministrators(Subscription subscription, boolean actingOnBehalf) { EmailType emailType = actingOnBehalf ? EmailType.SUBSCRIPTION_CREATED_ON_BEHALF_ACTING : EmailType.SUBSCRIPTION_CREATED; Long marketplaceKey = null; if (subscription.getMarketplace() != null) { marketplaceKey = Long.valueOf(subscription.getMarketplace().getKey()); } SendMailPayload payload = new SendMailPayload(); List<PlatformUser> users = manageBean.getCustomerAdminsAndSubscriptionOwner(subscription); for (PlatformUser user : users) { payload.addMailObjectForUser(user.getKey(), emailType, new Object[] { subscription.getSubscriptionId() }, marketplaceKey); } TaskMessage message = new TaskMessage(SendMailHandler.class, payload); tqs.sendAllMessages(Collections.singletonList(message)); } /** * Validates that subscribing to the given product is possible with the * specified subscription data. * * @param subscription * The subscription to be created. * @param product * The product to subscribe to. * @param voBillingContact * @throws ValidationException * Thrown in case the validation of the subscription failed. * @throws ObjectNotFoundException * Thrown in case the product could not be found. * @throws OperationNotPermittedException * Thrown in case the user is not permitted to perform this * operation. * @throws ServiceChangedException * Thrown in case the product has been modified in the meantime. * @throws PriceModelException * Thrown in case the product is not useable as it has no price * model. * @throws PaymentInformationException * Thrown in case the product is chargeable but the customer * does not have a payment information stored. * @throws ConcurrentModificationException * @throws NonUniqueBusinessKeyException */ private void validateSettingsForSubscribing(VOSubscription subscription, VOService product, VOPaymentInfo paymentInfo, VOBillingContact voBillingContact) throws ValidationException, ObjectNotFoundException, OperationNotPermittedException, ServiceChangedException, PriceModelException, PaymentInformationException, ConcurrentModificationException, NonUniqueBusinessKeyException { String subscriptionId = subscription.getSubscriptionId(); BLValidator.isId("subscriptionId", subscriptionId, true); String pon = subscription.getPurchaseOrderNumber(); BLValidator.isDescription("purchaseOrderNumber", pon, false); Product productTemplate = dataManager.getReference(Product.class, product.getKey()); // check product and org settings Organization targetCustomer = productTemplate.getTargetCustomer(); PlatformUser currentUser = dataManager.getCurrentUser(); Organization organization = currentUser.getOrganization(); if (targetCustomer == null) { // if it is no customer specific product, check if we have one // for the subscriber List<Product> resultList = getProductDao().getCopyForCustomer(productTemplate, organization); if (resultList.size() > 0) { ServiceChangedException sce = new ServiceChangedException( ServiceChangedException.Reason.SERVICE_MODIFIED); LOG.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, sce, LogMessageIdentifier.WARN_CUSTOMER_MUST_SUBSCRIBE_SPECIFIC_PRODUCT, organization.getOrganizationId(), productTemplate.getProductId(), resultList.get(0).getProductId()); throw sce; } } else if (organization.getKey() != targetCustomer.getKey()) { // if it is a specific one but not specified for the subscriber String message = String.format("Customer specific product '%s' is not specified for customer '%s'.", productTemplate.getProductId(), organization.getOrganizationId()); OperationNotPermittedException onp = new OperationNotPermittedException(message); LOG.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp, LogMessageIdentifier.WARN_CUSTOMER_SPECIFIC_PRODUCT_NOT_FOR_THE_CUSTOMER, productTemplate.getProductId(), organization.getOrganizationId()); throw onp; } checkIfProductIsUptodate(productTemplate, product); // now check the price model related to the product; if it is // chargeable, the organization must have specified a payment // information. If he has not, throw an exception PriceModel priceModel = productTemplate.getPriceModel(); if (priceModel == null && productTemplate.getType() == ServiceType.PARTNER_TEMPLATE) { priceModel = productTemplate.getTemplate().getPriceModel(); } if (priceModel == null) { PriceModelException mpme = new PriceModelException(PriceModelException.Reason.NOT_DEFINED); LOG.logWarn(Log4jLogger.SYSTEM_LOG, mpme, LogMessageIdentifier.WARN_SUBSCRIBE_PRODUCT_FAILED, Long.toString(productTemplate.getKey())); throw mpme; } if (priceModel.isChargeable() && !isPaymentInfoHidden()) { PaymentDataValidator.validateNotNull(paymentInfo, voBillingContact); PaymentInfo pi = dataManager.getReference(PaymentInfo.class, paymentInfo.getKey()); BillingContact bc = dataManager.getReference(BillingContact.class, voBillingContact.getKey()); validatePaymentInfoAndBillingContact(pi, bc, paymentInfo, voBillingContact); PermissionCheck.owns(pi, organization, LOG); PermissionCheck.owns(bc, organization, LOG); PaymentDataValidator.validatePaymentTypeSupportedBySupplier(organization, productTemplate, pi.getPaymentType()); PaymentDataValidator.validatePaymentInfoDataForUsage(pi); } } UserGroup getUnit(long unitKey, String unitName, long organizationKey) throws ObjectNotFoundException { UserGroup unit = null; if (unitKey == 0L) { if (unitName == null || unitName.trim().length() == 0) { return null; } unit = new UserGroup(); unit.setName(unitName); unit.setOrganization_tkey(organizationKey); unit = (UserGroup) dataManager.getReferenceByBusinessKey(unit); } else { try { unit = dataManager.getReference(UserGroup.class, unitKey); if (unit.getOrganization_tkey() != organizationKey) { throw new ObjectNotFoundException("The unit does not belong to your organization."); } } catch (ObjectNotFoundException e) { e.setMessageParams(new String[] { unitName }); throw e; } } return unit; } private void checkIfServiceAvailable(long productKey, String productId, PlatformUser currentUser) throws OperationNotPermittedException, ObjectNotFoundException { if (currentUser.isOrganizationAdmin()) { return; } List<Long> invisibleProductKeys = userGroupService.getInvisibleProductKeysForUser(currentUser.getKey()); if (invisibleProductKeys.contains(Long.valueOf(productKey))) { String message = String.format("Service '%s' is not avalible.", productId); OperationNotPermittedException onp = new OperationNotPermittedException(message); LOG.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, onp, LogMessageIdentifier.WARN_SERVICE_NOT_AVAILABLE, productId); throw onp; } } /** * Checks if there already exists a subscription to the technical service to * which the specified VOService belongs to. * * @param product * the VOService for which to check if already has active * subscriptions. */ private void checkIfSubscriptionAlreadyExists(Product product) throws SubscriptionAlreadyExistsException, ObjectNotFoundException { // Fetch the technical product to which the defined product belongs to. TechnicalProduct technicalProduct = product.getTechnicalProduct(); // Only in case one subscription is allowed check the number of already // existing subscriptions. if (technicalProduct.isOnlyOneSubscriptionAllowed()) { Organization organization = dataManager.getCurrentUser().getOrganization(); Long numberOfSubscriptions = getSubscriptionDao().getNumberOfVisibleSubscriptions(technicalProduct, organization); // If there are already subscriptions to the technical product // based on the product, throw an exception. if (numberOfSubscriptions.longValue() > 0) { Object[] params = new Object[] { product.getProductId() }; SubscriptionAlreadyExistsException subAlreadyExistsException = new SubscriptionAlreadyExistsException( params); LOG.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, subAlreadyExistsException, LogMessageIdentifier.WARN_USER_SUBSCRIBE_SERVICE_FAILED_ONLY_ONE_ALLOWED, Long.toString(dataManager.getCurrentUser().getKey()), Long.toString(product.getKey()), Long.toString(organization.getKey())); throw subAlreadyExistsException; } } } /** * Checks if there already exists a subscription with the same subscriptions * name for this Organization. * * @param currentUser * current platform user * @param voSubscription * subscription * @throws NonUniqueBusinessKeyException */ private void verifyIdAndKeyUniqueness(PlatformUser currentUser, VOSubscription voSubscription) throws NonUniqueBusinessKeyException { Subscription newSubscription = new Subscription(); newSubscription.setOrganizationKey(currentUser.getOrganization().getKey()); newSubscription.setSubscriptionId(voSubscription.getSubscriptionId()); dataManager.validateBusinessKeyUniqueness(newSubscription); } /** * Its responsible for updating the values for the subscribed product from * the parameter list. * * @param product * the product to update * @param parameters * the list of parameters * @param subscription * - target subscription */ List<Parameter> updateConfiguredParameterValues(Product product, List<VOParameter> parameters, Subscription subscription) { Map<String, Parameter> paramMap = new HashMap<>(); if (product.getParameterSet() != null) { for (Parameter parameter : product.getParameterSet().getParameters()) { paramMap.put(parameter.getParameterDefinition().getParameterId(), parameter); } } // reload all the parameter values from old subscription if (subscription != null && subscription.getParameterSet() != null && !subscription.getProduct().equals(product)) { List<Parameter> params = subscription.getParameterSet().getParameters(); if (params != null && params.size() > 0) { for (Parameter param : params) { String parameterId = param.getParameterDefinition().getParameterId(); Parameter uParam = paramMap.get(parameterId); if (uParam != null) { uParam.setValue(param.getValue()); } } } } List<Parameter> modifiedParametesForLog = new ArrayList<>(); for (VOParameter voParameter : parameters) { String parameterID = voParameter.getParameterDefinition().getParameterId(); Parameter param = paramMap.get(parameterID); if (param != null) { String oldValue = param.getValue(); param.setValue(voParameter.getValue()); String defaultValue = param.getParameterDefinition().getDefaultValue(); if ((oldValue != null && !oldValue.equals(param.getValue())) || (oldValue == null && param.getValue() != null && !param.getValue().equals(defaultValue))) { modifiedParametesForLog.add(param); } } } dataManager.flush(); return modifiedParametesForLog; } /** * If a product with a customer specific price model, was copied the * subscription specific one would refer to the template's one when getting * localized values - so we have to copy those ones from the original price * model * * @param targetProduct * @param sourceProduct */ private void copyLocalizedPricemodelValues(Product targetProduct, Product sourceProduct) { List<VOLocalizedText> localizedValues; final PriceModel priceModelTarget = targetProduct.getPriceModel(); final PriceModel priceModelSource = sourceProduct.getType() == ServiceType.PARTNER_TEMPLATE ? sourceProduct.getTemplate().getPriceModel() : sourceProduct.getPriceModel(); if ((priceModelTarget != null) && (priceModelSource != null)) { long targetKey = targetProduct.getPriceModel().getKey(); long sourceKey = priceModelSource.getKey(); localizedValues = localizer.getLocalizedValues(sourceKey, LocalizedObjectTypes.PRICEMODEL_DESCRIPTION); if (localizedValues != null && !localizedValues.isEmpty()) { localizer.storeLocalizedResources(targetKey, LocalizedObjectTypes.PRICEMODEL_DESCRIPTION, localizedValues); } // license copy if (sourceProduct.getVendor().getGrantedRoleTypes().contains(OrganizationRoleType.RESELLER)) { localizedValues = localizer.getLocalizedValues(sourceProduct.getKey(), LocalizedObjectTypes.RESELLER_PRICEMODEL_LICENSE); } else { localizedValues = localizer.getLocalizedValues(sourceKey, LocalizedObjectTypes.PRICEMODEL_LICENSE); } if (localizedValues != null && !localizedValues.isEmpty()) { localizer.storeLocalizedResources(targetKey, LocalizedObjectTypes.PRICEMODEL_LICENSE, localizedValues); } } } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public boolean addRevokeUser(String subscriptionId, List<VOUsageLicense> usersToBeAdded, List<VOUser> usersToBeRevoked) throws ObjectNotFoundException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationNotPermittedException, ConcurrentModificationException, OperationPendingException { ArgumentValidator.notNull("subscriptionId", subscriptionId); Subscription sub = manageBean.checkSubscriptionOwner(subscriptionId, 0); stateValidator.checkAddRevokeUserAllowed(sub); // validation if (usersToBeAdded != null) { for (VOUsageLicense lic : usersToBeAdded) { try { PlatformUser user = dataManager.getReference(PlatformUser.class, lic.getUser().getKey()); getAndCheckServiceRole(lic, sub.getProduct()); // fill user ID for trigger process lic.getUser().setUserId(user.getUserId()); } catch (ObjectNotFoundException e) { throw new ObjectNotFoundException(DomainObjectException.ClassEnum.USER, String.valueOf(lic.getUser().getUserId())); } } } if (usersToBeRevoked != null) { for (VOUser entry : usersToBeRevoked) { try { PlatformUser user = dataManager.getReference(PlatformUser.class, entry.getKey()); // fill user ID for trigger process entry.setUserId(user.getUserId()); } catch (ObjectNotFoundException e) { throw new ObjectNotFoundException(DomainObjectException.ClassEnum.USER, String.valueOf(entry.getUserId())); } } } TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); List<TriggerProcessIdentifier> pendingAddRevokeUsers = validator.getPendingAddRevokeUsers(subscriptionId, usersToBeAdded, usersToBeRevoked); if (!pendingAddRevokeUsers.isEmpty()) { String userIds = determineUserIds(pendingAddRevokeUsers); OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request for subscription '%s' to add or revoke the users: %s", subscriptionId, userIds), ReasonEnum.ADD_REVOKE_USER, new Object[] { subscriptionId, userIds }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_ADD_REVOKE_USER_FAILED_DUE_TO_TRIGGER_CONFLICT, subscriptionId); throw ope; } validateTriggerProcessForSubscription(sub); // now send a suspending message for the processing TriggerMessage message = new TriggerMessage(TriggerType.ADD_REVOKE_USER); List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message)); TriggerProcess proc = list.get(0).getTrigger(); proc.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscriptionId); proc.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscriptionId); proc.addTriggerProcessParameter(TriggerProcessParameterName.USERS_TO_ADD, usersToBeAdded); proc.addTriggerProcessParameter(TriggerProcessParameterName.USERS_TO_REVOKE, usersToBeRevoked); try { // if processing is not suspended, call finishing method TriggerDefinition triggerDefinition = proc.getTriggerDefinition(); if (triggerDefinition == null) { addRevokeUserInt(proc); return true; } else if (triggerDefinition.isSuspendProcess()) { proc.setTriggerProcessIdentifiers(TriggerProcessIdentifiers.createAddRevokeUser(dataManager, TriggerType.ADD_REVOKE_USER, subscriptionId, usersToBeAdded, usersToBeRevoked)); dataManager.merge(proc); } } catch (ObjectNotFoundException | SubscriptionStateException | TechnicalServiceNotAliveException | TechnicalServiceOperationException e) { sessionCtx.setRollbackOnly(); throw e; } return false; } void validateTriggerProcessForSubscription(Subscription subscription) throws OperationPendingException { validateTriggerProcessForUnsubscribeFromService(subscription.getSubscriptionId()); validateTriggerProcessForUpgradeSubscriptionBySubscriptionKey(subscription.getKey(), subscription.getSubscriptionId()); validateTriggerProcessForModifySubscriptionBySubscriptionKey(subscription.getKey(), subscription.getSubscriptionId()); } /** * Determines the user identifiers that are contained in the passed in list * of process identifiers. * * @param pendingAddRevokeUsers * The list of process identifiers to check. * @return The user identifiers. */ private String determineUserIds(List<TriggerProcessIdentifier> pendingAddRevokeUsers) { StringBuilder userIds = new StringBuilder(); for (TriggerProcessIdentifier id : pendingAddRevokeUsers) { if (userIds.length() != 0) { userIds.append(", "); } if (TriggerProcessIdentifierName.USER_TO_ADD.equals(id.getName()) || TriggerProcessIdentifierName.USER_TO_REVOKE.equals(id.getName())) { userIds.append(id.getValue()); } } return userIds.toString(); } /** * Tries to read the service role that should be assigned (if not * <code>null</code>). If the role exists, it will be checked if it belongs * to the product's technical product. * * @param lic * the {@link VOUsageLicense} * @param prod * the {@link Product} * @return <code>null</code> if no service roles are defined on the * technical product and the specified one on the * {@link VOUsageLicense} is <code>null</code>. Otherwise the read * role will be returned if valid * @throws ObjectNotFoundException * in case the role wasn't found. * @throws OperationNotPermittedException * in case the read role doesn't belong to the subscriptions * technical product or no role is specified when service roles * have to be used on the technical product */ private RoleDefinition getAndCheckServiceRole(VOUsageLicense lic, Product prod) throws ObjectNotFoundException, OperationNotPermittedException { TechnicalProduct tp = prod.getTechnicalProduct(); List<RoleDefinition> roles = tp.getRoleDefinitions(); if (roles == null || roles.isEmpty()) { lic.setRoleDefinition(null); return null; } if (lic.getRoleDefinition() == null) { String message = "User assignment to technical service '%s' without service role not possible."; OperationNotPermittedException onp = new OperationNotPermittedException( String.format(message, Long.valueOf(tp.getKey()))); LOG.logError(Log4jLogger.SYSTEM_LOG, onp, LogMessageIdentifier.ERROR_USER_ASSIGNMENT_TO_TECHNICAL_SERVICE_FAILED_NO_SERVICE_ROLE, Long.toString(tp.getKey())); throw onp; } RoleDefinition role = dataManager.getReference(RoleDefinition.class, lic.getRoleDefinition().getKey()); for (RoleDefinition roleDefinition : roles) { if (role.getKey() == roleDefinition.getKey()) { return role; } } String message = "Role '%s' is not defined on technical service '%s'"; OperationNotPermittedException onp = new OperationNotPermittedException( String.format(message, Long.valueOf(role.getKey()), Long.valueOf(tp.getKey()))); LOG.logError(Log4jLogger.SYSTEM_LOG, onp, LogMessageIdentifier.ERROR_NO_ROLE_FOR_TECHNICAL_SERVICE, Long.toString(role.getKey()), Long.toString(tp.getKey())); throw onp; } @Override @RolesAllowed("ORGANIZATION_ADMIN") @TransactionAttribute(TransactionAttributeType.MANDATORY) public void addRevokeUserInt(TriggerProcess tp) throws ObjectNotFoundException, ServiceParameterException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationNotPermittedException, ConcurrentModificationException { TriggerProcessParameter mod; mod = tp.getParamValueForName(TriggerProcessParameterName.SUBSCRIPTION); String subscriptionId = mod.getValue(String.class); mod = tp.getParamValueForName(TriggerProcessParameterName.USERS_TO_ADD); List<?> usersToBeAdded = mod.getValue(List.class); mod = tp.getParamValueForName(TriggerProcessParameterName.USERS_TO_REVOKE); List<?> usersToBeRevoked = mod.getValue(List.class); // Try to find the subscription Subscription subscription = manageBean.loadSubscription(subscriptionId, 0); List<UsageLicense> addedUsersLicenses = new ArrayList<>(); List<PlatformUser> usersToAdd = new ArrayList<>(); // loop for adding users if (usersToBeAdded != null) { for (Object entry : usersToBeAdded) { final VOUsageLicense lic = (VOUsageLicense) entry; final PlatformUser usr = dataManager.getReference(PlatformUser.class, lic.getUser().getKey()); final RoleDefinition roleDef = getAndCheckServiceRole(lic, subscription.getProduct()); final UsageLicense usageLicenseForUser = subscription.getUsageLicenseForUser(usr); try { if (usageLicenseForUser == null) { // Create a new usage License final UsageLicense newUsageLicense = addUserToSubscription(subscription, usr, roleDef); usersToAdd.add(usr); // store the new user's licenses for later mail sending addedUsersLicenses.add(newUsageLicense); } else { // Update an existing usage license: BaseAssembler.verifyVersionAndKey(usageLicenseForUser, lic); modifyUserRole(subscription, usr, roleDef); } } catch (UserNotAssignedException | UserAlreadyAssignedException e) { // Must not happen here throw new AssertionError(e); } } } // loop for revoking users if (usersToBeRevoked != null) { List<PlatformUser> platformUsers = new ArrayList<>(); for (Object qryUsr : usersToBeRevoked) { PlatformUser usr = dataManager.getReference(PlatformUser.class, VOUser.class.cast(qryUsr).getKey()); platformUsers.add(usr); } revokeUserFromSubscription(subscription, platformUsers); } verifyParameterNamedUser(subscription); informProductAboutNewUsers(subscription, usersToAdd); sendToSubscriptionAddedMail(subscription, addedUsersLicenses); // notify all listeners triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.ADD_REVOKE_USER, tp.getTriggerProcessParameters(), dataManager.getCurrentUser().getOrganization())); } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public void modifyUserRole(Subscription subscription, PlatformUser usr, RoleDefinition roleDef) throws SubscriptionStateException, UserNotAssignedException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { stateValidator.checkModifyUserRoleAllowed(subscription); UsageLicense license = subscription.getUsageLicenseForUser(usr); RoleDefinition oldRoleDef = null; if (license != null) { oldRoleDef = license.getRoleDefinition(); if (oldRoleDef == roleDef) { // nothing to do - role not changed; avoid sending of mail and // notification of service return; } } UsageLicense targetLicense = subscription.changeRole(usr, roleDef); // Inform application about changed role if (canModifyApplicationUsers(subscription) && targetLicense != null) { appManager.updateUsers(subscription, Collections.singletonList(targetLicense)); // log deassign user role for service audit.deassignUserRoleForService(dataManager, subscription, usr, oldRoleDef); // log assign user role for service audit.assignUserRoleForService(dataManager, subscription, usr, roleDef); } try { String accessInfo = targetLicense == null ? "" : getAccessInfo(subscription, targetLicense.getUser()); commService.sendMail(usr, EmailType.SUBSCRIPTION_ACCESS_GRANTED, new Object[] { subscription.getSubscriptionId(), accessInfo }, subscription.getMarketplace()); } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_GRANT_ROLE_IN_SUBSCRIPTION_CONFIRMING_FAILED); } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public UsageLicense addUserToSubscription(Subscription subscription, PlatformUser user, RoleDefinition serviceRole) throws UserAlreadyAssignedException { final UsageLicense usageLicense = subscription.addUser(user, serviceRole); audit.assignUserToSubscription(dataManager, subscription, usageLicense); return usageLicense; } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public void revokeUserFromSubscription(final Subscription subscription, final List<PlatformUser> users) throws SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { stateValidator.checkAddRevokeUserAllowed(subscription); revokeUserFromSubscriptionInt(subscription, users); } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public void revokeUserFromSubscriptionInt(final Subscription subscription, final List<PlatformUser> users) throws TechnicalServiceNotAliveException, TechnicalServiceOperationException { List<UsageLicense> usageLicenses = doRevokeUserFromSubscriptionInt(subscription, users); audit.deassignUserFromSubscription(dataManager, subscription, usageLicenses); } /** * Worker method for revokeUserFromSubscription, allows to ignore * lastUser-Check (only for use in unsubscribe) * * @param subscription * The subscription to be changed. * @param users * The users to be revoked from the subscription. * @return the list of usage licenses * @throws TechnicalServiceNotAliveException * @throws TechnicalServiceOperationException */ private List<UsageLicense> doRevokeUserFromSubscriptionInt(Subscription subscription, List<PlatformUser> users) throws TechnicalServiceNotAliveException, TechnicalServiceOperationException { List<UsageLicense> licenseList = new ArrayList<>(); for (PlatformUser user : users) { UsageLicense license = subscription.revokeUser(user); // if the subscription is invalid (e.g. instance creation // failed) no call to the application is performed if (isValidSubscription(subscription) && license != null) { licenseList.add(license); } } // Bug 9998. while the subscription is still pending, no call to // ProvisioningService.deleteUsers(String, List<User>) has to be // performed. if (subscription.getStatus() != SubscriptionStatus.PENDING) { appManager.deleteUsers(subscription, licenseList); } for (UsageLicense license : licenseList) { // explicitly remove the user's usage license if (license != null) { dataManager.remove(license); } dataManager.flush(); } if (subscription.getStatus() == SubscriptionStatus.ACTIVE && subscription.getProduct().getTechnicalProduct().getAccessType() != ServiceAccessType.DIRECT) { // as the user gets no mail when being added to a pending // subscription, the notification about the removal will also not be // sent. Long marketplaceKey = null; if (subscription.getMarketplace() != null) { marketplaceKey = Long.valueOf(subscription.getMarketplace().getKey()); } SendMailPayload payload = new SendMailPayload(); for (PlatformUser user : users) { payload.addMailObjectForUser(user.getKey(), EmailType.SUBSCRIPTION_USER_REMOVED, new Object[] { subscription.getSubscriptionId() }, marketplaceKey); } TaskMessage message = new TaskMessage(SendMailHandler.class, payload); tqs.sendAllMessages(Collections.singletonList(message)); } return licenseList; } private boolean isValidSubscription(Subscription subscription) { return subscription.getStatus() != SubscriptionStatus.INVALID; } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public List<Subscription> getSubscriptionsForUserInt(PlatformUser user) { return getSubscriptionDao().getSubscriptionsForUser(user); } @Override @RolesAllowed("ORGANIZATION_ADMIN") public List<VOUserSubscription> getSubscriptionsForUser(VOUser user) throws ObjectNotFoundException, OperationNotPermittedException { return getSubscriptionsForUser(user, PerformanceHint.ALL_FIELDS); } @RolesAllowed("ORGANIZATION_ADMIN") public List<VOUserSubscription> getSubscriptionsForUser(VOUser user, PerformanceHint performanceHint) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("user", user); PlatformUser platformUser = idManager.getPlatformUser(user.getUserId(), dataManager.getCurrentUser().getTenantId(), true); LocalizerFacade facade = new LocalizerFacade(localizer, platformUser.getLocale()); List<Subscription> subs = getSubscriptionsForUserInt(platformUser); ArrayList<VOUserSubscription> result = new ArrayList<>(); for (Subscription sub : subs) { VOUserSubscription voSub = SubscriptionAssembler.toVOUserSubscription(sub, platformUser, facade, performanceHint); result.add(voSub); } return result; } @Override public List<VOUserSubscription> getSubscriptionsForCurrentUser() { ArrayList<VOUserSubscription> result = new ArrayList<>(); PlatformUser user = dataManager.getCurrentUser(); List<Subscription> subs = getSubscriptionsForUserInt(user); LocalizerFacade facade = new LocalizerFacade(localizer, user.getLocale()); SubscriptionAssembler.prefetchData(subs, facade); for (Subscription sub : subs) { VOUserSubscription voSub = SubscriptionAssembler.toVOUserSubscription(sub, user, facade); result.add(voSub); } return result; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public List<VOSubscription> getSubscriptionsForOrganization() { return getSubscriptionsForOrganizationWithFilter(null); } @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public List<VOSubscription> getSubscriptionsForOrganization(PerformanceHint performanceHint) { return getSubscriptionsForOrganizationWithFilter(null, performanceHint); } @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public List<VOSubscription> getAllSubscriptionsForOrganization(PerformanceHint performanceHint) { ArrayList<VOSubscription> result = new ArrayList<>(); List<Subscription> subscriptions = subscriptionListService.getAllSubscriptionsForOrganization(); LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); for (Subscription sub : subscriptions) { VOSubscription voSub = SubscriptionAssembler.toVOSubscription(sub, facade, performanceHint); result.add(voSub); } return result; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public List<VOSubscription> getSubscriptionsForOrganizationWithFilter(Set<SubscriptionStatus> requiredStatus) { return getSubscriptionsForOrganizationWithFilter(requiredStatus, PerformanceHint.ALL_FIELDS); } @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public List<VOSubscription> getSubscriptionsForOrganizationWithFilter(Set<SubscriptionStatus> requiredStatus, PerformanceHint performanceHint) { ArrayList<VOSubscription> result = new ArrayList<>(); // load all subscriptions List<Subscription> subs = subscriptionListService.getSubscriptionsForOrganization(requiredStatus); // create transfer objects LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); for (Subscription sub : subs) { VOSubscription voSub = SubscriptionAssembler.toVOSubscription(sub, facade, performanceHint); result.add(voSub); } return result; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public boolean validateSubscriptionIdForOrganization(String subscriptionId) { // load all subscriptions List<Subscription> subs = subscriptionListService.getSubscriptionsForOrganization(null); boolean subscriptionIdAlreadyExists = false; for (Subscription sub : subs) { if (sub.getSubscriptionId().equals(subscriptionId)) { subscriptionIdAlreadyExists = true; break; } } return subscriptionIdAlreadyExists; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscriptionDetails getSubscriptionDetails(String subscriptionId) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); Subscription subscription = manageBean.checkSubscriptionOwner(subscriptionId, 0); return getSubscriptionDetails(subscription); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscriptionDetails getSubscriptionDetails(long subscriptionKey) throws ObjectNotFoundException, OperationNotPermittedException { Subscription subscription = manageBean.checkSubscriptionOwner(null, subscriptionKey); return getSubscriptionDetails(subscription); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscriptionDetails getSubscriptionDetailsWithoutOwnerCheck(long subscriptionKey) throws ObjectNotFoundException { Subscription subscription = manageBean.loadSubscription(null, subscriptionKey); return getSubscriptionDetails(subscription); } private VOSubscriptionDetails getSubscriptionDetails(Subscription subscription) { LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); return SubscriptionAssembler.toVOSubscriptionDetails(subscription, facade); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public boolean unsubscribeFromService(String subscriptionId) throws ObjectNotFoundException, SubscriptionStillActiveException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationPendingException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); manageBean.checkSubscriptionOwner(subscriptionId, 0); // Find the subscription and validate if operation can be performed validateSubsciptionForUnsubscribe(subscriptionId); validateTriggerProcessForUnsubscribeFromService(subscriptionId); TriggerProcess triggerProcess = createTriggerProcessForUnsubscribeFromService(subscriptionId); TriggerDefinition triggerDefinition = triggerProcess.getTriggerDefinition(); if (triggerDefinition == null) { try { unsubscribeFromServiceInt(triggerProcess); return true; } catch (ObjectNotFoundException | SubscriptionStillActiveException | SubscriptionStateException | TechnicalServiceNotAliveException | TechnicalServiceOperationException e) { sessionCtx.setRollbackOnly(); throw e; } } else if (triggerDefinition.isSuspendProcess()) { triggerProcess.setTriggerProcessIdentifiers(TriggerProcessIdentifiers.createUnsubscribeFromService( dataManager, TriggerType.UNSUBSCRIBE_FROM_SERVICE, subscriptionId)); dataManager.merge(triggerProcess); } return false; } /** * @param subscriptionId * @return */ private TriggerProcess createTriggerProcessForUnsubscribeFromService(String subscriptionId) { TriggerMessage message = new TriggerMessage(TriggerType.UNSUBSCRIBE_FROM_SERVICE); List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message)); TriggerProcess triggerProcess = list.get(0).getTrigger(); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscriptionId); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscriptionId); return triggerProcess; } private void validateTriggerProcessForUnsubscribeFromService(String subscriptionId) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); if (validator.isSubscribeOrUnsubscribeServicePending(subscriptionId)) { OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to unsubscribe from the subscription or create a subscription with identifier '%s'", subscriptionId), ReasonEnum.UNSUBSCRIBE_FROM_SERVICE, new Object[] { subscriptionId }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_UNSUBSCRIBE_FROM_SERVICE_FAILED_DUE_TO_TRIGGER_CONFLICT, subscriptionId); throw ope; } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public void unsubscribeFromServiceInt(TriggerProcess tp) throws ObjectNotFoundException, SubscriptionStateException, SubscriptionStillActiveException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { String subId = tp.getParamValueForName(TriggerProcessParameterName.SUBSCRIPTION).getValue(String.class); Subscription subscription = validateSubsciptionForUnsubscribe(subId); stopInstance(subscription); unsubscribe(subscription); manageBean.removeUsageLicenses(subscription); operationRecordBean.removeOperationsForSubscription(subscription.getKey()); // rename the subscription as last step because id is still used for // exceptions and mails rename to allow the reuse of the id final String oldSubscriptionId = subscription.getSubscriptionId(); subscription.setSubscriptionId(String.valueOf(System.currentTimeMillis())); boolean removed = removeOnBehalfActingReference(subscription); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.UNSUBSCRIBE_FROM_SERVICE, tp.getTriggerProcessParameters(), dataManager.getCurrentUser().getOrganization())); tp.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, buildNotification(oldSubscriptionId)); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.SUBSCRIPTION_TERMINATION, tp.getTriggerProcessParameters(), subscription.getProduct().getVendor())); sendConfirmDeactivationEmail(removed, oldSubscriptionId, subscription); } void unsubscribe(Subscription subscription) { // delete product final Product product = subscription.getProduct(); product.setStatus(ServiceStatus.DELETED); final Product asyncTempProduct = subscription.getAsyncTempProduct(); if (asyncTempProduct != null) { subscription.setAsyncTempProduct(null); dataManager.remove(asyncTempProduct); } modUpgBean.deleteModifiedEntityForSubscription(subscription); // deactivate subscription subscription.setStatus(SubscriptionStatus.DEACTIVATED); long txTime = DateFactory.getInstance().getTransactionTime(); subscription.setDeactivationDate(Long.valueOf(txTime)); // log audit.unsubscribeFromService(dataManager, subscription); } /** * @param subscription */ private void stopInstance(Subscription subscription) { boolean stopInstance = subscription.getStatus() != SubscriptionStatus.INVALID; if (stopInstance) { try { appManager.deleteInstance(subscription); } catch (SaaSApplicationException e) { LOG.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR_UNSUBSCRIBE_SUBSCRIPTION_FAILED, Long.toString(subscription.getKey())); manageBean.sendTechnicalServiceErrorMail(subscription); } } } private VONotification buildNotification(String subscriptionId) { VONotificationBuilder builder = new VONotificationBuilder(); return builder.addParameter(VOProperty.SUBSCRIPTION_SUBSCRIPTION_ID, subscriptionId).build(); } private boolean removeOnBehalfActingReference(Subscription subscription) { // execute isLastSubscription named query Long isLastSubscription = getSubscriptionDao().hasSubscriptionsBasedOnOnBehalfServicesForTp(subscription); // if is last subscription... if (isLastSubscription.longValue() == 0) { // search for the reference... Organization sourceOrganization = subscription.getProduct().getTechnicalProduct().getOrganization(); Organization targetOrganization = subscription.getOrganization(); OrganizationReference reference = getOrganizationReference(sourceOrganization, targetOrganization); // if found remove it if (reference != null) { removeTemporaryOnbehalfUsers(sourceOrganization, targetOrganization); dataManager.remove(reference); dataManager.flush(); return true; } } return false; } private void removeTemporaryOnbehalfUsers(Organization sourceOrganization, Organization targetOrganization) { for (PlatformUser onbehalfUser : sourceOrganization.getOnBehalfUsersFor(targetOrganization)) { removeActiveSessionsForOnbehalfUser(onbehalfUser); OnBehalfUserReference master = onbehalfUser.getMaster(); onbehalfUser.getOrganization().getPlatformUsers().remove(onbehalfUser); dataManager.remove(master); } } private void removeActiveSessionsForOnbehalfUser(PlatformUser onbehalfUser) { List<Session> sessions = getSessionDao().getActiveSessionsForUser(onbehalfUser); for (Session s : sessions) { dataManager.remove(s); } } private void sendConfirmDeactivationEmail(boolean removed, final String oldSubscriptionId, final Subscription subscription) { EmailType emailType = removed ? EmailType.SUBSCRIPTION_DELETED_ON_BEHALF_ACTING : EmailType.SUBSCRIPTION_DELETED; Marketplace marketplace = subscription.getMarketplace(); List<PlatformUser> users = manageBean.getCustomerAdminsAndSubscriptionOwner(subscription); for (PlatformUser user : users) { try { commService.sendMail(user, emailType, new Object[] { oldSubscriptionId }, marketplace); } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_DELETION_OF_SUBSCRIPTION_CONFIRMING_FAILED); } } } /** * Obtains the subscription and verifies that the current settings allow a * unsubscribe operation. * * @param subId * The subscription id to unsubscribe from. * @return The subscription to unsubscribe from. * @throws SubscriptionStateException * Thrown in case the operation cannot be performed due to the * current state of the subscription. * @throws SubscriptionStillActiveException * Thrown in case the subscription is still active. * @throws ObjectNotFoundException * Thrown in case the subscription cannot be found. */ private Subscription validateSubsciptionForUnsubscribe(String subId) throws SubscriptionStateException, SubscriptionStillActiveException, ObjectNotFoundException { Subscription subscription = manageBean.loadSubscription(subId, 0); stateValidator.checkUnsubscribingAllowed(subscription); // Check whether there are active sessions List<Session> activeSessions = prodSessionMgmt.getProductSessionsForSubscriptionTKey(subscription.getKey()); if (activeSessions.size() > 0) { // there are still active sessions, so deletion must // fail: throw exception sessionCtx.setRollbackOnly(); SubscriptionStillActiveException ssa = new SubscriptionStillActiveException( "There are still " + activeSessions.size() + " sessions active for the subscription with key '" + subscription.getKey() + "'", SubscriptionStillActiveException.Reason.ACTIVE_SESSIONS); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ssa, LogMessageIdentifier.WARN_SUBSCRIPTION_DELETION_FAILED); throw ssa; } return subscription; } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public Subscription loadSubscription(long subscriptionKey) throws ObjectNotFoundException { return dataManager.getReference(Subscription.class, subscriptionKey); } /** * Check if one of the platform parameter constraints are violated. * * @param subscription * the subscription * @param currentUser * the current user * @throws SubscriptionMigrationException * in case one of the platform parameters is violated */ private void checkPlatformParameterConstraints(Subscription subscription, Product targetProduct, PlatformUser currentUser) throws SubscriptionMigrationException { ParameterSet paramSet = targetProduct.getParameterSet(); if (paramSet == null || paramSet.getParameters() == null || paramSet.getParameters().isEmpty()) { return; } for (Parameter param : paramSet.getParameters()) { ParameterDefinition def = param.getParameterDefinition(); if (def.getParameterType() == ParameterType.PLATFORM_PARAMETER && param.getValue() != null) { String subscriptionId = subscription.getSubscriptionId(); if (PlatformParameterIdentifiers.NAMED_USER.equals(def.getParameterId())) { int current = subscription.getUsageLicenses().size(); long max = param.getLongValue(); if (current > max) { sessionCtx.setRollbackOnly(); SubscriptionMigrationException e = new SubscriptionMigrationException( "Parameter check failed", Reason.PARAMETER_USERS, new Object[] { subscriptionId, String.valueOf(current), String.valueOf(max) }); LOG.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR_MIGRATE_SUBSCRIPTION_AS_CHECK_PARAMETER, Long.toString(currentUser.getKey()), Long.toString(subscription.getKey()), Long.toString(subscription.getOrganizationKey()), def.getParameterId()); throw e; } } else if (PlatformParameterIdentifiers.PERIOD.equals(def.getParameterId())) { long usedTime = DateFactory.getInstance().getTransactionTime() - subscription.getActivationDate().longValue(); if (usedTime > param.getLongValue()) { sessionCtx.setRollbackOnly(); SubscriptionMigrationException e = new SubscriptionMigrationException( "Parameter check failed", Reason.PARAMETER_PERIOD, new Object[] { subscriptionId }); LOG.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR_MIGRATE_SUBSCRIPTION_AS_CHECK_PARAMETER, Long.toString(currentUser.getKey()), Long.toString(subscription.getKey()), Long.toString(subscription.getOrganizationKey()), def.getParameterId()); throw e; } } } } } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public List<VOService> getUpgradeOptions(String subscriptionId) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); Subscription subscription = manageBean.checkSubscriptionOwner(subscriptionId, 0); return getUpgradeOptions(subscription); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public List<VOService> getUpgradeOptions(long subscriptionKey) throws ObjectNotFoundException, OperationNotPermittedException { Subscription subscription = manageBean.checkSubscriptionOwner(null, subscriptionKey); return getUpgradeOptions(subscription); } private List<VOService> getUpgradeOptions(Subscription subscription) throws OperationNotPermittedException { // 1. retrieve the related product Organization organization = dataManager.getCurrentUser().getOrganization(); PermissionCheck.owns(subscription, organization, LOG); Product product = subscription.getProduct(); // 2. retrieve and return the compatible products List<Product> compatibleProducts = product.getCompatibleProductsList(); // 3. get customer specific products long supplierKey = product.getVendor().getKey(); compatibleProducts = replaceByCustomerSpecificProducts(compatibleProducts, supplierKey, organization); List<VOService> result = new ArrayList<>(compatibleProducts.size()); LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); for (Product prod : compatibleProducts) { result.add(ProductAssembler.toVOProduct(prod, facade)); } return result; } /** * Replaces global products by customer specific products based on the * global ones if existing. Global and specific products that are not active * will be removed. * * @param products * the products to get customer specific ones for * @param supplierKey * the supplier key identifying the owner of the products * @param organization * the customer organization to get customer specific products * for * @return the list containing the customer specific products and the global * ones not having customer specific versions */ List<Product> replaceByCustomerSpecificProducts(List<Product> products, long supplierKey, Organization organization) { // read customer specific products List<Product> customerProducts = getProductDao().getProductForCustomerOnly(supplierKey, organization); // replace products by customer specific products if existent Map<Long, Product> keyToProduct = new HashMap<>(); for (Product prod : customerProducts) { keyToProduct.put(Long.valueOf(prod.getTemplate().getKey()), prod); } List<Product> temp = new ArrayList<>(products.size()); for (Product prod : products) { Long key = Long.valueOf(prod.getKey()); if (keyToProduct.containsKey(key)) { Product custSpec = keyToProduct.get(key); if (custSpec.getStatus() == ServiceStatus.ACTIVE) { temp.add(custSpec); } } else if (prod.getStatus() == ServiceStatus.ACTIVE) { temp.add(prod); } } return temp; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscription upgradeSubscription(VOSubscription subscription, VOService service, VOPaymentInfo paymentInfo, VOBillingContact billingContact, List<VOUda> udas) throws ObjectNotFoundException, OperationNotPermittedException, SubscriptionMigrationException, PaymentInformationException, SubscriptionStateException, ServiceChangedException, PriceModelException, ConcurrentModificationException, TechnicalServiceNotAliveException, OperationPendingException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException, NonUniqueBusinessKeyException, ValidationException { ArgumentValidator.notNull("subscription", subscription); ArgumentValidator.notNull("service", service); PlatformUser currentUser = dataManager.getCurrentUser(); if (isPaymentInfoHidden() && service.getPriceModel().isChargeable()) { Organization organization = currentUser.getOrganization(); billingContact = createBillingContactForOrganization(currentUser); paymentInfo = createPaymentInfoForOrganization(organization); } manageBean.checkSubscriptionOwner(subscription.getSubscriptionId(), subscription.getKey()); validateSettingsForUpgrading(subscription, service, paymentInfo, billingContact); validateTriggerProcessForUpgradeSubscription(subscription); TriggerProcess triggerProcess = createTriggerProcessForUpgradeSubscription(subscription, service, paymentInfo, billingContact, udas); TriggerDefinition triggerDefinition = triggerProcess.getTriggerDefinition(); Subscription upgradedSub = null; if (triggerDefinition == null) { try { upgradedSub = upgradeSubscriptionInt(triggerProcess); } catch (ObjectNotFoundException | SubscriptionStateException | OperationNotPermittedException | ServiceChangedException | PriceModelException | PaymentInformationException | SubscriptionMigrationException | ConcurrentModificationException | TechnicalServiceNotAliveException | MandatoryUdaMissingException | NonUniqueBusinessKeyException | ValidationException | MandatoryCustomerUdaMissingException e) { sessionCtx.setRollbackOnly(); throw e; } } else if (triggerDefinition.isSuspendProcess()) { triggerProcess.setTriggerProcessIdentifiers(TriggerProcessIdentifiers .createUpgradeSubscription(dataManager, TriggerType.UPGRADE_SUBSCRIPTION, subscription)); dataManager.merge(triggerProcess); } return SubscriptionAssembler.toVOSubscription(upgradedSub, new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale())); } private TriggerProcess createTriggerProcessForUpgradeSubscription(VOSubscription subscription, VOService service, VOPaymentInfo paymentInfo, VOBillingContact billingContact, List<VOUda> udas) { TriggerMessage message = new TriggerMessage(TriggerType.UPGRADE_SUBSCRIPTION); List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message)); TriggerProcess triggerProcess = list.get(0).getTrigger(); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscription.getSubscriptionId()); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscription); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.PRODUCT, service); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.PAYMENTINFO, paymentInfo); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.BILLING_CONTACT, billingContact); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.UDAS, udas); return triggerProcess; } private void validateTriggerProcessForUpgradeSubscription(VOSubscription subscription) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); if (validator.isModifyOrUpgradeSubscriptionPending(subscription)) { String subID = subscription.getSubscriptionId(); OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to upgrade or modify the subscription with ID '%s'", subID), ReasonEnum.UPGRADE_SUBSCRIPTION, new Object[] { subID }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_UPGRADE_SUBSCRIPTION_FAILED_DUE_TO_TRIGGER_CONFLICT, subID); throw ope; } } private void validateTriggerProcessForUpgradeSubscriptionBySubscriptionKey(long subscriptionKey, String subscriptionID) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); if (validator.isUpgradeSubscriptionPending(subscriptionKey)) { OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to upgrade or modify the subscription with ID '%s'", subscriptionID), ReasonEnum.UPGRADE_SUBSCRIPTION, new Object[] { subscriptionID }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_ADD_REVOKE_USER_FAILED_DUE_TO_TRIGGER_CONFLICT, subscriptionID); throw ope; } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public Subscription upgradeSubscriptionInt(TriggerProcess tp) throws ObjectNotFoundException, SubscriptionStateException, OperationNotPermittedException, ServiceChangedException, PriceModelException, PaymentInformationException, SubscriptionMigrationException, ConcurrentModificationException, TechnicalServiceNotAliveException, NonUniqueBusinessKeyException, ValidationException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException { VOSubscription current = tp.getParamValueForName(TriggerProcessParameterName.SUBSCRIPTION) .getValue(VOSubscription.class); VOService voTargetProduct = tp.getParamValueForName(TriggerProcessParameterName.PRODUCT) .getValue(VOService.class); VOPaymentInfo voPaymentInfo = tp.getParamValueForName(TriggerProcessParameterName.PAYMENTINFO) .getValue(VOPaymentInfo.class); VOBillingContact voBillingContact = tp.getParamValueForName(TriggerProcessParameterName.BILLING_CONTACT) .getValue(VOBillingContact.class); List<VOUda> udas = ParameterizedTypes .list(tp.getParamValueForName(TriggerProcessParameterName.UDAS).getValue(List.class), VOUda.class); Product dbTargetProduct = dataManager.getReference(Product.class, voTargetProduct.getKey()); Subscription dbSubscription = dataManager.getReference(Subscription.class, current.getKey()); PlatformUser currentUser = dataManager.getCurrentUser(); Subscription subscription = manageBean.loadSubscription(current.getSubscriptionId(), 0); BaseAssembler.verifyVersionAndKey(subscription, current); Product initialProduct = subscription.getProduct(); PaymentInfo initialPaymentInfo = subscription.getPaymentInfo(); BillingContact initialBillingContact = subscription.getBillingContact(); validateSettingsForUpgrading(current, voTargetProduct, voPaymentInfo, voBillingContact); // validates parameter version and parameter values checkIfParametersAreModified(subscription, dbSubscription, initialProduct, dbTargetProduct, voTargetProduct.getParameters(), true); PaymentInfo paymentInfo = null; BillingContact bc = null; if (voPaymentInfo != null) { // Valid payment information have been passed -> use it for the // upgraded subscription paymentInfo = dataManager.getReference(PaymentInfo.class, voPaymentInfo.getKey()); } if (voBillingContact != null) { bc = dataManager.getReference(BillingContact.class, voBillingContact.getKey()); } // log payment info before subscription changed! audit.editPaymentType(dataManager, subscription, paymentInfo); // log billing address before subscription changed! audit.editBillingAddress(dataManager, subscription, bc); // update subscription subscription.setPaymentInfo(paymentInfo); subscription.setBillingContact(bc); try { appManager.saveAttributes(dbSubscription); } catch (TechnicalServiceOperationException e) { // Log and continue LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_TECH_SERVICE_WS_ERROR, subscription.getSubscriptionId(), e.getMessage()); } List<Uda> existingUdas = manageBean.getExistingUdas(subscription); List<VOUda> updatedList = getUpdatedSubscriptionAttributes(udas, existingUdas); logSubscriptionAttributeForEdit(subscription, updatedList); // save subscription attributes before provisioning call, to have them // available at the API if (dbTargetProduct.getTechnicalProduct().getProvisioningType().equals(ProvisioningType.ASYNCHRONOUS)) { saveUdasForAsyncModifyOrUpgradeSubscription(udas, dbSubscription); } else { saveUdasForSubscription(udas, subscription); } // product and parameters are copied copyProductAndModifyParametersForUpgrade(subscription, dbTargetProduct, currentUser, voTargetProduct.getParameters()); if (dbTargetProduct.getTechnicalProduct().getProvisioningType().equals(ProvisioningType.SYNCHRONOUS)) { // bugfix 8068 String oldServiceId = initialProduct.getTemplate() != null ? initialProduct.getTemplate().getProductId() : initialProduct.getProductId(); String newServiceId = dbTargetProduct.getTemplate() != null ? dbTargetProduct.getTemplate().getProductId() : dbTargetProduct.getProductId(); // remove old product dataManager.remove(initialProduct); // finally send confirmation mail to the organization admin modUpgBean.sendConfirmUpgradationEmail(subscription, oldServiceId, newServiceId); } else if (dbTargetProduct.getTechnicalProduct().getProvisioningType() .equals(ProvisioningType.ASYNCHRONOUS)) { long subscriptionKey = subscription.getKey(); if (paymentInfo != null) { modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_PAYMENTINFO, String.valueOf(paymentInfo.getKey())); } if (bc != null) { modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_BILLINGCONTACT, String.valueOf(bc.getKey())); } subscription.setPaymentInfo(initialPaymentInfo); subscription.setBillingContact(initialBillingContact); } dataManager.flush(); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.UPGRADE_SUBSCRIPTION, tp.getTriggerProcessParameters(), dataManager.getCurrentUser().getOrganization())); // log upDowngrade subscription audit.upDowngradeSubscription(dataManager, subscription, initialProduct, dbTargetProduct); return subscription; } /** * Validates the data of the specified subscription and target product to * evaluate if the upgrade is possible. * * @param current * The current subscription. * @param newProduct * The product to migrate to. * @param voBillingContact * @throws ObjectNotFoundException * Thrown in case the subscription or target product cannot be * found. * @throws SubscriptionStateException * Thrown in case the subscription state does not allow a * migration. * @throws OperationNotPermittedException * Thrown in case the caller tries to modify another * organization's object. * @throws ServiceChangedException * Thrown in case the target product definition has been * modified in the meantime. * @throws PriceModelException * Thrown in case the target product is missing a price model. * @throws PaymentInformationException * Thrown in case the calling organization does not have a * payment record. * @throws SubscriptionMigrationException * Thrown in case the migration is not possible as the products * are not compatible. */ void validateSettingsForUpgrading(VOSubscription current, VOService newProduct, VOPaymentInfo paymentInfo, VOBillingContact voBillingContact) throws ObjectNotFoundException, SubscriptionStateException, OperationNotPermittedException, ServiceChangedException, PriceModelException, PaymentInformationException, SubscriptionMigrationException { // 1. retrieve the subscription details and the target product Subscription subscription = manageBean.loadSubscription(current.getSubscriptionId(), 0); stateValidator.checkModifyAllowedForUpgrading(subscription); PlatformUser currentUser = dataManager.getCurrentUser(); Organization organization = currentUser.getOrganization(); PermissionCheck.owns(subscription, organization, LOG); Product currentProduct = subscription.getProduct(); Product targetProduct = dataManager.getReference(Product.class, newProduct.getKey()); // check if the product has been changed in the meantime or it // is inactive.... checkIfProductIsUptodate(targetProduct, newProduct); // 2. check if the payment information parameter is set correctly PriceModel targetPriceModel = targetProduct.getPriceModel(); if (targetPriceModel == null) { PriceModelException mpme = new PriceModelException(PriceModelException.Reason.NOT_DEFINED); LOG.logWarn(Log4jLogger.SYSTEM_LOG, mpme, LogMessageIdentifier.WARN_MIGRATE_PRODUCT_FAILED, Long.toString(targetProduct.getKey())); throw mpme; } if (targetPriceModel.isChargeable() && !isPaymentInfoHidden()) { PaymentDataValidator.validateNotNull(paymentInfo, voBillingContact); PaymentInfo pi = dataManager.getReference(PaymentInfo.class, paymentInfo.getKey()); BillingContact bc = dataManager.getReference(BillingContact.class, voBillingContact.getKey()); PermissionCheck.owns(pi, organization, LOG); PermissionCheck.owns(bc, organization, LOG); PaymentDataValidator.validatePaymentTypeSupportedBySupplier(organization, targetProduct, pi.getPaymentType()); PaymentDataValidator.validatePaymentInfoDataForUsage(pi); } // 3. now check if the new product is really compatible to the // current one List<Product> compatibleProducts = currentProduct.getCompatibleProductsList(); // we won't find products in this list that are not active compatibleProducts = replaceByCustomerSpecificProducts(compatibleProducts, currentProduct.getVendorKey(), organization); boolean isCompatibleProduct = false; for (Product compatibleProduct : compatibleProducts) { if (compatibleProduct.getKey() == targetProduct.getKey()) { isCompatibleProduct = true; } } if (!isCompatibleProduct) { sessionCtx.setRollbackOnly(); SubscriptionMigrationException smf = new SubscriptionMigrationException( "Migration of subscription failed", Reason.INCOMPATIBLE_SERVICES, new Object[] { subscription.getSubscriptionId(), targetProduct.getCleanProductId() }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, smf, LogMessageIdentifier.WARN_MIGRATE_PRODUCT_FAILED_NOT_COMPATIBLE, Long.toString(currentUser.getKey()), Long.toString(subscription.getKey()), Long.toString(subscription.getOrganizationKey()), Long.toString(targetProduct.getKey()), Long.toString(subscription.getProduct().getKey())); throw smf; } } /** * Copies the product and modifies the given parameters. Also informs the * technical service about the changed parameter set. * * @param subscription * the subscription to create a copy of the product for * @param targetProduct * the product to copy * @param currentUser * the currently logged in user * @param voTargetParameters * the list of modified or configurable parameters * @throws SubscriptionMigrationException * @throws TechnicalServiceNotAliveException * Thrown in case the service for the operation cannot be * reached. */ void copyProductAndModifyParametersForUpdate(Subscription subscription, Product targetProduct, PlatformUser currentUser, List<VOParameter> voTargetParameters) throws SubscriptionMigrationException, TechnicalServiceNotAliveException { Product targetProductCopy = null; ProvisioningType provisioningType = targetProduct.getTechnicalProduct().getProvisioningType(); if (provisioningType.equals(ProvisioningType.SYNCHRONOUS)) { targetProductCopy = targetProduct; } else if (provisioningType.equals(ProvisioningType.ASYNCHRONOUS)) { targetProductCopy = copyProductForSubscription(targetProduct, subscription, true); } List<Parameter> modifiedParametersForLog = updateConfiguredParameterValues(targetProductCopy, voTargetParameters, subscription); // verify the platform parameter and send the new parameter to the // technical product checkPlatformParameterConstraints(subscription, targetProductCopy, currentUser); try { if (provisioningType.equals(ProvisioningType.ASYNCHRONOUS)) { subscription.setAsyncTempProduct(targetProductCopy); handleAsyncUpdateSubscription(subscription, targetProductCopy); } else if (provisioningType.equals(ProvisioningType.SYNCHRONOUS)) { // BUG 9998. It has to be ensured, that no call on pending // subscription if (subscription.getStatus() != SubscriptionStatus.PENDING) { appManager.modifySubscription(subscription); } } } catch (TechnicalServiceNotAliveException e) { sessionCtx.setRollbackOnly(); throw e; } catch (TechnicalServiceOperationException e1) { sessionCtx.setRollbackOnly(); Object[] params; String subscriptionId = subscription.getSubscriptionId(); if (e1.getMessageParams() != null && e1.getMessageParams().length > 1) { params = new Object[] { subscriptionId, e1.getMessage(), e1.getMessageParams()[1] }; } else { params = new Object[] { subscriptionId, e1.getMessage(), "" }; } SubscriptionMigrationException smf = new SubscriptionMigrationException("Modify ParameterSet failed", Reason.PARAMETER, params); LOG.logError(Log4jLogger.SYSTEM_LOG, smf, LogMessageIdentifier.ERROR_MODIFY_PARAMETER_SET_FAILED); throw smf; } audit.editSubscriptionParameterConfiguration(dataManager, targetProductCopy, modifiedParametersForLog); } /** * Copies the product and modifies the given parameters. Also informs the * technical service about the changed parameter set. * * @param subscription * the subscription to create a copy of the product for * @param targetProduct * the product to copy * @param currentUser * the currently logged in user * @param voTargetParameters * the list of modified or configurable parameters * @throws SubscriptionMigrationException * @throws TechnicalServiceNotAliveException * Thrown in case the service for the operation cannot be * reached. * @throws ObjectNotFoundException */ void copyProductAndModifyParametersForUpgrade(Subscription subscription, Product targetProduct, PlatformUser currentUser, List<VOParameter> voTargetParameters) throws SubscriptionMigrationException, TechnicalServiceNotAliveException, ObjectNotFoundException { ProvisioningType provisioningType = targetProduct.getTechnicalProduct().getProvisioningType(); Product targetProductCopy = copyProductForSubscription(targetProduct, subscription, false); List<Parameter> modifiedParametersForLog = updateConfiguredParameterValues(targetProductCopy, voTargetParameters, subscription); // verify the platform parameter checkPlatformParameterConstraints(subscription, targetProductCopy, currentUser); try { if (provisioningType.equals(ProvisioningType.ASYNCHRONOUS)) { subscription.setAsyncTempProduct(targetProductCopy); handleAsyncUpgradeSubscription(subscription, targetProductCopy); } else if (provisioningType.equals(ProvisioningType.SYNCHRONOUS)) { targetProductCopy.getPriceModel().setProvisioningCompleted(true); subscription.bindToProduct(targetProductCopy); // BE08022: Activate the subscription if it is up-/downgraded if (isActivationAllowed(subscription, true)) { appManager.activateInstance(subscription); subscription.setStatus(SubscriptionStatus.ACTIVE); } // BUG 9998. It has to be ensured, that no call on pending // subscription if (subscription.getStatus() != SubscriptionStatus.PENDING) { appManager.upgradeSubscription(subscription); } } } catch (TechnicalServiceNotAliveException e) { sessionCtx.setRollbackOnly(); throw e; } catch (TechnicalServiceOperationException e1) { sessionCtx.setRollbackOnly(); Object[] params; String subscriptionId = subscription.getSubscriptionId(); if (e1.getMessageParams() != null && e1.getMessageParams().length > 1) { params = new Object[] { subscriptionId, e1.getMessage(), e1.getMessageParams()[1] }; } else { params = new Object[] { subscriptionId, e1.getMessage(), "" }; } SubscriptionMigrationException smf = new SubscriptionMigrationException("Modify ParameterSet failed", Reason.PARAMETER, params); LOG.logError(Log4jLogger.SYSTEM_LOG, smf, LogMessageIdentifier.ERROR_MODIFY_PARAMETER_SET_FAILED); throw smf; } audit.editSubscriptionParameterConfiguration(dataManager, targetProductCopy, modifiedParametersForLog); } private Product copyProductForSubscription(Product targetProduct, Subscription subscription, boolean provisiongCompleted) { Product targetProductCopy = targetProduct.copyForSubscription(targetProduct.getTargetCustomer(), subscription); if (targetProductCopy.getPriceModel() != null) { // FIXME LG clean // For asynchronous upgrade the first target pricemodel version is // created when subscription is still in PENDING_UPD, but must be // fitered for billing. Set the indicating flag before persisting. targetProductCopy.getPriceModel().setProvisioningCompleted(provisiongCompleted); } try { dataManager.persist(targetProductCopy); } catch (NonUniqueBusinessKeyException ex) { // this must never happen as the product copy must have unique // id SaaSSystemException se = new SaaSSystemException( "The product copy method didn't create a unique productId.", ex); LOG.logError(Log4jLogger.SYSTEM_LOG, se, LogMessageIdentifier.ERROR_CREATE_UNIQUE_PRODUCT_ID_FAILED_IN_PRODUCT_COPY); throw se; } copyLocalizedPricemodelValues(targetProductCopy, targetProduct); return targetProductCopy; } /** * Sets subscription status and informs the technical service about the * changed parameter set when asynchronously update subscription. * * @param subscription * the subscription to update * @param targetProduct * the target product owned by the subscription * @throws TechnicalServiceNotAliveException * @throws TechnicalServiceOperationException */ void handleAsyncUpdateSubscription(Subscription subscription, Product targetProduct) throws TechnicalServiceNotAliveException, TechnicalServiceOperationException { EnumSet<SubscriptionStatus> set = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.SUSPENDED); SubscriptionStatus status = subscription.getStatus(); if (set.contains(status)) { appManager.asyncModifySubscription(subscription, targetProduct); switch (status) { case ACTIVE: subscription.setStatus(SubscriptionStatus.PENDING_UPD); break; case SUSPENDED: subscription.setStatus(SubscriptionStatus.SUSPENDED_UPD); break; default: break; } } } /** * Sets subscription status and informs the technical service about the * changed parameter set when asynchronously upgrade subscription. * * @param subscription * the subscription to update * @param targetProduct * the target product owned by the subscription * @throws TechnicalServiceNotAliveException * @throws TechnicalServiceOperationException */ void handleAsyncUpgradeSubscription(Subscription subscription, Product targetProduct) throws TechnicalServiceNotAliveException, TechnicalServiceOperationException { EnumSet<SubscriptionStatus> set = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.EXPIRED, SubscriptionStatus.SUSPENDED); SubscriptionStatus status = subscription.getStatus(); if (set.contains(status)) { appManager.asyncUpgradeSubscription(subscription, targetProduct); switch (status) { case ACTIVE: subscription.setStatus(SubscriptionStatus.PENDING_UPD); break; case SUSPENDED: subscription.setStatus(SubscriptionStatus.SUSPENDED_UPD); break; case EXPIRED: default: break; } } } boolean isActivationAllowed(Subscription subscription, boolean activateExpiredSubscription) throws ObjectNotFoundException { if (subscription == null) { return false; } if (activateExpiredSubscription && (subscription.getStatus() == SubscriptionStatus.EXPIRED)) { return true; } if (subscription.getStatus() != SubscriptionStatus.SUSPENDED) { return false; } if (!subscription.getPriceModel().isChargeable()) { return true; } if (subscription.getBillingContact() == null) { return false; } PaymentType subPayType = null; PaymentInfo paymentInfo = subscription.getPaymentInfo(); if (paymentInfo != null) { subPayType = paymentInfo.getPaymentType(); } if (subPayType == null) { return false; } long serviceKey = subscription.getProduct().getKey(); boolean paymentEnabled; paymentEnabled = accountService.isPaymentTypeEnabled(serviceKey, subPayType.getKey()); return paymentEnabled; } /** * Checks if the product read from the database is in active state and equal * to the one passed in as voproduct comparing the versions. * * @param prod * the product just read from the database * @param voProd * the product passed in through the interface * @throws ServiceChangedException * in case the product read from the database isn't active or * version based different to the passed voproduct */ private void checkIfProductIsUptodate(Product prod, VOService voProd) throws ServiceChangedException { if (prod.getStatus() != ServiceStatus.ACTIVE) { throw new ServiceChangedException(ServiceChangedException.Reason.SERVICE_INACCESSIBLE); } if (prod.getVersion() != voProd.getVersion()) { throw new ServiceChangedException(ServiceChangedException.Reason.SERVICE_MODIFIED); } ParameterSet parameterSet = prod.getParameterSet(); if (parameterSet != null) { List<Parameter> parameters = parameterSet.getParameters(); for (Parameter doParameter : parameters) { for (VOParameter param : voProd.getParameters()) { if (param.getKey() == doParameter.getKey() && param.getVersion() != doParameter.getVersion()) { throw new ServiceChangedException(ServiceChangedException.Reason.SERVICE_MODIFIED); } } } } } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscriptionDetails modifySubscription(VOSubscription subscription, List<VOParameter> parameters, List<VOUda> udas) throws NonUniqueBusinessKeyException, ObjectNotFoundException, OperationNotPermittedException, ValidationException, SubscriptionMigrationException, ConcurrentModificationException, TechnicalServiceNotAliveException, OperationPendingException, MandatoryUdaMissingException, SubscriptionStateException, MandatoryCustomerUdaMissingException { ArgumentValidator.notNull("subscription", subscription); Subscription subFromDB = manageBean.checkSubscriptionOwner(subscription.getSubscriptionId(), subscription.getKey()); validateSubscriptionSettings(subscription); validateTriggerProcessForModifySubscription(subscription); PlatformUser currentUser = dataManager.getCurrentUser(); boolean canModify = PermissionCheck.shouldWeProceedWithUpdatingSubscription(subscription, subFromDB, currentUser); VOSubscriptionDetails result; if (canModify) { TriggerProcess triggerProcess = createTriggerProcessForModifySubscription(subscription, parameters, udas); result = processTriggerProcessForSubscriptionModification(subscription, triggerProcess); } else { LocalizerFacade facade = new LocalizerFacade(localizer, currentUser.getLocale()); result = SubscriptionAssembler.toVOSubscriptionDetails(subFromDB, facade); } return result; } private VOSubscriptionDetails processTriggerProcessForSubscriptionModification(VOSubscription subscription, TriggerProcess triggerProcess) throws ObjectNotFoundException, NonUniqueBusinessKeyException, ValidationException, OperationNotPermittedException, SubscriptionMigrationException, ConcurrentModificationException, TechnicalServiceNotAliveException, MandatoryUdaMissingException, MandatoryCustomerUdaMissingException, SubscriptionStateException { VOSubscriptionDetails result = null; TriggerDefinition triggerDefinition = triggerProcess.getTriggerDefinition(); if (triggerDefinition == null) { try { result = modifySubscriptionInt(triggerProcess); } catch (ObjectNotFoundException | NonUniqueBusinessKeyException | OperationNotPermittedException | SubscriptionMigrationException | ConcurrentModificationException | TechnicalServiceNotAliveException | ValidationException | MandatoryUdaMissingException | SubscriptionStateException | MandatoryCustomerUdaMissingException e) { sessionCtx.setRollbackOnly(); throw e; } } else if (triggerDefinition.isSuspendProcess()) { triggerProcess.setTriggerProcessIdentifiers(TriggerProcessIdentifiers .createUpgradeSubscription(dataManager, TriggerType.MODIFY_SUBSCRIPTION, subscription)); dataManager.merge(triggerProcess); } return result; } private TriggerProcess createTriggerProcessForModifySubscription(VOSubscription subscription, List<VOParameter> parameters, List<VOUda> udas) { TriggerMessage message = new TriggerMessage(TriggerType.MODIFY_SUBSCRIPTION); List<TriggerProcessMessageData> list = triggerQS.sendSuspendingMessages(Collections.singletonList(message)); TriggerProcess triggerProcess = list.get(0).getTrigger(); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.OBJECT_ID, subscription.getSubscriptionId()); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.SUBSCRIPTION, subscription); List<VOParameter> params = parameters; if (params == null) { params = new ArrayList<>(); } triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.PARAMETERS, params); triggerProcess.addTriggerProcessParameter(TriggerProcessParameterName.UDAS, udas); return triggerProcess; } private void validateTriggerProcessForModifySubscription(VOSubscription subscription) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); String subID = subscription.getSubscriptionId(); if (validator.isModifyOrUpgradeSubscriptionPending(subscription)) { OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to modify or upgrade the subscription with key '%s'", subID), ReasonEnum.MODIFY_SUBSCRIPTION, new Object[] { subID }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_MODIFY_SUBSCRIPTION_FAILED_DUE_TO_TRIGGER_CONFLICT, subID); throw ope; } } private void validateTriggerProcessForModifySubscriptionBySubscriptionKey(long subscriptionKey, String subscriptionID) throws OperationPendingException { TriggerProcessValidator validator = new TriggerProcessValidator(dataManager); if (validator.isModifySubscriptionPending(subscriptionKey)) { OperationPendingException ope = new OperationPendingException(String.format( "Operation cannot be performed. There is already another pending request to modify or upgrade the subscription with key '%s'", subscriptionID), ReasonEnum.MODIFY_SUBSCRIPTION, new Object[] { subscriptionID }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, ope, LogMessageIdentifier.WARN_ADD_REVOKE_USER_FAILED_DUE_TO_TRIGGER_CONFLICT, subscriptionID); throw ope; } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public VOSubscriptionDetails modifySubscriptionInt(TriggerProcess tp) throws ObjectNotFoundException, NonUniqueBusinessKeyException, ValidationException, OperationNotPermittedException, SubscriptionMigrationException, ConcurrentModificationException, TechnicalServiceNotAliveException, MandatoryUdaMissingException, SubscriptionStateException, MandatoryCustomerUdaMissingException { VOSubscription subscription = tp.getParamValueForName(TriggerProcessParameterName.SUBSCRIPTION) .getValue(VOSubscription.class); List<VOParameter> modifiedParameters = ParameterizedTypes.list( tp.getParamValueForName(TriggerProcessParameterName.PARAMETERS).getValue(List.class), VOParameter.class); List<VOUda> udas = ParameterizedTypes .list(tp.getParamValueForName(TriggerProcessParameterName.UDAS).getValue(List.class), VOUda.class); Subscription dbSubscription = validateSubscriptionSettings(subscription); final PlatformUser currentUser = dataManager.getCurrentUser(); Organization organization = currentUser.getOrganization(); UserGroup unit = getUnit(subscription.getUnitKey(), subscription.getUnitName(), organization.getKey()); stateValidator.checkModifyAllowedForUpdating(dbSubscription); // get initial values of subscription String dbSubscriptionId = dbSubscription.getSubscriptionId(); boolean subIdChanged = !dbSubscriptionId.equals(subscription.getSubscriptionId()); String dbReferenceId = dbSubscription.getPurchaseOrderNumber(); boolean refIdChanged = dbReferenceId == null && subscription.getPurchaseOrderNumber() != null || dbReferenceId != null && !dbReferenceId.equals(subscription.getPurchaseOrderNumber()); PlatformUser dbOwner = dbSubscription.getOwner(); Product dbProduct = dbSubscription.getProduct(); String dbPurchaseNumber = dbSubscription.getPurchaseOrderNumber(); UserGroup dbUnit = dbSubscription.getUserGroup(); // set new values for subscription dbSubscription.setSubscriptionId(subscription.getSubscriptionId()); dbSubscription.setPurchaseOrderNumber(subscription.getPurchaseOrderNumber()); if (currentUser.isOrganizationAdmin() || currentUser.isUnitAdmin()) { dbSubscription.setUserGroup(unit); } manageBean.setSubscriptionOwner(dbSubscription, subscription.getOwnerId(), true); try { appManager.saveAttributes(dbSubscription); } catch (TechnicalServiceOperationException e) { // Log and continue LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_TECH_SERVICE_WS_ERROR, subscription.getSubscriptionId(), e.getMessage()); } List<Uda> existingUdas = manageBean.getExistingUdas(dbSubscription); List<VOUda> updatedUdas = getUpdatedSubscriptionAttributes(udas, existingUdas); boolean changedValues = false; try { changedValues = subIdChanged || refIdChanged || checkIfParametersAreModified(dbSubscription, dbSubscription, dbProduct, dbProduct, modifiedParameters, false) || updatedUdas.size() > 0; } catch (ServiceChangedException e) { throw new ConcurrentModificationException(e.getMessage()); } boolean asynch = dbProduct.getTechnicalProduct().getProvisioningType() .equals(ProvisioningType.ASYNCHRONOUS); logSubscriptionAttributeForEdit(dbSubscription, updatedUdas); logSubscriptionOwner(dbSubscription, dbOwner); if (changedValues && asynch) { long subscriptionKey = dbSubscription.getKey(); modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_SUBSCRIPTIONID, subscription.getSubscriptionId()); modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_ORGANIZATIONID, dbSubscription.getOrganization().getOrganizationId()); modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_PURCHASEORDERNUMBER, subscription.getPurchaseOrderNumber()); modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_OWNERID, subscription.getOwnerId()); modUpgBean.storeModifiedEntity(subscriptionKey, ModifiedEntityType.SUBSCRIPTION_UNIT, String.valueOf(subscription.getUnitKey())); // save subscription attributes before provisioning call, to have // them // available at the API saveUdasForAsyncModifyOrUpgradeSubscription(udas, dbSubscription); } else { saveUdasForSubscription(udas, dbSubscription); } dataManager.flush(); if (changedValues) { copyProductAndModifyParametersForUpdate(dbSubscription, dbProduct, currentUser, modifiedParameters); } if (changedValues && asynch) { // set initial values again dbSubscription.setSubscriptionId(dbSubscriptionId); dbSubscription.setPurchaseOrderNumber(dbPurchaseNumber); String dbOwnerId = dbOwner == null ? null : dbOwner.getUserId(); manageBean.setSubscriptionOwner(dbSubscription, dbOwnerId, true); dbSubscription.setUserGroup(dbUnit); } LocalizerFacade facade = new LocalizerFacade(localizer, currentUser.getLocale()); VOSubscriptionDetails result = SubscriptionAssembler.toVOSubscriptionDetails(dbSubscription, facade); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.MODIFY_SUBSCRIPTION, tp.getTriggerProcessParameters(), dataManager.getCurrentUser().getOrganization())); triggerQS.sendAllNonSuspendingMessages(TriggerMessage.create(TriggerType.SUBSCRIPTION_MODIFICATION, tp.getTriggerProcessParameters(), dbSubscription.getProduct().getVendor())); return result; } /** * Validates the settings of the specified subscription object. * * @param subscription * The subscription to be validated. * @return The domain object representation of the subscription to be * modified. * @throws ValidationException * Thrown in case the settings could not be validated. * @throws ObjectNotFoundException * Thrown in case the subscription could not be found. * @throws OperationNotPermittedException * Thrown in case the caller tries to modify another * organization's object. * @throws NonUniqueBusinessKeyException * Thrown in case there already is a subscription with the given * id for the current organization (only checked in caes of * changing the current id). * @throws ConcurrentModificationException */ private Subscription validateSubscriptionSettings(VOSubscription subscription) throws ValidationException, ObjectNotFoundException, OperationNotPermittedException, NonUniqueBusinessKeyException, ConcurrentModificationException { subscription.setSubscriptionId(BaseAssembler.trim(subscription.getSubscriptionId())); String subscriptionId = subscription.getSubscriptionId(); BLValidator.isId("subscriptionId", subscriptionId, true); BLValidator.isDescription("purchaseOrderNumber", subscription.getPurchaseOrderNumber(), false); Subscription subscriptionToModify = dataManager.getReference(Subscription.class, subscription.getKey()); PermissionCheck.owns(subscriptionToModify, dataManager.getCurrentUser().getOrganization(), LOG); BaseAssembler.verifyVersionAndKey(subscriptionToModify, subscription); String ownerId = subscription.getOwnerId(); if (ownerId != null && ownerId.length() != 0) { checkRolesForSubscriptionOwner(ownerId, dataManager.getCurrentUser().getTenantId()); } if (!subscriptionToModify.getSubscriptionId().equals(subscriptionId)) { Subscription sub = new Subscription(); sub.setOrganization(subscriptionToModify.getOrganization()); sub.setSubscriptionId(subscriptionId); dataManager.validateBusinessKeyUniqueness(sub); // Validate unique subscirptionId and organization in temporary // table Long result = getModifiedEntityDao() .countSubscriptionOfOrganizationAndSubscription(subscriptionToModify, subscriptionId); if (result.longValue() > 0) { NonUniqueBusinessKeyException ex = new NonUniqueBusinessKeyException(); LOG.logError(Log4jLogger.SYSTEM_LOG, ex, LogMessageIdentifier.ERROR_SUBSCRIPTIONID_ALREADY_EXIST_IN_MODIFIEDENTITY, subscriptionId, subscriptionToModify.getOrganization().getOrganizationId()); throw ex; } } return subscriptionToModify; } void checkRolesForSubscriptionOwner(String ownerId, String tenantId) throws ObjectNotFoundException, OperationNotPermittedException { PlatformUser owner = idManager.getPlatformUser(ownerId, tenantId, true); if (!owner.hasSubscriptionOwnerRole()) { String rolesString = UserRoleType.ORGANIZATION_ADMIN + ", " + UserRoleType.SUBSCRIPTION_MANAGER; String message = "Add subscription owner failed. User '%s' does not have required roles: %s"; OperationNotPermittedException onp = new OperationNotPermittedException( String.format(message, ownerId, rolesString)); LOG.logWarn(Log4jLogger.SYSTEM_LOG, onp, LogMessageIdentifier.WARN_ADD_SUBSCRIPTION_OWNER_FAILED, ownerId, rolesString); throw onp; } } /** * To avoid unneeded migrations, check if parameters really have changed and * if they can be changed * * @param dbSourceProduct * the current product * @param voTargetParameters * the parameters to modify * @return <code>true</code> if at least one parameter will be changed. * @throws ConcurrentModificationException * @throws ValidationException */ boolean checkIfParametersAreModified(Subscription subscription, Subscription dbSubscription, Product dbSourceProduct, Product dbTargetProduct, List<VOParameter> voTargetParameters, boolean upgrade) throws SubscriptionMigrationException, ServiceChangedException, ConcurrentModificationException, ValidationException { boolean result = false; verifyIfParameterConcurrentlyChanged(dbTargetProduct, voTargetParameters, upgrade); // load db target product parameter and cache the values for better // performance in a map Map<String, Parameter> dbTargetParameterMap = new HashMap<>(); if (dbTargetProduct.getParameterSet() != null) { for (Parameter dbTargetParameter : dbTargetProduct.getParameterSet().getParameters()) { dbTargetParameterMap.put(dbTargetParameter.getParameterDefinition().getParameterId(), dbTargetParameter); } } Map<String, Parameter> dbSubscriptionParameterMap = new HashMap<>(); if (dbSubscription.getParameterSet() != null) { for (Parameter dbSubParameter : dbSubscription.getParameterSet().getParameters()) { dbSubscriptionParameterMap.put(dbSubParameter.getParameterDefinition().getParameterId(), dbSubParameter); } } for (VOParameter voTargetParameter : voTargetParameters) { String dbParameterId = voTargetParameter.getParameterDefinition().getParameterId(); if (dbSubscriptionParameterMap.containsKey(dbParameterId)) { Parameter dbTargetParameter = dbTargetParameterMap.get(dbParameterId); Parameter dbSubscriptionParameter = dbSubscriptionParameterMap.get(dbParameterId); if (!upgrade && voTargetParameter.getParameterDefinition().getModificationType() .equals(ParameterModificationType.ONE_TIME)) { if (!compareParameterValue(dbTargetParameter, dbSubscriptionParameter)) { throw new ValidationException(ValidationException.ReasonEnum.ONE_TIME_PARAMETER_NOT_ALLOWED, null, new Object[] { dbTargetParameter.getParameterDefinition().getParameterId() }); } } if (upgrade && !isParameterUpOrDowngradeValid(dbTargetParameter, voTargetParameter)) { String sourceProductId = dbSourceProduct.getTemplateOrSelf().getProductId(); String targetProductId = dbTargetProduct.getTemplateOrSelf().getProductId(); SubscriptionMigrationException e = new SubscriptionMigrationException( "Incompatible parameter found", Reason.INCOMPATIBLE_PARAMETER, new Object[] { subscription.getSubscriptionId(), sourceProductId, targetProductId, voTargetParameter.getParameterDefinition().getParameterId() }); LOG.logWarn(Log4jLogger.SYSTEM_LOG | Log4jLogger.AUDIT_LOG, e, LogMessageIdentifier.WARN_NOT_CONFIGURABLE_PARAMETER_OF_SUBSCRIPTION_MODIFIED, dataManager.getCurrentUser().getUserId(), dbParameterId, Long.toString(dbSourceProduct.getKey())); throw e; } if (dbTargetParameter.isValueSet()) { // if currently no value is set, even null or empty as new // value mean a change if (!dbTargetParameter.getValue().equals(voTargetParameter.getValue())) { result = true; } } else { // if no value is currently set, the new value only needs to // be set if not null and not empty if (voTargetParameter.getValue() != null && voTargetParameter.getValue().trim().length() > 0) { result = true; } } } } return result; } /** * Verifies the version of a parameter. * * @param dbTargetProduct * target product * @param voTargetParameters * VO target parameters * @throws ConcurrentModificationException * Is thrown if the parameter version changed or the parameter * does not exist anymore. */ void verifyIfParameterConcurrentlyChanged(Product dbTargetProduct, List<VOParameter> voTargetParameters, boolean upgrade) throws ServiceChangedException, ConcurrentModificationException { List<Parameter> dbTargetParameterList; if (dbTargetProduct.getParameterSet() == null || dbTargetProduct.getParameterSet().getParameters() == null) { dbTargetParameterList = new LinkedList<>(); } else { dbTargetParameterList = dbTargetProduct.getParameterSet().getParameters(); } if (upgrade && dbTargetParameterList.size() != voTargetParameters.size()) { // Parameter changed, thus throw ex throw new ServiceChangedException(ServiceChangedException.Reason.SERVICE_MODIFIED); } Map<String, Parameter> dbParameterMap = new HashMap<>(); for (Parameter parameter : dbTargetParameterList) { dbParameterMap.put(parameter.getParameterDefinition().getParameterId(), parameter); } for (VOParameter voParameter : voTargetParameters) { Parameter dbParameter = dbParameterMap.get(voParameter.getParameterDefinition().getParameterId()); if (dbParameter == null) { String message = String.format("Parameter '%s' does not exist.", voParameter.getParameterDefinition().getParameterId()); throw new ServiceChangedException(message); } BaseAssembler.verifyVersionAndKey(dbParameter, voParameter); } } /** * Matrix : <br/> * 1. NOT CONFIGURABLE or CONFIGURABLE --> NOT CONFIGURABLE = OK, if values * are identical or parameter modification type is one time<br/> * * 2. Otherwise true is returned * * @param dbParameter * source (subscription) parameter * @param targetParameter * VO target parameter * @return see matrix. False is also returned if the parameter id of the * both parameters are not the same */ boolean isParameterUpOrDowngradeValid(Parameter dbParameter, VOParameter targetParameter) { if (!dbParameter.getParameterDefinition().getParameterId() .equals(targetParameter.getParameterDefinition().getParameterId())) { return false; } if (dbParameter.getParameterDefinition().getModificationType() != targetParameter.getParameterDefinition() .getModificationType()) { return false; } boolean targetOneTime = targetParameter.getParameterDefinition().getModificationType() .equals(ParameterModificationType.ONE_TIME); // non-configurable non-onetime parameter now only has default value, // bug #9422 return !(!dbParameter.isConfigurable() && !targetOneTime) || compareParameterValue(dbParameter, targetParameter); } /** * Compares the value of both parameters. A NULL value and empty String are * treated identically. * * @param sourceParameter * source (subscription) parameter * @param targetParameter * target parameter * @return true, if identical, otherwise false */ private boolean compareParameterValue(Parameter sourceParameter, Parameter targetParameter) { String sourceParameterValue = sourceParameter.getValue() == null ? "" : sourceParameter.getValue().trim(); String targetParameterValue = targetParameter.getValue() == null ? "" : targetParameter.getValue().trim(); return sourceParameterValue.equals(targetParameterValue); } /** * Compares the value of both parameters. A NULL value and empty String are * treated identically. * * @param sourceParameter * source (subscription) parameter * @param targetParameter * VO target parameter * @return true, if identical, otherwise false */ private boolean compareParameterValue(Parameter sourceParameter, VOParameter targetParameter) { String sourceParameterValue = sourceParameter.getValue() == null ? "" : sourceParameter.getValue().trim(); String targetParameterValue = targetParameter.getValue() == null ? "" : targetParameter.getValue().trim(); return sourceParameterValue.equals(targetParameterValue); } @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<String> getSubscriptionIdentifiers() throws OrganizationAuthoritiesException { Organization org = dataManager.getCurrentUser().getOrganization(); Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING); return getSubscriptionDao().getSubscriptionIdsForMyCustomers(org, states); } private List<Subscription> getQueryResultListSubIdsAndOrgs(Set<SubscriptionStatus> states) { List<Subscription> result = new ArrayList<>(); result.addAll(executeQueryLoadSubIdsAndOrgsForMyCustomers(states)); Organization org = dataManager.getCurrentUser().getOrganization(); if (org.hasRole(OrganizationRoleType.SUPPLIER)) { result.addAll(executeQueryLoadSubIdsAndOrgsForMyBrokerCustomers()); } return result; } List<Subscription> getQueryResultListSubIdsAndOrgs(Set<SubscriptionStatus> states, Pagination pagination) { List<Subscription> result = new ArrayList<>(); Organization org = dataManager.getCurrentUser().getOrganization(); if (org.hasRole(OrganizationRoleType.SUPPLIER)) { result.addAll(executeQueryLoadSubIdsAndOrgsForMyBrokerCustomers(states, pagination)); } else { result.addAll(executeQueryLoadSubIdsAndOrgsForMyCustomers(states, pagination)); } return result; } List<Subscription> executeQueryLoadSubIdsAndOrgsForMyCustomers(Set<SubscriptionStatus> states) { Organization org = dataManager.getCurrentUser().getOrganization(); return getSubscriptionDao().getSubscriptionsForMyCustomers(org, states); } List<Subscription> executeQueryLoadSubIdsAndOrgsForMyBrokerCustomers() { Organization org = dataManager.getCurrentUser().getOrganization(); return getSubscriptionDao().getSubscriptionsForMyBrokerCustomers(org); } List<Subscription> executeQueryLoadSubIdsAndOrgsForMyCustomers(Set<SubscriptionStatus> states, Pagination pagination) { return getSubscriptionDao().getSubscriptionsForMyCustomers(dataManager.getCurrentUser(), states, pagination); } List<Subscription> executeQueryLoadSubIdsAndOrgsForMyBrokerCustomers(Set<SubscriptionStatus> states, Pagination pagination) { return getSubscriptionDao().getSubscriptionsForMyBrokerCustomers(dataManager.getCurrentUser(), states, pagination); } @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<VOOrganization> getCustomersForSubscriptionId(String subscriptionId) throws OrganizationAuthoritiesException { ArgumentValidator.notNull("subscriptionId", subscriptionId); List<VOOrganization> result = new ArrayList<>(); Organization offerer = dataManager.getCurrentUser().getOrganization(); Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING); List<Organization> customers = getOrganizationDao().getCustomersForSubscriptionId(offerer, subscriptionId, states); LocalizerFacade lf = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); for (Organization customer : customers) { result.add(OrganizationAssembler.toVOOrganization(customer, false, lf, PerformanceHint.ONLY_IDENTIFYING_FIELDS)); } return result; } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public boolean expireOverdueSubscriptions(long currentTime) { return terminateBean.expireOverdueSubscriptions(currentTime); } @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public boolean expireSubscription(Subscription subscriptionToExpire) { return terminateBean.expireSubscription(subscriptionToExpire); } /** * Checks if the usercount of the subscription is lower or equal to the * value of the product parameter NAMED_USER. If this condition does not * hold, an ProductParameterException will be thrown. * * @param subscription * The subscription to be checked. * @throws ServiceParameterException * Thrown in case the user count of the subscription is greater * than the value of the product parameter NAMED_USER */ void verifyParameterNamedUser(Subscription subscription) throws ServiceParameterException { if (subscription == null) { return; } ParameterSet parameterSet = subscription.getParameterSet(); if (parameterSet == null || parameterSet.getParameters() == null) { return; } for (Parameter parameter : parameterSet.getParameters()) { if (parameter.getParameterDefinition().getParameterType() == ParameterType.PLATFORM_PARAMETER && PlatformParameterIdentifiers.NAMED_USER .equals(parameter.getParameterDefinition().getParameterId())) { // if it is not defined to be mandatory, it can be empty if (Strings.isEmpty(parameter.getValue())) { return; } try { // If a value is set, it must be ensured by validation that // it is an integer - so the NumberFormatException should // not happen if (subscription.getActiveUsers().size() > Integer.parseInt(parameter.getValue())) { sessionCtx.setRollbackOnly(); String text = "Subscription '" + subscription.getSubscriptionId() + "'/Product '" + subscription.getProduct().getProductId() + "'"; ServiceParameterException e = new ServiceParameterException(text, ParameterType.PLATFORM_PARAMETER, PlatformParameterIdentifiers.NAMED_USER, new Object[] { parameter.getValue(), subscription.getSubscriptionId() }); LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_TOO_MANY_NAMED_USER_FOR_SUBSCRIPTION, subscription.getSubscriptionId(), subscription.getProduct().getProductId()); throw e; } } catch (NumberFormatException e) { LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_PROCESS_NAMED_USER_FAILED_AS_NUMBER_FORMAT, parameter.getValue(), subscription.getProduct().getProductId()); } } } } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public void informProductAboutNewUsers(Subscription sub, List<PlatformUser> users) throws SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { stateValidator.checkInformProductAboutNewUsers(sub); if (!canModifyApplicationUsers(sub)) { return; } List<UsageLicense> licenses = getUsageLicensesForUsers(sub, users); User[] createdUsers = appManager.createUsers(sub, licenses); mapApplicationUserIdToLicense(licenses, createdUsers); } /** * Maps the application user id back to the usage license and sets the * affected usage license dirty * * @param licenses * the usage licenses to update * @param createdUsers * the user array containing the application user id */ private void mapApplicationUserIdToLicense(List<UsageLicense> licenses, User[] createdUsers) { if (createdUsers == null) { return; } String applicationUserId; for (UsageLicense license : licenses) { for (User createdUser : createdUsers) { applicationUserId = createdUser.getApplicationUserId(); if (createdUser.getUserId().equals(license.getUser().getUserId()) && applicationUserId != null) { license.setApplicationUserId(applicationUserId); break; } } } } /** * Determines the usage license for the given users of the subscription. * * @param sub * the subscription to get the usage licenses from * @param users * the users to get the usage licenses for * @return the list of usage licenses */ private List<UsageLicense> getUsageLicensesForUsers(Subscription sub, List<PlatformUser> users) { List<UsageLicense> result = new ArrayList<>(); List<UsageLicense> licenses = sub.getUsageLicenses(); for (PlatformUser user : users) { for (UsageLicense license : licenses) { if (user.getKey() == license.getUser().getKey()) { result.add(license); } } } return result; } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void abortAsyncSubscription(String subscriptionId, String organizationId, List<VOLocalizedText> reason) throws ObjectNotFoundException, SubscriptionStateException, OrganizationAuthoritiesException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); manageBean.validateTechnoloyProvider(subscription); if (subscription.getStatus() != SubscriptionStatus.PENDING) { throw new SubscriptionStateException( "Operation not allowed for subscription " + subscription.getSubscriptionId(), SubscriptionStateException.Reason.ONLY_PENDING); } abortSubscription(subscriptionId, subscription); removeLocalizedResources(reason, subscription); deleteProduct(subscription); List<PlatformUser> receivers = loadReceiversForAbortAsyncSubscription(subscription); sendSubscriptionAbortEmail(subscriptionId, organizationId, subscription, receivers); } private void deleteProduct(Subscription subscription) { Product product = subscription.getProduct(); product.setStatus(ServiceStatus.DELETED); } private void abortSubscription(String subscriptionId, Subscription subscription) { subscription.setStatus(SubscriptionStatus.INVALID); subscription.setSubscriptionId(subscriptionId + "#" + String.valueOf(System.currentTimeMillis())); } void removeLocalizedResources(List<VOLocalizedText> reason, Subscription subscription) { long key = subscription.getKey(); localizer.removeLocalizedValues(key, LocalizedObjectTypes.SUBSCRIPTION_PROVISIONING_PROGRESS); localizer.storeLocalizedResources(key, LocalizedObjectTypes.SUBSCRIPTION_PROVISIONING_PROGRESS, reason); } void sendSubscriptionAbortEmail(String subscriptionId, String organizationId, Subscription subscription, List<PlatformUser> receivers) { for (PlatformUser platformUser : receivers) { LocalizerFacade facade = new LocalizerFacade(localizer, platformUser.getLocale()); String text = facade.getText(subscription.getKey(), LocalizedObjectTypes.SUBSCRIPTION_PROVISIONING_PROGRESS); try { commService.sendMail(platformUser, EmailType.SUBSCRIPTION_INVALIDATED, new Object[] { subscriptionId, organizationId, text }, subscription.getMarketplace()); } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_INVALIDATION_SUBSCRIPTION_CONFIRMING_FAILED); } } } /** * The mail for aborting asynchronous subscription is sent to: * * - administrators of the technology provider organization;<br/> * - administrators of the customer organization;<br/> * - subscription owner if it is not already administrator. */ List<PlatformUser> loadReceiversForAbortAsyncSubscription(Subscription subscription) { return manageBean.getCustomerAndTechnicalProductAdminForSubscription(subscription); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void completeAsyncSubscription(String subscriptionId, String organizationId, VOInstanceInfo instanceInfo) throws ObjectNotFoundException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OrganizationAuthoritiesException, OperationNotPermittedException, ValidationException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("instance", instanceInfo); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); String productInstanceId = instanceInfo.getInstanceId(); TechnicalProduct techProduct = subscription.getProduct().getTechnicalProduct(); if (getSubscriptionDao().checkIfProductInstanceIdExists(productInstanceId, techProduct)) { ValidationException ex = new ValidationException("The product instance ID already exist"); LOG.logError(Log4jLogger.SYSTEM_LOG, ex, LogMessageIdentifier.ERROR_PRODUCT_INSTANCE_ID_ALREADY_EXIST); throw ex; } updateInstanceInfoForCompletion(subscription, instanceInfo); manageBean.validateTechnoloyProvider(subscription); if (subscription.getStatus() != SubscriptionStatus.PENDING) { throw new SubscriptionStateException( "Operation not allowed for subscription " + subscription.getSubscriptionId(), SubscriptionStateException.Reason.ONLY_PENDING); } subscription.setActivationDate(Long.valueOf(DateFactory.getInstance().getTransactionTime())); boolean deactivateInstance = !modUpgBean.isPaymentValidOrFree(subscription); if (deactivateInstance) { subscription.setStatus(SubscriptionStatus.SUSPENDED); } else { activateSubscriptionFirstTime(subscription); } NotifyProvisioningServicePayload payload = new NotifyProvisioningServicePayload(subscription.getKey(), deactivateInstance); TaskMessage message = new TaskMessage(NotifyProvisioningServiceHandler.class, payload); tqs.sendAllMessages(Collections.singletonList(message)); PriceModel pm = subscription.getProduct().getPriceModel(); pm.setProvisioningCompleted(true); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void updateAsyncSubscriptionProgress(String subscriptionId, String organizationId, List<VOLocalizedText> progress) throws ObjectNotFoundException, SubscriptionStateException, OrganizationAuthoritiesException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); manageBean.validateTechnoloyProvider(subscription); if (subscription.getStatus() != SubscriptionStatus.PENDING) { throw new SubscriptionStateException( "Operation not allowed for subscription " + subscription.getSubscriptionId(), SubscriptionStateException.Reason.ONLY_PENDING); } localizer.storeLocalizedResources(subscription.getKey(), LocalizedObjectTypes.SUBSCRIPTION_PROVISIONING_PROGRESS, progress); } @Override @TransactionAttribute(TransactionAttributeType.MANDATORY) public boolean notifyAboutTimedoutSubscriptions(long currentTime) { // we could also use a separate query inclusing the checked conditions List<Subscription> subscriptions = getSubscriptionDao() .getSubscriptionsByStatus(SubscriptionStatus.PENDING); for (Subscription subscription : subscriptions) { Long timeout = subscription.getProduct().getTechnicalProduct().getProvisioningTimeout(); if (!subscription.isTimeoutMailSent() && subscription.getCreationDate().longValue() + timeout.longValue() < currentTime) { List<PlatformUser> users = subscription.getProduct().getTechnicalProduct().getOrganization() .getPlatformUsers(); if (notifyTechnicalAdminsAboutTimeout(subscription, users)) { // if at least one mail was sent, set the mail sent flag of // the subscription subscription.setTimeoutMailSent(true); } } } return true; } /** * Tries to send a notification mail to the admin users contained in the * list of users of the technical product owner organization informing about * a timed out pending subscription. * * @param subscription * the subscription that timed ot in pending state * @param users * the users of the organization owning the technical product * @return <code>true</code> if at leas one mail was sent otherwise * <code>false</code> */ private boolean notifyTechnicalAdminsAboutTimeout(Subscription subscription, List<PlatformUser> users) { boolean result = false; String organizationId = subscription.getOrganization().getOrganizationId(); String subscriptionId = subscription.getSubscriptionId(); for (PlatformUser user : users) { if (!user.isOrganizationAdmin()) { continue; } try { commService.sendMail(user, EmailType.SUBSCRIPTION_TIMEDOUT, new Object[] { subscriptionId, organizationId }, subscription.getMarketplace()); // at least one admin could be informed result = true; } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_TIMEOUT_OF_SUBSCRIPTION_NOTIFYING_FAILED); } } return result; } /** * Checks if the users of the subscription can be modified. this is not * possible if the subscription is in pending or invalid state because no * product instance is there in this case. * * @param subscription * the subscription to modify the application side users for * @return <code>true</code> if product instance can be informed otherwise * <code>false</code> */ private boolean canModifyApplicationUsers(Subscription subscription) { return !(subscription.getStatus() == SubscriptionStatus.PENDING || subscription.getStatus() == SubscriptionStatus.INVALID); } /** * It returns a list of VOSubscriptionIdAndOrganizations objects, which * contain the subscriptionIdentifier and the associated customers of the * subscription in form of list.If there are no subscriptions an empty list * is returned. * * The role of organization as a supplier, a broker or a reseller is needed * to execute this method. * * @return List<VOSubscriptionIdAndOrganizations> */ @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<VOSubscriptionIdAndOrganizations> getCustomerSubscriptions() throws OrganizationAuthoritiesException { Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING); LocalizerFacade lf = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); List<Subscription> queryResultList = getQueryResultListSubIdsAndOrgs(states); Map<String, VOSubscriptionIdAndOrganizations> mapSubIdsAndOrgs = getSubIdsAndOrgs(lf, queryResultList); return new ArrayList<>(mapSubIdsAndOrgs.values()); } /** * It returns a list of VOSubscriptionIdAndOrganizations objects, which * contain the subscriptionIdentifier and the associated customers of the * subscription in form of list.If there are no subscriptions an empty list * is returned. * * The role of organization as a supplier, a broker or a reseller is needed * to execute this method. * * @return List<VOSubscriptionIdAndOrganizations> */ @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<VOSubscriptionIdAndOrganizations> getSubscriptionsForTerminate() throws OrganizationAuthoritiesException { Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING, SubscriptionStatus.EXPIRED); LocalizerFacade lf = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); List<Subscription> queryResultList = getQueryResultListSubIdsAndOrgs(states); Map<String, VOSubscriptionIdAndOrganizations> mapSubIdsAndOrgs = getSubIdsAndOrgs(lf, queryResultList); return new ArrayList<>(mapSubIdsAndOrgs.values()); } private Map<String, VOSubscriptionIdAndOrganizations> getSubIdsAndOrgs(LocalizerFacade lf, List<Subscription> subscriptionList) { Map<String, VOSubscriptionIdAndOrganizations> mapSubIdsAndOrgs = new LinkedHashMap<>(); for (Subscription subscription : subscriptionList) { String subId = subscription.getSubscriptionId(); Organization customer = subscription.getOrganization(); if (mapSubIdsAndOrgs.containsKey(subId)) { VOSubscriptionIdAndOrganizations subAndOrgs = mapSubIdsAndOrgs.get(subId); List<VOOrganization> customersForSubId = subAndOrgs.getOrganizations(); customersForSubId.add(OrganizationAssembler.toVOOrganization(customer, false, lf, PerformanceHint.ONLY_IDENTIFYING_FIELDS)); subAndOrgs.setOrganizations(customersForSubId); mapSubIdsAndOrgs.put(subId, subAndOrgs); } else { VOSubscriptionIdAndOrganizations subAndOrgs = new VOSubscriptionIdAndOrganizations(); subAndOrgs.setSubscriptionId(subId); List<VOOrganization> customers = new ArrayList<>(); customers.add(OrganizationAssembler.toVOOrganization(customer, false, lf, PerformanceHint.ONLY_IDENTIFYING_FIELDS)); subAndOrgs.setOrganizations(customers); mapSubIdsAndOrgs.put(subId, subAndOrgs); } } return mapSubIdsAndOrgs; } @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public VOSubscriptionDetails getSubscriptionForCustomer(String organizationId, String subscriptionId) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("subscriptionId", subscriptionId); // find organization of customer Organization customerTmpl = new Organization(); customerTmpl.setOrganizationId(organizationId); Organization customer = (Organization) dataManager.getReferenceByBusinessKey(customerTmpl); final PlatformUser currentUser = dataManager.getCurrentUser(); PermissionCheck.sellerOfCustomer(currentUser.getOrganization(), customer, LOG, sessionCtx); Subscription subscriptionTmpl = new Subscription(); subscriptionTmpl.setSubscriptionId(subscriptionId); subscriptionTmpl.setOrganizationKey(customer.getKey()); Subscription subscription = (Subscription) dataManager.getReferenceByBusinessKey(subscriptionTmpl); // fill subscription VO LocalizerFacade facade = new LocalizerFacade(localizer, currentUser.getLocale()); VOSubscriptionDetails voSubscriptionDetail = SubscriptionAssembler.toVOSubscriptionDetails(subscription, facade); // log view subscription audit.viewSubscription(dataManager, subscription, customer); return voSubscriptionDetail; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public List<VORoleDefinition> getServiceRolesForSubscription(String subscriptionId) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); Subscription sub = manageBean.checkSubscriptionOwner(subscriptionId, 0); return getServiceRolesForSubscription(sub); } @Override public List<VORoleDefinition> getServiceRolesForSubscription(long subscriptionKey) throws ObjectNotFoundException, OperationNotPermittedException { Subscription sub = manageBean.checkSubscriptionOwner(null, subscriptionKey); return getServiceRolesForSubscription(sub); } private List<VORoleDefinition> getServiceRolesForSubscription(Subscription subscription) { LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); List<RoleDefinition> roleDefinitions = subscription.getProduct().getTechnicalProduct().getRoleDefinitions(); return RoleAssembler.toVORoleDefinitions(roleDefinitions, facade); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public List<VORoleDefinition> getServiceRolesForService(VOService service) throws ObjectNotFoundException, OperationNotPermittedException { ArgumentValidator.notNull("service", service); Product prod = dataManager.getReference(Product.class, service.getKey()); LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); List<RoleDefinition> roleDefinitions = prod.getTechnicalProduct().getRoleDefinitions(); return RoleAssembler.toVORoleDefinitions(roleDefinitions, facade); } @Override public void executeServiceOperation(VOSubscription subscription, VOTechnicalServiceOperation operation) throws ObjectNotFoundException, OperationNotPermittedException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OrganizationAuthoritiesException, ConcurrentModificationException, ValidationException, SubscriptionStateException, NonUniqueBusinessKeyException { ArgumentValidator.notNull("subscription", subscription); ArgumentValidator.notNull("operation", operation); Subscription sub = manageBean.loadSubscription(subscription.getSubscriptionId(), 0); stateValidator.checkExecuteServiceOperationAllowed(sub); TechnicalProductOperation op = dataManager.getReference(TechnicalProductOperation.class, operation.getKey()); BaseAssembler.verifyVersionAndKey(sub, subscription); BaseAssembler.verifyVersionAndKey(op, operation); List<VOServiceOperationParameter> list = operation.getOperationParameters(); Map<String, String> params = new HashMap<>(); if (list != null) { for (VOServiceOperationParameter p : list) { params.put(p.getParameterId(), p.getParameterValue()); } } checkIfParamsModified(op, list); String transactionid = IdGenerator.generateArtificialIdentifier(); OperationResult result = manageBean.executeServiceOperation(sub, op, params, transactionid); createOperationRecord(sub, op, transactionid, result.isAsyncExecution()); } private void createOperationRecord(Subscription subscription, TechnicalProductOperation operation, String transactionid, boolean isAsyncExecution) throws NonUniqueBusinessKeyException { Date executiondate = DateFactory.getInstance().getTransactionDate(); OperationRecord record = givenOperationRecord(subscription, operation, executiondate, transactionid, isAsyncExecution); operationRecordBean.createOperationRecord(record); } OperationRecord givenOperationRecord(Subscription subscription, TechnicalProductOperation operation, Date executiondate, String transactionid, boolean isAsyncExecution) { OperationRecord record = new OperationRecord(); record.setTransactionid(transactionid); record.setSubscription(subscription); record.setTechnicalProductOperation(operation); record.setExecutiondate(executiondate); record.setUser(dataManager.getCurrentUser()); if (isAsyncExecution) { record.setStatus(OperationStatus.RUNNING); } else { record.setStatus(OperationStatus.COMPLETED); } return record; } private void checkIfParamsModified(TechnicalProductOperation op, List<VOServiceOperationParameter> list) throws ConcurrentModificationException { List<OperationParameter> opParams = op.getParameters(); Map<Long, OperationParameter> opParamsMap = new HashMap<>(); if (opParams != null) { for (OperationParameter p : opParams) { opParamsMap.put(Long.valueOf(p.getKey()), p); } } if (opParamsMap.size() != list.size()) { throw new ConcurrentModificationException(); } for (VOServiceOperationParameter parameter : list) { OperationParameter param = opParamsMap.get(Long.valueOf(parameter.getKey())); if (param == null) { ConcurrentModificationException cme = new ConcurrentModificationException(parameter); LOG.logWarn(Log4jLogger.SYSTEM_LOG, cme, LogMessageIdentifier.WARN_CONCURRENT_MODIFICATION, parameter.getClass().getSimpleName()); throw cme; } else { BaseAssembler.verifyVersionAndKey(param, parameter); } } } @Override @RolesAllowed({ "SERVICE_MANAGER", "RESELLER_MANAGER" }) public void terminateSubscription(VOSubscription subscrVO, String reason) throws ObjectNotFoundException, OrganizationAuthoritiesException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, ConcurrentModificationException, SubscriptionStateException { ArgumentValidator.notNull("subscription", subscrVO); Subscription subscription = loadSubscription(subscrVO.getKey()); BaseAssembler.verifyVersionAndKey(subscription, subscrVO); // Remove corresponding operation record of the subscription operationRecordBean.removeOperationsForSubscription(subscription.getKey()); terminateBean.terminateSubscription(subscription, reason); } @Override public boolean hasCurrentUserSubscriptions() { String userKey = sessionCtx.getCallerPrincipal().getName(); Long userKeyLong; try { userKeyLong = Long.valueOf(userKey); } catch (NumberFormatException e) { SaaSSystemException saaSSystemException = new SaaSSystemException(e); LOG.logError(Log4jLogger.SYSTEM_LOG, saaSSystemException, LogMessageIdentifier.ERROR_WRONG_USER_KEY_IN_SESSION, userKey); throw saaSSystemException; } Long count = getSubscriptionDao().hasCurrentUserSubscriptions(userKeyLong, VISIBLE_SUBSCRIPTION_STATUS); return count.longValue() > 0; } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public VOSubscriptionDetails modifySubscriptionPaymentData(VOSubscription subscription, VOBillingContact billingContact, VOPaymentInfo paymentInfo) throws ObjectNotFoundException, ConcurrentModificationException, OperationNotPermittedException, PaymentInformationException, SubscriptionStateException, PaymentDataException, TechnicalServiceNotAliveException, TechnicalServiceOperationException { ArgumentValidator.notNull("subscription", subscription); Subscription sub = manageBean.checkSubscriptionOwner(subscription.getSubscriptionId(), subscription.getKey()); PlatformUser user = dataManager.getCurrentUser(); Organization customer = user.getOrganization(); PermissionCheck.owns(sub, customer, LOG); BaseAssembler.verifyVersionAndKey(sub, subscription); EnumSet<SubscriptionStatus> set = EnumSet.of(SubscriptionStatus.DEACTIVATED, SubscriptionStatus.EXPIRED, SubscriptionStatus.INVALID); if (set.contains(sub.getStatus())) { Object[] params = new Object[] { sub.getStatus().name() }; SubscriptionStateException sse = new SubscriptionStateException( SubscriptionStateException.Reason.SUBSCRIPTION_INVALID_STATE, null, params); LOG.logWarn(Log4jLogger.SYSTEM_LOG, sse, LogMessageIdentifier.WARN_SUBSCRIPTION_STATE_INVALID, sub.getStatus().name()); throw sse; } if (!sub.getPriceModel().isChargeable()) { if (billingContact != null || paymentInfo != null) { PaymentDataException pde = new PaymentDataException( "No payment info required for a non-chargeable price model", PaymentDataException.Reason.NO_PAYMENT_REQUIRED); LOG.logWarn(Log4jLogger.SYSTEM_LOG, pde, LogMessageIdentifier.WARN_NO_PAYMENT_INFO_REQUIRED); throw pde; } } PaymentInfo pi = null; BillingContact bc = null; if (sub.getPriceModel().isChargeable()) { PaymentDataValidator.validateNotNull(paymentInfo, billingContact); pi = dataManager.getReference(PaymentInfo.class, paymentInfo.getKey()); bc = dataManager.getReference(BillingContact.class, billingContact.getKey()); PermissionCheck.owns(pi, customer, LOG); PermissionCheck.owns(bc, customer, LOG); BaseAssembler.verifyVersionAndKey(pi, paymentInfo); BaseAssembler.verifyVersionAndKey(bc, billingContact); PaymentDataValidator.validatePaymentTypeSupportedBySupplier(customer, sub.getProduct(), pi.getPaymentType()); PaymentDataValidator.validatePaymentInfoDataForUsage(pi); SubscriptionStatus current = sub.getStatus(); if (current.isSuspendedOrSuspendedUpd()) { try { appManager.activateInstance(sub); } catch (TechnicalServiceNotAliveException | TechnicalServiceOperationException e) { sessionCtx.setRollbackOnly(); throw e; } sub.setStatus(current.getNextForPaymentTypeRevoked()); } } // log before subscription changed! audit.editPaymentType(dataManager, sub, pi); audit.editBillingAddress(dataManager, sub, bc); // change subscription sub.setPaymentInfo(pi); sub.setBillingContact(bc); // flush to get the correct version of the subscription dataManager.flush(); return SubscriptionAssembler.toVOSubscriptionDetails(sub, new LocalizerFacade(localizer, user.getLocale())); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER" }) public void reportIssue(String subscriptionId, String subject, String issueText) throws IllegalArgumentException, ObjectNotFoundException, OperationNotPermittedException, MailOperationException, ValidationException { manageBean.reportIssue(subscriptionId, subject, issueText); } void activateSubscriptionFirstTime(Subscription subscription) { subscription.setStatus(SubscriptionStatus.ACTIVE); Organization chargingOrganization = subscription.getProduct().determineChargingOrganization(); subscription.setCutOffDay(chargingOrganization.getCutOffDay()); } /** * Check if PaymentInfo or BillingContact is concurrently modified. * * @param paymentInfo * the existing PaymentInfo stored in database * @param billingContact * the existing BillingContact stored in database * @param voPaymentInfo * @param voBillingContact * @throws ConcurrentModificationException */ void validatePaymentInfoAndBillingContact(PaymentInfo paymentInfo, BillingContact billingContact, VOPaymentInfo voPaymentInfo, VOBillingContact voBillingContact) throws ConcurrentModificationException { BaseAssembler.verifyVersionAndKey(paymentInfo, voPaymentInfo); BaseAssembler.verifyVersionAndKey(billingContact, voBillingContact); } @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<Subscription> getSubscriptionsForManagers() throws OrganizationAuthoritiesException { Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING, SubscriptionStatus.EXPIRED, SubscriptionStatus.PENDING_UPD, SubscriptionStatus.SUSPENDED, SubscriptionStatus.SUSPENDED_UPD); return getQueryResultListSubIdsAndOrgs(states); } @Override @RolesAllowed({ "SERVICE_MANAGER", "BROKER_MANAGER", "RESELLER_MANAGER" }) public List<Subscription> getSubscriptionsForManagers(Pagination pagination) throws OrganizationAuthoritiesException { Set<SubscriptionStatus> states = EnumSet.of(SubscriptionStatus.ACTIVE, SubscriptionStatus.PENDING, SubscriptionStatus.EXPIRED, SubscriptionStatus.PENDING_UPD, SubscriptionStatus.SUSPENDED, SubscriptionStatus.SUSPENDED_UPD); return getQueryResultListSubIdsAndOrgs(states, pagination); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void completeAsyncModifySubscription(String subscriptionId, String organizationId, VOInstanceInfo instance) throws ObjectNotFoundException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OrganizationAuthoritiesException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("instance", instance); Subscription subscription = modUpgBean.findSubscriptionForAsyncCallBack(subscriptionId, organizationId); stateValidator.checkCompleteModifyAllowed(subscription); updateInstanceInfoForCompletion(subscription, instance); manageBean.validateTechnoloyProvider(subscription); modUpgBean.setStatusForModifyComplete(subscription); Map<String, Parameter> paramMap = new HashMap<>(); if (subscription.getParameterSet() != null) { for (Parameter parameter : subscription.getParameterSet().getParameters()) { paramMap.put(parameter.getParameterDefinition().getParameterId(), parameter); } } Product asyncTempProduct = subscription.getAsyncTempProduct(); if (asyncTempProduct.getParameterSet() != null) { for (Parameter tempParam : asyncTempProduct.getParameterSet().getParameters()) { String parameterID = tempParam.getParameterDefinition().getParameterId(); Parameter param = paramMap.get(parameterID); if (param != null) { param.setValue(tempParam.getValue()); } } } subscription.setAsyncTempProduct(null); dataManager.remove(asyncTempProduct); modUpgBean.updateSubscriptionAttributesForAsyncUpdate(subscription); List<PlatformUser> receivers = manageBean.getCustomerAdminsAndSubscriptionOwner(subscription); for (PlatformUser platformUser : receivers) { try { commService.sendMail(platformUser, EmailType.SUBSCRIPTION_PARAMETER_MODIFIED, new Object[] { subscriptionId }, subscription.getMarketplace()); } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_MODIFY_PARAMETER_OF_SUBSCRIPTION_CONFIRMING_FAILED); } } } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void abortAsyncModifySubscription(String subscriptionId, String organizationId, List<VOLocalizedText> reason) throws ObjectNotFoundException, SubscriptionStateException, OrganizationAuthoritiesException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); Subscription subscription = modUpgBean.findSubscriptionForAsyncCallBack(subscriptionId, organizationId); stateValidator.checkAbortAllowedForModifying(subscription); abortAsyncUpgradeOrModifySubscription(subscription, organizationId, reason); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void completeAsyncUpgradeSubscription(String subscriptionId, String organizationId, VOInstanceInfo instance) throws ObjectNotFoundException, TechnicalServiceOperationException, OperationNotPermittedException, SubscriptionStateException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("instance", instance); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); stateValidator.checkCompleteUpgradeAllowed(subscription); updateInstanceInfoForCompletion(subscription, instance); manageBean.validateTechnoloyProvider(subscription); Product initialProduct = subscription.getProduct(); Product asyncTempProduct = subscription.getAsyncTempProduct(); subscription.bindToProduct(asyncTempProduct); subscription.setAsyncTempProduct(null); modUpgBean.updateSubscriptionAttributesForAsyncUpgrade(subscription); modUpgBean.setStatusForUpgradeComplete(subscription); String oldServiceId = initialProduct.getTemplate() != null ? initialProduct.getTemplate().getProductId() : initialProduct.getProductId(); String newServiceId = asyncTempProduct.getTemplate() != null ? asyncTempProduct.getTemplate().getProductId() : asyncTempProduct.getProductId(); dataManager.remove(initialProduct); dataManager.flush(); PriceModel pm = subscription.getProduct().getPriceModel(); pm.setProvisioningCompleted(true); if (subscription.getStatus() == SubscriptionStatus.SUSPENDED) { manageBean.suspend(subscription); } modUpgBean.sendConfirmUpgradationEmail(subscription, oldServiceId, newServiceId, instance.getAccessInfo()); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void abortAsyncUpgradeSubscription(String subscriptionId, String organizationId, List<VOLocalizedText> reason) throws ObjectNotFoundException, SubscriptionStateException, OrganizationAuthoritiesException, OperationNotPermittedException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); stateValidator.checkAbortAllowedForUpgrading(subscription); abortAsyncUpgradeOrModifySubscription(subscription, organizationId, reason); } void abortAsyncUpgradeOrModifySubscription(Subscription subscription, String organizationId, List<VOLocalizedText> reason) throws OperationNotPermittedException { manageBean.validateTechnoloyProvider(subscription); final SubscriptionStatus currentState = subscription.getStatus(); subscription.setStatus(currentState.getNextForAbort()); Product asyncTempProduct = subscription.getAsyncTempProduct(); subscription.setAsyncTempProduct(null); dataManager.remove(asyncTempProduct); modUpgBean.deleteModifiedEntityForSubscription(subscription); // send notify mail to administrators of the customer organization, // subscription owner and technology provider organization sendAbortAsyncModifySubscriptionEmail(subscription, organizationId, reason); } void sendAbortAsyncModifySubscriptionEmail(Subscription subscription, String organizationId, List<VOLocalizedText> reason) { localizer.removeLocalizedValues(subscription.getKey(), LocalizedObjectTypes.SUBSCRIPTION_MODIFICATION_REASON); localizer.storeLocalizedResources(subscription.getKey(), LocalizedObjectTypes.SUBSCRIPTION_MODIFICATION_REASON, reason); dataManager.flush(); List<PlatformUser> receivers = manageBean.getCustomerAndTechnicalProductAdminForSubscription(subscription); for (PlatformUser platformUser : receivers) { LocalizerFacade facade = new LocalizerFacade(localizer, platformUser.getLocale()); String text = facade.getText(subscription.getKey(), LocalizedObjectTypes.SUBSCRIPTION_MODIFICATION_REASON); try { commService.sendMail(platformUser, EmailType.SUBSCRIPTION_PARAMETER_MODIFY_ABORT, new Object[] { subscription.getSubscriptionId(), organizationId, text }, subscription.getMarketplace()); } catch (MailOperationException e) { LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_MODIFY_PARAMETER_OF_SUBSCRIPTION_ABORT_CONFIRMING_FAILED); } } } @Override public List<VOServiceOperationParameterValues> getServiceOperationParameterValues(VOSubscription subscription, VOTechnicalServiceOperation operation) throws ObjectNotFoundException, OperationNotPermittedException, TechnicalServiceNotAliveException, ConcurrentModificationException, TechnicalServiceOperationException { ArgumentValidator.notNull("subscription", subscription); ArgumentValidator.notNull("operation", operation); Subscription sub = manageBean.loadSubscription(subscription.getSubscriptionId(), 0); TechnicalProductOperation op = dataManager.getReference(TechnicalProductOperation.class, operation.getKey()); BaseAssembler.verifyVersionAndKey(sub, subscription); BaseAssembler.verifyVersionAndKey(op, operation); Map<String, List<String>> operationParameterValues = manageBean.getOperationParameterValues(sub, op); List<VOServiceOperationParameterValues> result = new LinkedList<>(); LocalizerFacade facade = new LocalizerFacade(localizer, dataManager.getCurrentUser().getLocale()); for (Entry<String, List<String>> e : operationParameterValues.entrySet()) { OperationParameter param = op.findParameter(e.getKey()); if (param != null) { VOServiceOperationParameterValues vo = TechServiceOperationParameterAssembler .toVOServiceOperationParameterValues(param, facade, e.getValue()); result.add(vo); } } return result; } void updateInstanceInfoForCompletion(Subscription subscription, VOInstanceInfo info) throws TechnicalServiceOperationException { new SubscriptionInstanceInfo(localizer, subscription).validateAndUpdateInstanceInfoForCompletion(info); } @Override @RolesAllowed("TECHNOLOGY_MANAGER") public void updateAccessInformation(String subscriptionId, String organizationId, VOInstanceInfo instance) throws ObjectNotFoundException, SubscriptionStateException, OperationNotPermittedException, ValidationException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("instance", instance); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); manageBean.validateTechnoloyProvider(subscription); checkIPAddressChangedAndSendMailToUsers(subscription, instance); updateInstanceInfo(subscription, instance); } void checkIPAddressChangedAndSendMailToUsers(Subscription subscription, VOInstanceInfo instance) { String currentAccessInfo = instance.getAccessInfo(); String lastAccessInfo = null; if (isUsableAWSAccessInfo(currentAccessInfo)) { List<String> accessInfos = getSubscriptionHistoryDao().getAccessInfos(subscription, instance); for (String oldAccessInfo : accessInfos) { if (isUsableAWSAccessInfo(oldAccessInfo)) { lastAccessInfo = oldAccessInfo; break; } } if (checkIPAddressChanged(lastAccessInfo, currentAccessInfo)) { sendMailForIPAddressChanged(subscription, currentAccessInfo); } } } boolean checkIPAddressChanged(String lastAccessInfo, String currentAccessInfo) { return (lastAccessInfo != null && !lastAccessInfo.isEmpty() && !lastAccessInfo.equals(currentAccessInfo)); } private void sendMailForIPAddressChanged(Subscription subscription, String currentAccessInfo) { List<UsageLicense> userLicenses = getUsageLicenseDao().getUsersforSubscription(subscription); EmailType emailType = EmailType.SUBSCRIPTION_ACCESSINFO_CHANGED; Long marketplaceKey = null; if (subscription.getMarketplace() != null) { marketplaceKey = Long.valueOf(subscription.getMarketplace().getKey()); } SendMailPayload payload = new SendMailPayload(); for (UsageLicense usageLicense : userLicenses) { payload.addMailObjectForUser(usageLicense.getUser().getKey(), emailType, new Object[] { subscription.getSubscriptionId(), getPublicDNS(currentAccessInfo), getIPAddress(currentAccessInfo), getKeyPairName(currentAccessInfo) }, marketplaceKey); } TaskMessage message = new TaskMessage(SendMailHandler.class, payload); tqs.sendAllMessages(Collections.singletonList(message)); } boolean isAWSAccessInfo(String accessInfo) { try { String subAccessInfo = accessInfo.substring(accessInfo.indexOf(":"), accessInfo.indexOf(KEY_PAIR_NAME)); return subAccessInfo.contains(AMAZONAWS_COM); } catch (StringIndexOutOfBoundsException e) { return false; } } boolean isUsableAWSAccessInfo(String accessInfo) { return !(accessInfo == null || accessInfo.isEmpty()) && isAWSAccessInfo(accessInfo) && !hasNullAccessInfo(accessInfo); } boolean hasNullAccessInfo(String accessInfo) { return getPublicDNS(accessInfo).isEmpty() || getIPAddress(accessInfo).isEmpty() || getKeyPairName(accessInfo).isEmpty(); } String getPublicDNS(String accessInfo) { try { return accessInfo.substring(0, accessInfo.indexOf(KEY_PAIR_NAME)); } catch (StringIndexOutOfBoundsException e) { return ""; } } String getIPAddress(String accessInfo) { try { return accessInfo.substring(accessInfo.indexOf("-"), accessInfo.indexOf(".")).substring(1).replace('-', '.'); } catch (StringIndexOutOfBoundsException e) { return ""; } } String getKeyPairName(String accessInfo) { try { return accessInfo.substring(accessInfo.indexOf(KEY_PAIR_NAME), accessInfo.length()); } catch (StringIndexOutOfBoundsException e) { return ""; } } void updateInstanceInfo(Subscription subscription, VOInstanceInfo info) throws ValidationException { new SubscriptionInstanceInfo(localizer, subscription).validateAndUpdateInstanceInfo(info); } /** * Save udas for asynchronously modify or upgrade subscription. Store uda * value in modifieduda temporarily. * * @param udas * The list of VOUda with new value. * @param dbSubscription * The subscription to be modified or upgraded. */ void saveUdasForAsyncModifyOrUpgradeSubscription(List<VOUda> udas, Subscription dbSubscription) throws MandatoryUdaMissingException, MandatoryCustomerUdaMissingException, ValidationException, NonUniqueBusinessKeyException, ObjectNotFoundException, OperationNotPermittedException, ConcurrentModificationException { Organization supplier = dbSubscription.getProduct().getSupplierOrResellerTemplate().getVendor(); manageBean.getUdaAccess().validateUdaAndAdaptTargetKey(udas, supplier, dbSubscription); List<VOUda> newUdas = new ArrayList<>(); List<VOUda> defaultValueUdas = new ArrayList<>(); for (VOUda voUda : udas) { if (voUda.getKey() > 0) { modUpgBean.storeModifiedUda(voUda.getKey(), ModifiedEntityType.UDA_VALUE, voUda.getUdaValue(), dbSubscription.getKey(), voUda.getUdaDefinition().isEncrypted()); } else { newUdas.add(voUda); } } for (VOUda voUda : newUdas) { VOUda defaultValueUda = new VOUda(); defaultValueUda.setTargetObjectKey(voUda.getTargetObjectKey()); defaultValueUda.setUdaDefinition(voUda.getUdaDefinition()); if (voUda.getUdaDefinition().getDefaultValue() == null) { defaultValueUda.setUdaValue(""); } else { defaultValueUda.setUdaValue(voUda.getUdaDefinition().getDefaultValue()); } defaultValueUdas.add(defaultValueUda); } manageBean.getUdaAccess().saveUdas(defaultValueUdas, dbSubscription.getOrganization()); for (VOUda voUda : newUdas) { Uda uda = new Uda(); uda.setTargetObjectKey(voUda.getTargetObjectKey()); uda.setUdaDefinitionKey(voUda.getUdaDefinition().getKey()); uda = (Uda) dataManager.getReferenceByBusinessKey(uda); modUpgBean.storeModifiedUda(uda.getKey(), ModifiedEntityType.UDA_VALUE, voUda.getUdaValue(), dbSubscription.getKey(), uda.getUdaDefinition().isEncrypted()); } } public SubscriptionHistoryDao getSubscriptionHistoryDao() { return new SubscriptionHistoryDao(dataManager); } public UsageLicenseDao getUsageLicenseDao() { return new UsageLicenseDao(dataManager); } public SubscriptionDao getSubscriptionDao() { return new SubscriptionDao(dataManager); } public MarketplaceDao getMarketplaceDao() { return new MarketplaceDao(dataManager); } public ModifiedEntityDao getModifiedEntityDao() { return new ModifiedEntityDao(dataManager); } public OrganizationDao getOrganizationDao() { return new OrganizationDao(dataManager); } public ProductDao getProductDao() { return new ProductDao(dataManager); } public SessionDao getSessionDao() { return new SessionDao(dataManager); } public BillingContactDao getBillingContactDao() { return new BillingContactDao(dataManager); } @Override @RolesAllowed({ "TECHNOLOGY_MANAGER" }) public void updateAsyncOperationProgress(String transactionId, OperationStatus status, List<VOLocalizedText> progress) throws OperationNotPermittedException, OperationStateException { ArgumentValidator.notNull("transactionId", transactionId); ArgumentValidator.notNull("status", status); operationRecordBean.updateOperationStatus(transactionId, status, progress); } @Override @RolesAllowed({ "TECHNOLOGY_MANAGER" }) public void updateAsyncSubscriptionStatus(String subscriptionId, String organizationId, VOInstanceInfo instanceInfo) throws ObjectNotFoundException { ArgumentValidator.notNull("subscriptionId", subscriptionId); ArgumentValidator.notNull("organizationId", organizationId); ArgumentValidator.notNull("instance", instanceInfo); Subscription subscription = manageBean.findSubscription(subscriptionId, organizationId); subscription.setStatus(SubscriptionStatus.PENDING); List<PlatformUser> receivers = manageBean.getCustomerAdminsAndSubscriptionOwner(subscription); for (PlatformUser platformUser : receivers) { try { commService.sendMail(platformUser, EmailType.SUBSCRIPTION_INSTANCE_NOT_FOUND, new Object[] { subscriptionId, instanceInfo.getInstanceId() }, subscription.getMarketplace()); } catch (MailOperationException e) { // only log the exception and proceed LOG.logWarn(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.WARN_MODIFY_PARAMETER_OF_SUBSCRIPTION_CONFIRMING_FAILED); } } } @TransactionAttribute(TransactionAttributeType.MANDATORY) private List<Subscription> getSubscriptionsForUserInt(PlatformUser user, PaginationFullTextFilter pagination) { String fullTextFilterValue = pagination.getFullTextFilterValue(); List<Subscription> subscriptions = Collections.emptyList(); if (StringUtils.isNotEmpty(fullTextFilterValue)) { Collection<Long> subscriptionKeys = Collections.emptySet(); try { subscriptionKeys = getFilteredOutSubscriptionKeys(fullTextFilterValue); } catch (InvalidPhraseException e) { LOG.logError(Log4jLogger.SYSTEM_LOG, e, LogMessageIdentifier.ERROR); } catch (ObjectNotFoundException e) { LOG.logDebug("No subscription keys found"); } if (!subscriptionKeys.isEmpty()) { subscriptions = getSubscriptionDao().getSubscriptionsForUserWithSubscriptionKeys(user, pagination, subscriptionKeys); } } else { subscriptions = getSubscriptionDao().getSubscriptionsForUser(user, pagination); } return subscriptions; } /** * Implementation of method which should return set of Long object, which * represents subscriptions retunred in full text search process * * @param filterValue * Text enetered by user to filter subscriptions by * @return Set of primary keys of subscriptions which are valid against the * filter value or empty (not null!) set. */ private Collection<Long> getFilteredOutSubscriptionKeys(String filterValue) throws InvalidPhraseException, ObjectNotFoundException { return subscriptionSearchService.searchSubscriptions(filterValue); } @Override public List<Subscription> getSubscriptionsForCurrentUserWithFiltering(PaginationFullTextFilter pagination) { PlatformUser user = dataManager.getCurrentUser(); return getSubscriptionsForUserInt(user, pagination); } @Override public Integer getSubscriptionsSizeForCurrentUserWithFiltering(PaginationFullTextFilter pagination) { PlatformUser user = dataManager.getCurrentUser(); return getSubscriptionsForUserInt(user, pagination).size(); } @Override public UsageLicense getSubscriptionUsageLicense(PlatformUser user, Long subKey) { return getSubscriptionDao().getUserLicense(user, subKey == null ? -1 : subKey.longValue()); } @Override public void removeSubscriptionOwner(Subscription sub) { sub.setOwner(null); dataManager.merge(sub); } @Override public Subscription getMySubscriptionDetails(long key) { return getSubscriptionDao().getMySubscriptionDetails(key); } @Override @RolesAllowed({ "ORGANIZATION_ADMIN", "SUBSCRIPTION_MANAGER", "UNIT_ADMINISTRATOR" }) public boolean unsubscribeFromService(Long key) throws ObjectNotFoundException, SubscriptionStillActiveException, SubscriptionStateException, TechnicalServiceNotAliveException, TechnicalServiceOperationException, OperationPendingException, OperationNotPermittedException { Subscription mySubscriptionDetails = getMySubscriptionDetails(key.longValue()); return unsubscribeFromService(mySubscriptionDetails.getSubscriptionId()); } }