alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Jetty example source code file (AbstractGenerator.java)

This example Jetty source code file (AbstractGenerator.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Jetty tags/keywords

buffer, buffer, bytearraybuffer, closed, eofexception, http, illegalstateexception, io, ioexception, ioexception, max_output_chars, output, reflection, request, response, servlet, state_header, state_header, string, string

The Jetty AbstractGenerator.java source code

//========================================================================
//$Id: HttpGenerator.java,v 1.7 2005/11/25 21:17:12 gregwilkins Exp $
//Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
//------------------------------------------------------------------------
//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 org.mortbay.jetty;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.io.Buffer;
import org.mortbay.io.Buffers;
import org.mortbay.io.ByteArrayBuffer;
import org.mortbay.io.EndPoint;
import org.mortbay.io.View;
import org.mortbay.log.Log;
import org.mortbay.util.ByteArrayOutputStream2;
import org.mortbay.util.StringUtil;
import org.mortbay.util.TypeUtil;

/* ------------------------------------------------------------ */
/**
 * Abstract Generator. Builds HTTP Messages.
 * 
 * Currently this class uses a system parameter "jetty.direct.writers" to control
 * two optional writer to byte conversions. buffer.writers=true will probably be 
 * faster, but will consume more memory.   This option is just for testing and tuning.
 * 
 * @author gregw
 * 
 */
public abstract class AbstractGenerator implements Generator
{
    // states
    public final static int STATE_HEADER = 0;
    public final static int STATE_CONTENT = 2;
    public final static int STATE_FLUSHING = 3;
    public final static int STATE_END = 4;
    
    private static byte[] NO_BYTES = {};
    private static int MAX_OUTPUT_CHARS = 512; 

    private static Buffer[] __reasons = new Buffer[505];
    static
    {
        Field[] fields = HttpServletResponse.class.getDeclaredFields();
        for (int i=0;i<fields.length;i++)
        {
            if ((fields[i].getModifiers()&Modifier.STATIC)!=0 &&
                 fields[i].getName().startsWith("SC_"))
            {
                try
                {
                    int code = fields[i].getInt(null);
                    if (code<__reasons.length)
                        __reasons[code]=new ByteArrayBuffer(fields[i].getName().substring(3));
                }
                catch(IllegalAccessException e)
                {}
            }    
        }
    }
    
    protected static Buffer getReasonBuffer(int code)
    {
        Buffer reason=(code<__reasons.length)?__reasons[code]:null;
        return reason==null?null:reason;
    }
    
    public static String getReason(int code)
    {
        Buffer reason=(code<__reasons.length)?__reasons[code]:null;
        return reason==null?TypeUtil.toString(code):reason.toString();
    }

    // data
    protected int _state = STATE_HEADER;
    
    protected int _status = 0;
    protected int _version = HttpVersions.HTTP_1_1_ORDINAL;
    protected  Buffer _reason;
    protected  Buffer _method;
    protected  String _uri;

    protected long _contentWritten = 0;
    protected long _contentLength = HttpTokens.UNKNOWN_CONTENT;
    protected boolean _last = false;
    protected boolean _head = false;
    protected boolean _noContent = false;
    protected boolean _close = false;

    protected Buffers _buffers; // source of buffers
    protected EndPoint _endp;

    protected int _headerBufferSize;
    protected int _contentBufferSize;
    
    protected Buffer _header; // Buffer for HTTP header (and maybe small _content)
    protected Buffer _buffer; // Buffer for copy of passed _content
    protected Buffer _content; // Buffer passed to addContent
    
    private boolean _sendServerVersion;

    
    /* ------------------------------------------------------------------------------- */
    /**
     * Constructor.
     * 
     * @param buffers buffer pool
     * @param headerBufferSize Size of the buffer to allocate for HTTP header
     * @param contentBufferSize Size of the buffer to allocate for HTTP content
     */
    public AbstractGenerator(Buffers buffers, EndPoint io, int headerBufferSize, int contentBufferSize)
    {
        this._buffers = buffers;
        this._endp = io;
        _headerBufferSize=headerBufferSize;
        _contentBufferSize=contentBufferSize;
    }

    /* ------------------------------------------------------------------------------- */
    public void reset(boolean returnBuffers)
    {
        _state = STATE_HEADER;
        _status = 0;
        _version = HttpVersions.HTTP_1_1_ORDINAL;
        _reason = null;
        _last = false;
        _head = false;
        _noContent=false;
        _close = false;
        _contentWritten = 0;
        _contentLength = HttpTokens.UNKNOWN_CONTENT;

        synchronized(this)
        {
            if (returnBuffers)
            {
                if (_header != null) 
                    _buffers.returnBuffer(_header);
                _header = null;
                if (_buffer != null) 
                    _buffers.returnBuffer(_buffer);
                _buffer = null;
            }
            else
            {
                if (_header != null) 
                    _header.clear();

                if (_buffer != null)
                {
                    _buffers.returnBuffer(_buffer);
                    _buffer = null;
                }
            }
        }
        _content = null;
        _method=null;
    }

    /* ------------------------------------------------------------------------------- */
    public void resetBuffer()
    {                   
        if(_state>=STATE_FLUSHING)
            throw new IllegalStateException("Flushed");
        
        _last = false;
        _close = false;
        _contentWritten = 0;
        _contentLength = HttpTokens.UNKNOWN_CONTENT;
        _content=null;
        if (_buffer!=null)
            _buffer.clear();  
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the contentBufferSize.
     */
    public int getContentBufferSize()
    {
        return _contentBufferSize;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param contentBufferSize The contentBufferSize to set.
     */
    public void increaseContentBufferSize(int contentBufferSize)
    {
        if (contentBufferSize > _contentBufferSize)
        {
            _contentBufferSize = contentBufferSize;
            if (_buffer != null)
            {
                Buffer nb = _buffers.getBuffer(_contentBufferSize);
                nb.put(_buffer);
                _buffers.returnBuffer(_buffer);
                _buffer = nb;
            }
        }
    }
    
    /* ------------------------------------------------------------ */    
    public Buffer getUncheckedBuffer()
    {
        return _buffer;
    }
    
    /* ------------------------------------------------------------ */    
    public boolean getSendServerVersion ()
    {
        return _sendServerVersion;
    }
    
    /* ------------------------------------------------------------ */    
    public void setSendServerVersion (boolean sendServerVersion)
    {
        _sendServerVersion = sendServerVersion;
    }
    
    /* ------------------------------------------------------------ */
    public int getState()
    {
        return _state;
    }

    /* ------------------------------------------------------------ */
    public boolean isState(int state)
    {
        return _state == state;
    }

    /* ------------------------------------------------------------ */
    public boolean isComplete()
    {
        return _state == STATE_END;
    }

    /* ------------------------------------------------------------ */
    public boolean isIdle()
    {
        return _state == STATE_HEADER && _method==null && _status==0;
    }

    /* ------------------------------------------------------------ */
    public boolean isCommitted()
    {
        return _state != STATE_HEADER;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the head.
     */
    public boolean isHead()
    {
        return _head;
    }

    /* ------------------------------------------------------------ */
    public void setContentLength(long value)
    {
        if (value<0)
            _contentLength=HttpTokens.UNKNOWN_CONTENT;
        else
            _contentLength=value;
    }
    
    /* ------------------------------------------------------------ */
    /**
     * @param head The head to set.
     */
    public void setHead(boolean head)
    {
        _head = head;
    }

    /* ------------------------------------------------------------ */
    /**
     * @return <code>false if the connection should be closed after a request has been read,
     * <code>true if it should be used for additional requests.
     */
    public boolean isPersistent()
    {
        return !_close;
    }

    /* ------------------------------------------------------------ */
    public void setPersistent(boolean persistent)
    {
        _close=!persistent;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param version The version of the client the response is being sent to (NB. Not the version
     *            in the response, which is the version of the server).
     */
    public void setVersion(int version)
    {
        if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");
        _version = version;
        if (_version==HttpVersions.HTTP_0_9_ORDINAL && _method!=null)
            _noContent=true;
    }

    /* ------------------------------------------------------------ */
    public int getVersion()
    {
        return _version;
    }
    
    /* ------------------------------------------------------------ */
    /**
     */
    public void setRequest(String method, String uri)
    {
        if (method==null || HttpMethods.GET.equals(method) )
            _method=HttpMethods.GET_BUFFER;
        else
            _method=HttpMethods.CACHE.lookup(method);
        _uri=uri;
        if (_version==HttpVersions.HTTP_0_9_ORDINAL)
            _noContent=true;
    }

    /* ------------------------------------------------------------ */
    /**
     * @param status The status code to send.
     * @param reason the status message to send.
     */
    public void setResponse(int status, String reason)
    {
        if (_state != STATE_HEADER) throw new IllegalStateException("STATE!=START");

        _status = status;
        if (reason!=null)
        {
            int len=reason.length();
            if (len>_headerBufferSize/2)
                len=_headerBufferSize/2;
            _reason=new ByteArrayBuffer(len);
            for (int i=0;i<len;i++)
            {
                char ch = reason.charAt(i);
                if (ch!='\r'&&ch!='\n')
                    _reason.put((byte)ch);
                else
                    _reason.put((byte)' ');
            }
        }
    }

    /* ------------------------------------------------------------ */
    /** Prepare buffer for unchecked writes.
     * Prepare the generator buffer to receive unchecked writes
     * @return the available space in the buffer.
     * @throws IOException
     */
    protected abstract int prepareUncheckedAddContent() throws IOException;

    /* ------------------------------------------------------------ */
    void uncheckedAddContent(int b)
    {
        _buffer.put((byte)b);
    }

    /* ------------------------------------------------------------ */
    void completeUncheckedAddContent()
    {
        if (_noContent)
        {
            if(_buffer!=null)
                _buffer.clear();
            return;
        }
        else 
        {
            _contentWritten+=_buffer.length();
            if (_head)
                _buffer.clear();
        }
    }
    
    /* ------------------------------------------------------------ */
    public boolean isBufferFull()
    {
        // Should we flush the buffers?
        boolean full =  
            (_buffer != null && _buffer.space() == 0) ||
            (_content!=null && _content.length()>0);
             
        return full;
    }
    
    /* ------------------------------------------------------------ */
    public boolean isContentWritten()
    {
        return _contentLength>=0 && _contentWritten>=_contentLength;
    }
    
    /* ------------------------------------------------------------ */
    public abstract void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException;
    
    /* ------------------------------------------------------------ */
    /**
     * Complete the message.
     * 
     * @throws IOException
     */
    public void complete() throws IOException
    {
        if (_state == STATE_HEADER)
        {
            throw new IllegalStateException("State==HEADER");
        }

        if (_contentLength >= 0 && _contentLength != _contentWritten && !_head)
        {
            if (Log.isDebugEnabled())
                Log.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
            _close = true;
        }
    }

    /* ------------------------------------------------------------ */
    public abstract long flush() throws IOException;
    

    /* ------------------------------------------------------------ */
    /**
     * Utility method to send an error response. If the builder is not committed, this call is
     * equivalent to a setResponse, addcontent and complete call.
     * 
     * @param code
     * @param reason
     * @param content
     * @param close
     * @throws IOException
     */
    public void sendError(int code, String reason, String content, boolean close) throws IOException
    {
        if (!isCommitted())
        {
            setResponse(code, reason);
            _close = close;
            completeHeader(null, false);
            if (content != null) 
                addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);
            complete();
        }
    }

    /* ------------------------------------------------------------ */
    /**
     * @return Returns the contentWritten.
     */
    public long getContentWritten()
    {
        return _contentWritten;
    }


    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /** Output.
     * 
     * <p>
     * Implements  {@link javax.servlet.ServletOutputStream} from the {@link javax.servlet} package.   
     * </p>
     * A {@link ServletOutputStream} implementation that writes content
     * to a {@link AbstractGenerator}.   The class is designed to be reused
     * and can be reopened after a close.
     */
    public static class Output extends ServletOutputStream 
    {
        protected AbstractGenerator _generator;
        protected long _maxIdleTime;
        protected ByteArrayBuffer _buf = new ByteArrayBuffer(NO_BYTES);
        protected boolean _closed;
        
        // These are held here for reuse by Writer
        String _characterEncoding;
        Writer _converter;
        char[] _chars;
        ByteArrayOutputStream2 _bytes;
        

        /* ------------------------------------------------------------ */
        public Output(AbstractGenerator generator, long maxIdleTime)
        {
            _generator=generator;
            _maxIdleTime=maxIdleTime;
        }
        
        /* ------------------------------------------------------------ */
        /*
         * @see java.io.OutputStream#close()
         */
        public void close() throws IOException
        {
            _closed=true;
        }

        /* ------------------------------------------------------------ */
        void  blockForOutput() throws IOException
        {
            if (_generator._endp.isBlocking())
            {
                try
                {
                    flush();
                }
                catch(IOException e)
                {
                    _generator._endp.close();
                    throw e;
                }
            }
            else
            {
                if (!_generator._endp.blockWritable(_maxIdleTime))
                {
                    _generator._endp.close();
                    throw new EofException("timeout");
                }
                
                _generator.flush();
            }
        }
        
        /* ------------------------------------------------------------ */
        void reopen()
        {
            _closed=false;
        }
        
        /* ------------------------------------------------------------ */
        public void flush() throws IOException
        {
            // block until everything is flushed
            Buffer content = _generator._content;
            Buffer buffer = _generator._buffer;
            if (content!=null && content.length()>0 ||buffer!=null && buffer.length()>0)
            {
                _generator.flush();
                
                while ((content!=null && content.length()>0 ||buffer!=null && buffer.length()>0) && _generator._endp.isOpen())
                    blockForOutput();
            }
        }

        /* ------------------------------------------------------------ */
        public void write(byte[] b, int off, int len) throws IOException
        {
            _buf.wrap(b, off, len);
            write(_buf);
        }

        /* ------------------------------------------------------------ */
        /*
         * @see java.io.OutputStream#write(byte[])
         */
        public void write(byte[] b) throws IOException
        {
            _buf.wrap(b);
            write(_buf);
        }

        /* ------------------------------------------------------------ */
        /*
         * @see java.io.OutputStream#write(int)
         */
        public void write(int b) throws IOException
        {
            if (_closed)
                throw new IOException("Closed");
            if (!_generator._endp.isOpen())
                throw new EofException();
            
            // Block until we can add _content.
            while (_generator.isBufferFull())
            {
                blockForOutput();
                if (_closed)
                    throw new IOException("Closed");
                if (!_generator._endp.isOpen())
                    throw new EofException();
            }

            // Add the _content
            if (_generator.addContent((byte)b))
                // Buffers are full so flush.
                flush();
           
            if (_generator.isContentWritten())
            {
                flush();
                close();
            }
        }

        /* ------------------------------------------------------------ */
        private void write(Buffer buffer) throws IOException
        {
            if (_closed)
                throw new IOException("Closed");
            if (!_generator._endp.isOpen())
                throw new EofException();
            
            // Block until we can add _content.
            while (_generator.isBufferFull())
            {
                blockForOutput();
                if (_closed)
                    throw new IOException("Closed");
                if (!_generator._endp.isOpen())
                    throw new EofException();
            }

            // Add the _content
            _generator.addContent(buffer, Generator.MORE);

            // Have to flush and complete headers?
            if (_generator.isBufferFull())
                flush();
            
            if (_generator.isContentWritten())
            {
                flush();
                close();
            }

            // Block until our buffer is free
            while (buffer.length() > 0 && _generator._endp.isOpen())
                blockForOutput();
        }

        /* ------------------------------------------------------------ */
        /* 
         * @see javax.servlet.ServletOutputStream#print(java.lang.String)
         */
        public void print(String s) throws IOException
        {
            write(s.getBytes());
        }
    }
    
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /* ------------------------------------------------------------ */
    /** OutputWriter.
     * A writer that can wrap a {@link Output} stream and provide
     * character encodings.
     *
     * The UTF-8 encoding is done by this class and no additional 
     * buffers or Writers are used.
     * The UTF-8 code was inspired by http://javolution.org
     */
    public static class OutputWriter extends Writer
    {
        private static final int WRITE_CONV = 0;
        private static final int WRITE_ISO1 = 1;
        private static final int WRITE_UTF8 = 2;
        
        Output _out;
        AbstractGenerator _generator;
        int _writeMode;
        int _surrogate;

        /* ------------------------------------------------------------ */
        public OutputWriter(Output out)
        {
            _out=out;
            _generator=_out._generator;
             
        }

        /* ------------------------------------------------------------ */
        public void setCharacterEncoding(String encoding)
        {
            if (encoding == null || StringUtil.__ISO_8859_1.equalsIgnoreCase(encoding))
            {
                _writeMode = WRITE_ISO1;
            }
            else if (StringUtil.__UTF8.equalsIgnoreCase(encoding))
            {
                _writeMode = WRITE_UTF8;
            }
            else
            {
                _writeMode = WRITE_CONV;
                if (_out._characterEncoding == null || !_out._characterEncoding.equalsIgnoreCase(encoding))
                    _out._converter = null; // Set lazily in getConverter()
            }
            
            _out._characterEncoding = encoding;
            if (_out._bytes==null)
                _out._bytes = new ByteArrayOutputStream2(MAX_OUTPUT_CHARS);
        }

        /* ------------------------------------------------------------ */
        public void close() throws IOException
        {
            _out.close();
        }

        /* ------------------------------------------------------------ */
        public void flush() throws IOException
        {
            _out.flush();
        }

        /* ------------------------------------------------------------ */
        public void write (String s,int offset, int length) throws IOException
        {   
            while (length > MAX_OUTPUT_CHARS)
            {
                write(s, offset, MAX_OUTPUT_CHARS);
                offset += MAX_OUTPUT_CHARS;
                length -= MAX_OUTPUT_CHARS;
            }

            if (_out._chars==null)
            {
                _out._chars = new char[MAX_OUTPUT_CHARS]; 
            }
            char[] chars = _out._chars;
            s.getChars(offset, offset + length, chars, 0);
            write(chars, 0, length);
        }

        /* ------------------------------------------------------------ */
        public void write (char[] s,int offset, int length) throws IOException
        {              
            Output out = _out; 
            
            while (length > 0)
            {  
                out._bytes.reset();
                int chars = length>MAX_OUTPUT_CHARS?MAX_OUTPUT_CHARS:length;

                switch (_writeMode)
                {
                    case WRITE_CONV:
                    {
                        Writer converter=getConverter();
                        converter.write(s, offset, chars);
                        converter.flush();
                    }
                    break;

                    case WRITE_ISO1:
                    {
                        byte[] buffer=out._bytes.getBuf();
                        int bytes=out._bytes.getCount();
                        
                        if (chars>buffer.length-bytes)
                            chars=buffer.length-bytes;

                        for (int i = 0; i < chars; i++)
                        {
                            int c = s[offset+i];
                            buffer[bytes++]=(byte)(c<256?c:'?'); // ISO-1 and UTF-8 match for 0 - 255
                        }
                        if (bytes>=0)
                            out._bytes.setCount(bytes);

                        break;
                    }

                    case WRITE_UTF8:
                    {
                        byte[] buffer=out._bytes.getBuf();
                        int bytes=out._bytes.getCount();
         
                        if (bytes+chars>buffer.length)
                            chars=buffer.length-bytes;
                        
                        for (int i = 0; i < chars; i++)
                        {
                            int code = s[offset+i];

                            if ((code & 0xffffff80) == 0) 
                            {
                                // 1b
                                buffer[bytes++]=(byte)(code);
                            }
                            else if((code&0xfffff800)==0)
                            {
                                // 2b
                                if (bytes+2>buffer.length)
                                {
                                    chars=i;
                                    break;
                                }
                                buffer[bytes++]=(byte)(0xc0|(code>>6));
                                buffer[bytes++]=(byte)(0x80|(code&0x3f));

                                if (bytes+chars-i-1>buffer.length)
                                    chars-=1;
                            }
                            else if((code&0xffff0000)==0)
                            {
                                // 3b
                                if (bytes+3>buffer.length)
                                {
                                    chars=i;
                                    break;
                                }
                                buffer[bytes++]=(byte)(0xe0|(code>>12));
                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
                                buffer[bytes++]=(byte)(0x80|(code&0x3f));

                                if (bytes+chars-i-1>buffer.length)
                                    chars-=2;
                            }
                            else if((code&0xff200000)==0)
                            {
                                // 4b
                                if (bytes+4>buffer.length)
                                {
                                    chars=i;
                                    break;
                                }
                                buffer[bytes++]=(byte)(0xf0|(code>>18));
                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
                                buffer[bytes++]=(byte)(0x80|(code&0x3f));

                                if (bytes+chars-i-1>buffer.length)
                                    chars-=3;
                            }
                            else if((code&0xf4000000)==0)
                            {
                                // 5b
                                if (bytes+5>buffer.length)
                                {
                                    chars=i;
                                    break;
                                }
                                buffer[bytes++]=(byte)(0xf8|(code>>24));
                                buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
                                buffer[bytes++]=(byte)(0x80|(code&0x3f));

                                if (bytes+chars-i-1>buffer.length)
                                    chars-=4;
                            }
                            else if((code&0x80000000)==0)
                            {
                                // 6b
                                if (bytes+6>buffer.length)
                                {
                                    chars=i;
                                    break;
                                }
                                buffer[bytes++]=(byte)(0xfc|(code>>30));
                                buffer[bytes++]=(byte)(0x80|((code>>24)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>18)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>12)&0x3f));
                                buffer[bytes++]=(byte)(0x80|((code>>6)&0x3f));
                                buffer[bytes++]=(byte)(0x80|(code&0x3f));

                                if (bytes+chars-i-1>buffer.length)
                                    chars-=5;
                            }
                            else
                            {
                                buffer[bytes++]=(byte)('?');
                            }
                        }
                        out._bytes.setCount(bytes);
                        break;
                    }
                    default:
                        throw new IllegalStateException();
                }
                
                out._bytes.writeTo(out);
                length-=chars;
                offset+=chars;
            }
        }

        /* ------------------------------------------------------------ */
        private Writer getConverter() throws IOException
        {
            if (_out._converter == null)
                _out._converter = new OutputStreamWriter(_out._bytes, _out._characterEncoding);
            return _out._converter;
        }   
    }
}

Other Jetty examples (source code examples)

Here is a short list of links related to this Jetty AbstractGenerator.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.