ServerTrafficCounter.java 10.8 KB
Newer Older
Gaston Dombiak's avatar
Gaston Dombiak committed
1 2 3 4 5 6 7 8 9 10 11
/**
 * $RCSfile$
 * $Revision: $
 * $Date: $
 *
 * Copyright (C) 2006 Jive Software. All rights reserved.
 *
 * This software is published under the terms of the GNU Public License (GPL),
 * a copy of which is included in this distribution.
 */

12
package org.jivesoftware.openfire.net;
Gaston Dombiak's avatar
Gaston Dombiak committed
13 14

import org.jivesoftware.util.LocaleUtils;
15 16
import org.jivesoftware.openfire.stats.Statistic;
import org.jivesoftware.openfire.stats.StatisticsManager;
Gaston Dombiak's avatar
Gaston Dombiak committed
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.concurrent.atomic.AtomicLong;

/**
 * A ServerTrafficCounter counts the number of bytes read and written by the server. This
 * includes client-server, server-server, external components and connection managers traffic.
 * Note that traffic is monitored only for entities that are directly connected to the server.
 * However, traffic generated by file transfers is not considered unless files were sent using
 * the in-band method.
 *
 * @author Gaston Dombiak
 */
public class ServerTrafficCounter {

    /**
     * Outgoing server traffic counter.
     */
    private static final AtomicLong outgoingCounter = new AtomicLong(0);
    /**
     * Incoming server traffic counter.
     */
    private static final AtomicLong incomingCounter = new AtomicLong(0);

    private static final String trafficStatGroup = "server_bytes";
    private static final String incomingStatKey = "server_bytes_in";
    private static final String outgoingStatKey = "server_bytes_out";

50 51 52 53
    /**
     * Creates and adds statistics to statistic manager.
     */
    public static void initStatistics() {
Gaston Dombiak's avatar
Gaston Dombiak committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
        addReadBytesStat();
        addWrittenBytesStat();
    }

    /**
     * Wraps the specified input stream to count the number of bytes that were read.
     *
     * @param originalStream the input stream to wrap.
     * @return The wrapped input stream over the original stream.
     */
    public static InputStream wrapInputStream(InputStream originalStream) {
        return new InputStreamWrapper(originalStream);
    }

    /**
     * Wraps the specified output stream to count the number of bytes that were written.
     *
     * @param originalStream the output stream to wrap.
     * @return The wrapped output stream over the original stream.
     */
    public static OutputStream wrapOutputStream(OutputStream originalStream) {
        return new OutputStreamWrapper(originalStream);
    }

    /**
     * Wraps the specified readable channel to count the number of bytes that were read.
     *
     * @param originalChannel the readable byte channel to wrap.
     * @return The wrapped readable channel over the original readable channel .
     */
    public static ReadableByteChannel wrapReadableChannel(ReadableByteChannel originalChannel) {
        return new ReadableByteChannelWrapper(originalChannel);
    }

    /**
     * Wraps the specified writable channel to count the number of bytes that were written.
     *
     * @param originalChannel the writable byte channel to wrap.
     * @return The wrapped writable channel over the original writable channel .
     */
    public static WritableByteChannel wrapWritableChannel(WritableByteChannel originalChannel) {
        return new WritableByteChannelWrapper(originalChannel);
    }

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
    /**
     * Increments the counter of read bytes by delta.
     *
     * @param delta the delta of bytes that were read.
     */
    public static void incrementIncomingCounter(long delta) {
        incomingCounter.getAndAdd(delta);
    }

    /**
     * Increments the counter of written bytes by delta.
     *
     * @param delta the delta of bytes that were written.
     */
    public static void incrementOutgoingCounter(long delta) {
        outgoingCounter.getAndAdd(delta);
    }

Gaston Dombiak's avatar
Gaston Dombiak committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    private static void addReadBytesStat() {
        // Register a statistic.
        Statistic statistic = new Statistic() {
            public String getName() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.incoming.name");
            }

            public Type getStatType() {
                return Type.rate;
            }

            public String getDescription() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.incoming.description");
            }

            public String getUnits() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.incoming.label");
            }

            public double sample() {
Matt Tucker's avatar
Matt Tucker committed
136 137
                // Divide result by 1024 so that we return the result in Kb.
                return incomingCounter.getAndSet(0)/1024;
Gaston Dombiak's avatar
Gaston Dombiak committed
138
            }
139 140 141 142

            public boolean isPartialSample() {
                return true;
            }
Gaston Dombiak's avatar
Gaston Dombiak committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        };
        StatisticsManager.getInstance()
                .addMultiStatistic(incomingStatKey, trafficStatGroup, statistic);
    }

    private static void addWrittenBytesStat() {
        // Register a statistic.
        Statistic statistic = new Statistic() {
            public String getName() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.outgoing.name");
            }

            public Type getStatType() {
                return Type.rate;
            }

            public String getDescription() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.outgoing.description");
            }

            public String getUnits() {
                return LocaleUtils.getLocalizedString("server_bytes.stats.outgoing.label");
            }

            public double sample() {
168
                return outgoingCounter.getAndSet(0)/1024;
Gaston Dombiak's avatar
Gaston Dombiak committed
169
            }
170 171 172 173

            public boolean isPartialSample() {
                return true;
            }
Gaston Dombiak's avatar
Gaston Dombiak committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
        };
        StatisticsManager.getInstance()
                .addMultiStatistic(outgoingStatKey, trafficStatGroup, statistic);
    }

    /**
     * Wrapper on an input stream to intercept and count number of read bytes.
     */
    private static class InputStreamWrapper extends InputStream {
        /**
         * Original input stream being wrapped to count incmoing traffic.
         */
        private InputStream originalStream;

        public InputStreamWrapper(InputStream originalStream) {
            this.originalStream = originalStream;
        }

        public int read() throws IOException {
            int readByte = originalStream.read();
            if (readByte > -1) {
195
                incrementIncomingCounter(1);
Gaston Dombiak's avatar
Gaston Dombiak committed
196 197 198 199 200 201 202
            }
            return readByte;
        }

        public int read(byte b[]) throws IOException {
            int bytes = originalStream.read(b);
            if (bytes > -1) {
203
                incrementIncomingCounter(bytes);
Gaston Dombiak's avatar
Gaston Dombiak committed
204 205 206 207 208 209 210
            }
            return bytes;
        }

        public int read(byte b[], int off, int len) throws IOException {
            int bytes = originalStream.read(b, off, len);
            if (bytes > -1) {
211
                incrementIncomingCounter(bytes);
Gaston Dombiak's avatar
Gaston Dombiak committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
            }
            return bytes;
        }

        public int available() throws IOException {
            return originalStream.available();
        }

        public void close() throws IOException {
            originalStream.close();
        }

        public synchronized void mark(int readlimit) {
            originalStream.mark(readlimit);
        }

        public boolean markSupported() {
            return originalStream.markSupported();
        }

        public synchronized void reset() throws IOException {
            originalStream.reset();
        }

        public long skip(long n) throws IOException {
            return originalStream.skip(n);
        }
    }

    /**
     * Wrapper on an output stream to intercept and count number of written bytes.
     */
    private static class OutputStreamWrapper extends OutputStream {
        /**
         * Original output stream being wrapped to count outgoing traffic.
         */
        private OutputStream originalStream;

        public OutputStreamWrapper(OutputStream originalStream) {
            this.originalStream = originalStream;
        }

        public void write(int b) throws IOException {
            // forward request to wrapped stream
            originalStream.write(b);
            // update outgoingCounter
258
            incrementOutgoingCounter(1);
Gaston Dombiak's avatar
Gaston Dombiak committed
259 260 261 262 263 264
        }

        public void write(byte b[]) throws IOException {
            // forward request to wrapped stream
            originalStream.write(b);
            // update outgoingCounter
265
            incrementOutgoingCounter(b.length);
Gaston Dombiak's avatar
Gaston Dombiak committed
266 267 268 269 270 271
        }

        public void write(byte b[], int off, int len) throws IOException {
            // forward request to wrapped stream
            originalStream.write(b, off, len);
            // update outgoingCounter
272
            incrementOutgoingCounter(b.length);
Gaston Dombiak's avatar
Gaston Dombiak committed
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
        }

        public void close() throws IOException {
            originalStream.close();
        }

        public void flush() throws IOException {
            originalStream.flush();
        }
    }

    /**
     * Wrapper on a ReadableByteChannel to intercept and count number of read bytes.
     */
    private static class ReadableByteChannelWrapper implements ReadableByteChannel {
        private ReadableByteChannel originalChannel;

        public ReadableByteChannelWrapper(ReadableByteChannel originalChannel) {
            this.originalChannel = originalChannel;
        }

        public int read(ByteBuffer dst) throws IOException {
            int bytes = originalChannel.read(dst);
            if (bytes > -1) {
297
                incrementIncomingCounter(bytes);
Gaston Dombiak's avatar
Gaston Dombiak committed
298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
            }
            return bytes;
        }

        public void close() throws IOException {
            originalChannel.close();
        }

        public boolean isOpen() {
            return originalChannel.isOpen();
        }
    }

    /**
     * Wrapper on a WritableByteChannel to intercept and count number of written bytes.
     */
    private static class WritableByteChannelWrapper implements WritableByteChannel {
        private WritableByteChannel originalChannel;

        public WritableByteChannelWrapper(WritableByteChannel originalChannel) {
            this.originalChannel = originalChannel;
        }

        public void close() throws IOException {
            originalChannel.close();
        }

        public boolean isOpen() {
            return originalChannel.isOpen();
        }

        public int write(ByteBuffer src) throws IOException {
            int bytes = originalChannel.write(src);
331
            incrementOutgoingCounter(bytes);
Gaston Dombiak's avatar
Gaston Dombiak committed
332 333 334 335
            return bytes;
        }
    }
}