/* * 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.configuration; import java.io.Serializable; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.JoinColumn; import javax.persistence.MappedSuperclass; import javax.persistence.OneToOne; import javax.persistence.PrePersist; import javax.persistence.PreUpdate; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlTransient; @MappedSuperclass /** * Base class for resource configuration and plugin configuration updates (i.e. history items). */ @XmlAccessorType(XmlAccessType.FIELD) public abstract class AbstractConfigurationUpdate implements Serializable { private static final long serialVersionUID = 1L; @JoinColumn(name = "CONFIGURATION_ID", referencedColumnName = "ID", nullable = false) @OneToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY) @XmlTransient protected Configuration configuration; @Column(name = "STATUS", nullable = false) @Enumerated(EnumType.STRING) protected ConfigurationUpdateStatus status; @Column(name = "ERROR_MESSAGE") protected String errorMessage; @Column(name = "SUBJECT_NAME") protected String subjectName; @Column(name = "CTIME", nullable = false) protected long createdTime = System.currentTimeMillis(); @Column(name = "MTIME", nullable = false) protected long modifiedTime = System.currentTimeMillis(); protected AbstractConfigurationUpdate() { } protected AbstractConfigurationUpdate(String subjectName) { this.subjectName = subjectName; this.status = ConfigurationUpdateStatus.INPROGRESS; } /** * Contains the entire configuration that was submitted as the new configuration for a resource. If the update * request failed, this configuration will contain properties whose error messages are non-null - this can be used * to find out which properties were invalid and why. * * @return the new configuration for the updated resource */ public Configuration getConfiguration() { return configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } /** * The status of the request which indicates that the request is either still in progress, or it has completed and * either succeeded or failed. * * @return the request status */ public ConfigurationUpdateStatus getStatus() { return status; } public void setStatus(ConfigurationUpdateStatus status) { if (status == null) { throw new NullPointerException("status==null"); } this.status = status; } /** * If not <code>null</code>, this is an error message (possibly a full stack trace) to indicate the overall error * that occurred when the configuration update failed. This will normally be <code>null</code> unless the * {@link #getStatus() status} indicates a {@link ConfigurationUpdateStatus#FAILURE}. * * <p>If the update failed because one or more configuration property values were invalid, then you can also examine * the error messages in each of the failed properties to find out more specific details about the errors for each * invalid property.</p> * * @return overall error that occurred */ public String getErrorMessage() { return errorMessage; } /** * Calling this method with a non-<code>null</code> error message implies that the request's status is * {@link ConfigurationUpdateStatus#FAILURE}. The inverse is <i>not</i> true - that is, if you set the error message * to <code>null</code>, the status is left as-is; it will not assume that a <code>null</code> error message means * the status is successful. * * @param errorMessage */ public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; if (this.errorMessage != null) { setStatus(ConfigurationUpdateStatus.FAILURE); } } /** * For auditing purposes, this method tells you the username of the person that created the request. This is not a * relationship to an actual Subject because we want to maintain the audit trail, even if a Subject has been deleted * from the database. * * <p>If the configuration update was automatically detected by the plugin (that is, was not initiated by a JBoss ON * user; for example, someone modified a configuration file on disk), this will return <code>null</code> as an * indication that we don't know who modified it</p> * * @return the actual name string of the submitter of the request */ public String getSubjectName() { return subjectName; } /** * The time this entity was originally created; in other words, when the request was originally made. * * @return creation time */ public long getCreatedTime() { return this.createdTime; } /** * The time this entity was last modified. This is the last time the status was updated. If the status has never * been updated, this will be the {@link #getCreatedTime() created time}. * * @return last modified time */ public long getModifiedTime() { return this.modifiedTime; } /** * The duration of the configuration update request which simply is the difference between the * {@link #getCreatedTime()} and the {@link #getModifiedTime()}. If the request hasn't completed yet, this will be * the difference between the current time and the created time. * * @return the duration of time that the request took or is taking to complete */ public long getDuration() { long start = this.createdTime; long end; if ((status == null) || (status == ConfigurationUpdateStatus.INPROGRESS)) { end = System.currentTimeMillis(); } else { end = this.modifiedTime; } return end - start; } @Override public int hashCode() { final int PRIME = 31; int result = 1; result = (PRIME * result) + (int) (createdTime ^ (createdTime >>> 32)); result = (PRIME * result) + ((subjectName == null) ? 0 : subjectName.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if ((obj == null) || !(obj instanceof AbstractConfigurationUpdate)) { return false; } final AbstractConfigurationUpdate other = (AbstractConfigurationUpdate) obj; if (this.createdTime != other.createdTime) { return false; } if (this.subjectName == null) { if (other.subjectName != null) { return false; } } else if (!this.subjectName.equals(other.subjectName)) { return false; } return true; } @PrePersist void onPersist() { // don't set createdTime - we use it in equals/hashCode - it is already set at instantiation time this.modifiedTime = System.currentTimeMillis(); } @PreUpdate void onUpdate() { this.modifiedTime = System.currentTimeMillis(); } @Override public String toString() { StringBuilder str = new StringBuilder(this.getClass().getName().substring( this.getClass().getName().lastIndexOf(".") + 1)); str.append("[status=").append(this.status); str.append(", configuration=").append(this.configuration); str.append(", subjectName=").append(this.subjectName); str.append(", createdTime=").append(this.createdTime); str.append(", modifiedTime=").append(this.modifiedTime); String err = this.errorMessage; if ((err != null) && (err.indexOf('\n') > -1)) { err = err.substring(0, err.indexOf('\n')) + "..."; } str.append(", errorMessage=").append(err); appendToStringInternals(str); // ask subclasses if they have anything else to add str.append(']'); return str.toString(); } /** * Subclasses can override this to add things it wants to see in the toString. * * @param str the builder to append strings to */ protected void appendToStringInternals(StringBuilder str) { return; } }