Package org.rhq.core.domain.alert

Examples of org.rhq.core.domain.alert.AlertCondition


        pretty = getShortPrettyAlertConditionString(condition);
        assert "Res Config Chg".equals(pretty) : pretty;
    }

    public void testPrettyPrintDRIFT() {
        AlertCondition condition = createCondition(AlertConditionCategory.DRIFT, "?riftName", null, null, "fil.*", null);
        pretty = getPrettyAlertConditionString(condition);
        assert "Drift detected for files that match \"fil.*\" and for drift definition [?riftName]".equals(pretty) : pretty;
        pretty = getShortPrettyAlertConditionString(condition);
        assert "Drift matching \"fil.*\", config=[?riftName]".equals(pretty) : pretty;

View Full Code Here


        assert "Drift!".equals(pretty) : pretty;
    }

    public void testPrettyPrintRANGE() {
        MeasurementDefinition md = createDynamicMeasurementDefinition();
        AlertCondition condition = createCondition(AlertConditionCategory.RANGE, md.getDisplayName(), "<=", 1.0,
            "22.2", md);
        pretty = getPrettyAlertConditionString(condition);
        check(AlertI18NResourceKeys.ALERT_RANGE_INSIDE_INCL, "Foo Prop", String.format("%1.1fB", 1d),
            String.format("%2.1fB", 22.2d));
        pretty = getShortPrettyAlertConditionString(condition);
View Full Code Here

        return alert;
    }

    private AlertCondition createCondition(AlertConditionCategory category, String name, String comparator,
        Double threshold, String option, MeasurementDefinition measDef) {
        AlertCondition condition = new AlertCondition();
        condition.setCategory(category);
        condition.setName(name);
        condition.setComparator(comparator);
        condition.setThreshold(threshold);
        condition.setOption(option);
        condition.setMeasurementDefinition(measDef);
        return condition;
    }
View Full Code Here

                if (conditionLogs.size() > 1) {
                    conditionText = "Multiple Conditions";
                } else if (conditionLogs.size() == 1) {
                    AlertConditionLog conditionLog = conditionLogs.iterator().next();
                    AlertCondition condition = conditionLog.getCondition();
                    conditionText = formatCondition(condition);
                    conditionValue = conditionLog.getValue();
                    if (condition.getMeasurementDefinition() != null) {
                        try {
                            conditionValue = MeasurementConverter.format(conditionLog.getValue(),
                                condition.getMeasurementDefinition().getUnits());
                        } catch (Exception e) {
                            // the condition log value was probably not a number (most likely a trait). Ignore this exception.
                            // even if any other errors occur trying to format the value, ignore this and just use the raw value string
                        }
                    }
                } else {
                    conditionText = "No Conditions";
                    conditionValue = "--";
                }

                return conditionText;
            }

            private String formatCondition(AlertCondition condition) {
                StringBuilder builder = new StringBuilder();
                AlertConditionCategory category = condition.getCategory();
                AlertConditionOperator operator;
                String formattedThreshold;

                switch (category) {
                    case AVAILABILITY:
                        builder.append("Availability [");
                        operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase());
                        switch (operator) {
                            case AVAIL_GOES_DISABLED:
                                builder.append("Goes disabled");
                                break;
                            case AVAIL_GOES_DOWN:
                                builder.append("Goes down");
                                break;
                            case AVAIL_GOES_UNKNOWN:
                                builder.append("Goes unknown");
                                break;
                            case AVAIL_GOES_UP:
                                builder.append("Goes up");
                                break;
                            case AVAIL_GOES_NOT_UP:
                                builder.append("Goes not up");
                                break;
                            default:
                                builder.append("*ERROR*");
                        }
                        builder.append("]");
                        break;
                    case AVAIL_DURATION:
                        builder.append("Availability Duration [");
                        operator = AlertConditionOperator.valueOf(condition.getName().toUpperCase());
                        switch (operator) {
                            case AVAIL_DURATION_DOWN:
                                builder.append("Stays Down");
                                break;
                            case AVAIL_DURATION_NOT_UP:
                                builder.append("Stays Not Up");
                                break;
                            default:
                                builder.append("*ERROR*");
                        }
                        builder.append(" For ");

                        String value = String.valueOf(Integer.valueOf(condition.getOption()) / 60);
                        String formatted = MeasurementConverter.format(value, MeasurementUnits.MINUTES);

                        builder.append(formatted).append("]");
                        break;
                    case THRESHOLD:
                        MeasurementUnits units = condition.getMeasurementDefinition().getUnits();
                        formattedThreshold = MeasurementConverter.format(condition.getThreshold(), units, true);

                        if (condition.getOption() == null) {
                            builder.append("Metric Value Threshold [")
                                .append(condition.getName())
                                .append(" ")
                                .append(condition.getComparator())
                                .append(" ")
                                .append(formattedThreshold)
                                .append("]");
                        } else {
                            // this is a calltime threshold condition
                            builder.append("Call Time Value Threshold [");
                            if (condition.getMeasurementDefinition() != null) {
                                builder.append(condition.getMeasurementDefinition().getDisplayName()).append(" ");
                            }
                            builder.append(condition.getOption()) // MIN, MAX, AVG (never null)
                                .append(" ")
                                .append(condition.getComparator())  // <, >, =
                                .append(" ")
                                .append(formattedThreshold)
                                .append("]");
                            if (condition.getName() != null && condition.getName().length() > 0) {
                                builder.append(" with call destination matching '")
                                    .append(condition.getName())
                                    .append("'");
                            }
                        }
                        break;
                    case BASELINE:
                        formattedThreshold = MeasurementConverter.format(condition.getThreshold(),
                            MeasurementUnits.PERCENTAGE, true);
                        builder.append("Metric Value Baseline [")
                            .append(condition.getName())
                            .append(" ")
                            .append(condition.getComparator())
                            .append(" ")
                            .append(formattedThreshold)
                            .append(" of ")
                            .append(condition.getOption())
                            .append("]");
                        break;
                    case CHANGE:
                        if (condition.getOption() == null) {
                            builder.append("Metric Value Change [")
                                .append(condition.getName())
                                .append(" ")
                                .append("]");
                        } else {
                            // this is a calltime change condition
                            formattedThreshold = MeasurementConverter.format(condition.getThreshold(),
                                MeasurementUnits.PERCENTAGE, true);
                            builder.append("Call Time Value Changes [");
                            if (condition.getMeasurementDefinition() != null) {
                                builder.append(condition.getMeasurementDefinition().getDisplayName()).append(" ");
                            }
                            builder.append(condition.getOption()) // MIN, MAX, AVG (never null)
                                .append(" ")
                                .append(getCalltimeChangeComparator(condition.getComparator()))
                                .append(" by at least ")
                                .append(formattedThreshold)
                                .append("]");
                            if (condition.getName() != null && condition.getName().length() > 0) {
                                builder.append(" with call destination matching '")
                                    .append(condition.getName())
                                    .append("'");
                            }
                        }
                        break;
                    case TRAIT:
                        builder.append("Trait Change [")
                            .append(condition.getName())
                            .append("]");
                        if (condition.getOption() != null && condition.getOption().length() > 0) {
                            builder.append(" with trait value matching '")
                                .append(condition.getOption())
                                .append("'");
                        }                       
                        break;
                    case CONTROL:
                        builder.append("Operation Execution [")
                            .append(condition.getName())
                            .append("] with result status [")
                            .append(condition.getOption())
                            .append("]");
                        break;
                    case RESOURCE_CONFIG:
                        builder.append("Resource Configuration Change");
                        break;
                    case EVENT:
                        builder.append("Event Detection [")
                            .append(condition.getName())
                            .append("]");
                        if (condition.getOption() != null && condition.getOption().length() > 0) {
                            builder.append(" with event source matching '")
                                .append(condition.getOption())
                                .append("'");
                        }
                        break;
                    case DRIFT:
                        String configNameRegex = condition.getName();
                        String pathNameRegex = condition.getOption();
                        if (configNameRegex == null || configNameRegex.length() == 0) {
                            if (pathNameRegex == null || pathNameRegex.length() == 0) {
                                // neither a config name regex nor path regex was specified
                                builder.append("Drift Detection");
                            } else {
                                // a path name regex was specified, but not a config name regex
                                builder.append("Drift Detection for files that match \"")
                                    .append(pathNameRegex)
                                    .append("\"");
                            }
                        } else {
                            if (pathNameRegex == null || pathNameRegex.length() == 0) {
                                // a config name regex was specified, but not a path name regex
                                builder.append("Drift Detection for drift definition [")
                                    .append(configNameRegex)
                                    .append("]");
                            } else {
                                // both a config name regex and a path regex was specified
                                builder.append("Drift Detection for files that match \"")
                                    .append(pathNameRegex)
                                    .append("\" and for drift detection [")
                                    .append(configNameRegex)
                                    .append("]");
                            }
                        }
                        break;
                    case RANGE:
                        String metricName = condition.getName();
                        MeasurementUnits metricUnits = condition.getMeasurementDefinition().getUnits();
                        double loValue = condition.getThreshold();
                        String formattedLoValue = MeasurementConverter.format(loValue, metricUnits, true);
                        String formattedHiValue = condition.getOption();
                        try {
                            double hiValue = Double.parseDouble(formattedHiValue);
                            formattedHiValue = MeasurementConverter.format(hiValue, metricUnits, true);
                        } catch (Exception e) {
                            formattedHiValue = "?[" + formattedHiValue + "]?"; // signify something is wrong with the value
                        }

                        // < means "inside the range", > means "outside the range" - exclusive
                        // <= means "inside the range", >= means "outside the range" - inclusive

                        if (condition.getComparator().equals("<")) {
                            // Metric Value Range: [{0}] between [{1}] and [{2}], exclusive
                            builder.append("Metric Value Range: [")
                                .append(metricName)
                                .append("] between ")
                                .append(formattedLoValue)
                                .append("] and [")
                                .append(formattedHiValue)
                                .append("], exclusive");
                        } else if (condition.getComparator().equals(">")) {
                            // Metric Value Range: [{0}] outside [{1}] and [{2}], exclusive
                            builder.append("Metric Value Range: [")
                                .append(metricName)
                                .append("] outside [")
                                .append(formattedLoValue)
                                .append("] and [")
                                .append(formattedHiValue)
                                .append("], exclusive");
                        } else if (condition.getComparator().equals("<=")) {
                            // Metric Value Range: [{0}] between [{1}] and [{2}], inclusive
                            builder.append("Metric Value Range: [")
                                .append(metricName)
                                .append("] between [")
                                .append(formattedLoValue)
                                .append("] and [")
                                .append(formattedHiValue)
                                .append("], inclusive");
                        } else if (condition.getComparator().equals(">=")) {
                            // Metric Value Range: [{0}] outside [{1}] and [{2}], inclusive
                            builder.append("Metric Value Range: [")
                                .append(metricName)
                                .append("] outside [")
                                .append(formattedLoValue)
                                .append("] and [")
                                .append(formattedHiValue)
                                .append("], inclusive");
                        } else {
                            builder.append("BAD COMPARATOR! Report this bug: ").append(condition.getComparator());
                        }
                        break;
                    default:
                        // Invalid condition category - please report this as a bug: {0}
                        builder.append("Invalid condition category - please report this as a bug: ")
View Full Code Here

        // find out if this is the ALL - if it is, we can't have more than one conditional use the same metric (BZ 737565)
        Set<String> metricIdsToHide = new HashSet<String>();
        if (BooleanExpression.ALL.name().equals(this.conditionExpression.getValue().toString())) {
            for (AlertCondition condition : this.conditions) {
                if (editMode) { // do not hide the metric if it is part of the currently modified condition
                    AlertCondition conditionWithoutId = new AlertCondition(existingCondition); // newly created conditions don't have id yet
                    AlertCondition modifiedConditionWithoutId = new AlertCondition(condition);
                    if (conditionWithoutId.equals(modifiedConditionWithoutId)) {
                        continue;
                    }
                }
                if (condition.getMeasurementDefinition() != null) {
View Full Code Here

        alertDefinition.setAlertDampening(new AlertDampening(AlertDampening.Category.NONE));
        alertDefinition.setConditionExpression(BooleanExpression.ANY);
        alertDefinition.setRecoveryId(0);
        alertDefinition.setGroup(resourceGroup);
        alertDefinition.setEnabled(true);
        AlertCondition alertCondition = new AlertCondition(alertDefinition, AlertConditionCategory.AVAILABILITY);
        alertCondition.setName(AlertConditionOperator.AVAIL_GOES_UP.name());
        alertDefinition.addCondition(alertCondition);
        groupAlertDefinitionManager.createGroupAlertDefinitions(subject, alertDefinition, resourceGroup.getId());
        return alertDefinition;
    }
View Full Code Here

    }

    private void insertAlertConditionComposite(int agentId, AbstractAlertConditionCategoryComposite composite,
        AlertConditionCacheStats stats) {

        AlertCondition alertCondition = composite.getCondition();
        int alertConditionId = alertCondition.getId(); // auto-unboxing is safe here because as the PK it's guaranteed to be non-null

        AlertConditionCategory alertConditionCategory = alertCondition.getCategory();
        AlertConditionOperator alertConditionOperator = AlertConditionCacheUtils
            .getAlertConditionOperator(alertCondition);

        if (DataType.CALLTIME == composite.getDataType()) { // call-time cases start here
            if (alertConditionCategory == AlertConditionCategory.CHANGE) {
                AlertConditionChangesCategoryComposite changesComposite = (AlertConditionChangesCategoryComposite) composite;
                int scheduleId = changesComposite.getScheduleId();

                try {
                    CallTimeDataCacheElement cacheElement = new CallTimeDataCacheElement(alertConditionOperator,
                        CallTimeElementValue.valueOf(alertCondition.getOption()), alertCondition.getComparator(),
                        alertCondition.getThreshold(), alertConditionId, alertCondition.getName());

                    addTo("callTimeDataCache", callTimeCache, scheduleId, cacheElement, alertConditionId, stats);
                } catch (InvalidCacheElementException icee) {
                    log.info("Failed to create CallTimeDataCacheElement with parameters: "
                        + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                            null, alertCondition.getThreshold(), icee));
                }
            } else if (alertConditionCategory == AlertConditionCategory.THRESHOLD) {
                AlertConditionScheduleCategoryComposite thresholdComposite = (AlertConditionScheduleCategoryComposite) composite;

                try {
                    CallTimeDataCacheElement cacheElement = new CallTimeDataCacheElement(alertConditionOperator,
                        CallTimeElementValue.valueOf(alertCondition.getOption()), null, alertCondition.getThreshold(),
                        alertConditionId, alertCondition.getName());

                    addTo("measurementDataCache", callTimeCache, thresholdComposite.getScheduleId(), cacheElement,
                        alertConditionId, stats);
                } catch (InvalidCacheElementException icee) {
                    log.info("Failed to create CallTimeDataCacheElement with parameters: "
                        + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                            null, alertCondition.getThreshold(), icee));
                }

            }// last call-time case
        } else if (alertConditionCategory == AlertConditionCategory.BASELINE) { // normal cases start here
            AlertConditionBaselineCategoryComposite baselineComposite = (AlertConditionBaselineCategoryComposite) composite;
            // option status for baseline gets set to "mean", but it's rather useless since the UI
            // current doesn't allow alerting off of other baseline properties such as "min" and "max"
            Double threshold = alertCondition.getThreshold();
            String optionStatus = alertCondition.getOption();

            /*
             * yes, calculatedValue may be null, but that's OK because the match
             * method for MeasurementBaselineCacheElement handles nulls just fine
             */
            Double calculatedValue = getCalculatedBaselineValue(alertConditionId, baselineComposite, optionStatus,
                threshold);

            try {
                MeasurementBaselineCacheElement cacheElement = new MeasurementBaselineCacheElement(
                    alertConditionOperator, calculatedValue, alertConditionId, optionStatus);

                // auto-boxing (of alertConditionId) is always safe
                addTo("measurementDataCache", measurementDataCache, baselineComposite.getScheduleId(), cacheElement,
                    alertConditionId, stats);
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create MeasurementBaselineCacheElement with parameters: "
                    + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                        null, calculatedValue, icee));
            }
        } else if (alertConditionCategory == AlertConditionCategory.CHANGE) {
            AlertConditionChangesCategoryComposite changesComposite = (AlertConditionChangesCategoryComposite) composite;
            int scheduleId = changesComposite.getScheduleId();

            MeasurementDataNumeric numeric = measurementDataManager.getCurrentNumericForSchedule(scheduleId);

            try {
                MeasurementNumericCacheElement cacheElement = new MeasurementNumericCacheElement(
                    alertConditionOperator, (numeric == null) ? null : numeric.getValue(), alertConditionId);

                addTo("measurementDataCache", measurementDataCache, scheduleId, cacheElement, alertConditionId, stats);
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create MeasurementNumericCacheElement with parameters: "
                    + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                        null, numeric, icee));
            }
        } else if (alertConditionCategory == AlertConditionCategory.TRAIT) {
            AlertConditionTraitCategoryComposite traitsComposite = (AlertConditionTraitCategoryComposite) composite;
            String value = null;

            switch (alertConditionOperator) {
            case CHANGES:
                value = traitsComposite.getValue();
                break;
            case REGEX:
                value = traitsComposite.getCondition().getOption();
                break;
            default:
                log.error("Invalid operator for Trait condition: " + alertConditionOperator);
            }

            try {
                /*
                 * don't forget special defensive handling to allow for null trait calculation;
                 * this might happen if a newly committed resource has some alert template applied to
                 * it for some trait that it has not yet gotten from the agent
                 */
                MeasurementTraitCacheElement cacheElement = new MeasurementTraitCacheElement(alertConditionOperator,
                    value, alertConditionId);

                addTo("measurementTraitCache", measurementTraitCache, traitsComposite.getScheduleId(), cacheElement,
                    alertConditionId, stats);
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create MeasurementTraitCacheElement with parameters: "
                    + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                        null, value, icee));
            }

        } else if (alertConditionCategory == AlertConditionCategory.THRESHOLD) {
            AlertConditionScheduleCategoryComposite thresholdComposite = (AlertConditionScheduleCategoryComposite) composite;
            Double thresholdValue = alertCondition.getThreshold();

            MeasurementNumericCacheElement cacheElement = null;
            try {
                cacheElement = new MeasurementNumericCacheElement(alertConditionOperator, thresholdValue,
                    alertConditionId);
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create MeasurementNumericCacheElement with parameters: "
                    + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                        null, thresholdValue, icee));
            }

            if (cacheElement != null) {
                addTo("measurementDataCache", measurementDataCache, thresholdComposite.getScheduleId(), cacheElement,
                    alertConditionId, stats);

            }
        } else if (alertConditionCategory == AlertConditionCategory.EVENT) {
            AlertConditionEventCategoryComposite eventComposite = (AlertConditionEventCategoryComposite) composite;
            EventSeverity eventSeverity = EventSeverity.valueOf(alertCondition.getName());
            String eventDetails = alertCondition.getOption();

            EventCacheElement cacheElement = null;
            try {
                if (eventDetails == null) {
                    cacheElement = new EventCacheElement(alertConditionOperator, eventSeverity, alertConditionId);
                } else {
                    String regexEventDetails = "", regexSourceLocation = "";
                    if (eventDetails.contains(AlertCondition.ADHOC_SEPARATOR)) {
                        String[] regexes = eventDetails.split(AlertCondition.ADHOC_SEPARATOR);
                        if (regexes.length > 0) {
                            regexEventDetails = regexes[0];
                            if (regexes.length > 1) {
                                regexSourceLocation = regexes[1];
                            }
                        }
                    } else {
                        regexEventDetails = eventDetails; // let's keep backward compatibility here, because there may be REST
                        // clients using the old approach
                    }
                    cacheElement = new EventCacheElement(alertConditionOperator, eventDetails, regexEventDetails,
                        regexSourceLocation, eventSeverity, alertConditionId);
                }
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create EventCacheElement with parameters: "
                    + AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId, alertConditionOperator,
                        eventDetails, eventSeverity, icee));
            }

            addTo("eventsCache", eventsCache, eventComposite.getResourceId(), cacheElement, alertConditionId, stats);
        } else if (alertConditionCategory == AlertConditionCategory.DRIFT) {
            AlertConditionDriftCategoryComposite driftComposite = (AlertConditionDriftCategoryComposite) composite;

            String driftDefNameRegexStr = driftComposite.getCondition().getName();
            String driftPathNameRegexStr = driftComposite.getCondition().getOption();

            DriftCacheElement cacheElement = null;
            try {
                cacheElement = new DriftCacheElement(alertConditionOperator, driftDefNameRegexStr,
                    driftPathNameRegexStr, alertConditionId);
            } catch (InvalidCacheElementException icee) {
                log.info("Failed to create DriftCacheElement: id=" + alertConditionId + ", operator="
                    + alertConditionOperator + ", driftDefNameRegex=" + driftDefNameRegexStr + ", driftPathNameRegex="
                    + driftPathNameRegexStr);
            }
            addTo("driftCache", driftCache, driftComposite.getResourceId(), cacheElement, alertConditionId, stats);
        } else if (alertConditionCategory == AlertConditionCategory.RANGE) {
            AlertConditionRangeCategoryComposite rangeComposite = (AlertConditionRangeCategoryComposite) composite;
            Double loValue = alertCondition.getThreshold();
            String hiValueStr = alertCondition.getOption();

            MeasurementNumericCacheElement cacheElement = null;
            try {
                if (hiValueStr == null) {
                    throw new NumberFormatException("The range alert condition is missing the high value");
View Full Code Here

        Iterator<AlertCondition> condsIterator = conds.iterator();
        Iterator<AlertConditionLog> condLogsIterator = condLogs.iterator();

        for (AlertConditionBean alertCondBean : alertCondBeans) {
            AlertCondition cond = condsIterator.next();
            AlertConditionLog condLog = condLogsIterator.next();
            AlertConditionCategory category = cond.getCategory();

            if (category == AlertConditionCategory.CONTROL) {
                alertCondBean.setActualValue(RequestUtils.message(request, "alert.current.list.ControlActualValue"));
            } else if ((category == AlertConditionCategory.THRESHOLD) || (category == AlertConditionCategory.BASELINE)
                || (category == AlertConditionCategory.CHANGE)) {
View Full Code Here

                if (alert.getConditionLogs().size() > 1) {
                    results.add(new AlertWithLatestConditionLog(alert, "Multiple Conditions", "--", recoveryInfo));
                } else if (alert.getConditionLogs().size() == 1) {
                    AlertConditionLog log = alert.getConditionLogs().iterator().next();
                    AlertCondition condition = log.getCondition();
                    String displayText = AlertDefUtil.formatAlertConditionForDisplay(condition, request);

                    String firedValue = log.getValue();
                    if (isPureNumeric(condition)) {
                        firedValue = MeasurementConverter.format(Double.valueOf(log.getValue()), condition
                            .getMeasurementDefinition().getUnits(), true);
                    }

                    results.add(new AlertWithLatestConditionLog(alert, displayText, firedValue, recoveryInfo));
                } else {
View Full Code Here

        traitConverter = new TraitConverterValidator();
        resourceConfigurationConverter = new ResConfigConvertValidator();
    }

    public static AlertCondition exportProperties(Subject subject, ConditionBean fromBean) {
        AlertCondition toCondition = new AlertCondition();

        if (fromBean.getTrigger().equals(measurementConverter.getTriggerName())) {
            measurementConverter.exportProperties(subject, fromBean, toCondition);
        } else if (fromBean.getTrigger().equals(traitConverter.getTriggerName())) {
            traitConverter.exportProperties(subject, fromBean, toCondition);
View Full Code Here

TOP

Related Classes of org.rhq.core.domain.alert.AlertCondition

Copyright © 2018 www.massapicom. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.