/* * NOTE: This copyright does *not* cover user programs that use Hyperic * 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-2012], VMware, Inc. * This file is part of Hyperic. * * Hyperic 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.api.transfer.impl; import java.sql.SQLException; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PostConstruct; import javax.ws.rs.core.Response; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.cxf.jaxrs.ext.search.SearchContext; import org.hibernate.ObjectNotFoundException; import org.hyperic.hq.api.model.ID; import org.hyperic.hq.api.model.common.ExternalEndpointStatus; import org.hyperic.hq.api.model.common.RegistrationID; import org.hyperic.hq.api.model.common.ExternalRegistrationStatus; import org.hyperic.hq.api.model.measurements.BulkResourceMeasurementRequest; import org.hyperic.hq.api.model.measurements.HttpEndpointDefinition; import org.hyperic.hq.api.model.measurements.MeasurementRequest; import org.hyperic.hq.api.model.measurements.MetricFilterRequest; import org.hyperic.hq.api.model.measurements.MetricResponse; import org.hyperic.hq.api.model.measurements.ResourceMeasurementBatchResponse; import org.hyperic.hq.api.model.measurements.ResourceMeasurementRequest; import org.hyperic.hq.api.model.measurements.ResourceMeasurementRequests; import org.hyperic.hq.api.model.measurements.ResourceMeasurementResponse; import org.hyperic.hq.api.services.impl.ApiMessageContext; import org.hyperic.hq.api.transfer.MeasurementTransfer; import org.hyperic.hq.api.transfer.NotificationsTransfer; import org.hyperic.hq.api.transfer.mapping.ExceptionToErrorCodeMapper; import org.hyperic.hq.api.transfer.mapping.MeasurementMapper; import org.hyperic.hq.api.transfer.mapping.UnknownEndpointException; import org.hyperic.hq.authz.server.session.AuthzSubject; import org.hyperic.hq.authz.server.session.Resource; import org.hyperic.hq.authz.shared.PermissionException; import org.hyperic.hq.authz.shared.PermissionManager; import org.hyperic.hq.authz.shared.ResourceManager; import org.hyperic.hq.common.NotFoundException; import org.hyperic.hq.common.TimeframeBoundriesException; import org.hyperic.hq.context.Bootstrap; import org.hyperic.hq.measurement.MeasurementConstants; import org.hyperic.hq.measurement.server.session.Measurement; import org.hyperic.hq.measurement.server.session.MeasurementTemplate; import org.hyperic.hq.measurement.server.session.ReportProcessorImpl; import org.hyperic.hq.measurement.server.session.TimeframeSizeException; import org.hyperic.hq.measurement.shared.DataManager; import org.hyperic.hq.measurement.shared.HighLowMetricValue; import org.hyperic.hq.measurement.shared.MeasurementManager; import org.hyperic.hq.measurement.shared.TemplateManager; import org.hyperic.hq.notifications.DefaultEndpoint; import org.hyperic.hq.notifications.HttpEndpoint; import org.hyperic.hq.notifications.NotificationEndpoint; import org.hyperic.hq.notifications.filtering.AgnosticFilter; import org.hyperic.hq.notifications.filtering.Filter; import org.hyperic.hq.notifications.filtering.FilterChain; import org.hyperic.hq.notifications.filtering.FilteringCondition; import org.hyperic.hq.notifications.filtering.MetricDestinationEvaluator; import org.hyperic.hq.notifications.model.MetricNotification; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; public class MeasurementTransferImpl implements MeasurementTransfer { protected final Log log = LogFactory.getLog(ReportProcessorImpl.class); protected static final int MAX_DTPS = 400; protected ResourceManager resourceManager ; protected MeasurementManager measurementMgr; protected TemplateManager tmpltMgr; protected DataManager dataMgr; protected MeasurementMapper mapper; protected ExceptionToErrorCodeMapper errorHandler ; protected MetricDestinationEvaluator evaluator; private NotificationsTransfer notificationsTransfer; @Autowired protected PermissionManager permissionManager; @javax.ws.rs.core.Context protected SearchContext context ; @Autowired public MeasurementTransferImpl(ResourceManager resourceManager,MeasurementManager measurementMgr, TemplateManager tmpltMgr, DataManager dataMgr, MeasurementMapper mapper, ExceptionToErrorCodeMapper errorHandler, MetricDestinationEvaluator evaluator) { super(); this.resourceManager = resourceManager; this.measurementMgr = measurementMgr; this.tmpltMgr = tmpltMgr; this.mapper=mapper; this.dataMgr = dataMgr; this.errorHandler = errorHandler; this.evaluator = evaluator; } @PostConstruct public void init() { this.notificationsTransfer = (NotificationsTransfer) Bootstrap.getBean("notificationsTransfer"); } protected List<Measurement> getMeasurements(final String rscId, final List<MeasurementTemplate> tmps, final AuthzSubject authzSubject) throws PermissionException { // get measurements Map<Integer, List<Integer>> resIdsToTmpIds = new HashMap<Integer, List<Integer>>(); List<Integer> tmpIds = new ArrayList<Integer>(); for (MeasurementTemplate tmp : tmps) { tmpIds.add(tmp.getId()); } resIdsToTmpIds.put(Integer.valueOf(rscId), tmpIds); Map<Resource, List<Measurement>> rscTohqMsmts = null; rscTohqMsmts = this.measurementMgr.findMeasurements(authzSubject, resIdsToTmpIds); if (rscTohqMsmts==null || rscTohqMsmts.size()==0 || rscTohqMsmts.values().isEmpty()) { throw new ObjectNotFoundException(tmps.toString(), Measurement.class.getName()); } // there should be only one list of measurements for one resource List<Measurement> hqMsmts = rscTohqMsmts.values().iterator().next(); if (hqMsmts==null || hqMsmts.isEmpty()) { throw new ObjectNotFoundException(tmps.toString(), Measurement.class.getName()); } return hqMsmts; } @Transactional(readOnly=true) public RegistrationID register(final ApiMessageContext messageContext,final MetricFilterRequest request, ApiMessageContext apiMessageContext) throws PermissionException { if (request==null) { if (log.isDebugEnabled()) { log.debug("illegal request"); } throw errorHandler.newWebApplicationException(new Throwable(), Response.Status.BAD_REQUEST, ExceptionToErrorCodeMapper.ErrorCode.BAD_REQ_BODY); } AuthzSubject authzSubject = messageContext.getAuthzSubject(); this.permissionManager.checkIsSuperUser(authzSubject); List<Filter<MetricNotification,? extends FilteringCondition<?>>> userFilters = mapper.toMetricFilters(request); if (userFilters.isEmpty()) { userFilters.add(new AgnosticFilter<MetricNotification,FilteringCondition<?>>()); } RegistrationID registrationID = new RegistrationID(); final HttpEndpointDefinition httpEndpointDefinition = request.getHttpEndpointDef(); final NotificationEndpoint endpoint = (httpEndpointDefinition == null) ? new DefaultEndpoint(registrationID.getId()) : getHttpEndpoint(registrationID, httpEndpointDefinition); notificationsTransfer.register(endpoint, apiMessageContext.getAuthzSubject().getId()); evaluator.register(endpoint, userFilters); return registrationID; } private HttpEndpoint getHttpEndpoint(RegistrationID registrationID, HttpEndpointDefinition def) { return new HttpEndpoint(registrationID.getId(), def.getUrl(), def.getUsername(), def.getPassword(), def.getContentType(), def.getEncoding(), def.getBodyPrepend()); } public ExternalRegistrationStatus getRegistrationStatus(final ApiMessageContext messageContext, final String registrationID) throws PermissionException,NotFoundException, UnknownEndpointException { AuthzSubject authzSubject = messageContext.getAuthzSubject(); this.permissionManager.checkIsSuperUser(authzSubject); FilterChain<MetricNotification> filterChain = evaluator.getRegistration(registrationID); NotificationsTransferImpl.EndpointStatusAndDefinition endpointStatusAndDefinition = this.notificationsTransfer.getEndointStatus(registrationID); return new ExternalRegistrationStatus(endpointStatusAndDefinition.getEndpoint(),filterChain, registrationID, endpointStatusAndDefinition.getExternalEndpointStatus()); } public void unregister(final ApiMessageContext apiMessageContext, NotificationEndpoint endpoint) throws PermissionException { evaluator.unregisterAll(endpoint); } public MetricResponse getMetrics(ApiMessageContext apiMessageContext, List<String> templateNames, final String rscId, final Date begin, final Date end) throws ParseException, PermissionException, UnsupportedOperationException, ObjectNotFoundException, TimeframeBoundriesException, TimeframeSizeException { MetricResponse res = new MetricResponse(); if (templateNames.isEmpty()) { throw new UnsupportedOperationException("message body is missing or corrupted"); } validateTimeFrame(begin,end); if (rscId==null || "".equals(rscId)) { throw new UnsupportedOperationException("The request URL is missing the resource ID"); } AuthzSubject authzSubject = apiMessageContext.getAuthzSubject(); // extract all input measurement templates List<MeasurementTemplate> tmps = tmpltMgr.findTemplatesByName(templateNames); if (tmps==null || tmps.isEmpty()) { throw new ObjectNotFoundException(templateNames.toString(), MeasurementTemplate.class.getName()); } List<Measurement> hqMsmts = getMeasurements(rscId, tmps,authzSubject); // get metrics for (Measurement hqMsmt : hqMsmts) { org.hyperic.hq.api.model.measurements.Measurement msmt = mapper.toMeasurement(hqMsmt); List<HighLowMetricValue> hqMetrics = dataMgr.getHistoricalData(hqMsmt, begin.getTime(), end.getTime(), true, MAX_DTPS); if (hqMetrics!=null && !hqMetrics.isEmpty()) { List<org.hyperic.hq.api.model.measurements.Metric> metrics = mapper.toMetrics(hqMetrics); msmt.setMetrics(metrics); } res.add(msmt); } return res; } protected void validateTimeFrame(Date begin, Date end) throws TimeframeBoundriesException { StringBuilder errorMsg = new StringBuilder(); if (begin==null) { errorMsg.append("The request URL is missing the time frame begining"); } if (end==null) { if (errorMsg.length()>0) { errorMsg.append(" and end"); } else { errorMsg.append("The request URL is missing the time frame end"); } } if (errorMsg.length()>0) { throw new TimeframeBoundriesException(errorMsg.toString()); } if (begin.after(end)) { errorMsg.append("Time frame end time is before its start time"); } if (end.after(Calendar.getInstance().getTime())) { errorMsg.append("Time frame ends in the future"); } if (errorMsg.length()>0) { throw new TimeframeBoundriesException(errorMsg.toString()); } } @Transactional(readOnly = true) public ResourceMeasurementBatchResponse getAggregatedMetricData(ApiMessageContext apiMessageContext, ResourceMeasurementRequests requests, Date begin, Date end) throws TimeframeBoundriesException, PermissionException, SQLException, UnsupportedOperationException, ObjectNotFoundException { ResourceMeasurementBatchResponse res = new ResourceMeasurementBatchResponse(this.errorHandler); List<ResourceMeasurementRequest> measRequests; if (requests==null || (measRequests = requests.getMeasurementRequests()) == null || measRequests.isEmpty()) { throw new UnsupportedOperationException("failed parsing the supplied filter"); } validateTimeFrame(begin,end); AuthzSubject authzSubject = apiMessageContext.getAuthzSubject(); // extract all input measurement templates Map<String,List<String>> tmpNameToRscs = new HashMap<String,List<String>>(); List<String> tmpNames = null; String rscId = null; for (ResourceMeasurementRequest request : requests.getMeasurementRequests()) { rscId = request.getRscId(); if (rscId==null || "".equals(rscId)) { throw new ObjectNotFoundException("no resource ID supplied",Resource.class.getName()); } tmpNames = request.getMeasurementTemplateNames(); for (String tmpName : tmpNames) { List<String> rscs = tmpNameToRscs.get(tmpName); if (rscs==null) { rscs = new ArrayList<String>(); tmpNameToRscs.put(tmpName, rscs); } rscs.add(rscId); } } // extract tmp Ids per rsc List<MeasurementTemplate> tmps = tmpltMgr.findTemplatesByName(new ArrayList<String>(tmpNameToRscs.keySet())); // will contain all the resources for which at least one of the templates requested for them exists Map<Integer, List<Integer>> rscIdsToTmpIds = new HashMap<Integer, List<Integer>>(); List<String> rscIds = null; for (MeasurementTemplate tmp : tmps) { rscIds = tmpNameToRscs.get(tmp.getAlias()); if (rscIds==null) { continue; } for (String _rscId : rscIds) { Integer rscIdInt = Integer.valueOf(_rscId); List<Integer> tmpIds = rscIdsToTmpIds.get(rscIdInt); if (tmpIds==null) { tmpIds = new ArrayList<Integer>(); rscIdsToTmpIds.put(rscIdInt,tmpIds ); } tmpIds.add(tmp.getId()); } } // mark resources for which no measurements were found final String TEMPLATE_NOT_FOUND_ERR_CODE = ExceptionToErrorCodeMapper.ErrorCode.TEMPLATE_NOT_FOUND.getErrorCode(); rscId = null; for (ResourceMeasurementRequest hqMsmtReq : requests.getMeasurementRequests()) { // by now we know that all reqs are with valid rscs, o/w we wouldn't get here rscId = hqMsmtReq.getRscId(); // if the requested rsc is not in the map of rscs for which at least one template was found, mark it as a failed rsc if (!rscIdsToTmpIds.keySet().contains(Integer.valueOf(rscId))) { res.addFailedResource(rscId,TEMPLATE_NOT_FOUND_ERR_CODE,null,new Object[] {""}); } } Map<Integer,Exception> failedRscs = new HashMap<Integer,Exception>(); Map<Resource, List<Measurement>> rscToHqMsmts = this.measurementMgr.findBulkMeasurements(authzSubject, rscIdsToTmpIds, failedRscs); if (rscToHqMsmts==null) { throw new ObjectNotFoundException(tmps.toString(), Measurement.class.getName()); } final String MEASUREMENT_NOT_FOUND = ExceptionToErrorCodeMapper.ErrorCode.MEASUREMENT_NOT_FOUND.getErrorCode(); for (Map.Entry<Integer,Exception> failedRscEntry : failedRscs.entrySet()) { Integer failedRscId = failedRscEntry.getKey(); Exception e = failedRscEntry.getValue(); if (e==null) { res.addFailedResource(String.valueOf(failedRscId),MEASUREMENT_NOT_FOUND,null,new Object[] {""}); } else { res.addFailedResource(String.valueOf(failedRscId),e.getMessage(),null, new Object[] {""}); } } // validate that all rscs have msmts, and map msmt names to rscs Map<Integer,Resource> msmtIdToRsc = new HashMap<Integer,Resource>(); Set<Measurement> allMsmts = new HashSet<Measurement>(); List<Measurement> msmts = null; for (Map.Entry<Resource,List<Measurement>> rscToHqMsmtsEntry : rscToHqMsmts.entrySet()) { Resource rsc = rscToHqMsmtsEntry.getKey(); msmts = rscToHqMsmtsEntry.getValue(); if (msmts==null || msmts.size()==0) { res.addFailedResource(String.valueOf(rsc.getId()),TEMPLATE_NOT_FOUND_ERR_CODE,null,new Object[] {""}); } else { for (Measurement msmt : msmts) { msmtIdToRsc.put(msmt.getId(),rsc); } allMsmts.addAll(msmts); } } // sort msmts as per their IDs Map<Integer,Measurement> msmtIdToMsmt = new HashMap<Integer,Measurement>(); for (Measurement msmt : allMsmts) { msmtIdToMsmt.put(msmt.getId(), msmt); } Map<Integer, double[]> msmtIdToAgg = dataMgr.getAggregateDataAndAvailUpByMetric(new ArrayList<Measurement>(allMsmts), begin.getTime(), end.getTime()); Map<Integer,ResourceMeasurementResponse> rscIdToRes = new HashMap<Integer,ResourceMeasurementResponse>(); Resource rsc = null; for (Map.Entry<Integer, double[]> msmtIdToAggEntry : msmtIdToAgg.entrySet()) { Integer msmtId = msmtIdToAggEntry.getKey(); rsc = msmtIdToRsc.get(msmtId); double[] agg = msmtIdToAggEntry.getValue(); // no val for that msmt if (agg==null || agg.length<=MeasurementConstants.IND_AVG) { continue; } double avg = agg[MeasurementConstants.IND_AVG]; Measurement hqMsmt = msmtIdToMsmt.get(msmtId); // ignore tmps which were not requested (should not happen) if (hqMsmt==null) { continue; } org.hyperic.hq.api.model.measurements.Measurement msmt = this.mapper.toMeasurement(hqMsmt,avg); ResourceMeasurementResponse rscRes = rscIdToRes.get(rsc.getId()); if (rscRes==null) { rscRes = new ResourceMeasurementResponse(String.valueOf(rsc.getId())); rscIdToRes.put(rsc.getId(), rscRes); res.addResponse(rscRes); } rscRes.add(msmt); } return res; } @Transactional(readOnly = true) public ResourceMeasurementBatchResponse getMeasurements(ApiMessageContext apiMessageContext, List<ID> ids) { ResourceMeasurementBatchResponse res = new ResourceMeasurementBatchResponse(this.errorHandler); AuthzSubject authzSubject = apiMessageContext.getAuthzSubject(); List<Integer> rids = this.mapper.toIds(ids); for(Integer rid:rids) { Resource rsc = this.resourceManager.findResourceById(rid); if (rsc==null) { res.addFailedResource(String.valueOf(rid), ExceptionToErrorCodeMapper.ErrorCode.RESOURCE_NOT_FOUND_BY_ID.getErrorCode(), null,new Object[] {""}); log.error("resource not found for resource id - " + rid); continue; } Collection<Measurement> hqMsmts = this.measurementMgr.findMeasurements(authzSubject, rsc); if (hqMsmts==null || hqMsmts.isEmpty()) { res.addFailedResource(String.valueOf(rid), ExceptionToErrorCodeMapper.ErrorCode.RESOURCE_NOT_FOUND_BY_ID.getErrorCode(), null,new Object[] {""}); log.error("no measurements for resource id - " + rid); continue; } ResourceMeasurementResponse rscRes = new ResourceMeasurementResponse(); rscRes.setResourceId(String.valueOf(rid)); for(Measurement hqMsmt:hqMsmts) { MeasurementTemplate hqTmpl = hqMsmt.getTemplate();//tmpltMgr.getTemplate(tmplId); org.hyperic.hq.api.model.measurements.Measurement msmt = this.mapper.toMeasurementExtendedData(hqMsmt,hqTmpl); rscRes.add(msmt); } res.addResponse(rscRes); } return res; } }