/*
* 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.cloud;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
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.PrePersist;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import org.rhq.core.domain.cloud.composite.FailoverListComposite;
import org.rhq.core.domain.cloud.composite.FailoverListComposite.ServerEntry;
import org.rhq.core.domain.resource.Agent;
/**
* An RHQ server node in the cluster
*
* @author Joseph Marques
*/
@Entity(name = "Server")
@NamedQueries( //
{
@NamedQuery(name = Server.QUERY_FIND_ALL, query = "SELECT s FROM Server s"),
@NamedQuery(name = Server.QUERY_FIND_ALL_COMPOSITES, query = "" //
+ "SELECT NEW org.rhq.core.domain.cloud.composite.ServerWithAgentCountComposite" //
+ " ( " //
+ " s, " //
+ " (SELECT COUNT(a) FROM Agent a WHERE a.server = s) " //
+ " ) " //
+ " FROM Server s"),
@NamedQuery(name = Server.QUERY_FIND_BY_NAME, query = "" //
+ " SELECT s " //
+ " FROM Server s " //
+ "LEFT JOIN FETCH s.affinityGroup ag " //
+ " WHERE s.name = :name"),
@NamedQuery(name = Server.QUERY_FIND_ALL_CLOUD_MEMBERS, query = "SELECT s FROM Server s WHERE NOT s.operationMode = 'INSTALLED'"),
@NamedQuery(name = Server.QUERY_FIND_ALL_NORMAL_CLOUD_MEMBERS, query = "SELECT s FROM Server s WHERE s.operationMode = 'NORMAL'"),
@NamedQuery(name = Server.QUERY_FIND_BY_AFFINITY_GROUP, query = "" //
+ "SELECT s " //
+ " FROM Server s " //
+ " WHERE s.affinityGroup.id = :affinityGroupId "),
@NamedQuery(name = Server.QUERY_FIND_WITHOUT_AFFINITY_GROUP, query = "" //
+ "SELECT s " //
+ " FROM Server s " //
+ " WHERE s.affinityGroup IS NULL "), //
@NamedQuery(name = Server.QUERY_DELETE_BY_ID, query = "" //
+ "DELETE FROM Server s WHERE s.id = :serverId "), //
@NamedQuery(name = Server.QUERY_UPDATE_SET_STALE_DOWN, query = "" //
+ "UPDATE Server s " //
+ " SET s.operationMode = :downMode " //
+ " WHERE s.operationMode = :normalMode " //
+ " AND s.mtime < :staleTime " //
+ " AND ( s.name <> :thisServerName OR :thisServerName IS NULL ) "), //
@NamedQuery(name = Server.QUERY_UPDATE_STATUS_BY_NAME, query = "" //
+ " UPDATE Server s " //
+ " SET s.status = 3 " //Change this to the only value possible before adding MANUAL_MAINTENANCE_MODE.
//This status should never be set to negative since negative values are not
//allowed by the bitmask.
+ " WHERE s.status = 0 ") })
@SequenceGenerator(allocationSize = org.rhq.core.domain.util.Constants.ALLOCATION_SIZE, name = "RHQ_SERVER_ID_SEQ", sequenceName = "RHQ_SERVER_ID_SEQ")
@Table(name = "RHQ_SERVER")
public class Server implements Serializable {
public static final long serialVersionUID = 1L;
public static final String QUERY_FIND_ALL = "Server.findAll";
public static final String QUERY_FIND_ALL_COMPOSITES = "Server.findAllComposites";
public static final String QUERY_FIND_BY_NAME = "Server.findByName";
public static final String QUERY_FIND_ALL_CLOUD_MEMBERS = "Server.findAllCloudMembers";
public static final String QUERY_FIND_BY_AFFINITY_GROUP = "Server.findByAffinityGroup";
public static final String QUERY_FIND_WITHOUT_AFFINITY_GROUP = "Server.findWithoutAffinityGroup";
public static final String QUERY_DELETE_BY_ID = "Server.deleteById";
public static final String QUERY_UPDATE_SET_STALE_DOWN = "Server.updateSetStaleDown";
public static final String QUERY_FIND_ALL_NORMAL_CLOUD_MEMBERS = "Server.findAllNormalCloudMembers";
public static final String QUERY_UPDATE_STATUS_BY_NAME = "Server.updateStatusByName";
@Column(name = "ID", nullable = false)
@GeneratedValue(strategy = GenerationType.AUTO, generator = "RHQ_SERVER_ID_SEQ")
@Id
private int id;
@Column(name = "NAME", nullable = false)
private String name;
@Column(name = "ADDRESS", nullable = false)
private String address;
@Column(name = "PORT", nullable = false)
private int port;
@Column(name = "SECURE_PORT", nullable = false)
private int securePort;
@Column(name = "OPERATION_MODE", nullable = false)
@Enumerated(EnumType.STRING)
private OperationMode operationMode;
@Column(name = "COMPUTE_POWER", nullable = false)
private int computePower;
// the time this server node was installed into the infrastructure
@Column(name = "CTIME", nullable = false)
private long ctime;
// the time this server node was last updated
@Column(name = "MTIME", nullable = false)
private long mtime;
@JoinColumn(name = "AFFINITY_GROUP_ID", referencedColumnName = "ID", nullable = true)
@ManyToOne
private AffinityGroup affinityGroup;
@OneToMany(mappedBy = "server", fetch = FetchType.LAZY)
private List<Agent> agents = new ArrayList<Agent>();
@Column(name = "STATUS", nullable = false)
private int status;
@Column(name = "VERSION", nullable = false)
private String version;
// required for JPA
public Server() {
}
public Server(int serverId) {
this.id = serverId;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getSecurePort() {
return securePort;
}
public void setSecurePort(int securePort) {
this.securePort = securePort;
}
public int getComputePower() {
return computePower;
}
public void setComputePower(int computePower) {
this.computePower = computePower;
}
public long getCtime() {
return ctime;
}
public long getMtime() {
return mtime;
}
public void setMtime(long mtime) {
this.mtime = mtime;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public AffinityGroup getAffinityGroup() {
return affinityGroup;
}
public void setAffinityGroup(AffinityGroup affinityGroup) {
this.affinityGroup = affinityGroup;
}
public OperationMode getOperationMode() {
return operationMode;
}
public void setOperationMode(OperationMode operationMode) {
this.operationMode = operationMode;
}
public enum OperationMode {
DOWN("This server is down member of the HA server cloud", true), //
INSTALLED("This server is newly installed but not yet fully operating", true), //
MAINTENANCE("This server is a Maintenance Mode member of the HA server cloud", false), //
NORMAL("This server is a Normal Member of the HA server cloud", true);
public final String message;
private final boolean readOnly;
private OperationMode(String message, boolean configurable) {
this.message = message;
this.readOnly = configurable;
}
public String getMessage() {
return message;
}
public boolean isReadOnly() {
return readOnly;
}
}
public List<Agent> getAgents() {
return agents;
}
public void setAgents(List<Agent> agents) {
this.agents = agents;
}
public int getAgentCount() {
return this.agents.size();
}
public ServerEntry getServerEntry() {
return new FailoverListComposite.ServerEntry(id, address, port, securePort);
}
/**
* Returns 0 if this server is current. Otherwise, returns a mask of {@link Server.Status}
* elements corresponding to the updates that have occurred that are related to this server.
*
* @return 0 if this server is current. Otherwise, returns a mask of {@link Server.Status}
* elements corresponding to the updates that have occurred that are related to this server.
*/
public int getStatus() {
return status;
}
/**
* Verifies if bitmask status is set.
*
* @param queryStatus status
* @return true if status set, false otherwise
*/
public boolean hasStatus(Status queryStatus) {
return (this.status & queryStatus.mask) == queryStatus.mask;
}
/**
* Clears the specified bitmask status.
*
* @param removeStatus status to be removed
*/
public void clearStatus(Status removeStatus) {
this.status &= ~removeStatus.mask;
}
/**
* @deprecated do not use this method as it may corrupt the tracking of the server status. Use the new
* {@link #clearStatus(org.rhq.core.domain.cloud.Server.Status)} instead.
*/
@Deprecated
public void clearStatus() {
this.status = 0;
}
/**
* If some subsystem makes a change to some data that this server cares about (as summarized
* by the various {@link Status} elements), then that change should be added via this method.
* Periodically, a background job will come along, check the status, and possibly perform
* work on behalf of this server based on the type of change.
* @param newStatus
*/
public void addStatus(Status newStatus) {
this.status |= newStatus.mask;
}
/**
* @return list all messages for the status mask
*/
public List<String> getStatusMessages() {
return Status.getMessages(status);
}
//Please read BZ 535484 for initial design: https://bugzilla.redhat.com/show_bug.cgi?id=535484
//Prior to MANUAL_MAINTENANCE_MODE only used for debug purposes, design now changed to
//persist statuses between server restarts in production code
public enum Status {
//Debug only flags (first five bits are reserved for debug flags)
RESOURCE_HIERARCHY_UPDATED(1, "The resource hierarchy has been updated"), //
ALERT_DEFINITION(2, "Some alert definition with a global condition category was updated"),
//Production flags
MANUAL_MAINTENANCE_MODE(32, "Manual Maintenance mode setup by the user either via UI or properties file.");
public final int mask;
public final String message;
private Status(int mask, String message) {
this.mask = mask;
this.message = message;
}
public static List<String> getMessages(int mask) {
List<String> results = new ArrayList<String>();
for (Status next : Status.values()) {
if (next.mask == (next.mask & mask)) {
results.add(next.message);
}
}
return results;
}
}
@Override
public String toString() {
return "Server[id=" + getId() + ",name=" + getName() + ",address=" + getAddress() + ",port=" + getPort()
+ ",securePort=" + getSecurePort() + "]";
}
@PrePersist
void onPersist() {
this.ctime = System.currentTimeMillis();
this.mtime = this.ctime;
// I'd like to do this bu smartgwt can't handle the getPackage() call
//this.version = this.getClass().getPackage().getImplementationVersion();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (ctime ^ (ctime >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !(obj instanceof Server)) {
return false;
}
final Server other = (Server) obj;
if (ctime != other.ctime) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
return true;
}
}