/*
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2004, 2005, 2006], Hyperic, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License 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 for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*/
package org.hyperic.hq.autoinventory;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.appdef.shared.AIPlatformValue;
import org.hyperic.hq.appdef.shared.AIServerExtValue;
import org.hyperic.hq.appdef.shared.AIServerValue;
import org.hyperic.hq.product.ServerResource;
import org.hyperic.util.StringUtil;
import org.hyperic.util.StringifiedException;
public class ScanState {
private static HashMap installdirExcludes = new HashMap();
private static List installdirExcludesPrefixes = new ArrayList();
private static final Log _log = LogFactory.getLog(ScanState.class);
static {
loadInstalldirExcludes();
}
private DateFormat dateFmt
= DateFormat.getDateTimeInstance(DateFormat.SHORT,
DateFormat.MEDIUM);
private ScanStateCore _core;
/** ScanMethodClassName->ScanMethod */
private Map _scanMethods = null;
private boolean _isDefaultScan = false;
public ScanState () {
_core = new ScanStateCore();
}
public ScanState (ScanStateCore core) {
_core = core;
}
public ScanStateCore getCore () { return _core; }
public void setCore (ScanStateCore core) { _core = core; }
public boolean getIsDefaultScan () { return _isDefaultScan; }
public void setIsDefaultScan (boolean b) { _isDefaultScan = b; }
public boolean getAreServersIncluded () {
return _core.getAreServersIncluded();
}
public void setAreServersIncluded (boolean b) {
_core.setAreServersIncluded(b);
}
public long getStartTime() {
return _core.getStartTime();
}
/**
* @return the formatted start time
*/
public String getStartTimeStr() {
if ( getStartTime() == 0 )
return "00/00/00 00:00:00";
return dateFmt.format(new Long(getStartTime()));
}
public void setStartTime(long startTime) {
_core.setStartTime(startTime);
}
public long getEndTime() {
return _core.getEndTime();
}
public String getEndTimeStr() {
if ( getEndTime() == 0 )
return "00/00/00 00:00:00";
return dateFmt.format(new Long(getEndTime()));
}
public void setEndTime(long endTime) {
_core.setEndTime(endTime);
}
/**
* @return the formatted relapsed time
*/
public String getElapsedTimeStr() {
long end = 0;
// if the start time is zero, return no elapsed time
if (getStartTime() == 0)
return StringUtil.formatDuration(0);
if (getEndTime() == 0)
end = (new Date()).getTime();
else
end = getEndTime();
return StringUtil.formatDuration(end - getStartTime());
}
public boolean getIsDone () { return _core.getIsDone(); }
public void setIsDone () {
_core.setIsDone(true);
}
public boolean getIsInterrupted () { return _core.getIsInterrupted(); }
public void setIsInterrupted () {
_core.setIsInterrupted(true);
}
public StringifiedException getGlobalException() {
return _core.getGlobalException();
}
public void setGlobalException(Throwable _globalException) {
_core.setGlobalException(new StringifiedException(_globalException));
}
/**
* Tell the scan state what scan methods will be run.
* @param scanMethods An array of ScanMethod class names that
* represent the ScanMethods that will be run in this scan.
*/
public void setScanMethods ( String[] scanMethods )
throws AutoinventoryException {
// Walk thru the list and construct the ScanMethodState[] array
ScanMethodState[] smStates = new ScanMethodState[scanMethods.length];
for ( int i=0; i<scanMethods.length; i++ ) {
smStates[i] = new ScanMethodState();
smStates[i].setMethodClass(scanMethods[i]);
}
_core.setScanMethodStates(smStates);
setupMethodHash();
}
/**
* Setup our internal hash of ScanMethodClassName->ScanMethod
*/
protected void setupMethodHash () throws AutoinventoryException {
_scanMethods = new HashMap();
ScanMethodState[] smStates = _core.getScanMethodStates();
if ( smStates == null ) return;
ScanMethod method;
String methodClass;
for ( int i=0; i<smStates.length; i++ ) {
methodClass = smStates[i].getMethodClass();
try {
method = (ScanMethod) Class.forName(methodClass).newInstance();
} catch ( Exception e ) {
throw new AutoinventoryException(methodClass
+ ": error instantiating: "
+ e, e);
}
_scanMethods.put(methodClass, method);
}
}
public boolean completedOK () {
return _core.getIsDone()
&& (!hasExceptions())
&& (!_core.getIsInterrupted());
}
public boolean hasExceptions () {
ScanMethodState[] smStates = _core.getScanMethodStates();
StringifiedException[] exceptions;
for ( int i=0; i<smStates.length; i++ ) {
exceptions = smStates[i].getExceptions();
if ( exceptions != null && exceptions.length > 0 ) {
return true;
}
}
return false;
}
public void initStartTime () {
_core.setStartTime(System.currentTimeMillis());
}
public void initEndTime () {
_core.setEndTime(System.currentTimeMillis());
}
public long getScanDuration () {
long startTime = _core.getStartTime();
long endTime = _core.getEndTime();
if ( endTime == 0 ) {
if ( startTime == 0 ) {
return 0;
}
return System.currentTimeMillis() - startTime;
}
return endTime - startTime;
}
public AIPlatformValue getPlatform () { return _core.getPlatform(); }
public void setPlatform ( AIPlatformValue platform ) {
_core.setPlatform(platform);
}
public String getCertDN () { return _core.getPlatform().getCertdn(); }
public void setCertDN ( String certDN ) {
_core.setCertDN(certDN);
}
public void addScanException(ScanMethod scanMethod, Throwable t) {
ScanMethodState smState = findSMState("addScanException", scanMethod);
smState.addException(new StringifiedException(t));
}
public void addScanExceptions(ScanMethod scanMethod, Throwable[] t) {
ScanMethodState smState = findSMState("addScanException", scanMethod);
smState.addExceptions(t);
}
public void setScanStatus ( ScanMethod scanMethod, String status ) {
ScanMethodState smState = findSMState("addScanException", scanMethod);
smState.setStatus(status);
}
public ScanMethodState[] getScanMethodStates () {
ScanMethodState[] smStates = _core.getScanMethodStates();
return smStates;
}
//XXX temporary during refactoring
private AIServerValue getServerValue(Object o) {
AIServerValue server;
if (o instanceof AIServerValue) {
server = (AIServerValue)o;
}
else {
server = (AIServerValue)((ServerResource)o).getResource();
}
if (!server.cTimeHasBeenSet()) {
server.setCTime(new Long(System.currentTimeMillis()));
}
return server;
}
private List excludeServers(List servers) {
if (installdirExcludes.size() == 0) {
return servers;
}
ArrayList includes = new ArrayList();
for (int i=0; i<servers.size(); i++) {
Object server = servers.get(i);
String installpath =
getServerValue(server).getInstallPath();
boolean exclude = false;
if (installdirExcludes.get(installpath) != null) {
continue;
}
for (int j=0; j<installdirExcludesPrefixes.size(); j++) {
String prefix = (String)installdirExcludesPrefixes.get(j);
if (installpath.startsWith(prefix)) {
exclude = true;
break;
}
}
if (!exclude) {
includes.add(server);
}
}
return includes;
}
/**
* Add servers to the list of servers detected for a particular
* scan method.
* @param scanMethod The scan method to add servers to.
* @param servers A List of AIServer objects representing
* the servers (and their services) that were detected and should be
* added and associated with the given scan method.
*/
public void addServers ( ScanMethod scanMethod, List servers ) {
servers = excludeServers(servers);
if(servers.isEmpty()) {
return;
}
ScanMethodState smState = findSMState("addServers", scanMethod);
AIServerValue[] newServers;
AIServerValue[] existingServers = smState.getServers();
if ( existingServers == null ) {
newServers = new AIServerValue[servers.size()];
for ( int i=0; i<newServers.length; i++ ) {
newServers[i] = getServerValue(servers.get(i));
}
} else {
List<AIServerValue> allServers = new ArrayList<AIServerValue>(Arrays.asList(existingServers));
//Get rid of any servers we have already discovered (by autoinventoryidentifier).
//ServerDetectors are ordered, so first one should always win
for(Iterator iterator= servers.iterator();iterator.hasNext();) {
AIServerValue server = getServerValue(iterator.next());
String identifier =server.getAutoinventoryIdentifier();
boolean newIdentifier = true;
for(AIServerValue existingServer : existingServers) {
if(existingServer.getAutoinventoryIdentifier().equals(identifier)) {
newIdentifier = false;
break;
}
}
if(newIdentifier) {
allServers.add(server);
}
}
newServers = allServers.toArray(new AIServerValue[allServers.size()]);
}
smState.setServers(newServers);
}
/**
* For debugging purposes, print out the servers that were
* detected.
*/
public void printServers () {
ScanMethodState[] smStates = _core.getScanMethodStates();
AIServerValue[] servers;
for ( int i=0; i<smStates.length; i++ ) {
System.err.println("Detected by: " + smStates[i].getMethodClass());
servers = smStates[i].getServers();
for ( i=0; i<servers.length; i++ ) {
System.err.println("\t" + servers[i]);
}
}
}
/**
* For debugging purposes, print stack traces for all exceptions
*/
public void printStackTraces () {
ScanMethodState[] smStates = _core.getScanMethodStates();
StringifiedException[] exc;
for ( int i=0; i<smStates.length; i++ ) {
exc = smStates[i].getExceptions();
if ( exc != null && exc.length > 0 ) {
System.err.println("Exceptions for method "
+ smStates[i].getMethodClass() + ":");
for ( int j=0; j<exc.length; j++ ) {
System.err.println("\n" + exc[j].getStackTrace());
}
}
}
}
/**
* For debugging and command-line use, pretty-print full status info.
* @param out The stream to write to.
*/
public void printFullStatus ( PrintStream out )
throws AutoinventoryException {
StringifiedException globalEx = _core.getGlobalException();
ScanMethodState[] smStates = _core.getScanMethodStates();
if ( globalEx != null ) {
out.println("Severe failure: " + globalEx);
out.println(globalEx.getStackTrace());
}
if ( smStates == null ) {
out.println("scan not yet started.");
return;
}
printMainStatus(out);
for ( int i=0; i<smStates.length; i++ ) {
printMethodStatus(smStates[i], out);
}
}
public void printMainStatus ( PrintStream out ) {
out.print("\nOVERALL STATUS: ");
String status = null;
if ( _core.getIsInterrupted() ) {
status = "interrupted before normal completion";
} else if ( _core.getIsDone() ) {
status = "completed";
} else {
status = "scan in progress";
}
if ( _core.getGlobalException() != null ) {
status += ", however a general scanning error occurred";
} else if ( hasExceptions() ) {
status += " successfully, however one or more scan methods had errors";
} else if ( _core.getIsDone() ) {
status += " successfully with no errors";
}
out.println(status);
String duration = StringUtil.formatDuration(getScanDuration());
out.println("Run time: " + duration);
AIPlatformValue platform = _core.getPlatform();
if ( platform != null ) {
out.println("\nPlatform Detected:");
out.println("\t" + platform);
out.println("\tIP addresses: "
+ StringUtil.arrayToString(platform.getAIIpValues()));
} else {
out.println("\nNo Platform Detected!");
}
}
public void printMethodStatus ( ScanMethodState smState, PrintStream out )
throws AutoinventoryException {
ScanMethod method = findScanMethod(smState.getMethodClass());
out.println("\n" + method.getName() + ":");
// Print exception (if any)
StringifiedException[] t = smState.getExceptions();
String status = smState.getStatus();
if ( t != null && t.length > 0 ) {
out.println("\t* SCAN FAILED: ");
if ( status != null ) {
out.println("\t* Last status before failure: " + status);
} else {
out.println("\t* No status message available.");
}
for ( int i=0; i<t.length; i++ ) {
out.println("\t* " + t[i].toString());
out.println("\t* Stack Trace:");
out.println(t[i].getStackTrace());
}
} else {
if ( status == null ) {
out.println("\t* Status: unknown");
} else {
out.println("\t* Status: " + status);
}
}
// PRINT SERVERS DETECTED
AIServerValue[] servers = smState.getServers();
if ( servers == null || servers.length == 0 ) {
out.println("\t* No Servers Detected");
} else {
out.println("\t* Detected Servers:");
for ( int i=0; i<servers.length; i++ ) {
out.println("\t" + servers[i]);
}
}
out.println("\n");
}
public String toString () {
if( _core == null ) return "[ScanState]";
return _core.toString();
}
/**
* Get the set of all servers detected in this autoinventory scan.
* This is the method that reconciles the fact that multiple scan methods
* may have discovered the same server. We assemble the list of all
* servers by iterating over each scan method in order of authority level.
* The scan methods with the highest authority level have their servers
* added first. Scan methods with lower authority levels will have their
* servers added as long as they have a different autoinventory ID from
* ones discovered by methods with higher authority levels.
* @return A Set of AIServerValue objects. The Set uniqueness
* is based on the server autoinventory identifier, which is
* usually the same as the install path.
*/
public Set getAllServers () throws AutoinventoryException {
// allServers will guarantee uniqueness on the AIID.
Set allServers = new TreeSet(COMPARE_AIID);
// Put all the scan methods in a list
Map scanMethods = getScanMethodMap();
Iterator iter = scanMethods.keySet().iterator();
List smList = new ArrayList();
while ( iter.hasNext() ) {
smList.add(scanMethods.get(iter.next()));
}
// Sort the scan methods by authority level.
Collections.sort(smList, COMPARE_AUTH);
// Iterate over the scan methods
AIServerValue[] servers;
String methodClass;
ScanMethod method;
ScanMethodState smState;
for ( int i=0; i<smList.size(); i++ ) {
method = (ScanMethod) smList.get(i);
smState = findSMState("getAllServers", method);
methodClass = smState.getMethodClass();
servers = smState.getServers();
if ( servers != null ) {
for ( int j=0; j<servers.length; j++ ) {
if ( !allServers.add(servers[j]) ) {
if ( _log != null ) {
_log.info("Server not added because another scan "
+ "method already detected it:"
+ servers[j]);
}
}
}
}
}
Map mServers = new HashMap();
//look for servers with the same Metric Connect HashCode
//for example, two JBoss servers with different installpath
//but the same config: jnp://localhost:1099
for (Iterator allIter = allServers.iterator();
allIter.hasNext();)
{
Object o = allIter.next();
if (!(o instanceof AIServerExtValue)) {
continue;
}
AIServerExtValue server = (AIServerExtValue)o;
if (!server.getAutoEnable()) {
continue;
}
int hashCode = server.getMetricConnectHashCode();
if (hashCode == 0) {
continue;
}
Integer key = new Integer(hashCode);
AIServerExtValue cServer =
(AIServerExtValue)mServers.get(key);
if (cServer == null) {
//based on ScanImpl authority, the first server
//found is the most likely to be running.
mServers.put(key, server);
}
else {
//found a server with the same connect config
//turn off AutoEnable
server.setAutoEnable(false);
//disable metric collection
server.unsetMeasurementConfig();
if (_log != null) {
_log.info("Turning off AutoEnable for server " +
server.getName() +
" [" + server.getInstallPath() + "]" +
", has the same metric connect config as " +
cServer.getName() +
" [" + cServer.getInstallPath() + "]");
}
}
}
return allServers;
}
protected ScanMethodState findSMState (String caller,
ScanMethod scanMethod) {
ScanMethodState[] smStates = _core.getScanMethodStates();
String smClassName = scanMethod.getClass().getName();
for ( int i=0; i<smStates.length; i++ ) {
if ( smStates[i].getMethodClass().equals(smClassName) ) {
return smStates[i];
}
}
throw new IllegalArgumentException("Error finding smState: "
+ smClassName + ", caller="
+ caller);
}
protected ScanMethod findScanMethod(String methodClass)
throws AutoinventoryException {
Map scanMethods = getScanMethodMap();
ScanMethod m = (ScanMethod) scanMethods.get(methodClass);
if ( m != null ) return m;
throw new IllegalArgumentException("ScanMethod not found: "
+ methodClass);
}
private Map getScanMethodMap () throws AutoinventoryException {
if ( _scanMethods == null ) setupMethodHash();
return _scanMethods;
}
public boolean isSameState (ScanState other) throws AutoinventoryException {
// Compare platform attributes
AIPlatformValue p1, p2;
p1 = getPlatform();
p2 = other.getPlatform();
if (!AICompare.compareAIPlatforms(p1, p2)) return false;
Set servers1, servers2;
servers1 = getAllServers();
servers2 = other.getAllServers();
if (!AICompare.compareAIServers(servers1, servers2)) return false;
return true;
}
private static Comparator COMPARE_AIID = new ServerComparator_AIID();
static class ServerComparator_AIID implements Comparator {
public ServerComparator_AIID () {}
public int compare(Object o1, Object o2) {
if ( o1 instanceof AIServerValue &&
o2 instanceof AIServerValue ) {
return ((AIServerValue) o2).getAutoinventoryIdentifier()
.compareTo(((AIServerValue) o1).getAutoinventoryIdentifier());
}
return 0; // all other object are "equal"
}
public boolean equals(Object o) { return false; }
}
private static Comparator COMPARE_AUTH = new ServerComparator_AuthLevel();
static class ServerComparator_AuthLevel implements Comparator {
public ServerComparator_AuthLevel () {}
public int compare(Object o1, Object o2) {
if ( o1 instanceof ScanMethod &&
o2 instanceof ScanMethod ) {
return ((ScanMethod) o2).getAuthorityLevel()
- ((ScanMethod) o1).getAuthorityLevel();
}
return 0; // all other object are "equal"
}
public boolean equals(Object o) { return false; }
}
//any installpath found in ~/.hq/installdir.excludes
//will not be reported by AI
private static void loadInstalldirExcludes() {
String path =
System.getProperty("user.home") + File.separator +
".hq" + File.separator + "installdir.excludes";
File excludes = new File(path);
if (!excludes.exists()) {
return;
}
FileReader is = null;
try {
is = new FileReader(excludes);
BufferedReader in = new BufferedReader(is);
String line;
while ((line = in.readLine()) != null) {
line = line.trim();
if (line.length() == 0) {
continue;
}
if (line.charAt(0) == '#') {
continue;
}
if (line.endsWith("*")) {
line = line.substring(0, line.length()-1);
installdirExcludesPrefixes.add(line);
}
installdirExcludes.put(line, Boolean.TRUE);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {}
}
}
}
}