/*
* 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-2008], 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.bizapp.server.session;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.zip.GZIPOutputStream;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.util.EntityUtils;
import org.hyperic.hq.appdef.shared.PlatformManager;
import org.hyperic.hq.appdef.shared.ServerManager;
import org.hyperic.hq.appdef.shared.ServiceManager;
import org.hyperic.hq.auth.shared.SessionException;
import org.hyperic.hq.auth.shared.SessionManager;
import org.hyperic.hq.authz.server.session.AuthzSubject;
import org.hyperic.hq.bizapp.shared.UpdateBoss;
import org.hyperic.hq.common.server.session.ServerConfigAuditFactory;
import org.hyperic.hq.common.shared.ProductProperties;
import org.hyperic.hq.common.shared.ServerConfigManager;
import org.hyperic.hq.hqu.server.session.UIPlugin;
import org.hyperic.hq.hqu.shared.UIPluginManager;
import org.hyperic.hq.security.ServerKeystoreConfig;
import org.hyperic.util.http.HQHttpClient;
import org.hyperic.util.http.HttpConfig;
import org.hyperic.util.security.KeystoreConfig;
import org.hyperic.util.timer.StopWatch;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
*/
@Service
@Transactional
public class UpdateBossImpl implements UpdateBoss {
private final Log log = LogFactory.getLog(UpdateBossImpl.class.getName());
private ServerConfigManager serverConfigManager;
private PlatformManager platformManager;
private ServerManager serverManager;
private ServiceManager serviceManager;
private UIPluginManager uiPluginManager;
private UpdateStatusDAO updateDAO;
private ServerConfigAuditFactory serverConfigAuditFactory;
private String updateNotifyUrl;
private static final int HTTP_TIMEOUT_MILLIS = 30000;
private DataSource dataSource;
private KeystoreConfig keystoreConf;
private boolean acceptUnverifiedCertificates;
@Autowired
public UpdateBossImpl(
UpdateStatusDAO updateDAO,
ServerConfigManager serverConfigManager,
PlatformManager platformManager,
ServerManager serverManager,
ServiceManager serviceManager,
UIPluginManager uiPluginManager,
ServerConfigAuditFactory serverConfigAuditFactory,
DataSource dataSource,
@Value("#{tweakProperties['hq.updateNotify.url'] }") String updateNotifyUrl,
ServerKeystoreConfig serverKeystoreConfig,
@Value("#{securityProperties['accept.unverified.certificates']}")
boolean acceptUnverifiedCertificates) {
this.updateDAO = updateDAO;
this.serverConfigManager = serverConfigManager;
this.platformManager = platformManager;
this.serverManager = serverManager;
this.serviceManager = serviceManager;
this.uiPluginManager = uiPluginManager;
this.serverConfigAuditFactory = serverConfigAuditFactory;
this.dataSource = dataSource;
this.updateNotifyUrl = updateNotifyUrl;
keystoreConf = serverKeystoreConfig;
this.acceptUnverifiedCertificates = acceptUnverifiedCertificates;
}
protected Properties getRequestInfo(UpdateStatus status) {
Properties req = new Properties();
String guid = serverConfigManager.getGUID();
req.setProperty("hq.updateStatusMode", "" + status.getMode().getCode());
req.setProperty("hq.version", ProductProperties.getVersion());
req.setProperty("hq.guid", guid);
req.setProperty("hq.flavour", ProductProperties.getFlavour());
req.setProperty("platform.time", "" + System.currentTimeMillis());
req.setProperty("os.name", System.getProperty("os.name"));
req.setProperty("os.arch", System.getProperty("os.arch"));
req.setProperty("os.version", System.getProperty("os.version"));
req.setProperty("java.version", System.getProperty("java.version"));
req.setProperty("java.vendor", System.getProperty("java.vendor"));
List<Object[]> plats = platformManager.getPlatformTypeCounts();
List<Object[]> svrs = serverManager.getServerTypeCounts();
List<Object[]> svcs = serviceManager.getServiceTypeCounts();
addResourceProperties(req, plats, "hq.rsrc.plat.");
addResourceProperties(req, svrs, "hq.rsrc.svr.");
addResourceProperties(req, svcs, "hq.rsrc.svc.");
req.putAll(SysStats.getCpuMemStats());
try {
req.putAll(SysStats.getDBStats(dataSource.getConnection()));
} catch (SQLException e) {
log.warn("Error obtaining DB Stats: " + e.getMessage(),e);
}
req.putAll(getHQUPlugins());
return req;
}
private Properties getHQUPlugins() {
Collection<UIPlugin> plugins = uiPluginManager.findAll();
Properties res = new Properties();
for (UIPlugin p : plugins) {
res.setProperty("hqu.plugin." + p.getName(), p.getPluginVersion());
}
return res;
}
private void addResourceProperties(Properties p, List<Object[]> resCounts, String prefix) {
for (Object[] val : resCounts) {
p.setProperty(prefix + val[0], "" + val[1]);
}
}
/**
* Meant to be called internally by the fetching thread
*
*
*/
public void fetchReport() {
final boolean debug = log.isDebugEnabled();
final StopWatch watch = new StopWatch();
UpdateStatus status = getOrCreateStatus();
Properties req;
byte[] reqBytes;
if (status.getMode().equals(UpdateStatusMode.NONE)) {
return;
}
req = getRequestInfo(status);
try {
ByteArrayOutputStream bOs = new ByteArrayOutputStream();
GZIPOutputStream gOs = new GZIPOutputStream(bOs);
req.store(gOs, "");
gOs.flush();
gOs.close();
bOs.flush();
bOs.close();
reqBytes = bOs.toByteArray();
} catch (IOException e) {
log.warn("Error creating report request", e);
return;
}
if(debug){
log.debug("Generated report. Size=" + reqBytes.length + " report:\n" + req);
}
try {
HttpConfig config = new HttpConfig(HTTP_TIMEOUT_MILLIS, HTTP_TIMEOUT_MILLIS, null, -1);
HQHttpClient client = new HQHttpClient(keystoreConf, config, acceptUnverifiedCertificates);
HttpPost post = new HttpPost(updateNotifyUrl);
post.addHeader("x-hq-guid", req.getProperty("hq.guid"));
ByteArrayInputStream bIs = new ByteArrayInputStream(reqBytes);
HttpEntity entity = new InputStreamEntity(bIs, reqBytes.length);
post.setEntity(entity);
if (debug) watch.markTimeBegin("post");
HttpResponse response = client.execute(post);
if (debug) watch.markTimeEnd("post");
if (response != null && response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
processReport(response.getStatusLine().getStatusCode(),
EntityUtils.toString(response.getEntity(), "UTF-8"));
} else {
if (debug) {
log.debug("fetchReport: " + watch
+ ", currentReport {" + status.getReport()
+ "}, latestReport {url=" + updateNotifyUrl
+ ", statusCode=" + response.getStatusLine().getStatusCode()
+ ", response=" + EntityUtils.toString(response.getEntity(), "UTF-8")
+ "}");
}
}
} catch (ClientProtocolException e) {
log.error(e);
} catch (IOException e) {
log.error(e);
}
}
private void processReport(int statusCode, String response) {
UpdateStatus curStatus = getOrCreateStatus();
String curReport;
if (response.length() >= 4000) {
log.warn("Update report exceeded 4k");
return;
}
if (statusCode != 200) {
log.debug("Bad status code returned: " + statusCode);
return;
}
if (curStatus.getMode().equals(UpdateStatusMode.NONE))
return;
response = response.trim();
curReport = curStatus.getReport() == null ? "" : curStatus.getReport();
if (curReport.equals(response))
return;
curStatus.setReport(response);
curStatus.setIgnored(response.trim().length() == 0);
}
/**
* Returns null if there is no status report (or it's been ignored), else
* the string status report
*
*
*/
@Transactional(readOnly = true)
public String getUpdateReport() {
UpdateStatus status = getOrCreateStatus();
if (status.isIgnored())
return null;
if (status.getReport() == null || status.getReport().equals("")) {
return null;
}
return status.getReport();
}
/**
*
*/
public void setUpdateMode(int sess, UpdateStatusMode mode) throws SessionException {
AuthzSubject subject = SessionManager.getInstance().getSubject(sess);
UpdateStatus status = getOrCreateStatus();
if (!status.getMode().equals(mode)) {
serverConfigAuditFactory.updateAnnounce(subject, mode, status.getMode());
}
status.setMode(mode);
if (mode.equals(UpdateStatusMode.NONE)) {
status.setIgnored(true);
status.setReport("");
}
}
/**
*
*/
@Transactional(readOnly = true)
public UpdateStatusMode getUpdateMode() {
return getOrCreateStatus().getMode();
}
/**
*
*/
public void ignoreUpdate() {
UpdateStatus status = getOrCreateStatus();
status.setIgnored(true);
}
private UpdateStatus getOrCreateStatus() {
UpdateStatus res = updateDAO.get();
if (res == null) {
res = new UpdateStatus("", UpdateStatusMode.MAJOR);
updateDAO.save(res);
}
return res;
}
}