001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.core.service;
021
022import java.util.concurrent.atomic.AtomicInteger;
023import java.util.concurrent.locks.Lock;
024import java.util.concurrent.locks.ReentrantLock;
025
026/**
027 * Provides usage statistics for an {@link AbstractIoService} instance.
028 * 
029 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
030 * @since 2.0.0-M3
031 */
032public class IoServiceStatistics {
033
034    private AbstractIoService service;
035
036    /** The number of bytes read per second */
037    private double readBytesThroughput;
038
039    /** The number of bytes written per second */
040    private double writtenBytesThroughput;
041
042    /** The number of messages read per second */
043    private double readMessagesThroughput;
044
045    /** The number of messages written per second */
046    private double writtenMessagesThroughput;
047
048    /** The biggest number of bytes read per second */
049    private double largestReadBytesThroughput;
050
051    /** The biggest number of bytes written per second */
052    private double largestWrittenBytesThroughput;
053
054    /** The biggest number of messages read per second */
055    private double largestReadMessagesThroughput;
056
057    /** The biggest number of messages written per second */
058    private double largestWrittenMessagesThroughput;
059
060    /** The number of read bytes since the service has been started */
061    private long readBytes;
062
063    /** The number of written bytes since the service has been started */
064    private long writtenBytes;
065
066    /** The number of read messages since the service has been started */
067    private long readMessages;
068
069    /** The number of written messages since the service has been started */
070    private long writtenMessages;
071
072    /** The time the last read operation occurred */
073    private long lastReadTime;
074
075    /** The time the last write operation occurred */
076    private long lastWriteTime;
077
078    private long lastReadBytes;
079
080    private long lastWrittenBytes;
081
082    private long lastReadMessages;
083
084    private long lastWrittenMessages;
085
086    private long lastThroughputCalculationTime;
087
088    private int scheduledWriteBytes;
089
090    private int scheduledWriteMessages;
091
092    /** The time (in second) between the computation of the service's statistics */
093    private final AtomicInteger throughputCalculationInterval = new AtomicInteger(3);
094
095    private final Lock throughputCalculationLock = new ReentrantLock();
096
097    public IoServiceStatistics(AbstractIoService service) {
098        this.service = service;
099    }
100
101    /**
102     * @return The maximum number of sessions which were being managed at the
103     *         same time.
104     */
105    public final int getLargestManagedSessionCount() {
106        return service.getListeners().getLargestManagedSessionCount();
107    }
108
109    /**
110     * @return The cumulative number of sessions which were managed (or are
111     *         being managed) by this service, which means 'currently managed
112     *         session count + closed session count'.
113     */
114    public final long getCumulativeManagedSessionCount() {
115        return service.getListeners().getCumulativeManagedSessionCount();
116    }
117
118    /**
119     * @return the time in millis when the last I/O operation (read or write)
120     *         occurred.
121     */
122    public final long getLastIoTime() {
123        throughputCalculationLock.lock();
124
125        try {
126            return Math.max(lastReadTime, lastWriteTime);
127        } finally {
128            throughputCalculationLock.unlock();
129        }
130    }
131
132    /**
133     * @return The time in millis when the last read operation occurred.
134     */
135    public final long getLastReadTime() {
136        throughputCalculationLock.lock();
137
138        try {
139            return lastReadTime;
140        } finally {
141            throughputCalculationLock.unlock();
142        }
143    }
144
145    /**
146     * @return The time in millis when the last write operation occurred.
147     */
148    public final long getLastWriteTime() {
149        throughputCalculationLock.lock();
150
151        try {
152            return lastWriteTime;
153        } finally {
154            throughputCalculationLock.unlock();
155        }
156    }
157
158    /**
159     * @return The number of bytes this service has read so far
160     */
161    public final long getReadBytes() {
162        throughputCalculationLock.lock();
163
164        try {
165            return readBytes;
166        } finally {
167            throughputCalculationLock.unlock();
168        }
169    }
170
171    /**
172     * @return The number of bytes this service has written so far
173     */
174    public final long getWrittenBytes() {
175        throughputCalculationLock.lock();
176
177        try {
178            return writtenBytes;
179        } finally {
180            throughputCalculationLock.unlock();
181        }
182    }
183
184    /**
185     * @return The number of messages this services has read so far
186     */
187    public final long getReadMessages() {
188        throughputCalculationLock.lock();
189
190        try {
191            return readMessages;
192        } finally {
193            throughputCalculationLock.unlock();
194        }
195    }
196
197    /**
198     * @return The number of messages this service has written so far
199     */
200    public final long getWrittenMessages() {
201        throughputCalculationLock.lock();
202
203        try {
204            return writtenMessages;
205        } finally {
206            throughputCalculationLock.unlock();
207        }
208    }
209
210    /**
211     * @return The number of read bytes per second.
212     */
213    public final double getReadBytesThroughput() {
214        throughputCalculationLock.lock();
215
216        try {
217            resetThroughput();
218            return readBytesThroughput;
219        } finally {
220            throughputCalculationLock.unlock();
221        }
222    }
223
224    /**
225     * @return The number of written bytes per second.
226     */
227    public final double getWrittenBytesThroughput() {
228        throughputCalculationLock.lock();
229
230        try {
231            resetThroughput();
232            return writtenBytesThroughput;
233        } finally {
234            throughputCalculationLock.unlock();
235        }
236    }
237
238    /**
239     * @return The number of read messages per second.
240     */
241    public final double getReadMessagesThroughput() {
242        throughputCalculationLock.lock();
243
244        try {
245            resetThroughput();
246            return readMessagesThroughput;
247        } finally {
248            throughputCalculationLock.unlock();
249        }
250    }
251
252    /**
253     * @return The number of written messages per second.
254     */
255    public final double getWrittenMessagesThroughput() {
256        throughputCalculationLock.lock();
257
258        try {
259            resetThroughput();
260            return writtenMessagesThroughput;
261        } finally {
262            throughputCalculationLock.unlock();
263        }
264    }
265
266    /**
267     * @return The maximum number of bytes read per second since the service has
268     *         been started.
269     */
270    public final double getLargestReadBytesThroughput() {
271        throughputCalculationLock.lock();
272
273        try {
274            return largestReadBytesThroughput;
275        } finally {
276            throughputCalculationLock.unlock();
277        }
278    }
279
280    /**
281     * @return The maximum number of bytes written per second since the service
282     *         has been started.
283     */
284    public final double getLargestWrittenBytesThroughput() {
285        throughputCalculationLock.lock();
286
287        try {
288            return largestWrittenBytesThroughput;
289        } finally {
290            throughputCalculationLock.unlock();
291        }
292    }
293
294    /**
295     * @return The maximum number of messages read per second since the service
296     *         has been started.
297     */
298    public final double getLargestReadMessagesThroughput() {
299        throughputCalculationLock.lock();
300
301        try {
302            return largestReadMessagesThroughput;
303        } finally {
304            throughputCalculationLock.unlock();
305        }
306    }
307
308    /**
309     * @return The maximum number of messages written per second since the
310     *         service has been started.
311     */
312    public final double getLargestWrittenMessagesThroughput() {
313        throughputCalculationLock.lock();
314
315        try {
316            return largestWrittenMessagesThroughput;
317        } finally {
318            throughputCalculationLock.unlock();
319        }
320    }
321
322    /**
323     * @return the interval (seconds) between each throughput calculation. The
324     *         default value is <tt>3</tt> seconds.
325     */
326    public final int getThroughputCalculationInterval() {
327        return throughputCalculationInterval.get();
328    }
329
330    /**
331     * @return the interval (milliseconds) between each throughput calculation.
332     * The default value is <tt>3</tt> seconds.
333     */
334    public final long getThroughputCalculationIntervalInMillis() {
335        return throughputCalculationInterval.get() * 1000L;
336    }
337
338    /**
339     * Sets the interval (seconds) between each throughput calculation.  The
340     * default value is <tt>3</tt> seconds.
341     * 
342     * @param throughputCalculationInterval The interval between two calculation
343     */
344    public final void setThroughputCalculationInterval(int throughputCalculationInterval) {
345        if (throughputCalculationInterval < 0) {
346            throw new IllegalArgumentException("throughputCalculationInterval: " + throughputCalculationInterval);
347        }
348
349        this.throughputCalculationInterval.set(throughputCalculationInterval);
350    }
351
352    /**
353     * Sets last time at which a read occurred on the service.
354     * 
355     * @param lastReadTime
356     *            The last time a read has occurred
357     */
358    protected final void setLastReadTime(long lastReadTime) {
359        throughputCalculationLock.lock();
360
361        try {
362            this.lastReadTime = lastReadTime;
363        } finally {
364            throughputCalculationLock.unlock();
365        }
366    }
367
368    /**
369     * Sets last time at which a write occurred on the service.
370     * 
371     * @param lastWriteTime
372     *            The last time a write has occurred
373     */
374    protected final void setLastWriteTime(long lastWriteTime) {
375        throughputCalculationLock.lock();
376
377        try {
378            this.lastWriteTime = lastWriteTime;
379        } finally {
380            throughputCalculationLock.unlock();
381        }
382    }
383
384    /**
385     * Resets the throughput counters of the service if no session is currently
386     * managed.
387     */
388    private void resetThroughput() {
389        if (service.getManagedSessionCount() == 0) {
390            readBytesThroughput = 0;
391            writtenBytesThroughput = 0;
392            readMessagesThroughput = 0;
393            writtenMessagesThroughput = 0;
394        }
395    }
396
397    /**
398     * Updates the throughput counters.
399     * 
400     * @param currentTime The current time
401     */
402    public void updateThroughput(long currentTime) {
403        throughputCalculationLock.lock();
404
405        try {
406            int interval = (int) (currentTime - lastThroughputCalculationTime);
407            long minInterval = getThroughputCalculationIntervalInMillis();
408
409            if ((minInterval == 0) || (interval < minInterval)) {
410                return;
411            }
412
413            long readBytes = this.readBytes;
414            long writtenBytes = this.writtenBytes;
415            long readMessages = this.readMessages;
416            long writtenMessages = this.writtenMessages;
417
418            readBytesThroughput = (readBytes - lastReadBytes) * 1000.0 / interval;
419            writtenBytesThroughput = (writtenBytes - lastWrittenBytes) * 1000.0 / interval;
420            readMessagesThroughput = (readMessages - lastReadMessages) * 1000.0 / interval;
421            writtenMessagesThroughput = (writtenMessages - lastWrittenMessages) * 1000.0 / interval;
422
423            if (readBytesThroughput > largestReadBytesThroughput) {
424                largestReadBytesThroughput = readBytesThroughput;
425            }
426
427            if (writtenBytesThroughput > largestWrittenBytesThroughput) {
428                largestWrittenBytesThroughput = writtenBytesThroughput;
429            }
430
431            if (readMessagesThroughput > largestReadMessagesThroughput) {
432                largestReadMessagesThroughput = readMessagesThroughput;
433            }
434
435            if (writtenMessagesThroughput > largestWrittenMessagesThroughput) {
436                largestWrittenMessagesThroughput = writtenMessagesThroughput;
437            }
438
439            lastReadBytes = readBytes;
440            lastWrittenBytes = writtenBytes;
441            lastReadMessages = readMessages;
442            lastWrittenMessages = writtenMessages;
443
444            lastThroughputCalculationTime = currentTime;
445        } finally {
446            throughputCalculationLock.unlock();
447        }
448    }
449
450    /**
451     * Increases the count of read bytes by <code>nbBytesRead</code> and sets
452     * the last read time to <code>currentTime</code>.
453     * 
454     * @param nbBytesRead
455     *            The number of bytes read
456     * @param currentTime
457     *            The date those bytes were read
458     */
459    public final void increaseReadBytes(long nbBytesRead, long currentTime) {
460        throughputCalculationLock.lock();
461
462        try {
463            readBytes += nbBytesRead;
464            lastReadTime = currentTime;
465        } finally {
466            throughputCalculationLock.unlock();
467        }
468    }
469
470    /**
471     * Increases the count of read messages by 1 and sets the last read time to
472     * <code>currentTime</code>.
473     * 
474     * @param currentTime
475     *            The time the message has been read
476     */
477    public final void increaseReadMessages(long currentTime) {
478        throughputCalculationLock.lock();
479
480        try {
481            readMessages++;
482            lastReadTime = currentTime;
483        } finally {
484            throughputCalculationLock.unlock();
485        }
486    }
487
488    /**
489     * Increases the count of written bytes by <code>nbBytesWritten</code> and
490     * sets the last write time to <code>currentTime</code>.
491     * 
492     * @param nbBytesWritten
493     *            The number of bytes written
494     * @param currentTime
495     *            The date those bytes were written
496     */
497    public final void increaseWrittenBytes(int nbBytesWritten, long currentTime) {
498        throughputCalculationLock.lock();
499
500        try {
501            writtenBytes += nbBytesWritten;
502            lastWriteTime = currentTime;
503        } finally {
504            throughputCalculationLock.unlock();
505        }
506    }
507
508    /**
509     * Increases the count of written messages by 1 and sets the last write time
510     * to <code>currentTime</code>.
511     * 
512     * @param currentTime
513     *            The date the message were written
514     */
515    public final void increaseWrittenMessages(long currentTime) {
516        throughputCalculationLock.lock();
517
518        try {
519            writtenMessages++;
520            lastWriteTime = currentTime;
521        } finally {
522            throughputCalculationLock.unlock();
523        }
524    }
525
526    /**
527     * @return The count of bytes scheduled for write.
528     */
529    public final int getScheduledWriteBytes() {
530        throughputCalculationLock.lock();
531
532        try {
533            return scheduledWriteBytes;
534        } finally {
535            throughputCalculationLock.unlock();
536        }
537    }
538
539    /**
540     * Increments by <code>increment</code> the count of bytes scheduled for write.
541     * 
542     * @param increment The number of added bytes fro write
543     */
544    public final void increaseScheduledWriteBytes(int increment) {
545        throughputCalculationLock.lock();
546
547        try {
548            scheduledWriteBytes += increment;
549        } finally {
550            throughputCalculationLock.unlock();
551        }
552    }
553
554    /**
555     * @return the count of messages scheduled for write.
556     */
557    public final int getScheduledWriteMessages() {
558        throughputCalculationLock.lock();
559
560        try {
561            return scheduledWriteMessages;
562        } finally {
563            throughputCalculationLock.unlock();
564        }
565    }
566
567    /**
568     * Increments the count of messages scheduled for write.
569     */
570    public final void increaseScheduledWriteMessages() {
571        throughputCalculationLock.lock();
572
573        try {
574            scheduledWriteMessages++;
575        } finally {
576            throughputCalculationLock.unlock();
577        }
578    }
579
580    /**
581     * Decrements the count of messages scheduled for write.
582     */
583    public final void decreaseScheduledWriteMessages() {
584        throughputCalculationLock.lock();
585
586        try {
587            scheduledWriteMessages--;
588        } finally {
589            throughputCalculationLock.unlock();
590        }
591    }
592
593    /**
594     * Sets the time at which throughput counters where updated.
595     * 
596     * @param lastThroughputCalculationTime The time at which throughput counters where updated.
597     */
598    protected void setLastThroughputCalculationTime(long lastThroughputCalculationTime) {
599        throughputCalculationLock.lock();
600
601        try {
602            this.lastThroughputCalculationTime = lastThroughputCalculationTime;
603        } finally {
604            throughputCalculationLock.unlock();
605        }
606    }
607}