/* * RHQ Management Platform * Copyright (C) 2005-2008 Red Hat, Inc. * All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation, and/or the GNU Lesser * General Public License, version 2.1, also as published by the Free * Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License and the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU General Public License * and the GNU Lesser General Public License along with this program; * if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.rhq.core.domain.content; import java.io.Serializable; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; 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.ManyToOne; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import org.rhq.core.domain.configuration.Configuration; /** * Represents a real connection to an external source system. The type of external source system is described by the * {@link ContentSourceType} attribute of this class. The other attributes in this class serve to configure and control * the connection to the package source. * * @author Jason Dobies */ @Entity @NamedQueries( { @NamedQuery(name = ContentSource.QUERY_FIND_ALL, query = "SELECT cs FROM ContentSource cs "), @NamedQuery(name = ContentSource.QUERY_FIND_ALL_WITH_CONFIG, query = "SELECT cs FROM ContentSource cs LEFT JOIN FETCH cs.configuration config"), @NamedQuery(name = ContentSource.QUERY_FIND_BY_NAME_AND_TYPENAME, query = "SELECT cs " + " FROM ContentSource cs " + " LEFT JOIN FETCH cs.configuration config" + " WHERE cs.name = :name " + " AND cs.contentSourceType.name = :typeName "), @NamedQuery(name = ContentSource.QUERY_FIND_BY_ID_WITH_CONFIG, query = "SELECT cs " + " FROM ContentSource cs " + " LEFT JOIN FETCH cs.configuration config" + " WHERE cs.id = :id "), @NamedQuery(name = ContentSource.QUERY_FIND_BY_REPO_ID, // do not do a fetch join here query = "SELECT cs FROM ContentSource cs LEFT JOIN cs.repoContentSources ccs WHERE ccs.repo.id = :id"), @NamedQuery(name = ContentSource.QUERY_FIND_AVAILABLE_BY_REPO_ID, // query = "SELECT cs " // + " FROM ContentSource AS cs " // + " WHERE cs.id NOT IN " // + " ( SELECT ccs.contentSource.id " // + " FROM RepoContentSource ccs " // + " WHERE ccs.repo.id = :repoId ) ") }) @SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_CONTENT_SOURCE_ID_SEQ", sequenceName = "RHQ_CONTENT_SOURCE_ID_SEQ") @Table(name = "RHQ_CONTENT_SOURCE") public class ContentSource implements Serializable { public static final String QUERY_FIND_ALL = "ContentSource.findAll"; public static final String QUERY_FIND_ALL_WITH_CONFIG = "ContentSource.findAllWithConfig"; public static final String QUERY_FIND_BY_NAME_AND_TYPENAME = "ContentSource.findByNameAndTypeName"; public static final String QUERY_FIND_BY_ID_WITH_CONFIG = "ContentSource.findByIdWithConfig"; public static final String QUERY_FIND_BY_REPO_ID = "ContentSource.findByRepoId"; public static final String QUERY_FIND_AVAILABLE_BY_REPO_ID = "ContentSource.findAvailableByRepoId"; private static final long serialVersionUID = 1L; @Column(name = "ID", nullable = false) @GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_CONTENT_SOURCE_ID_SEQ") @Id private int id; @JoinColumn(name = "CONTENT_SOURCE_TYPE_ID", referencedColumnName = "ID", nullable = false) @ManyToOne(optional = false) private ContentSourceType contentSourceType; @Column(name = "NAME", nullable = false) private String name; @Column(name = "DESCRIPTION", nullable = true) private String description; @Column(name = "LAZY_LOAD", nullable = false) private boolean lazyLoad; @Column(name = "DOWNLOAD_MODE", nullable = false) @Enumerated(EnumType.STRING) private DownloadMode downloadMode = DownloadMode.DATABASE; @Column(name = "SYNC_SCHEDULE", nullable = true) private String syncSchedule = "0 0 3 * * ?"; @Column(name = "LOAD_ERROR_MESSAGE", nullable = true) private String loadErrorMessage; @JoinColumn(name = "CONFIGURATION_ID", referencedColumnName = "ID", nullable = true) @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true) private Configuration configuration; @Column(name = "CREATION_TIME", nullable = false) private long creationDate; @Column(name = "LAST_MODIFIED_TIME", nullable = false) private long lastModifiedDate; @OneToMany(mappedBy = "contentSource", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) @OrderBy("startTime DESC") // latest appears first, oldest last private List<ContentSourceSyncResults> syncResults; @OneToMany(mappedBy = "contentSource", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) private Set<RepoContentSource> repoContentSources; public ContentSource() { // for JPA use } public ContentSource(String name, ContentSourceType contentSourceType) { this.name = name; this.contentSourceType = contentSourceType; } public int getId() { return id; } public void setId(int id) { this.id = id; } /** * Describes the capabilities of this content source. */ public ContentSourceType getContentSourceType() { return contentSourceType; } public void setContentSourceType(ContentSourceType contentSourceType) { this.contentSourceType = contentSourceType; } /** * User defined programmatic name of this repo source. This name should not contain spaces or special characters. */ public String getName() { return name; } public void setName(String name) { this.name = name; } /** * User defined description to help distinguish the content source in the UI. */ public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } /** * If <code>true</code>, the content bits for all packages coming from this content source will only be loaded on * demand. This means the package contents will only be loaded when they are asked for the very first time. If * <code>false</code>, the content source should attempt to download all packages as soon as possible. */ public boolean isLazyLoad() { return lazyLoad; } public void setLazyLoad(boolean lazyLoad) { this.lazyLoad = lazyLoad; } /** * Download mode indicates where (and even if) package bits are downloaded, when they are downloaded. Note that if * this value is {@link DownloadMode#NEVER}, {@link #isLazyLoad()} is effectively ignored since package bits will * never be loaded. * * @return the download mode */ public DownloadMode getDownloadMode() { return downloadMode; } public void setDownloadMode(DownloadMode downloadMode) { this.downloadMode = downloadMode; } /** * Periodically, the content source plugin adapter will be asked to synchronize with the remote content source * repository. This gives the adapter a chance to see if any content has been added, updated or removed from the * remote repository. This attribute defines the schedule, as a cron string. The default will be set for everyday at * 3:00am local time. If this content source should never automatically sync, this should be null, but * an empty string would also indicate this, too. * * @return sync schedule as a cron string or null if the sync should not automatically occur */ public String getSyncSchedule() { return syncSchedule; } public void setSyncSchedule(String syncSchedule) { if (syncSchedule != null && syncSchedule.trim().length() == 0) { syncSchedule = null; } this.syncSchedule = syncSchedule; } /** * If not <code>null</code>, this will be an error message to indicate what errors occurred during the last attempt * to load package bits from this content source. If <code>null</code>, either no error occurred or this content * source has yet to attempt to load package bits for any of its packages. */ public String getLoadErrorMessage() { return loadErrorMessage; } public void setLoadErrorMessage(String loadErrorMessage) { this.loadErrorMessage = loadErrorMessage; } /** * Values that dictate how this content source will function. These values fulfill the configuration definition * defined in the {@link #getContentSourceType() content source type} associated with this instance. In other words, * these values indicate what underlying source of packages to use and how to connect to it. */ public Configuration getConfiguration() { return configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } /** * Timestamp of when this content source was created. */ public long getCreationDate() { return creationDate; } public void setCreationDate(long creationDate) { this.creationDate = creationDate; } /** * Timestamp of the last time the {@link #getConfiguration() configuration} of this content source was changed. It * is not necessarily the last time any other part of this content source object was changed (for example, this last * modified date does not necessarily correspond to the time when the description was modified). */ public long getLastModifiedDate() { return lastModifiedDate; } public void setLastModifiedDate(long lastModifiedDate) { this.lastModifiedDate = lastModifiedDate; } /** * The list of sync results - order not ensured */ public List<ContentSourceSyncResults> getSyncResults() { return syncResults; } public void addSyncResult(ContentSourceSyncResults syncResult) { if (this.syncResults == null) { this.syncResults = new ArrayList<ContentSourceSyncResults>(); } this.syncResults.add(syncResult); syncResult.setContentSource(this); } public void setSyncResults(List<ContentSourceSyncResults> syncResults) { this.syncResults = syncResults; } /** * Returns the explicit mapping entities. * * @return the mapping entities * * @see #getRepoContentSources() */ public Set<RepoContentSource> getRepoContentSources() { return repoContentSources; } /** * The repos that this content source provides content to. * * <p>The returned set is not backed by this entity - if you want to alter the set of associated repos, use * {@link #getRepoContentSources()} or {@link #addRepo(Repo)}, {@link #removeRepo(Repo)}.</p> */ public Set<Repo> getRepos() { HashSet<Repo> repos = new HashSet<Repo>(); if (repoContentSources != null) { for (RepoContentSource ccs : repoContentSources) { repos.add(ccs.getRepoContentSourcePK().getRepo()); } } return repos; } /** * Directly assign a repo to this content source. * * @param repo * * @return the mapping that was added */ public RepoContentSource addRepo(Repo repo) { if (this.repoContentSources == null) { this.repoContentSources = new HashSet<RepoContentSource>(); } RepoContentSource mapping = new RepoContentSource(repo, this); this.repoContentSources.add(mapping); repo.addContentSource(this); return mapping; } /** * Removes the repo from this content source, if it exists. If it does exist, the mapping that was removed is * returned; if the given repo did not exist as one that this content source is a member of, <code>null</code> is * returned. * * @param repo the repo to remove from this content source * * @return the mapping that was removed or <code>null</code> if the repo was not mapped to this content source */ public RepoContentSource removeRepo(Repo repo) { if ((this.repoContentSources == null) || (repo == null)) { return null; } RepoContentSource doomed = null; for (RepoContentSource ccs : this.repoContentSources) { if (repo.equals(ccs.getRepoContentSourcePK().getRepo())) { doomed = ccs; repo.removeContentSource(this); break; } } if (doomed != null) { this.repoContentSources.remove(doomed); } return doomed; } @PrePersist void onPersist() { this.lastModifiedDate = this.creationDate = System.currentTimeMillis(); } @PreUpdate void onUpdate() { this.lastModifiedDate = System.currentTimeMillis(); } @Override public String toString() { return "ContentSource: id=[" + this.id + "], name=[" + this.name + "]"; } @Override public int hashCode() { int result = 1; result = (31 * result) + ((contentSourceType == null) ? 0 : contentSourceType.hashCode()); result = (31 * result) + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if ((obj == null) || (!(obj instanceof ContentSource))) { return false; } final ContentSource other = (ContentSource) obj; if (contentSourceType == null) { if (other.contentSourceType != null) { return false; } } else if (!contentSourceType.equals(other.contentSourceType)) { return false; } if (name == null) { if (other.name != null) { return false; } } else if (!name.equals(other.name)) { return false; } return true; } }