com.francetelecom.clara.cloud.logicalmodel.ProcessingNode.java Source code

Java tutorial

Introduction

Here is the source code for com.francetelecom.clara.cloud.logicalmodel.ProcessingNode.java

Source

/**
 * Copyright (C) 2015 Orange
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.francetelecom.clara.cloud.logicalmodel;

import com.francetelecom.clara.cloud.commons.*;
import com.francetelecom.clara.cloud.commons.jaxb.AnyTypeAdapter;
import com.francetelecom.clara.cloud.logicalmodel.InvalidConfigServiceException.ErrorType;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.*;
import javax.validation.Valid;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.*;

@Entity
@Table(name = "PROCESSING_NODE")
//@MappedSuperclass
@XmlJavaTypeAdapter(AnyTypeAdapter.class)
public abstract class ProcessingNode extends LogicalModelItem {

    private static Logger logger = LoggerFactory.getLogger(ProcessingNode.class.getName());
    private static final long serialVersionUID = 1L;
    public static final int MAX_CONFIG_SET_ENTRIES_PER_EXEC_NODE = 300;
    /**
     * logical deployment.
     */
    @XmlIDREF
    @XmlElement(name = "logicalDeploymentRef")
    @ManyToOne
    @NotNull
    @GuiMapping(status = GuiMapping.StatusType.NA)
    protected LogicalDeployment logicalDeployment;
    /**
     * Logical Services used by the Current Node Cluster (relation). Use an
     * association Entity.
     */
    @XmlElementWrapper
    @XmlElement(name = "logicalNodeServiceAssociation")
    @OneToMany(cascade = { CascadeType.ALL }, mappedBy = "processingNode", orphanRemoval = true)
    @GuiMapping
    @Valid
    private List<LogicalNodeServiceAssociation> logicalNodeServiceAssociations = new ArrayList<LogicalNodeServiceAssociation>();
    /**
     * The application artefact that is hosted in this container (packaged as an EAR).
     */
    @Embedded
    @GuiMapping
    @Valid
    MavenReference softwareReference;
    /**
     * Expect the Architect to provided a non pre existing artifact
     * If this is activated, existence control will be skipped, and a default artifact
     * will be provided "on the fly" @ activation time
     */
    @GuiMapping
    boolean optionalSoftwareReference = false;
    private static final String[] EXCLUDED_EQUALS_FIELDS = EqualsUtils.mergeExcludedFieldLists(
            LogicalModelItem.EXCLUDED_EQUALS_FIELDS,
            new String[] { "logicalDeployment", "logicalNodeServiceAssociations" });

    /**
     * Minimum non persistent disk in Mega bytes (1048576 bytes) that should be allocated to this execution node
     * This should be considered as a hint for disk sizing of production environment
     */
    @GuiMapping(status = GuiMapping.StatusType.SUPPORTED, functional = false)
    @Min(768)
    @Max(100000)
    int minDiskMbHint = 1024;

    /**
     * Minimum memory in Mega bytes (1048576 bytes) that should be allocated to this execution node
     * This should be considered as a hint for memory sizing of production environment
     */
    @GuiMapping(status = GuiMapping.StatusType.SUPPORTED, functional = false)
    @Min(1)
    @Max(10000)
    int minMemoryMbHint = 128;
    /**
     * Memory per session in KB (1024 bytes)
     * This should be considered as a hint for memory sizing of production environment
     */
    @GuiMapping(status = GuiMapping.StatusType.SKIPPED, functional = false)
    @Min(1)
    @Max(10000)
    int memoryKbPerActiveSessionHint = 1024;

    public boolean isOptionalSoftwareReference() {
        return optionalSoftwareReference;
    }

    public void setOptionalSoftwareReference(boolean optionalSoftwareReference) {
        this.optionalSoftwareReference = optionalSoftwareReference;
    }

    /**
     * A custom Icon Url icon which can be null.
     * This is used to display own user icon
     */
    @GuiMapping(status = GuiMapping.StatusType.SUPPORTED, functional = false)
    @Size(min = 0, max = 255)
    String iconUrl;

    protected ProcessingNode() {
        super();
    }

    public ProcessingNode(String label, LogicalDeployment logicalDeployment) {
        super(UUIDUtils.generateUUID("en"), label);
        this.logicalDeployment = logicalDeployment;
        for (ProcessingNode executionNode : logicalDeployment.listProcessingNodes()) {
            Validate.isTrue(!executionNode.getLabel().equals(logicalDeployment),
                    "ExecutionNode label expected to be unique, found duplicate:" + logicalDeployment);
        }
        this.logicalDeployment.processingNodes.add(this);
    }

    /**
     * Logical Node destructor : remove current logical execution node from
     * logical deployment list
     */
    public void discard() {
        this.logicalDeployment.processingNodes.remove(this);
    }

    /**
     * Set the ClusterNode uses given Service Service must be on same deployment
     * <p>
     * An association persistent entity is created
     *
     * @param service
     */
    public void addLogicalServiceUsage(LogicalService service, LogicalServiceAccessTypeEnum accessType) {

        if (service.logicalDeployment != this.logicalDeployment)
            throw new TechnicalException("Node Cluster is not in the same deployment " + logicalDeployment);

        LogicalNodeServiceAssociation association = new LogicalNodeServiceAssociation(this, service);
        association.setAccessType(accessType);
        this.logicalNodeServiceAssociations.add(association);

        // reverse relationship (LogicalService => ExecutionNode)
        service.logicalNodeServiceAssociations.add(association);
    }

    /**
     * Remove an association
     * <p>
     * An association persistent entity is deleted
     *
     * @param logicalNodeServiceAssociation the association to delete
     */
    public void removeLogicalServiceUsage(LogicalNodeServiceAssociation logicalNodeServiceAssociation) {
        // remove reverse relationship (if not, we get a
        // javax.persistence.EntityNotFoundException:
        // deleted entity passed to persist)
        logicalNodeServiceAssociation.getLogicalService().logicalNodeServiceAssociations
                .remove(logicalNodeServiceAssociation);

        int index = 0;
        boolean found = false;
        for (LogicalNodeServiceAssociation assoc : this.logicalNodeServiceAssociations) {
            if (assoc.equalsDeep(logicalNodeServiceAssociation)) {
                found = true;
                break;
            }
            index++;
        }

        if (found) {
            this.logicalNodeServiceAssociations.remove(index);
        }

    }

    /**
     * Remove a list of associations.
     * <p>
     * one or more association persistent entity are deleted
     *
     * @param logicalNodeServiceAssociationList the list of
     */
    public void removeAllLogicalServiceUsage(
            List<LogicalNodeServiceAssociation> logicalNodeServiceAssociationList) {
        // NB : we can't call removeLogicalServiceUsage for each list item
        // because it throws a ConcurrentModificationException.

        // remove all reverse relationship
        for (LogicalNodeServiceAssociation assoc : logicalNodeServiceAssociationList) {
            assoc.getLogicalService().logicalNodeServiceAssociations.remove(assoc);
        }
        // remove all assoc
        this.logicalNodeServiceAssociations.removeAll(logicalNodeServiceAssociationList);

    }

    /**
     * List the Logical Services used by the ClusterNode
     * <p>
     * Retrieves all LogicalService through the LogicalNodeService association
     * Entity
     *
     * @return
     */
    public List<LogicalService> listLogicalServices() {
        return listLogicalServices(null);
    }

    /**
     * List the Logical Services of a specific class.
     * <p>
     * Retrieves all {@link LogicalService} that is connected to the current ExecNode through the {@link LogicalNodeServiceAssociation}
     *
     * @param filteredClass The class of the logicalService to restrict to (e.g. LogicalWebGUIService.class), or null
     *                      to not perform any filtering. Note this may be a superclass or an implementing interface.
     * @return a non-null, possibly empty list
     */
    public <E extends LogicalService> List<E> listLogicalServices(Class<E> filteredClass) {
        List<E> services = new ArrayList<E>();
        for (LogicalNodeServiceAssociation association : this.logicalNodeServiceAssociations) {
            LogicalService logicalService = association.logicalService;
            boolean includeThisClass = false;
            if (filteredClass == null) {
                includeThisClass = true;
            } else {
                includeThisClass = (filteredClass.isInstance(logicalService));
            }
            if (includeThisClass) {
                services.add((E) logicalService);
            }
        }

        return Collections.unmodifiableList(services);
    }

    /**
     * List all association to used Logical services
     *
     * @return
     */
    public List<LogicalNodeServiceAssociation> listLogicalServicesAssociations() {
        return Collections.unmodifiableList(this.logicalNodeServiceAssociations);
    }

    public MavenReference getSoftwareReference() {
        return softwareReference;
    }

    public void setSoftwareReference(MavenReference softwareReference) {
        this.softwareReference = softwareReference;
    }

    public int getMinDiskMbHint() {
        return minDiskMbHint;
    }

    public void setMinDiskMbHint(int minDiskMbHint) {
        this.minDiskMbHint = minDiskMbHint;
    }

    public int getMinMemoryMbHint() {
        return minMemoryMbHint;
    }

    public void setMinMemoryMbHint(int minMemoryMbHint) {
        this.minMemoryMbHint = minMemoryMbHint;
    }

    public int getMemoryKbPerActiveSessionHint() {
        return memoryKbPerActiveSessionHint;
    }

    public void setMemoryKbPerActiveSessionHint(int memoryKbPerActiveSessionHint) {
        this.memoryKbPerActiveSessionHint = memoryKbPerActiveSessionHint;
    }

    public String getIconUrl() {
        return iconUrl;
    }

    public void setIconUrl(String iconUrl) {
        this.iconUrl = iconUrl;
    }

    @Override
    public boolean equals(Object obj) {
        //See http://commons.apache.org/lang/api-2.5/org/apache/commons/lang/builder/HashCodeBuilder.html
        return EqualsBuilder.reflectionEquals(this, obj, EXCLUDED_EQUALS_FIELDS);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this, EXCLUDED_EQUALS_FIELDS);
    }

    /**
     * Utility method to apply consistency checking on the overall model
     *
     */
    public void checkConsistency() throws InvalidConfigServiceException {
        Properties mergedProperties = getMergedConfigServicesProperties();
        //TODO: add a debug trace on merged properties ?
    }

    /**
     * Utility method to return a merged list of {@link LogicalConfigService} content in a {@link Properties} format.
     *
     * @return a @{link Properties} instance with {@link String} as keys and
     * @throws InvalidConfigServiceException if some of the {@link LogicalConfigService} associated to this object were
     *                                       not valid
     */
    public Properties getMergedConfigServicesProperties() throws InvalidConfigServiceException {
        List<LogicalConfigService> logicalConfigServices = listLogicalServices(LogicalConfigService.class);

        Properties mergedProperties = new Properties();
        Set<String> duplicates = new HashSet<String>();
        StringBuffer collisions = new StringBuffer();
        for (LogicalConfigService logicalConfigService : logicalConfigServices) {
            //Check overlap in key names among the subscribed config services

            logicalConfigService.mergeAndCheckForDuplicateKeys(mergedProperties, duplicates, collisions);
        }
        if (mergedProperties.size() > MAX_CONFIG_SET_ENTRIES_PER_EXEC_NODE) {
            InvalidConfigServiceException invalidConfigServiceException = new InvalidConfigServiceException(
                    "Too many Config entries for ExecutionNode=" + this.getLabel());
            invalidConfigServiceException.setType(ErrorType.TOO_MANY_ENTRIES);
            invalidConfigServiceException.setEntryCount(mergedProperties.size());
            invalidConfigServiceException.setMaxEntryCount(MAX_CONFIG_SET_ENTRIES_PER_EXEC_NODE);
            invalidConfigServiceException.setImpactedElementName(getLabel());
            throw invalidConfigServiceException;
        }
        if (collisions.length() > 0) {
            InvalidConfigServiceException invalidConfigServiceException = new InvalidConfigServiceException(
                    "Collision for ExecutionNode=" + this.getLabel() + " collision=" + collisions.toString());
            invalidConfigServiceException.setType(ErrorType.DUPLICATE_KEYS);
            invalidConfigServiceException.getDuplicateKeys().addAll(duplicates);
            invalidConfigServiceException.setImpactedElementName(getLabel());
            throw invalidConfigServiceException;
        }
        return mergedProperties;
    }

    protected void setLogicalDeployment(LogicalDeployment logicalDeployment) {
        this.logicalDeployment = logicalDeployment;
    }

    public LogicalDeployment getLogicalDeployment() {
        return logicalDeployment;
    }

    @Override
    protected boolean isFieldExcludedFromToString(String fieldName) {
        return isFieldExcludedFromToString(fieldName, ProcessingNode.EXCLUDED_EQUALS_FIELDS);
    }

    /**
     * Utility method to check that each Service associated to an ExecutionNode is indeed also pointing to this node.
     * This is used with asserts.
     *
     * @return true if symetry is indeed respected, false otherwise.
     */
    public boolean isAssociationsSymmetryRespected() {
        for (LogicalNodeServiceAssociation association : logicalNodeServiceAssociations) {
            //For the current service, check it point back to us
            boolean currentExecNodeFound = false;
            final LogicalService logicalService = association.getLogicalService();
            final List<LogicalNodeServiceAssociation> logicalNodeServiceAssociations1 = logicalService
                    .listLogicalServicesAssociations();
            for (LogicalNodeServiceAssociation logicalNodeServiceAssociation : logicalNodeServiceAssociations1) {
                if (logicalNodeServiceAssociation.getProcessingNode() == this) {
                    currentExecNodeFound = true;
                }
            }
            if (!currentExecNodeFound) {
                logger.error(
                        "associations between execution nodes and services should be symmetric, did not find pointer back to:"
                                + this + " from:" + logicalService);
                return false;
            }
        }
        return true;
    }

}