/** * 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.coremodel; import java.net.URL; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.ManyToOne; import javax.persistence.OneToOne; import javax.persistence.Table; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlIDREF; import javax.xml.bind.annotation.XmlRootElement; import org.springframework.util.Assert; import com.francetelecom.clara.cloud.commons.GuiMapping; import com.francetelecom.clara.cloud.commons.TechnicalException; import com.francetelecom.clara.cloud.commons.UUIDUtils; import com.francetelecom.clara.cloud.logicalmodel.LogicalDeployment; @XmlRootElement @Entity @Table(name = "CORE_APPLICATION_RELEASE") /** * An Application Release is a version of an application */ public class ApplicationRelease extends CoreItem { private static final long serialVersionUID = -2984627032687890422L; /** * /** application release version. Mandatory. */ @NotNull @Size(max = 255) private String releaseVersion; /** * level of middleware products for this Release. used at projection time to * select correct set of Server products (Apache / Jonas / ...) */ @NotNull @Size(max = 255) private String middlewareProfileVersion = MiddlewareProfile.getDefault().getVersion(); /** * application release description. Optional. */ @GuiMapping() @Size(max = 255) private String description = ""; /** * Application release version control repository url. Optional. This is * useful for sharing apps source code of apps, in particular Paas samples. */ @GuiMapping(status = GuiMapping.StatusType.SUPPORTED) private URL versionControlUrl; /** * application the release is linked to. */ @XmlIDREF @XmlElement(name = "applicationRef") @ManyToOne // @NotNull workaround hibernate schema bug private Application application; @NotNull @XmlElement(name = "logicalDeployment") @OneToOne(cascade = { CascadeType.PERSIST, CascadeType.REMOVE, CascadeType.REFRESH }) private LogicalDeployment logicalDeployment; /** * Describes state changes as the release gets deployed on environments and * possibly discarded. FIXME This should be mapped as * * @Enumerated(EnumType.STRING) to be more maintenable. Currently, if you * change the enum values order this will be * inconsistent with your database */ @NotNull @GuiMapping(status = GuiMapping.StatusType.SKIPPED) private ApplicationReleaseStateEnum state = ApplicationReleaseStateEnum.EDITING; /** * public constructor. */ public ApplicationRelease() { super(UUIDUtils.generateUUID("r")); this.logicalDeployment = new LogicalDeployment(); } /** * public constructor. * * @param application * application the release belongs to (mandatory) * @param version * application release version (mandatory) */ public ApplicationRelease(Application application, String version) { super(UUIDUtils.generateUUID("r")); // an application release must belong to an application. Assert.notNull(application, "Cannot create application release. No application has been supplied."); // application release version must not be empty Assert.hasText(version, "Cannot create application release. No version has been supplied."); // cannot add an application release to a REMOVED application if (application.isRemoved()) { throw new IllegalArgumentException("Cannot create application release. You cannot add an application release to a REMOVED application."); } this.releaseVersion = version; this.application = application; // TODO create empty logical depl ? this.logicalDeployment = new LogicalDeployment(); } public void validate() { setState(ApplicationReleaseStateEnum.VALIDATED); } public void lock() { setState(ApplicationReleaseStateEnum.LOCKED); } private void setState(ApplicationReleaseStateEnum toState) throws TechnicalException { ApplicationReleaseStateEnum fromState = this.state; // TODO: consider moving that into the enum and define a global FSM // pattern consistent in the model. boolean isValidTransition = true; switch (fromState) { case EDITING: isValidTransition = toState == ApplicationReleaseStateEnum.REMOVED || (toState == ApplicationReleaseStateEnum.VALIDATED || toState == ApplicationReleaseStateEnum.DISCARDED); break; case VALIDATED: isValidTransition = toState == ApplicationReleaseStateEnum.REMOVED || (toState == ApplicationReleaseStateEnum.EDITING || toState == ApplicationReleaseStateEnum.LOCKED || toState == ApplicationReleaseStateEnum.DISCARDED); break; case DISCARDED: // final state isValidTransition = toState == ApplicationReleaseStateEnum.REMOVED; break; case LOCKED: isValidTransition = toState == ApplicationReleaseStateEnum.REMOVED || (toState == ApplicationReleaseStateEnum.DISCARDED); break; default: assert false : "unsupported state:" + fromState; } if (!isValidTransition) { throw new TechnicalException("Illegal state transition from " + fromState + " to " + toState); } this.state = toState; } /** * Utility method to replace the built-in logical deployment for the case of * the cloning of a LD. * * Should not be used by client code. * * @param logicalDeployment * a non null LogicalDeployment */ public void replaceLd(LogicalDeployment logicalDeployment) { // TODO: find a way to prevent misuses of this method as a way to break // encapsulation if (logicalDeployment == null) { throw new NullPointerException("unexpected null logicalDeployment param"); } this.logicalDeployment = logicalDeployment; } public void markAsRemoved() { this.setState(ApplicationReleaseStateEnum.REMOVED); this.setReleaseVersion("R" + System.currentTimeMillis() + "-" + this.releaseVersion); } public String getMiddlewareProfileVersion() { return middlewareProfileVersion; } public void setMiddlewareProfileVersion(String middlewareProfileVersion) { this.middlewareProfileVersion = middlewareProfileVersion; } // Getters and setters public Application getApplication() { return application; } public void setApplication(Application application) { Assert.notNull(application, "Cannot update application release. No application has been supplied."); this.application = application; } public LogicalDeployment getLogicalDeployment() { return logicalDeployment; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public URL getVersionControlUrl() { return versionControlUrl; } public void setVersionControlUrl(URL versionControlUrl) { this.versionControlUrl = versionControlUrl; } public String getReleaseVersion() { return releaseVersion; } public void setReleaseVersion(String releaseVersion) { Assert.hasText(releaseVersion, "Cannot update application release. No version has been supplied."); this.releaseVersion = releaseVersion; } public ApplicationReleaseStateEnum getState() { return state; } public boolean isValidated() { return ApplicationReleaseStateEnum.VALIDATED.equals(this.state); } public boolean isLocked() { return ApplicationReleaseStateEnum.LOCKED.equals(this.state); } public boolean isEditing() { return ApplicationReleaseStateEnum.EDITING.equals(this.state); } public boolean isRemoved() { return ApplicationReleaseStateEnum.REMOVED.equals(this.state); } public boolean isDiscarded() { return ApplicationReleaseStateEnum.DISCARDED.equals(this.state); } }