/*
 * Copyright 2013 Proofpoint, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.proofpoint.reporting;

import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Table;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject;

import javax.annotation.PostConstruct;
import javax.management.AttributeNotFoundException;
import javax.management.MBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;

class ReportCollector
{
    private ScheduledExecutorService collectionExecutorService = newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("reporting-collector-%s").setDaemon(true).build());
    private MinuteBucketIdProvider bucketIdProvider;
    private ReportedBeanRegistry reportedBeanRegistry;
    private ExecutorService clientExecutorService;
    private ReportClient reportClient;

    @Inject
    ReportCollector(MinuteBucketIdProvider bucketIdProvider, ReportedBeanRegistry reportedBeanRegistry, ReportClient reportClient)
    {
        this.bucketIdProvider = checkNotNull(bucketIdProvider, "bucketIdProvider is null");
        this.reportedBeanRegistry = checkNotNull(reportedBeanRegistry, "reportedBeanRegistry is null");
        this.reportClient = checkNotNull(reportClient, "reportClient is null");
        clientExecutorService = new ThreadPoolExecutor(1, 1, 0, TimeUnit.NANOSECONDS, new LinkedBlockingQueue<Runnable>(),
                new ThreadFactoryBuilder().setNameFormat("reporting-client-%s").setDaemon(true).build(),
                new DiscardOldestPolicy());
    }

    @PostConstruct
    public void start()
    {
        collectionExecutorService.scheduleAtFixedRate(new Runnable()
        {
            @Override
            public void run()
            {
                collectData();
            }
        }, 1, 1, TimeUnit.MINUTES);
    }

    private void collectData()
    {
        final long lastSystemTimeMillis = bucketIdProvider.getLastSystemTimeMillis();
        ImmutableTable.Builder<ObjectName, String, Number> builder = ImmutableTable.builder();
        for (Entry<ObjectName, ReportedBean> reportedBeanEntry : reportedBeanRegistry.getReportedBeans().entrySet()) {
            for (ReportedBeanAttribute attribute : reportedBeanEntry.getValue().getAttributes()) {
                try {
                    builder.put(reportedBeanEntry.getKey(), attribute.getName(), attribute.getValue(null));
                }
                catch (AttributeNotFoundException | MBeanException | ReflectionException ignored) {
                }
            }
        }
        final Table<ObjectName, String, Number> collectedData = builder.build();
        clientExecutorService.submit(new Runnable()
        {
            @Override
            public void run()
            {
                reportClient.report(lastSystemTimeMillis, collectedData);
            }
        });
    }
}

@@ -3358,94 +3358,79 @@
     
-try %7B%0A                    builder.put(reportedBeanEntry.getKey(), attribute.getName(),
+Number value = null;%0A%0A                try %7B%0A                    value =
  att
@@ -3450,17 +3450,16 @@
 ue(null)
-)
 ;%0A      
@@ -3579,32 +3579,183 @@
                %7D
+%0A%0A                if (isReportable(value)) %7B%0A                    builder.put(reportedBeanEntry.getKey(), attribute.getName(), value);%0A                %7D
 %0A            %7D%0A 
@@ -4052,32 +4052,32 @@
 ;%0A            %7D%0A
-
         %7D);%0A    
@@ -4077,11 +4077,826 @@
 );%0A    %7D
+%0A%0A    private static boolean isReportable(Number value)%0A    %7B%0A        if (value == null) %7B%0A            return false;%0A        %7D%0A        if (value instanceof Double) %7B%0A            return !(((Double) value).isNaN() %7C%7C ((Double) value).isInfinite());%0A        %7D%0A        if (value instanceof Float) %7B%0A            return !(((Float) value).isNaN() %7C%7C ((Float) value).isInfinite());%0A        %7D%0A        if (value instanceof Long) %7B%0A            return !(value.equals(Long.MAX_VALUE) %7C%7C value.equals(Long.MIN_VALUE));%0A        %7D%0A        if (value instanceof Integer) %7B%0A            return !(value.equals(Integer.MAX_VALUE) %7C%7C value.equals(Integer.MIN_VALUE));%0A        %7D%0A        if (value instanceof Short) %7B%0A            return !(value.equals(Short.MAX_VALUE) %7C%7C value.equals(Short.MIN_VALUE));%0A        %7D%0A        return true;%0A    %7D
 %0A%7D%0A

Exclude "not applicable" values from report