blob: f5d669e4f578725f98aae2ef2af3a4ba6527fc0a [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.ambari.server.orm.entities;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Transient;
import javax.persistence.UniqueConstraint;
import org.apache.ambari.server.StaticallyInject;
import org.apache.ambari.server.state.RepositoryType;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.repository.Release;
import org.apache.ambari.server.state.repository.VersionDefinitionXml;
import org.apache.ambari.server.state.stack.upgrade.RepositoryVersionHelper;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.Inject;
import com.google.inject.Provider;
@Entity
@Table(name = "repo_version", uniqueConstraints = {
@UniqueConstraint(columnNames = {"display_name"}),
@UniqueConstraint(columnNames = {"stack_id", "version"})
})
@TableGenerator(name = "repository_version_id_generator",
table = "ambari_sequences",
pkColumnName = "sequence_name",
valueColumnName = "sequence_value",
pkColumnValue = "repo_version_id_seq",
initialValue = 0
)
@NamedQueries({
@NamedQuery(name = "repositoryVersionByDisplayName", query = "SELECT repoversion FROM RepositoryVersionEntity repoversion WHERE repoversion.displayName=:displayname"),
@NamedQuery(name = "repositoryVersionByStack", query = "SELECT repoversion FROM RepositoryVersionEntity repoversion WHERE repoversion.stack.stackName=:stackName AND repoversion.stack.stackVersion=:stackVersion"),
@NamedQuery(name = "repositoryVersionByStackNameAndVersion", query = "SELECT repoversion FROM RepositoryVersionEntity repoversion WHERE repoversion.stack.stackName=:stackName AND repoversion.version=:version"),
@NamedQuery(name = "repositoryVersionsFromDefinition", query = "SELECT repoversion FROM RepositoryVersionEntity repoversion WHERE repoversion.versionXsd IS NOT NULL")
})
@StaticallyInject
public class RepositoryVersionEntity {
private static Logger LOG = LoggerFactory.getLogger(RepositoryVersionEntity.class);
@Inject
private static Provider<RepositoryVersionHelper> repositoryVersionHelperProvider;
@Id
@Column(name = "repo_version_id")
@GeneratedValue(strategy = GenerationType.TABLE, generator = "repository_version_id_generator")
private Long id;
/**
* Unidirectional one-to-one association to {@link StackEntity}
*/
@OneToOne
@JoinColumn(name = "stack_id", unique = false, nullable = false, insertable = true, updatable = true)
private StackEntity stack;
@Column(name = "version")
private String version;
@Column(name = "display_name")
private String displayName;
@Lob
@Column(name = "repositories")
private String operatingSystems;
@OneToMany(cascade = CascadeType.REMOVE, mappedBy = "repositoryVersion")
private Set<ClusterVersionEntity> clusterVersionEntities;
@OneToMany(cascade = CascadeType.REMOVE, mappedBy = "repositoryVersion")
private Set<HostVersionEntity> hostVersionEntities;
@Column(name = "repo_type", nullable = false, insertable = true, updatable = true)
@Enumerated(value = EnumType.STRING)
private RepositoryType type = RepositoryType.STANDARD;
@Basic(fetch=FetchType.LAZY)
@Lob
@Column(name="version_xml", insertable = true, updatable = true)
private String versionXml;
@Transient
private VersionDefinitionXml versionDefinition = null;
@Column(name="version_url", nullable=true, insertable=true, updatable=true)
private String versionUrl;
@Column(name="version_xsd", insertable = true, updatable = true)
private String versionXsd;
@ManyToOne
@JoinColumn(name = "parent_id")
private RepositoryVersionEntity parent;
@OneToMany(mappedBy = "parent")
private List<RepositoryVersionEntity> children;
// ----- RepositoryVersionEntity -------------------------------------------------------
public RepositoryVersionEntity() {
}
public RepositoryVersionEntity(StackEntity stack, String version,
String displayName, String operatingSystems) {
this.stack = stack;
this.version = version;
this.displayName = displayName;
this.operatingSystems = operatingSystems;
}
@PreUpdate
@PrePersist
public void removePrefixFromVersion() {
String stackName = stack.getStackName();
if (version.startsWith(stackName)) {
version = version.substring(stackName.length() + 1);
}
}
/**
* Update one-to-many relation without rebuilding the whole entity
* @param entity many-to-one entity
*/
public void updateClusterVersionEntityRelation(ClusterVersionEntity entity){
clusterVersionEntities.add(entity);
}
/**
* Update one-to-many relation without rebuilding the whole entity
* @param entity many-to-one entity
*/
public void updateHostVersionEntityRelation(HostVersionEntity entity){
hostVersionEntities.add(entity);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* Gets the repository version's stack.
*
* @return the stack.
*/
public StackEntity getStack() {
return stack;
}
/**
* Sets the repository version's stack.
*
* @param stack
* the stack to set for the repo version (not {@code null}).
*/
public void setStack(StackEntity stack) {
this.stack = stack;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
/**
* @param stackId the stack id for the version
* @param release the XML release instance
*/
public void setDisplayName(StackId stackId, Release release) {
if (StringUtils.isNotBlank(release.display)) {
displayName = release.display;
} else {
displayName = stackId.getStackName() + "-" + release.getFullVersion();
}
}
public String getOperatingSystemsJson() {
return operatingSystems;
}
public void setOperatingSystems(String repositories) {
operatingSystems = repositories;
}
/**
* Getter which hides json nature of operating systems and returns them as entities.
*
* @return empty list if stored json is invalid
*/
public List<OperatingSystemEntity> getOperatingSystems() {
if (StringUtils.isNotBlank(operatingSystems)) {
try {
return repositoryVersionHelperProvider.get().parseOperatingSystems(operatingSystems);
} catch (Exception ex) {
String msg = String.format("Failed to parse repository from OS/Repo information in the database: %s. Required fields: repo_name, repo_id, base_url", operatingSystems);
LOG.error(msg, ex);
}
}
return Collections.emptyList();
}
public String getStackName() {
return getStackId().getStackName();
}
public String getStackVersion() {
return getStackId().getStackVersion();
}
public StackId getStackId() {
if (null == stack) {
return null;
}
return new StackId(stack.getStackName(), stack.getStackVersion());
}
/**
* @return the type
*/
public RepositoryType getType() {
return type;
}
/**
* @param type the repo type
*/
public void setType(RepositoryType type) {
this.type = type;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
RepositoryVersionEntity that = (RepositoryVersionEntity) o;
if (id != null ? !id.equals(that.id) : that.id != null) {
return false;
}
if (stack != null ? !stack.equals(that.stack) : that.stack != null) {
return false;
}
if (version != null ? !version.equals(that.version) : that.version != null) {
return false;
}
if (displayName != null ? !displayName.equals(that.displayName) : that.displayName != null) {
return false;
}
if (operatingSystems != null ? !operatingSystems.equals(that.operatingSystems) : that.operatingSystems != null) {
return false;
}
return true;
}
/**
* @return the XML that is the basis for the version
*/
public String getVersionXml() {
return versionXml;
}
/**
* @param xml the XML that is the basis for the version
*/
public void setVersionXml(String xml) {
versionXml = xml;
}
/**
* @return The url used for the version. Optional in case the XML was loaded via blob.
*/
public String getVersionUrl() {
return versionUrl;
}
/**
* @param url the url used to load the XML.
*/
public void setVersionUrl(String url) {
versionUrl = url;
}
/**
* @return the XSD name extracted from the XML.
*/
public String getVersionXsd() {
return versionXsd;
}
/**
* @param xsdLocation the XSD name extracted from XML.
*/
public void setVersionXsd(String xsdLocation) {
versionXsd = xsdLocation;
}
/**
* Parse the version XML into its object representation. This causes the XML to be lazy-loaded
* from storage, and will only be parsed once per request.
* @return {@code null} if the XSD (from the XML) is not available.
* @throws Exception
*/
public VersionDefinitionXml getRepositoryXml() throws Exception {
if (null == versionXsd) {
return null;
}
if (null == versionDefinition) {
versionDefinition = VersionDefinitionXml.load(getVersionXml());
}
return versionDefinition;
}
@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (stack != null ? stack.hashCode() : 0);
result = 31 * result + (version != null ? version.hashCode() : 0);
result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
result = 31 * result + (operatingSystems != null ? operatingSystems.hashCode() : 0);
return result;
}
/**
* Determine if the version belongs to the stack.
* @param stackId Stack, such as HDP-2.3
* @param version Version number, such as 2.3.0.0, or 2.3.0.0-1234
* @return Return true if the version starts with the digits of the stack.
*/
public static boolean isVersionInStack(StackId stackId, String version) {
if (null != version && !StringUtils.isBlank(version)) {
String stackName = stackId.getStackName();
if (version.startsWith(stackName + "-")) {
version = version.substring(stackName.length() + 1);
}
String leading = stackId.getStackVersion(); // E.g, 2.3
// In some cases during unit tests, the leading can contain 3 digits, so only the major number (first two parts) are needed.
String[] leadingParts = leading.split("\\.");
if (null != leadingParts && leadingParts.length > 2) {
leading = leadingParts[0] + "." + leadingParts[1];
}
return version.startsWith(leading);
}
return false;
}
/**
* @param entity parent entity
*/
public void setParent(RepositoryVersionEntity entity) {
parent = entity;
parent.children.add(this);
}
/**
* @return the repositories that are denoted children
*/
public List<RepositoryVersionEntity> getChildren() {
return children;
}
/**
* @return the parentId, or {@code null} if the entity is already a parent
*/
public Long getParentId() {
return null == parent ? null : parent.getId();
}
}