/* * RHQ Management Platform * Copyright (C) 2005-2009 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.plugin; import java.io.Serializable; import java.util.HashSet; import java.util.Set; import javax.persistence.Column; import javax.persistence.DiscriminatorColumn; 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.Inheritance; import javax.persistence.InheritanceType; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import javax.persistence.PrePersist; import javax.persistence.SequenceGenerator; import javax.persistence.Table; import javax.persistence.Transient; import org.rhq.core.domain.cloud.Server; /** * Base plugin implementation that agent and server plugin implementations extend. * * @author John Mazzitelli */ @DiscriminatorColumn(name = "DEPLOYMENT") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @Entity @SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_PLUGIN_ID_SEQ", sequenceName = "RHQ_PLUGIN_ID_SEQ") @Table(name = Plugin.TABLE_NAME) public class AbstractPlugin implements Serializable { private static final long serialVersionUID = 1L; public static final String TABLE_NAME = "RHQ_PLUGIN"; @Column(name = "ID", nullable = false) @GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_PLUGIN_ID_SEQ") @Id private int id; @Column(name = "DEPLOYMENT", nullable = false, insertable = false, updatable = false) @Enumerated(EnumType.STRING) private PluginDeploymentType deployment; @Column(name = "NAME", nullable = false) private String name; @Column(name = "DISPLAY_NAME", nullable = false) private String displayName; @Column(name = "DESCRIPTION", nullable = true) private String description; @Column(name = "ENABLED", nullable = false) private boolean enabled = true; @Column(name = "STATUS", nullable = false) @Enumerated(EnumType.STRING) private PluginStatusType status = PluginStatusType.INSTALLED; @Column(name = "HELP", nullable = true) private String help; @Transient private String helpContentType; @Column(name = "VERSION", nullable = true) private String version; @Column(name = "AMPS_VERSION", nullable = true) private String ampsVersion; @Column(name = "PATH", nullable = false) private String path; @Column(name = "MD5", nullable = false) private String md5; @Column(name = "CTIME", nullable = false) private long ctime; @Column(name = "MTIME", nullable = false) private long mtime; @Column(name = "CONTENT", nullable = true) private byte[] content; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "RHQ_PLUGIN_SERVER_ACK_DELETE", joinColumns = @JoinColumn(name = "PLUGIN_ID"), inverseJoinColumns = @JoinColumn(name = "SERVER_ID")) private Set<Server> serversAcknowledgedDelete; public AbstractPlugin() { } /** * Constructor for {@link Plugin}. * * @param name the logical name of the plugin * @param path the actual filename of the plugin jar (see {@link #getPath()}) */ public AbstractPlugin(String name, String path) { this.name = name; this.path = path; } /** * Constructor for {@link Plugin}. * Note that this allows you to provide an MD5 without providing the plugin's * actual content. If you wish to persist this entity in the database, you should * either provide the {@link #setContent(byte[]) content} or update the entity * later by streaming the file content to the content column. * * @param name the logical name of the plugin * @param path the actual filename of the plugin jar (see {@link #getPath()}) * @param md5 the MD5 hash string of the plugin jar contents */ public AbstractPlugin(String name, String path, String md5) { this.name = name; this.path = path; this.md5 = md5; } /** * Constructor for {@link Plugin}. * * @param name the logical name of the plugin * @param path the actual filename of the plugin jar (see {@link #getPath()}) * @param content the actual jar file contents (the MD5 hash string will be generated from this) */ public AbstractPlugin(String name, String path, byte[] content) { this.name = name; this.path = path; this.content = content; } /** * Constructor that can build the full object except for the content byte array. * This is used mainly for the named queries that want to return a Plugin object * but does not eagerly load in the content array. */ public AbstractPlugin(int id, String name, String path, String displayName, boolean enabled, PluginStatusType status, String description, String help, String md5, String version, String ampsVersion, PluginDeploymentType deployment, long ctime, long mtime) { this.id = id; this.name = name; this.path = path; this.displayName = displayName; this.enabled = enabled; this.status = status; this.description = description; this.help = help; this.md5 = md5; this.version = version; this.ampsVersion = ampsVersion; this.deployment = deployment; this.ctime = ctime; this.mtime = mtime; } public int getId() { return this.id; } public void setId(int id) { this.id = id; } public String getName() { return this.name; } public void setName(String name) { this.name = name; } /** * See the javadoc of {@link #getMtime()} for * information about this field and its relationship * with "mtime". * * @return the time when this entity was persisted */ public long getCtime() { return this.ctime; } public void setCtime(long ctime) { this.ctime = ctime; } /** * The "mtime" of the plugin has slightly different semantics * than other "mtime" values found elsewhere. The "mtime" * will typically be the time that the content field was modified, * not necessarily the time when any field was modified. In other * words, look at "mtime" if you want to know when the actual * plugin content was last updated. Note that this "mtime" may in * fact be the last modified time of the plugin file from which * the content came from - this means mtime may actually be earlier * in time than "ctime" (in the case when the plugin jar file was * last touched prior to this entity being created). * * Note that the "ctime" field semantics remains the same as always, * it is the time when this entity was originally created. * * @return mtime of the content */ public long getMtime() { return this.mtime; } /** * This entity does not automatically update the "mtime" when it * is updated via a PreUpdate annotation, therefore, the owner of * this entity needs to explicitly call this setter in order to * set the "mtime". You normally set this value to the last * modified time of the plugin jar that provided * this plugin entity's {@link #getContent() content}. * * @param mtime */ public void setMtime(long mtime) { this.mtime = mtime; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public PluginStatusType getStatus() { return status; } public void setStatus(PluginStatusType status) { this.status = status; if (this.status == PluginStatusType.DELETED) { this.enabled = false; } } public String getHelp() { return help; } public void setHelp(String help) { this.help = help; } public String getHelpContentType() { return helpContentType; } public void setHelpContentType(String helpContentType) { this.helpContentType = helpContentType; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getAmpsVersion() { return ampsVersion; } public void setAmpsVersion(String ampsVersion) { this.ampsVersion = ampsVersion; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getMD5() { return getMd5(); } public void setMD5(String md5) { setMd5(md5); } /** * Returns the actual name of the plugin jar. This is not the absolute path, in fact, it does not include any * directory paths. It is strictly the name of the plugin jar as found on the file system (aka the filename). * * @return plugin filename */ public String getPath() { return this.path; } /** * Ensure that the path being set does not include any directory names. The plugin path is the filename. See * {@link #getPath()}. * * @param path the filename of the plugin, not including directory names */ public void setPath(String path) { this.path = path; } /** * Indicates how the plugin gets deployed (e.g. running in the agent or in the server). * * @return plugin deployment type */ public PluginDeploymentType getDeployment() { return deployment; } public void setDeployment(PluginDeploymentType deployment) { this.deployment = deployment; } /** * The list of the servers that acknowledged that this plugin is deleted. * Used to determine whether it is safe to purge the plugin from the database. */ public Set<Server> getServersAcknowledgedDelete() { if (serversAcknowledgedDelete == null) { serversAcknowledgedDelete = new HashSet<Server>(); } return serversAcknowledgedDelete; } /** * Returns the actual content of the plugin file. Be careful calling this * in an entity context - the entire plugin file content will be loaded in * memory (which may trigger an OutOfMemoryError if the file is very large). * * @return the content of the plugin file */ public byte[] getContent() { return this.content; } public void setContent(byte[] content) { this.content = content; } @PrePersist void onPersist() { this.ctime = System.currentTimeMillis(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if ((obj == null) || !(obj instanceof AbstractPlugin)) { return false; } AbstractPlugin that = (AbstractPlugin) obj; return name.equals(that.name); } @Override public int hashCode() { return name.hashCode(); } @Override public String toString() { return "[id=" + id + ", name=" + name + ", md5=" + md5 + "]"; } }