/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.executor;
import java.io.Serializable;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.Date;
import java.util.Enumeration;
import java.util.logging.Logger;
import net.opengis.wps10.ExecuteType;
import org.geotools.util.logging.Logging;
import org.opengis.feature.type.Name;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
/**
* Summarizes the execution state of a certain process. Note: the class implements equals and
* hashcode, but skips the exception in them, as commmon Java exceptions do not sport a usable
* equals/hashcode implementation, and the exceptions might be cloned to due network/database
* serialization.
*
* @author Andrea Aime - GeoSolutions
*/
public class ExecutionStatus implements Serializable {
static final Logger LOGGER = Logging.getLogger(ExecutionStatus.class);
private static final long serialVersionUID = -2433524030271115410L;
// TODO: find a GeoServer unified, non GUI specific way to get the node identifier
public static final String NODE_IDENTIFIER = getNodeIdentifier();
private static String getNodeIdentifier() {
try {
return getLocalAddress().getHostName();
} catch (Exception e) {
return null;
}
}
private static InetAddress getLocalAddress() throws UnknownHostException {
try {
InetAddress candidateAddress = null;
// Iterate all NICs (network interface cards)...
for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); interfaces
.hasMoreElements();) {
NetworkInterface ni = (NetworkInterface) interfaces.nextElement();
if (ni.getName() != null && ni.getName().startsWith("vmnet")) {
// skipping vmware interfaces
continue;
}
// each interface can have more than one address
for (Enumeration inetAddrs = ni.getInetAddresses(); inetAddrs.hasMoreElements();) {
InetAddress inetAddr = (InetAddress) inetAddrs.nextElement();
// we are not interested in loopback
if (!inetAddr.isLoopbackAddress() && !(inetAddr instanceof Inet6Address)) {
if (inetAddr.isSiteLocalAddress()) {
return inetAddr;
} else if (candidateAddress == null) {
candidateAddress = inetAddr;
}
}
}
}
if (candidateAddress != null) {
return candidateAddress;
}
// Fall back to whatever localhost provides
InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
if (jdkSuppliedAddress == null) {
throw new UnknownHostException(
"The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
}
return jdkSuppliedAddress;
} catch (Exception e) {
UnknownHostException unknownHostException = new UnknownHostException(
"Failed to determine LAN address");
unknownHostException.initCause(e);
throw unknownHostException;
}
}
/**
* The process being executed
*/
Name processName;
/**
* The execution id, can be used to retrieve the process results
*/
String executionId;
/**
* If the request was asynchronous, or not
*/
boolean asynchronous;
/**
* Current execution status
*/
ProcessState phase;
/**
* Process execution status (as a percentage between 0 and 100)
*/
float progress;
/**
* The name of the user that requested the process
*/
String userName;
/**
* Request creation time
*/
Date creationTime;
/**
* Request completion time
*/
Date completionTime = null;
/**
* A heartbeat field, used when clustering nodes
*/
Date lastUpdated;
/**
* What is the process currently working on
*/
String task;
/**
* The process failure
*/
Throwable exception;
/**
* The original request. This is a transient field will be available only inside the node that
* originated the request, and only during its execution
*/
transient ExecuteType request;
/**
* Node identifier
*/
String nodeId;
public ExecutionStatus(Name processName, String executionId, boolean asynchronous) {
this.processName = processName;
this.executionId = executionId;
setPhase(ProcessState.QUEUED);
this.creationTime = new Date();
this.lastUpdated = this.creationTime;
this.asynchronous = asynchronous;
// grab the user name that made the request
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
this.userName = authentication.getName();
}
// grab the node id
this.nodeId = NODE_IDENTIFIER;
}
public ExecutionStatus(ExecutionStatus other) {
this.processName = other.processName;
this.executionId = other.executionId;
setPhase(other.phase);
this.progress = other.progress;
this.task = other.task;
this.exception = other.exception;
this.creationTime = other.creationTime;
this.completionTime = other.completionTime;
this.request = other.request;
this.asynchronous = other.asynchronous;
this.userName = other.userName;
this.nodeId = other.nodeId;
this.lastUpdated = other.lastUpdated;
}
public void setException(Throwable exception) {
this.exception = exception;
setPhase(ProcessState.FAILED);
}
public Name getProcessName() {
return processName;
}
public String getSimpleProcessName() {
return processName.toString();
}
public String getExecutionId() {
return executionId;
}
public ProcessState getPhase() {
return phase;
}
/**
* Returns the progress percentage, as a number between 0 and 100
*
*
*/
public float getProgress() {
return progress;
}
public void setProcessName(Name processName) {
this.processName = processName;
}
public void setExecutionId(String executionId) {
this.executionId = executionId;
}
public void setPhase(ProcessState phase) {
this.phase = phase;
if (phase != null && phase.isExecutionCompleted()
//if there is already a completionTime don't overwrite it!
&&this.completionTime==null) {
this.completionTime = new Date();
}
}
public void setProgress(float progress) {
this.progress = progress;
}
public void setTask(String task) {
this.task = task;
}
public String getTask() {
return task;
}
public Throwable getException() {
return exception;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
/**
* The original request. This field is available only while the request is being processed, on
* the node that's processing it. For all other nodes, a copy of the request is stored on disk
*
*
*/
public ExecuteType getRequest() {
return request;
}
public void setRequest(ExecuteType request) {
this.request = request;
}
public Date getCreationTime() {
return creationTime;
}
public void setCreationTime(Date creationTime) {
this.creationTime = creationTime;
}
public Date getCompletionTime() {
return completionTime;
}
public void setCompletionTime(Date completionTime) {
this.completionTime = completionTime;
}
public boolean isAsynchronous() {
return asynchronous;
}
public String getNodeId() {
return nodeId;
}
/**
* Last time this bean has been updated
*/
public Date getLastUpdated() {
return lastUpdated;
}
/**
* Sets the last updated time. Only the {@link ProcessStatusTracker} should call this method
*
* @param lastUpdated
*/
public void setLastUpdated(Date lastUpdated) {
this.lastUpdated = lastUpdated;
}
@Override
public String toString() {
return "ExecutionStatus [processName=" + processName + ", executionId=" + executionId
+ ", asynchronous=" + asynchronous + ", phase=" + phase + ", progress=" + progress
+ ", userName=" + userName + ", creationTime=" + creationTime + ", completionTime="
+ completionTime + ", lastUpdated=" + lastUpdated + ", task=" + task
+ ", exception=" + exception + ", nodeId=" + nodeId + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (asynchronous ? 1231 : 1237);
result = prime * result + ((completionTime == null) ? 0 : completionTime.hashCode());
result = prime * result + ((creationTime == null) ? 0 : creationTime.hashCode());
result = prime * result + ((executionId == null) ? 0 : executionId.hashCode());
result = prime * result + ((lastUpdated == null) ? 0 : lastUpdated.hashCode());
result = prime * result + ((nodeId == null) ? 0 : nodeId.hashCode());
result = prime * result + ((phase == null) ? 0 : phase.hashCode());
result = prime * result + ((processName == null) ? 0 : processName.hashCode());
result = prime * result + Float.floatToIntBits(progress);
result = prime * result + ((task == null) ? 0 : task.hashCode());
result = prime * result + ((userName == null) ? 0 : userName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ExecutionStatus other = (ExecutionStatus) obj;
if (asynchronous != other.asynchronous)
return false;
if (completionTime == null) {
if (other.completionTime != null) {
return false;
}
} else if (!completionTime.equals(other.completionTime))
return false;
if (creationTime == null) {
if (other.creationTime != null)
return false;
} else if (!creationTime.equals(other.creationTime))
return false;
if (executionId == null) {
if (other.executionId != null)
return false;
} else if (!executionId.equals(other.executionId))
return false;
if (lastUpdated == null) {
if (other.lastUpdated != null)
return false;
} else if (!lastUpdated.equals(other.lastUpdated))
return false;
if (nodeId == null) {
if (other.nodeId != null)
return false;
} else if (!nodeId.equals(other.nodeId))
return false;
if (phase != other.phase)
return false;
if (processName == null) {
if (other.processName != null)
return false;
} else if (!processName.equals(other.processName))
return false;
if (Float.floatToIntBits(progress) != Float.floatToIntBits(other.progress))
return false;
if (task == null) {
if (other.task != null)
return false;
} else if (!task.equals(other.task))
return false;
if (userName == null) {
if (other.userName != null)
return false;
} else if (!userName.equals(other.userName))
return false;
return true;
}
}