Main Page | Packages | Class Hierarchy | Class List | Directories | File List | Class Members | Related Pages

ThrottledOutputStream.java

00001 // ThrottledOutputStream - output stream with throttling
00002 //
00003 // Copyright (C)1996,1998 by Jef Poskanzer <jef@acme.com>. All rights reserved.
00004 //
00005 // Redistribution and use in source and binary forms, with or without
00006 // modification, are permitted provided that the following conditions
00007 // are met:
00008 // 1. Redistributions of source code must retain the above copyright
00009 //    notice, this list of conditions and the following disclaimer.
00010 // 2. Redistributions in binary form must reproduce the above copyright
00011 //    notice, this list of conditions and the following disclaimer in the
00012 //    documentation and/or other materials provided with the distribution.
00013 //
00014 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00015 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00016 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00017 // ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00018 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00019 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00020 // OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00021 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00022 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00023 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00024 // SUCH DAMAGE.
00025 //
00026 // Visit the ACME Labs Java page for up-to-date versions of this and other
00027 // fine Java utilities: http://www.acme.com/java/
00028 
00029 package Acme.Serve;
00030 
00031 import java.io.*;
00032 import java.util.*;
00033 
00035 // <P>
00036 // Restricts output to a specified rate.  Also includes a static utility
00037 // routine for parsing a file of throttle settings.
00038 // <P>
00039 // <A HREF="/resources/classes/Acme/Serve/ThrottledOutputStream.java">Fetch the software.</A><BR>
00040 // <A HREF="/resources/classes/Acme.tar.Z">Fetch the entire Acme package.</A>
00041 
00042 public class ThrottledOutputStream extends FilterOutputStream
00043     {
00044 
00046     // <P>
00047     // A throttle file lets you set maximum byte rates on filename patterns.
00048     // The format of the throttle file is very simple.  A # starts a
00049     // comment, and the rest of the line is ignored.  Blank lines are ignored.
00050     // The rest of the lines should consist of a pattern, whitespace, and a
00051     // number.  The pattern is a simple shell-style filename pattern, using
00052     // ? and *, or multiple such patterns separated by |.
00053     // <P>
00054     // The numbers in the file are byte rates, specified in units of bytes
00055     // per second.  For comparison, a v.32b/v.42b modem gives about
00056     // 1500/2000 B/s depending on compression, a double-B-channel ISDN line
00057     // about 12800 B/s, and a T1 line is about 150000 B/s.
00058     // <P>
00059     // Example:
00060     // <BLOCKQUOTE><PRE>
00061     // # throttle file for www.acme.com
00062     // *               100000  # limit total web usage to 2/3 of our T1
00063     // *.jpg|*.gif     50000   # limit images to 1/3 of our T1
00064     // *.mpg           20000   # and movies to even less
00065     // jef/*           20000   # jef's pages are too popular
00066     // </PRE></BLOCKQUOTE>
00067     // <P>
00068     // The routine returns a WildcardDictionary.  Do a lookup in this
00069     // dictionary using a filename, and you'll get back a ThrottleItem
00070     // containing the corresponding byte rate.
00071     public static Acme.WildcardDictionary parseThrottleFile( String filename ) throws IOException
00072         {
00073         Acme.WildcardDictionary wcd = new Acme.WildcardDictionary();
00074         BufferedReader br = new BufferedReader( new FileReader( filename ) );
00075         while ( true )
00076             {
00077             String line = br.readLine();
00078             if ( line == null )
00079                 break;
00080             int i = line.indexOf( '#' );
00081             if ( i != -1 )
00082                 line = line.substring( 0, i );
00083             line = line.trim();
00084             if ( line.length() == 0 )
00085                 continue;
00086             String[] words = Acme.Utils.splitStr( line );
00087             if ( words.length != 2 )
00088                 throw new IOException( "malformed throttle line: " + line );
00089             try
00090                 {
00091                 wcd.put(
00092                     words[0], new ThrottleItem( Long.parseLong( words[1] ) ) );
00093                 }
00094             catch ( NumberFormatException e )
00095                 {
00096                 throw new IOException(
00097                     "malformed number in throttle line: " + line );
00098                 }
00099             }
00100         br.close();
00101         return wcd;
00102         }
00103 
00104 
00105     private long maxBps;
00106     private long bytes;
00107     private long start;
00108 
00110     public ThrottledOutputStream( OutputStream out, long maxBps )
00111         {
00112         super( out );
00113         this.maxBps = maxBps;
00114         bytes = 0;
00115         start = System.currentTimeMillis();
00116         }
00117 
00118     private byte[] oneByte = new byte[1];
00119 
00121     // written.
00122     // @param b the byte to be written
00123     // @exception IOException if an I/O error has occurred
00124     public void write( int b ) throws IOException
00125         {
00126         oneByte[0] = (byte) b;
00127         write( oneByte, 0, 1 );
00128         }
00129 
00131     // @param b the data to be written
00132     // @param off the start offset in the data
00133     // @param len the number of bytes that are written
00134     // @exception IOException if an I/O error has occurred
00135     public void write( byte b[], int off, int len ) throws IOException
00136         {
00137         // Check the throttle.
00138         bytes += len;
00139         long elapsed = System.currentTimeMillis() - start;
00140         long bps = bytes * 1000L / elapsed;
00141         if ( bps > maxBps )
00142             {
00143             // Oops, sending too fast.
00144             long wakeElapsed = bytes * 1000L / maxBps;
00145             try
00146                 {
00147                 Thread.sleep( wakeElapsed - elapsed );
00148                 }
00149             catch ( InterruptedException ignore ) {}
00150             }
00151 
00152         // Write the bytes.
00153         out.write( b, off, len );
00154         }
00155 
00156     }

Generated on Wed Dec 14 21:05:35 2005 for OpenMobileIS by  doxygen 1.4.4