/** * 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.EqualsUtils; import com.francetelecom.clara.cloud.commons.GuiClassMapping; import com.francetelecom.clara.cloud.commons.GuiMapping; import com.francetelecom.clara.cloud.commons.UUIDUtils; import com.francetelecom.clara.cloud.commons.jaxb.AnyTypeAdapter; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import javax.persistence.*; import javax.validation.constraints.NotNull; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.util.*; /** * Base class for Service in the Logical Model. * Factors out common properties to all services that are attached to a {@link ProcessingNode} * * Note that this does not directly map to each entry in the service catalog PDF document. * @author APOG7416 */ // @XmlRootElement @Entity @Table(name = "LOGICAL_SERVICE") @XmlJavaTypeAdapter(AnyTypeAdapter.class) public abstract class LogicalService extends LogicalModelItem { private static final long serialVersionUID = 1L; /** * logical deployment. */ @XmlIDREF @XmlElement(name = "logicalDeploymentRef") @ManyToOne @NotNull @GuiMapping(status = GuiMapping.StatusType.NA) LogicalDeployment logicalDeployment; /** * Logical Node using the logical Service. Use an assocation Entity. No xml * element for association (done on execution service side) */ @XmlTransient @OneToMany(cascade = CascadeType.ALL, mappedBy = "logicalService", orphanRemoval = true) @GuiMapping() protected List<LogicalNodeServiceAssociation> logicalNodeServiceAssociations = new ArrayList<LogicalNodeServiceAssociation>(); // @TODO: remove label when transition is ended /** * Note: the logicalNodeServiceAssociation is kept, but ignores its link back to us (LogicalService) */ private static final String[] EXCLUDED_EQUALS_FIELDS = EqualsUtils.mergeExcludedFieldLists(LogicalModelItem.EXCLUDED_EQUALS_FIELDS, new String[] {"logicalDeployment", "targetBasicatCode", "initialPopulationScript"}); private static final String[] EXCLUDED_HIBERNATE_BROKEN_EQUALS_COLLECTIONS = new String[] { "logicalNodeServiceAssociations" } ; /** * Default constructor. */ public LogicalService() { super(); } /** * Logical Service constructor * * @param label * @param logicalDeployment * @deprecated Should not be called anymore, use empty constructor instead * followed by {@link LogicalDeployment#addLogicalService(LogicalService)} */ public LogicalService(String label, LogicalDeployment logicalDeployment) { super(UUIDUtils.generateUUID("ls"), label); this.logicalDeployment = logicalDeployment; // add the LogicalService to the global deployment List //FIXME: use logicalDeployment.addLogicalService(this) instead logicalDeployment.addLogicalService(this); } /** * list all ExecutionNode using this Service * * @return */ public Set<ProcessingNode> listDependentProcessingNodes() { HashSet<ProcessingNode> nodes = new HashSet<ProcessingNode>(); for (LogicalNodeServiceAssociation association : this.logicalNodeServiceAssociations) { nodes.add(association.processingNode); } return Collections.unmodifiableSet(nodes); } /** * List all association to related LogicalExecution Node * * @return */ public List<LogicalNodeServiceAssociation> listLogicalServicesAssociations() { return Collections.unmodifiableList(this.logicalNodeServiceAssociations); } @Override public boolean equals(Object obj) { //See rationale for this specific impl in LogicalDeployment.equals() return equals(obj, false); } /** * Utility method to factor out logic between {@link #equals(Object)} and {@link #equalsShallow(LogicalService)} * @param obj The other object to compare * @param excludeAssociations set to true to exclude the {@link #logicalNodeServiceAssociations} in the comparison * or false to include them. * @return */ protected boolean equals(Object obj, boolean excludeAssociations) { if (obj == null) { return false; } if (obj == this) { return true; } if (obj.getClass() != getClass()) { return false; } LogicalService rhs = (LogicalService) obj; EqualsBuilder equalsBuilder = new EqualsBuilder() .appendSuper(super.equals(obj, EqualsUtils.mergeExcludedFieldLists(EXCLUDED_EQUALS_FIELDS, EXCLUDED_HIBERNATE_BROKEN_EQUALS_COLLECTIONS))); if (!excludeAssociations) { List<LogicalNodeServiceAssociation> thisLogicalNodeServiceAssociations = new ArrayList<LogicalNodeServiceAssociation>(logicalNodeServiceAssociations); Collections.sort(thisLogicalNodeServiceAssociations); List<LogicalNodeServiceAssociation> rhsLogicalNodeServiceAssociations = new ArrayList<LogicalNodeServiceAssociation>(rhs.logicalNodeServiceAssociations); Collections.sort(rhsLogicalNodeServiceAssociations); equalsBuilder.append(thisLogicalNodeServiceAssociations, rhsLogicalNodeServiceAssociations); } return equalsBuilder.isEquals(); } /** * Tests that two LogicalService are semantically equal, ignoring whether the object was connected to other * services. This is mainly used in tests (such as UI automated tests) * @param logicalService * @return */ public boolean equalsShallow(LogicalService logicalService) { return equals(logicalService, true); } @Override public int hashCode() { //Note: Same workarounds as for equals HashCodeBuilder builder = new HashCodeBuilder(13, 71). appendSuper(super.hashCode(EqualsUtils.mergeExcludedFieldLists(EXCLUDED_EQUALS_FIELDS, EXCLUDED_HIBERNATE_BROKEN_EQUALS_COLLECTIONS))); if (logicalNodeServiceAssociations != null) { //Hack: force eager fetch of associations logicalNodeServiceAssociations.size(); builder.append(new ArrayList<LogicalNodeServiceAssociation>(logicalNodeServiceAssociations)); } return builder.toHashCode(); } /** * Utility method to list all supported classes * @return */ public static Set<Class> listServicesClass() { Set<Class> classes = new HashSet<Class>(); classes.add(LogicalWebGUIService.class); classes.add(LogicalQueueSendService.class); classes.add(LogicalQueueReceiveService.class); classes.add(LogicalRelationalService.class); classes.add(LogicalConfigService.class); classes.add(LogicalSoapConsumer.class); classes.add(LogicalSoapService.class); //TODO: add others return classes; } protected void setLogicalDeployment(LogicalDeployment logicalDeployment) { this.logicalDeployment = logicalDeployment; } public LogicalDeployment getLogicalDeployment() { return logicalDeployment; } @Override protected boolean isFieldExcludedFromToString(String fieldName) { return isFieldExcludedFromToString(fieldName, LogicalService.EXCLUDED_EQUALS_FIELDS); } @Override public int compareTo(LogicalModelItem o) { GuiClassMapping annotationOther = o.getClass().getAnnotation(GuiClassMapping.class); GuiClassMapping annotationThis = this.getClass().getAnnotation(GuiClassMapping.class); if (annotationThis != null && annotationOther != null) { if (annotationThis.isExternal() && annotationOther.isExternal() || !annotationThis.isExternal() && !annotationOther.isExternal() ) { return super.compareTo(o); } else if (annotationThis.isExternal() && !annotationOther.isExternal()) { return -1; } else { return 1; } } else { //If services are in same category, then sort based on class name and labels return super.compareTo(o); } } }