Anonymous avatar Anonymous committed d9b2bb8

Initial Commit of Chapter Drafts

Comments (0)

Files changed (394)

Binary file added.

+Appendix A:  Using Other Tools with Jython
+==========================================
+
+The primary focus of this appendix is to provide information on using some external
+python packages with Jython.  In some circumstances, the tools must be used or installed
+a bit differently on Jython than on CPython, and those differences will be noted.  Since there
+is a good deal of documentation on the usage of these tools available on the web, this appendix
+will focus on using the tool specifically with Jython.  However, relevant URLs will be cited
+for finding more documentation on each of the topics.
+
+setuptools
+----------
+
+Setuptools is a library that builds upon distutils, the standard Python
+distribution facility. It offers some advanced tools like ``easy_install``, a
+command to automatically download and install a given Python package and its
+dependencies.
+
+To get setuptools, download ez_setup.py from
+http://peak.telecommunity.com/dist/ez_setup.py. Then, go to the directory where
+you left the downloaded file and execute::
+
+    $ jython ez_setup.py
+
+The output will be similar to the following::
+
+    Downloading http://pypi.python.org/packages/2.5/s/setuptools/setuptools-0.6c9-py2.5.egg
+    Processing setuptools-0.6c9-py2.5.egg
+    Copying setuptools-0.6c9-py2.5.egg to /home/lsoto/jython2.5.0/Lib/site-packages
+    Adding setuptools 0.6c9 to easy-install.pth file
+    Installing easy_install script to /home/lsoto/jython2.5.0/bin
+    Installing easy_install-2.5 script to /home/lsoto/jython2.5.0/bin
+    
+    Installed /home/lsoto/jython2.5.0/Lib/site-packages/setuptools-0.6c9-py2.5.egg
+    Processing dependencies for setuptools==0.6c9
+    Finished processing dependencies for setuptools==0.6c9
+
+As you can read on the output, the ``easy_install`` script has been installed to the
+``bin`` directory of the Jython installation (``/home/lsoto/jython2.5.0/bin`` in
+the example above). If you work frequently with Jython it's a good idea to
+prepend this directory to the ``PATH`` environment variable, so you don't have
+to type the whole path each time you want to use ``easy_install`` or other
+scripts installed to this directory. From now on I'll assume that this is the
+case. If you don't want to prepend Jython's bin directory to your ``PATH`` for
+any reason, remember to type the complete path on each example (i.e., type
+``/path/to/jython/bin/easy_install`` when I say ``easy_install``).
+
+OK, so now you have ``easy_install``. What's next? Let's grab a Python library
+with it! For example, let's say that we need to access twitter from a program
+written in Jython and we want to use python-twitter project, located at
+http://code.google.com/p/python-twitter/.
+
+Without ``easy_install`` you would go to that URL, read the building
+instructions and after downloading the latest version and executing a few
+commands you should be ready to go. Except that libraries often depend on other
+libraries (as the case with python-twitter which depends on simplejson)
+so you would have to repeat this boring process a few times.
+
+With ``easy_install`` you simply run::
+
+  $ easy_install python-twitter
+
+And you get the following output::
+
+  Searching for python-twitter
+  Reading http://pypi.python.org/simple/python-twitter/
+  Reading http://code.google.com/p/python-twitter/
+  Best match: python-twitter 0.6
+  Downloading http://python-twitter.googlecode.com/files/python-twitter-0.6.tar.gz
+  Processing python-twitter-0.6.tar.gz
+  Running python-twitter-0.6/setup.py -q bdist_egg --dist-dir /var/folders/mQ/mQkMNKiaE583pWpee85FFk+++TI/-Tmp-/easy_install-FU5COZ/python-twitter-0.6/egg-dist-tmp-EeR4RD
+  zip_safe flag not set; analyzing archive contents...
+  Unable to analyze compiled code on this platform.
+  Please ask the author to include a 'zip_safe' setting (either True or False) in the package's setup.py
+  Adding python-twitter 0.6 to easy-install.pth file
+  
+  Installed /home/lsoto/jython2.5.0/Lib/site-packages/python_twitter-0.6-py2.5.egg
+  Processing dependencies for python-twitter
+  Searching for simplejson
+  Reading http://pypi.python.org/simple/simplejson/
+  Reading http://undefined.org/python/#simplejson
+  Best match: simplejson 2.0.9
+  Downloading http://pypi.python.org/packages/source/s/simplejson/simplejson-2.0.9.tar.gz#md5=af5e67a39ca3408563411d357e6d5e47
+  Processing simplejson-2.0.9.tar.gz
+  Running simplejson-2.0.9/setup.py -q bdist_egg --dist-dir /var/folders/mQ/mQkMNKiaE583pWpee85FFk+++TI/-Tmp-/easy_install-VgAKxa/simplejson-2.0.9/egg-dist-tmp-jcntqu
+  ***************************************************************************
+  WARNING: The C extension could not be compiled, speedups are not enabled.
+  Failure information, if any, is above.
+  I'm retrying the build without the C extension now.
+  ***************************************************************************
+  ***************************************************************************
+  WARNING: The C extension could not be compiled, speedups are not enabled.
+  Plain-Python installation succeeded.
+  ***************************************************************************
+  Adding simplejson 2.0.9 to easy-install.pth file
+  
+  Installed /home/lsoto/jython2.5.0/Lib/site-packages/simplejson-2.0.9-py2.5.egg
+  Finished processing dependencies for python-twitter
+
+The output is a bit verbose, but it gives you a detailed idea of the steps
+automated by ``easy_install``. Let's review it piece by piece::
+
+  Searching for python-twitter
+  Reading http://pypi.python.org/simple/python-twitter/
+  Reading http://code.google.com/p/python-twitter/
+  Best match: python-twitter 0.6
+  Downloading http://python-twitter.googlecode.com/files/python-twitter-0.6.tar.gz
+
+We asked for "python-twitter", which is looked up on PyPI, the Python Package
+Index which lists all the Python packages produced by the community. The version
+0.6 was selected since it was the most recent version at the time I ran the
+command. 
+
+Let's see what's next on the ``easy_install`` output::
+
+  Running python-twitter-0.6/setup.py -q bdist_egg --dist-dir /var/folders/mQ/mQkMNKiaE583pWpee85FFk+++TI/-Tmp-/easy_install-FU5COZ/python-twitter-0.6/egg-dist-tmp-EeR4RD
+  zip_safe flag not set; analyzing archive contents...
+  Unable to analyze compiled code on this platform.
+  Please ask the author to include a 'zip_safe' setting (either True or False) in the package's setup.py
+  Adding python-twitter 0.6 to easy-install.pth file
+  
+  Installed /home/lsoto/jython2.5.0/Lib/site-packages/python_twitter-0.6-py2.5.egg
+
+Nothing special here: it ran the needed commands to install the library. The
+next bits are more interesting::
+
+  Processing dependencies for python-twitter
+  Searching for simplejson
+  Reading http://pypi.python.org/simple/simplejson/
+  Reading http://undefined.org/python/#simplejson
+  Best match: simplejson 2.0.9
+  Downloading http://pypi.python.org/packages/source/s/simplejson/simplejson-2.0.9.tar.gz#md5=af5e67a39ca3408563411d357e6d5e47
+
+As you can see, the dependency on simplejson was discovered and, since it is not
+already installed it is being downloaded. Next we see::
+
+  Processing simplejson-2.0.9.tar.gz
+  Running simplejson-2.0.9/setup.py -q bdist_egg --dist-dir /var/folders/mQ/mQkMNKiaE583pWpee85FFk+++TI/-Tmp-/easy_install-VgAKxa/simplejson-2.0.9/egg-dist-tmp-jcntqu
+  ***************************************************************************
+  WARNING: The C extension could not be compiled, speedups are not enabled.
+  Failure information, if any, is above.
+  I'm retrying the build without the C extension now.
+  ***************************************************************************
+  ***************************************************************************
+  WARNING: The C extension could not be compiled, speedups are not enabled.
+  Plain-Python installation succeeded.
+  ***************************************************************************
+  Adding simplejson 2.0.9 to easy-install.pth file
+  
+  Installed /home/lsoto/jython2.5.0/Lib/site-packages/simplejson-2.0.9-py2.5.egg
+
+The warnings are produced because the ``simplejson`` installation tries to
+compile a C extension which for obvious reasons only works with CPython and not
+with Jython.
+
+Finally, we see::
+
+  Finished processing dependencies for python-twitter
+
+Which signals the end of the automated installation process for
+python-twitter. You can test that it was successfully installed by running
+Jython and doing an ``import twitter`` on the interactive interpreter.
+
+As noted above ``easy_install`` will try to get the latest version for the
+library you specify. If you want a particular version, for example the 0.5
+release of python-twitter then you can specify it in this way::
+
+  $ easy_install python-twitter==0.5
+
+.. warning::
+
+  Note that it's not a good idea to have two version of the same library
+  installed at the same time. Take a look at the `virtualenv`_ section below for
+  a solution to the problem of running different programs requiring different
+  versions of the same library.
+
+For debugging purposes is always useful to know where the bits installed using
+``easy_install`` go. As you can stop of the install output, they are installed
+into ``<path-to-jython>/Lib/site-packages/<name_of_library>-<version>.egg``
+which may be a directory or a compressed zip file. Also, ``easy_install`` adds
+an entry to the file ``<path-to-jython>/Lib/site-packages/easy-install.pth``,
+which ends up adding the directory or zip file to ``sys.path`` by default.
+
+Unfortunately setuptools don't provide any automated way to uninstall
+packages. You will have to manually delete the package egg directory or zip file
+and remove the associated line on ``easy-install.pth``.
+
+
+virtualenv
+----------
+
+Oftentimes it is nice to have separate versions of tools running on the same machine.  The virtualenv tool
+provides a way to create a virtual Python environment that can be used for various purposes including installation
+of different package versions.  Virtual environments can also be nice for those who do not have administrative
+access for a particular Python installation but still need to have the ability to install packages to it, such is often
+the case when working with domain hosts.  Whatever the case may be, the virtualenv tool provides a means for creating
+one or more virtual environments for a particular Python installation so that the libraries can be installed into controlled
+environments other than the global site-packages area for your Python or Jython installation.  The release of Jython 2.5.0 opened new doors
+for the possibility of using such tools as virtualenv.
+
+To use virtualenv with Jython, we first need to obtain it.  The easiest way to do so is via the Python Package
+Index.  As you had learned in the previous section, easy_install is the way to install packages from the PyPI.  The following
+example shows how to install virtualenv using easy_install with Jython.
+
+::
+    jython easy_install.py virtualenv
+
+Once installed, it is quite easy to use the tool for creation of a virtual environment.  The virtual environment
+will include a Jython executable along with an installation of setuptools and it's own site-packages directory.
+This was done so that you have
+the ability to install different packages from the PyPI to your virtual environment.  Let's create an enviroment
+named JY2.5.1Env using the virtualenv.py module that exists within our Jython environment.
+
+::
+    
+    jython <<path to Jython>>/jython2.5.1/Lib/site-packages/virtualenv-1.3.3-py2.5.egg/virtualenv.py JY2.5.1Env
+    New jython executable in JY2.5.1Env/bin/jython
+    Installing setuptools............done.
+
+Now a new directory named JY2.5.1Env should have been created within your current working directory.  You can run
+Jython from this virtual environment by simply invoking the executable that was created.  The virtualenv tool allows
+us the ability to open a terminal and disignate it to be used for our virutal Jython environment exclusively via the
+use of the *activate* command.  To do so, open up a terminal and type the following:
+::
+    
+    source <path-to-virtual-environment/JY2.5.1Env/bin/activate
+
+Once this is done, you should notice that the command line is preceeded with the name of the virutal environment that
+you have activated.  Any Jython shell or tool used in this terminal will now be using the virtual environment.  This is an
+excellent way to run a tool using two different versions of a particular libarary or for running a production and development
+environment side-by-side.  If you run the easy_install.py tool within the activated virtual environment terminal then the tool(s)
+will be installed into the virtual environment.  There can be an unlimited number of virtual environments installed
+on a particular machine.  To stop using the virtual environment within the terminal, simply type:
+
+::
+    
+    deactivate
+
+Now your terminal should go back to normal use and default to the global Jython installation.  Once deactivated any of the Jython
+references made will call the global installation or libraries within the global site-packages area.  It should be noted that
+when you create a virtual environment, it automatically inherits all of the packages used by the global installation.  Therefore
+if you have a library installed in your global site-packages area then it can be used from the virtual environment right away.
+A good practice is to install only essential libraries into your global Jython environment and then install one-offs or test
+libraies into virtual environments.
+
+It is useful to have the ability to list installations that are in use within a particular environment.  One way
+to do this is to install the *yolk* utility and make use of it's *-l* command.  In order to install *yolk*, you must
+grab a copy of the latest version of Jython beyond 2.5.1 as there has been a patch submitted that corrects some functionaility
+which is used by *yolk*.  You must also be running with JDK 1.6 or above as the patched version of Jython makes use
+of the *webbrowser* module.  The *webbrowser* module makes use of some java.awt.Desktop features that are only available
+in JDK 1.6 and beyond.  To install *yolk*, use the ez_install.py script as we've shown previously.
+
+::
+    
+    jython ez_install.py yolk
+
+Once installed you can list the package installations for your Jython installations by issuing the *-l* command as follows:
+
+::
+    
+    yolk -l
+    Django          - 1.0.2-final  - non-active development (/jython2.5.1/Lib/site-packages)
+    Django          - 1.0.3        - active development (/jython2.5.1/Lib/site-packages/Django-1.0.3-py2.5.egg)
+    Django          - 1.1          - non-active development (/jython2.5.1/Lib/site-packages)
+    SQLAlchemy      - 0.5.4p2      - active development (/jython2.5.1/Lib/site-packages)
+    SQLAlchemy      - 0.6beta1     - non-active development (/jython2.5.1/Lib/site-packages)
+    django-jython   - 0.9          - active development (/jython2.5.1/Lib/site-packages/django_jython-0.9-py2.5.egg)
+    django-jython   - 1.0b1        - non-active development (/jython2.5.1/Lib/site-packages)
+    nose            - 0.11.1       - active development (/jython2.5.1/Lib/site-packages/nose-0.11.1-py2.5.egg)
+    setuptools      - 0.6c9        - active 
+    setuptools      - 0.6c9        - active 
+    snakefight      - 0.4          - active development (/jython2.5.1/Lib/site-packages/snakefight-0.4-py2.5.egg)
+    virtualenv      - 1.3.3        - active development (/jython2.5.1/Lib/site-packages/virtualenv-1.3.3-py2.5.egg)
+    wsgiref         - 0.1.2        - active development (/jython2.5.1/Lib)
+    yolk            - 0.4.1        - active
+
+As you can see, all installed packages will be listed.  If you are using yolk from within a virtual environment then
+you will see all packages installed in that virtual environment as well as those installed into the global
+environment.
+
+Similarly to setuptools, there is no way to automatically uninstall virtualenv.  You must also manually delete the
+package egg directory or zip file as well as remove references within ``easy-install.pth``.
+
+
+
+
+Appendix B:  Jython Cookbook - A compilation of community submitted code examples
+=================================================================================
+
+There are a plethora of examples for using Jython that can be found on the web.  This
+appendix is a compilation of some of the most useful examples that we have found.  There
+are hundreds of examples available on the web.  These that were chosen are focused on topics
+that are not widely covered elsewhere on the web.
+
+Unless otherwise noted, each
+of these examples have been originally authored for working on versions of Jython prior to 2.5.x but
+we have tested each of them using Jython 2.5.1 and function as advertised.
+
+
+Logging
+-------
+
+Using log4j With Jython - Josh Juneau
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Are you still using the Jython print command to show your errors? How about in a production environment, are you using any formal logging?
+If not, you should be doing so...and the Apache log4j API makes it easy to do so. Many Java developers have grown to love the log4j API
+and it is utilized throughout much of the community. That is great news for Jython developers since we've got direct access to Java libraries!
+
+**Setting Up Your Environment**
+
+The most difficult part about using log4j with Jython is the setup. You must ensure that the log4j.jar archive resides somewhere
+within your Jython PATH (usually this entails setting the CLASSPATH to include necessary files). You then set up a properties file for use
+with log4j. Within the properties file, you can include appender information, where logs should reside, and much more.
+
+*Example properties file:*
+
+::
+    
+    log4j.rootLogger=debug, stdout, R
+    
+    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+    
+    # Pattern to output the caller's file name and line number.
+    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n
+    
+    log4j.appender.R=org.apache.log4j.RollingFileAppender
+    log4j.appender.R.File=C:\\Jython\\testlog4j.log
+    
+    log4j.appender.R.MaxFileSize=100KB
+    # Keep one backup file
+    log4j.appender.R.MaxBackupIndex=1
+    
+    log4j.appender.R.layout=org.apache.log4j.PatternLayout
+    log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n
+
+You are now ready to use log4j in your Jython application. As you can see, if you've ever used log4j with Java, it is pretty much the same.
+
+**Using log4j in a Jython Application**
+
+Once again, using log4j within a Jython application is very similar to it's usage in the Java world.
+
+First, you must import the log4j packages:
+
+::
+    
+    from org.apache.log4j import *
+
+Second, you obtain a new logger for your class or module and set up a PropertyConfigurator:
+
+::
+    
+    logger = Logger.getLogger("myClass")
+    # Assume that the log4j properties resides within a folder named "utilities"
+    PropertyConfigurator.configure(sys.path[0] + "/utilities/log4j.properties")
+
+Lastly, use log4j:
+
+::
+    
+    # Example module within the class:
+    def submitDocument(self, event):
+        try:
+            # Assume we perform some SQL here              
+        except SQLException, ex:
+            self.logger.error("docPanel#submitDocument ERROR: %s" % (ex))
+
+Your logging will now take place within the file you specified in the properties file for log4j.appender.R.File.
+
+**Using log4j in Jython Scripts**
+
+Many may ask, why in the world would you be interested in logging information about your scripts? Most of the time a script is
+executed interactively via the command line. However, there are plenty of instances where it makes sense to have the system invoke
+a script for you. As you probably know, this technique is used quite often within an environment to run nightly tasks, or even daily
+tasks which are automatically invoked on a scheduled basis. For these cases, it can be extremely useful to log errors or information
+using log4j. Some may even wish to create a separate automated task to email these log files after the tasks complete.
+
+The overall implementation is the same as above, the most important thing to remember is that you must have the log4j.jar archive
+and properties file within your Jython path. Once this is ready to go you can use log4j in your script.
+
+::
+    
+    from org.apache.log4j import *
+    logger = Logger.getLogger("scriptname")
+    PropertyConfigurator.configure("C:\path_to_properties\log4j.properties")
+    logger.info("Test the logging")
+
+  
+Author: Josh Juneau
+URL:  http://wiki.python.org/jython/JythonMonthly/Articles/August2006/1
+
+Another log4j Example - Greg Moore
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This example require several things.
+
+- log4j on the classpath
+- log4j.properties (below) in the same directory as the example
+- example.xml (below) in the same directory as the example below
+- And of course Jython
+
+**log4j Example**
+
+This is a simple example to show how easy it is to use log4j in your own scripts. The source is well documented but
+if you have any questions or want to more info use your favorite search engine and type in log4j.
+
+::
+    
+    from org.apache.log4j import *
+     
+    class logtest:
+        def __init__(self):
+            log.info("start of Logtest")
+            log.debug('just before file read')
+            try:
+                log.warn('file read proceding to processing')
+                xmlStringData = open('example.xml').read()
+            except:
+                #yes, more could have been done here but this is just an example
+                log.error('file read FAILURE')
+            log.info('file read proceding to processing')
+            # since this is just an example processing would go here.
+            log.warn('its just an example, OK?')
+            pi = 3.141592681
+            msg = 'do you like?' + str(pi)
+            log.info(msg)
+            log.debug('lets try to parse the string')
+            if '[CDATA' in xmlStringData:
+            log.warn('No CDATA section.')
+            #say good bye and close the log file.
+            log.info('That all. The End. Good Bye')
+            log.shutdown()
+            
+            
+    if __name__ == '__main__':
+        # loggingTest is just a string that identifies this log.
+        log = Logger.getLogger("loggingTest")
+        #use the config data in the properties file
+        PropertyConfigurator.configure('log4j.properties')
+        log.info('This is the start of the log file')
+        logit = logtest()
+        print '\n\nif you change the log level in the properties'
+        print "file you'll get varing amouts of log data."
+
+**log4j.properties**
+
+This file is required by the code above. it need to be in the same directory as the example however It can be anywhere as log as
+you provide a full path to the file. It configures how log4j operates. If it is not found it defaults to a default logging level.
+Since this is for example purposes the file below is larger then really needed.
+
+::
+    
+    #define loging level and output
+    log4j.rootLogger=debug, stdout, LOGFILE
+    #log4j.rootLogger=info, LOGFILE
+    # this 2 lines tie the apache logging into log4j
+    #log4j.logger.org.apache.axis.SOAPPart=DEBUG
+    #log4j.logger.httpclient.wire.header=info
+    #log4j.logger.org.apache.commons.httpclient=DEBUG
+    
+    # where is the logging going. 
+    # This is for std out and defines the log output format
+    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+    log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} | %p | [%c] %m%n %t
+    
+    #log it to a file as well. and define a filename, max file size and number of backups
+    log4j.appender.LOGFILE=org.apache.log4j.RollingFileAppender
+    log4j.appender.LOGFILE.File=jythonTest.log
+    log4j.appender.LOGFILE.MaxFileSize=100KB
+    # Keep one backup file
+    log4j.appender.LOGFILE.MaxBackupIndex=1
+    
+    log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
+    # Pattern for logfile - only diff is that date is added
+    log4j.appender.LOGFILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} | %p | [%c] %m%n
+    # Other Examples: only time, loglog level, loggerName
+    #log4j.appender.LOGFILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss},%p,%c %m%n
+    #above plus filename, linenumber, Class Name, method name
+    #log4j.appender.LOGFILE.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss},%p,%c,%F,%L,%C{1},%M %m%n
+
+**Example xml file**
+
+This is here for completeness. Any text file could be use with the example above by changing the 'open' line
+in the above line.
+
+::
+    
+    <?xml version="1.0" encoding="utf-8"?>
+    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
+                       xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
+                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                       xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+      <SOAP-ENV:Body>
+        <GetXmlReport xmlns="http://localhost/Services/GetXmlReport">
+          <xmlrequest>
+            <Inquiry>
+              <Client>
+                <Type>W</Type>
+              </Client>
+              <Report>I</Report>
+              <Provider>
+                <ProviderID>TU</ProviderID>
+              </Provider>
+              <ClientInfo>
+                <Name>
+                  <First>Cathrine</First>
+                  <Middle />
+                  <Surname>Knight</Surname>
+                </Name>
+                <Account>34-5424-77</Account>
+                <DateOfBirth>10/12/1938</DateOfBirth>
+                <Address>
+                  <Line1>4780 Centerville</Line1>
+                  <CityStPostal>Saint Paul, MN 55127</CityStPostal>
+                </Address>
+              </ClientInfo>
+            </Inquiry>
+          </xmlrequest>
+        </GetXmlReport>
+      </SOAP-ENV:Body>
+    </SOAP-ENV:Envelope>
+
+Author:  Greg Moore
+URL:  http://wiki.python.org/jython/Log4jExample
+
+Working with Spreadsheets
+-------------------------
+
+Below are a few Apache Poi examples. These examples requires Apache Poi installed and on the classpath.
+
+**Create Spreadsheet**
+
+This is from the Jython mailing list and was posted September 2007
+
+This is based on Java code at http://officewriter.softartisans.com/OfficeWriter-306.aspx and converted to Jython by Alfonso Reyes
+
+::
+    
+    #jython poi example. from Jython mailing list
+     
+    from java.io import FileOutputStream
+    from java.util import Date
+    from java.lang import System, Math
+    from org.apache.poi.hssf.usermodel import *
+    from org.apache.poi.hssf.util import HSSFColor
+     
+    startTime = System.currentTimeMillis()
+     
+    wb = HSSFWorkbook()
+    fileOut = FileOutputStream("POIOut2.xls")
+     
+     
+    # Create 3 sheets
+    sheet1 = wb.createSheet("Sheet1")
+    sheet2 = wb.createSheet("Sheet2")
+    sheet3 = wb.createSheet("Sheet3")
+    sheet3 = wb.createSheet("Sheet4")
+     
+    # Create a header style
+    styleHeader = wb.createCellStyle()
+    fontHeader = wb.createFont()
+    fontHeader.setBoldweight(2)
+    fontHeader.setFontHeightInPoints(14)
+    fontHeader.setFontName("Arial")
+    styleHeader.setFont(fontHeader)
+     
+    # Create a style used for the first column
+    style0 = wb.createCellStyle()
+    font0 = wb.createFont()
+    font0.setColor(HSSFColor.RED.index)
+    style0.setFont(font0)
+     
+     
+    # Create the style used for dates.
+    styleDates = wb.createCellStyle()
+    styleDates.setDataFormat(HSSFDataFormat.getBuiltinFormat("m/d/yy h:mm"))
+     
+     
+    # create the headers
+    rowHeader = sheet1.createRow(1)
+    # String value
+    cell0 = rowHeader.createCell(0)
+    cell0.setCellStyle(styleHeader)
+    cell0.setCellValue("Name")
+     
+     
+    # numbers
+    for i in range(0, 8, 1):
+        cell = rowHeader.createCell((i + 1))
+        cell.setCellStyle(styleHeader)
+        cell.setCellValue("Data " + str( (i + 1)) )
+     
+    # Date
+    cell10 = rowHeader.createCell(9)
+    cell10.setCellValue("Date")
+    cell10.setCellStyle(styleHeader)
+     
+    for i in range(0, 100, 1):
+        # create a new row
+        row = sheet1.createRow(i + 2)
+        for j in range(0, 10, 1):
+            # create each cell
+            cell = row.createCell(j)
+            # Fill the first column with strings
+            if j == 0:
+                cell.setCellValue("Product " + str(i))
+                cell.setCellStyle(style0)  
+            # Fill the next 8 columns with numbers.
+            elif j < 9:
+                cell.setCellValue( (Math.random() * 100))
+            # Fill the last column with dates.
+            else:
+                cell.setCellValue(Date())
+                cell.setCellStyle(styleDates)
+    # Summary row
+    rowSummary = sheet1.createRow(102)
+    sumStyle = wb.createCellStyle()
+    sumFont = wb.createFont()
+    sumFont.setBoldweight( 5)
+    sumFont.setFontHeightInPoints(12)
+    sumStyle.setFont(sumFont)
+    sumStyle.setFillPattern(HSSFCellStyle.FINE_DOTS)
+    sumStyle.setFillForegroundColor(HSSFColor.GREEN.index)
+    cellSum0 = rowSummary.createCell( 0)
+    cellSum0.setCellValue("TOTALS:")
+    cellSum0.setCellStyle(sumStyle)
+     
+    # numbers
+    # B
+    cellB = rowSummary.createCell( 1)
+    cellB.setCellStyle(sumStyle)
+    cellB.setCellFormula("SUM(B3:B102)")
+
+**Read an Excel file**
+
+Posted to the Jython-users mailing list by Alfonso Reyes on October 14, 2007 This Jython code will open and read an existant
+Excel file you can download the file at http://www.nabble.com/file/p13199712/Book1.xls
+
+::
+        
+    """    read.py
+    Read an existant Excel file (Book1.xls) and show it on the screen
+    """
+    from org.apache.poi.hssf.usermodel import *
+    from java.io import FileInputStream
+        
+    file = "H:Book1.xls"
+    print file
+    fis = FileInputStream(file)
+    wb = HSSFWorkbook(fis)
+    sheet = wb.getSheetAt(0)
+        
+    # get No. of rows
+    rows = sheet.getPhysicalNumberOfRows()
+    print wb, sheet, rows
+    cols = 0 # No. of columns
+    tmp = 0
+    # This trick ensures that we get the data properly even if it
+    # does not start from first few rows
+    for i in range(0, 10,1):
+        row = sheet.getRow(i)
+        if(row != None):
+            tmp = sheet.getRow(i).getPhysicalNumberOfCells()
+            if tmp > cols:
+                cols = tmp
+    print cols
+        
+    for r in range(0, rows, 1):
+        row = sheet.getRow(r)
+        print r
+        if(row != None):
+            for c in range(0, cols, 1):
+                cell = row.getCell(c)
+                if cell != None:
+                    print cell
+    #wb.close()
+    fis.close()
+
+URL: http://wiki.python.org/jython/PoiExample
+
+Jython and XML - Greg Moore
+---------------------------
+
+Element tree
+
+Here is a simple example of using element tree with Jython. Element tree is useful for storing hierarchical
+data structures, such as simplified XML infosets, into memory and then save them to disk.
+More information on element tree is at http://effbot.org/zone/element-index.htm.
+
+Download element tree from http://effbot.org/downloads/
+
+::
+    
+    from  elementtree import ElementTree as ET
+    
+    root = ET.Element("html")
+    head = ET.SubElement(root, "head")
+    title = ET.SubElement(head, "title")
+    title.text = "Page Title"
+    body = ET.SubElement(root, "body")
+    body.set("bgcolor", "#ffffff")
+    body.text = "Hello, World!"
+    tree = ET.ElementTree(root)
+    tree.write("page.xhtml")
+    
+    import sys
+    tree.write(sys.stdout)
+
+which produces:
+
+::
+    
+    <html><head><title>Page Title</title></head><body bgcolor="#ffffff">Hello, World!</body></html>
+
+
+Author:  Greg Moore
+URL:  http://wiki.python.org/jython/XmlRelatedExamples
+
+Writing and Parsing RSS with ROME - Josh Juneau
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+**Introduction**
+
+RSS is an old technology now...it has been around for years. However, it is a technology which remains very useful for
+disseminating news and other information. The ROME project on java.net is helping to make parsing, generating, and publishing
+RSS and Atom feeds a breeze for any Java developer.
+
+Since I am particularly fond of translating Java to Jython code, I've taken simple examples from the Project ROME wiki and
+translated Java RSS reader and writer code into Jython. It is quite easy to do, and it only takes a few lines of code.
+
+Keep in mind that you would still need to build a front-end viewer for such an RSS reader, but I think you will get the idea
+of how easy it can be just to parse a feed with Project ROME and Jython.
+
+**Setting Up The CLASSPATH**
+
+In order to use this example, you must obtain the ROME and JDOM jar files and place them into your CLASSPATH.
+
+Windows:
+::
+    
+    set CLASSPATH=C:\Jython\Jython2.2\rome-0.9.jar;%CLASSPATH%
+    set CLASSPATH=C:\Jython\Jython2.2\jdom.jar;%CLASSPATH%
+    
+OSX:
+::
+    
+    export CLASSPATH=/path/to/rome-0.9.jar:/path/to/jdom.jar
+
+**Parsing Feeds**
+
+Parsing feeds is easy with ROME. Using ROME with Jython makes it even easier with the elegant Jython syntax. I am not a professional
+Python or Jython programmer, I am a Java programmer by profession, so my Jython interpretation may be even wordier than it should be.
+
+I took the FeedReader example from the ROME site and translated it into Jython below. You can copy and paste the following code
+into your own FeedReader.py module and run it to parse feeds. However, the output is unformatted and ugly...creating a good
+looking front end is up to you.
+
+FeedReader.py
+::
+    
+    ########################################
+    # File: FeedReader.py
+    #
+    # This module can be used to parse an RSS feed
+    ########################################
+    from java.net import URL
+    from java.io import InputStreamReader
+    from java.lang import Exception
+    from java.lang import Object
+    from com.sun.syndication.feed.synd import SyndFeed
+    from com.sun.syndication.io import SyndFeedInput
+    from com.sun.syndication.io import XmlReader
+    
+    class FeedReader(Object):
+       def __init__(self, url):
+          self.inUrl = url
+    
+       def readFeed(self):
+          ok = False
+          #####################################
+          # If url passed in is blank, then use a default
+          #####################################
+          if self.inUrl != '':
+             rssUrl = self.inUrl
+          else:
+             rssUrl = "http://www.dzone.com/feed/frontpage/java/rss.xml"
+          #####################################
+          # Parse feed located at given URL
+          #####################################
+          try:
+             feedUrl = URL(rssUrl)
+             input = SyndFeedInput()
+             feed = input.build(XmlReader(feedUrl))
+             ####################################
+             # Do something here with feed data
+             ####################################
+             print(feed)
+             ok = True
+          except Exception, e:
+             print 'An exception has occurred', e
+          if ok != True:
+             print 'An error has occurred in this reader'
+    
+    if __name__== "__main__":
+        reader = FeedReader('')
+        reader.readFeed()
+        print '****************Command Complete...RSS has been parsed*****************'
+
+**Creating Feeds**
+
+Similar to parsing a feed, writing a feed is also quite easy. When one creates a feed, it appears to be a bit more complex
+than parsing, but if you are familiar with XML and it's general structure then it should be relatively easy.
+
+Creating a feed is a three step process. You must first create the feed element itself, then you must add individual feed entries,
+and lastly you must publish the XML.
+
+FeedWriter.py
+::
+    
+    ########################################
+    # File: FeedReader.py
+    #
+    # This module can be used to create an RSS feed
+    ########################################
+    from com.sun.syndication.feed.synd import *
+    from com.sun.syndication.io import SyndFeedOutput
+    from java.io import FileWriter
+    from java.io import Writer
+    from java.text import DateFormat
+    from java.text import SimpleDateFormat
+    from java.util import ArrayList
+    from java.util import List
+    from java.lang import Object
+    
+    class FeedWriter(Object):
+        ####################################
+        # Set up the date format
+        ####################################
+        def __init__(self, type, name):
+           self.DATE_PARSER = SimpleDateFormat('yyyy-MM-dd')
+           self.feedType = type
+           self.fileName = name
+    
+        def writeFeed(self):
+           ok = False
+           try:
+            ################################
+            # Create the feed itself
+            ################################
+              feed = SyndFeedImpl()
+              feed.feedType =self.feedType
+              feed.title = 'Sample Feed (created with ROME)'
+              feed.link = 'http://rome.dev.java.net'
+              feed.description = 'This feed has been created using ROME and Jython'
+     
+             ###############################
+             # Add entries to the feed
+             ###############################
+              entries = ArrayList()
+              entry = SyndEntryImpl()
+              entry.title = 'ROME v1.0'
+              entry.link = 'http://wiki.java.net/bin/view/Javawsxml/Rome01'
+              entry.publishedDate = self.DATE_PARSER.parse("2004-06-08")
+              description = SyndContentImpl()
+              description.type = 'text/plain'
+              description.value = 'Initial Release of ROME'
+              entry.description = description
+              entries.add(entry)
+         
+              entry = SyndEntryImpl()
+              entry.title = 'ROME v2.0'
+              entry.link = 'http://wiki.java.net/bin/view/Javawsxml/Rome02'
+              entry.publishedDate = self.DATE_PARSER.parse("2004-06-16")
+              description = SyndContentImpl()
+              description.type = 'text/plain'
+              description.value = 'Bug fixes, minor API changes and some new features'
+              entry.description = description
+              entries.add(entry)
+    
+              entry = SyndEntryImpl()
+              entry.title = 'ROME v3.0'
+              entry.link = 'http://wiki.java.net/bin/view/Javawsxml/Rome03'
+              entry.publishedDate = self.DATE_PARSER.parse("2004-07-27")
+              description = SyndContentImpl()
+              description.type = 'text/plain'
+              description.value = '<p>More Bug fixes, mor API changes, some new features and some Unit testing</p>'
+              entry.description = description
+              entries.add(entry)
+    
+              feed.entries = entries
+             ###############################
+             # Publish the XML
+             ###############################
+              writer = FileWriter(self.fileName)
+              output = SyndFeedOutput()
+              output.output(feed,writer)
+              writer.close()
+     
+              print('The feed has been written to the file')
+    
+              ok = True
+      
+           except Exception, e:
+              print 'There has been an exception raised',e
+    
+           if ok == False:
+              print 'Feed Not Printed'
+    
+    if __name__== "__main__":
+        ####################################
+        # You must change his file location
+        # if not using Windows environment
+        ####################################
+        writer = FeedWriter('rss_2.0','C:\\TEMP\\testRss.xml')
+        writer.writeFeed()
+        print '****************Command Complete...RSS XML has been created*****************'
+
+After you have created the XML, you'll obviously need to place it on a web server somewhere so that others can use your feed.
+The FeedWriter.py module would probably be one module amongst many in an application for creating and managing RSS Feeds, but you get the idea.
+
+**Conclusion**
+
+As you can see, using the ROME library to work with RSS feeds is quite easy. Using the ROME library within a Jython application
+is straight forward. As you have now seen how easy it is to create and parse feeds, you can apply these technologies to a more
+complete RSS management application if you'd like. The world of RSS communication is at your fingertips!
+
+Author:  Josh Juneau
+URL:  http://wiki.python.org/jython/JythonMonthly/Articles/October2007/1
+
+Using the CLASSPATH - Steve Langer
+----------------------------------
+
+**Introduction**
+
+During Oct-Nov 2006 there was a thread in the jython-users group titled "adding JARs to sys.path". More accurately
+the objective there was to add JARs to the sys.path at runtime. Several people asked the question, "Why would you want to do that?"
+Well there are at least 2 good reasons. First, if you want to distribute a jython or Java package that includes non-standard
+Jars in it. Perhaps you want to make life easier for the target user and not demand that they know how to set environment variables.
+A second even more compelling reason is when there is no normal user account to provide environment variables.
+
+"What?", you ask. Well, in my case I came upon this problem in the following way. I am working on an open source IHE
+Image Archive Actor and needed a web interface. I'm using AJAX on the client side to route database calls through CGI to a
+jython-JDBC enabled API. Testing the jython-JDBC API from the command line worked fine, I had the PostGres driver in my CLASSPATH.
+But, when called via the web interface I got "zxJDBC error, postGres driver not found" errors. Why? Because APACHE was calling
+the API and APACHE is not a normal account with environment variables.
+
+**What to do?**
+
+The jython-users thread had many suggestions but none were found to work. For books, Chapter 11 of O'Reilly's "Jython Essentials"
+mentions under "System and File Modules" that "... to load a class at runtime one also needs an appropriate class loader."
+Of course, no mention is made beyond that. After a while, it occured to me that perhaps someone in the Java world had found a similar
+problem and had solved it. Then all that would be required is to translate that solution. And that is exactly what happened.
+
+**Method**
+
+For brevity we will not repeat the original Java code here. This is how I call the Jython class (note that one can use either
+addFile or addURL depending on whether the Jar is on a locally accesable file system or remote server).
+
+::
+    
+    import sys
+    from com.ziclix.python.sql import zxJDBC
+    
+    d,u,p,v = "jdbc:postgresql://localhost/img_arc2","postgres","","org.postgresql.Driver"
+    
+    try :
+        # if called from command line with .login CLASSPATH setup right,this works
+        db = zxJDBC.connect(d, u, p, v)
+    except:
+        # if called from Apache or account where the .login has not set CLASSPATH
+        # need to use run-time CLASSPATH Hacker
+        try :
+            jarLoad = classPathHacker()
+            a = jarLoad.addFile("/usr/share/java/postgresql-jdbc3.jar")
+            db = zxJDBC.connect(d, u, p, v)
+        except :
+            sys.exit ("still failed \n%s" % (sys.exc_info() ))
+
+And here is the class "classPathHacker" which is what the original author called his solution. In fact, you can simply Google
+on "classPathHacker" to find the Java solution.
+
+::
+    
+    class classPathHacker :
+    ##########################################################
+    # from http://forum.java.sun.com/thread.jspa?threadID=300557
+    #
+    # Author: SG Langer Jan 2007 translated the above Java to this
+    #       Jython class
+    # Purpose: Allow runtime additions of new Class/jars either from
+    #       local files or URL
+    ######################################################
+            import java.lang.reflect.Method
+            import java.io.File
+            import java.net.URL
+            import java.net.URLClassLoader
+            import jarray
+    
+            def addFile (self, s):
+            #############################################
+            # Purpose: If adding a file/jar call this first
+            #       with s = path_to_jar
+            #############################################
+                    module = "utils:classPathHacker: addFile"
+    
+                    # make a URL out of 's'
+                    f = self.java.io.File (s)
+                    u = f.toURL ()
+                    a = self.addURL (u)
+                    return a
+    
+            def addURL (self, u):
+            ##################################
+            # Purpose: Call this with u= URL for
+            #       the new Class/jar to be loaded
+            #################################
+                    module = "utils:classPathHacker: addURL"
+    
+                    parameters = self.jarray.array([self.java.net.URL], self.java.lang.Class)
+                    sysloader =  self.java.lang.ClassLoader.getSystemClassLoader()
+                    sysclass = self.java.net.URLClassLoader
+                    method = sysclass.getDeclaredMethod("addURL", parameters)
+                    a = method.setAccessible(1)
+                    jar_a = self.jarray.array([u], self.java.lang.Object)
+                    b = method.invoke(sysloader, jar_a)
+                    return u
+
+**Conclusions**
+
+That's it. Depressingly short for what it does, but then that's another proof of the power of this language. I hope you find
+this as powerful and useful as I have. It allows the possibility of distributing jython packages with all their file dependencies
+within the installation directory, freeing the user or developer from the need to alter user environment variables, which should
+lead to more programmer control and thus higher reliabliity.
+
+Author:  Steve Langer
+URL:  http://wiki.python.org/jython/JythonMonthly/Articles/January2007/3
+
+Ant
+---
+
+**The following Ant example works with Jython version 2.2.1 and earlier only due to the necessary jythonc usage.  Jythonc
+is no longer distributed with Jython as of 2.5.0.  This example could be re-written using object factories to work with
+current versions of Jython.**
+
+Writing Ant Tasks With Jython - Ed Takema
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Ant is the current tool of choice for java builds. This is so partially because it was the first java oriented build tool
+on the scene and because the reigning champion *Make* was getting long in the tooth and had fallen out of favour with the
+java crowd. But Java builds are getting more and more difficult and these days there is general dissatisfaction with ant1.
+Note particularly Bruce Eckel's Comments and Martin Fowler's further comments. The comments to Bruce Eckels's posting show
+similar fustrations. Fowler summarizes the issues like this:
+
+... Simple builds are easy to express as a series of tasks and dependencies. For such builds the facilities of ant/make
+work well. But more complex builds require conditional logic, and that requires more general programming language constructs
+- and that's where ant/make fall down.
+Ken Arnold's article The Sum of Ant led me to Jonathon Simon's article Scripting with Jython Instead of XML and got me thinking
+about extending ant with Jython. Simon's article presents a technique to drive Ant tasks, testing, etc all from Jython.
+What I am presenting is a technique to embed Jython scripts into Ant which is admittedly backwards from Simon's approach,
+but hopefully adds power and flexibility to ant builds.
+
+My experience working with large builds automated through ant is not dissimilar to what Fowler is referring to. Eventually, builds
+need to do either a lot of odd conditional logic in the xml file and ends up burying the logic in scripts, or in a large number of
+custom tasks written in java. This is particularly the case if your builds include non-java source that ant just isn't smart about
+building. In one case in particular, the set of custom tasks for the build is really its own system with maintenance and staff costs
+that are quite substantial. A large number of scripts can quickly become a problem for enterprise build systems as they are difficult
+to standardize and cross platform issues are always looming.
+
+Fortunately, all is not lost. Ant continues to evolve and version 1.6 was a significant step forward for large build systems. Mike Spille,
+in his article ANT's Finally a Real Build Tool, demonstrates that the new <import> tag now allows build managers to write truly modular
+and standardized build systems based on Ant! As Ant grows up, more and more of these issues will get resolved.
+
+One of the strengths that Make always had was the ability to easily call scripts and command utilities. This is something that is definitely
+possible with Ant script/exec tasks, but it feels very un-java. What we need is an elegant way to add adhoc behaviour to Ant builds
+... in a java-ish way.
+
+Writing Custom Ant Tasks
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+What I think can do the job is to take a more considered approach to using a scripting tool inside an ant build. Rather than just create
+a mishmash of scripts that are called from exec or script tasks, I suggest that we write custom ant build tasks in a high level scripting
+language...in this case, Jython.
+
+Writing custom ant tasks allows a build manager to leverage the huge number of already written tasks in their builds while writing
+what naturally belongs in a more flexible tool in custom ant tasks that can themselves then be reused, are as cross platform as java
+itself, and wholly integrated into Ant. Because Ant uses java introspection to determine the capabilities of custom tasks, Jython
+is the perfect tool to accomplish this. All we need to do is ensure that the methods that Ant expects are present in the Jython
+classes and Ant won't notice the difference.
+
+What we will implement is the perennial SimpleTask which is nothing more than a 'Hello World' for ant. It should be sufficient
+to demonstrate the key steps.
+
+**Setup Development Environment**
+
+To compile the jython source in this article you will need to add the ant.jar file to your classpath. This will make it
+available to Jython to extend which we'll do below. To do that define your classpath:
+
+::
+    
+    <DOS>
+    set CLASSPATH=c:\path\to\ant\lib\ant.jar
+
+::
+    
+    <UNIX>
+    export CLASSPATH=/path/to/ant/lib/ant.jar
+
+
+**SimpleTask Jython Class**
+
+The following is a very simple Ant task written in Jython(python). Save this as SimpleTask.py
+
+::
+    
+    from org.apache.tools.ant import Task
+    
+    class SimpleTask(Task): 
+    
+      message = ""
+    
+      def execute(self):
+         """@sig public void execute()"""
+         Task.log(self, "Message: " + self.message)
+    
+      def setMessage(this, aMessage):
+         """@sig public void setMessage(java.lang.String str)"""
+         this.message = aMessage
+
+
+This simple Jython class extends the ant Task superclass. For each of the properties we want to support for this task, we write a setXXXXX
+method where XXXXX corresponds to the property we are going to set in the ant build file. Ant creates an object from the class, calls the
+setXXXXX methods to setup the properties and then calls the execute method (actually, it calls the perform method on the Task superclass
+which calls the execute() method). So lets try it out.
+
+**Compiling Jython Code To A Jar**
+
+To build this into a jar file for use in Ant, do the following:
+
+::
+    
+    jythonc -a -c -d -j myTasks.jar SimpleTask.py
+
+This will produce a jar file myTasks.jar and include the jython core support classes in the jar. Copy this jar file into your
+ant installation's lib directory. In my case I copy it to c:\tools\ant\lib.
+
+**Build.XML file to use the Task**
+
+Once you've got that working, here is a very simple test ant build file to test your custom jython task.
+
+::
+    
+    <project name="ant jython demo" default="testit" basedir=".">
+    
+      <!-- Define the tasks we are building -->
+      <taskdef name="Simple" classname="SimpleTask" />
+    
+      <!-- Test Case starts here -->
+      <target name="testit"> 
+         <Simple message="Hello World!" />
+      </target>
+    
+    </project>
+
+**A Task Container Task**
+
+All right, that is a pretty simple task. What else can we do?  Well, the sky is the limit really. Here is an example
+of a task container. In this case, the task holds references to a set of other tasks (SimpleTask tasks in this case):
+
+::
+    
+    from org.apache.tools.ant import Task
+    from org.apache.tools.ant import TaskContainer
+    
+    class SimpleContainer(TaskContainer): 
+    
+      subtasks = []
+    
+      def execute(this):
+         """@sig public void execute()"""
+         
+         for task in this.subtasks:
+             task.perform()        
+         
+      def createSimpleTask(self):
+         """@sig public java.lang.Object createSimpleTask()"""   
+    
+         task = SimpleTask()
+         self.subtasks.append(task)
+         return task
+    
+    class SimpleTask(Task): 
+    
+      message = ""
+    
+      def execute(self):
+         """@sig public void execute()"""
+         Task.log(self, "Message: " + self.message)
+    
+      def setMessage(this, aMessage):
+         """@sig public void setMessage(java.lang.String str)"""
+         this.message = aMessage
+
+
+The SimpleContainer extends the TaskContainer java class. Its createSimpleTask method creates a SimpleTask object and returns
+it to Ant so its properties can be set. Then when all the tasks have been added to the container and their properties set, the execute
+method on the SimpleContainer class is called which in turn calls the perform method on each of the contained tasks. Note that the
+perform method is inherited from the Task superclass and it in turn calls the the execute method which we have overriden.
+
+**Build.XML file to use the TaskContainer**
+
+Here is a ant build file to test your custom jython task container. Note that you don't need to include a task definition
+for the contained SimpleTask unless you want to use it directly. The createSimpleTask factory method does it for you.
+
+::
+    
+    <project name="ant jython demo" default="testit" basedir=".">
+    
+      <!-- Define the tasks we are building -->
+      <taskdef name="Container" classname="SimpleContainer" />
+    
+      <!-- Test Case starts here -->
+      <target name="testit"> 
+    
+         <Container> 
+    
+             <SimpleTask message="hello" />
+             <SimpleTask message="there" />
+    
+         </Container>
+    
+      </target>
+    
+    </project>
+
+**Things To Look Out For**
+
+As I learned this technique I discovered that the magic doc strings are really necessary to force Jython to put the right methods
+in the generated java classes. For example:
+
+::
+    
+    """@sig public void execute()"""
+
+This is primarily due to Ant's introspection that looks for those specific methods and signatures. These docstrings are required
+or Ant won't recognize the classes as Ant tasks.
+
+I also learned that for Jython to extend a java class, it must specifically import the java classes using this syntax:
+
+::
+    
+    from org.apache.tools.ant import Task
+    from org.apache.tools.ant import TaskContainer
+    
+    class MyTask(Task):
+       ...
+    You can not use this syntax:
+    
+    import org.apache.tools.ant.Task
+    import org.apache.tools.ant.TaskContainer
+    
+     class MyTask(org.apache.tools.ant.Task):
+        ...
+
+
+This is because, for some reason, Jython doesn't figure out that MyTask is extending this java class and so doesn't generate the
+right Java wrapper classes. You will know that this working right when you see output like the following when you run the jythonc compiler:
+
+::
+    
+    processing SimpleTask
+    
+    Required packages:
+      org.apache.tools.ant
+    
+    Creating adapters:
+    
+    Creating .java files:
+      SimpleTask module
+        SimpleTask extends org.apache.tools.ant.Task <<<
+
+Summary
+~~~~~~~
+
+So there you have it. Here is a quick summary then of why this is a helpful technique.
+
+First, it is a lot faster to write ant tasks that integrate with third party tools and systems using a glue language and python/jython
+is excellent at that. That is really my prime motivation for trying out this technique.
+
+Secondly, Jython has the advantage over other scripting languages (which could be run using Ant's exec or script tasks) because
+it can be tightly integrated with Ant (i.e. use the same logging methods, same settings, etc). This makes it easier to build a standardized
+build environment.
+
+Finally, and related to the last point, Jython can be compiled to java byte code which runs like any java class file. This means you don't
+have to have jython installed to use the custom tasks and your custom task, if written well, can run on a wide variety of platforms.
+
+I think this is a reasonable way to add flexibility and additional integration points to Ant builds.
+
+Author: Ed Taekema
+URL:  http://www.fishandcross.com/articles/AntTasksWithJython.html
+
+Appendix C: Built-in Functions
+==============================
+
+Constructor Functions
+---------------------
+
+Constructor functions are used to create objects of a given type.
+
+.. note:: 
+
+  In Python, the type is a constructor function; there's no difference
+  at all in Python. So you can use the ``type`` function, which we
+  will discuss momentarily, to look up the type of an object, then make
+  instances of that same type.
+
+First we will look at the constructor functions, which are more
+typically used for conversion. This is because there is generally a
+convenient literal syntax available, or in the case of ``bool``, there
+are only two such constants, ``True`` and ``False``.
+
+.. function:: bool([x])
+
+   Convert a value to a Boolean, using the standard truth testing procedure.  If
+   *x* is false or omitted, this returns :const:`False`; otherwise it returns
+   :const:`True`. :class:`bool` is also a class, which is a subclass of
+   :class:`int`. Class :class:`bool` cannot be subclassed further.  Its only
+   instances are :const:`False` and :const:`True`.
+
+   If no argument is given, this function returns :const:`False`.
+
+.. function:: chr(i)
+
+   Return a string of one character whose ASCII code is the integer *i*.  For
+   example, ``chr(97)`` returns the string ``'a'``. This is the inverse of
+   :func:`ord`.  The argument must be in the range [0..255], inclusive;
+   :exc:`ValueError` will be raised if *i* is outside that range. See
+   also :func:`unichr`.
+
+.. function:: complex([real[, imag]])
+
+   Create a complex number with the value *real* + *imag*\*j or convert a string or
+   number to a complex number.  If the first parameter is a string, it will be
+   interpreted as a complex number and the function must be called without a second
+   parameter.  The second parameter can never be a string. Each argument may be any
+   numeric type (including complex). If *imag* is omitted, it defaults to zero and
+   the function serves as a numeric conversion function like :func:`int`,
+   :func:`long` and :func:`float`.  If both arguments are omitted, returns ``0j``.
+
+.. function:: dict([arg])
+   :noindex:
+
+   Create a new data dictionary, optionally with items taken from *arg*.
+   The dictionary type is described in :ref:`typesmapping`.
+
+   For other containers see the built in :class:`list`, :class:`set`, and
+   :class:`tuple` classes, and the :mod:`collections` module.
+
+Although there is a convenient literal for creating ``dict`` objects::
+
+  a_dict = { 'alpha' : 1, 'beta' : 2, 'gamma' : 3 }
+
+It can be more convenient to create them using the ``dict`` function::
+
+  a_dict = dict(alpha=1, beta=2, gamma=3)
+
+Of course in this latter case, the keys of the entries being created
+must be valid Python keywords.
+
+.. function:: float([x])
+
+   Convert a string or a number to floating point.  If the argument is a string, it
+   must contain a possibly signed decimal or floating point number, possibly
+   embedded in whitespace. The argument may also be [+|-]nan or [+|-]inf.
+   Otherwise, the argument may be a plain or long integer
+   or a floating point number, and a floating point number with the same value
+   (within Python's floating point precision) is returned.  If no argument is
+   given, returns ``0.0``.
+
+.. function:: frozenset([iterable])
+   :noindex:
+
+   Return a frozenset object, optionally with elements taken from *iterable*.
+   The frozenset type is described in :ref:`types-set`.
+
+   For other containers see the built in :class:`dict`, :class:`list`, and
+   :class:`tuple` classes, and the :mod:`collections` module.
+
+.. function:: int([x[, radix]])
+
+   Convert a string or number to a plain integer.  If the argument is a string,
+   it must contain a possibly signed decimal number representable as a Python
+   integer, possibly embedded in whitespace.  The *radix* parameter gives the
+   base for the conversion (which is 10 by default) and may be any integer in
+   the range [2, 36], or zero.  If *radix* is zero, the proper radix is
+   determined based on the contents of string; the interpretation is the same as
+   for integer literals.  (See :ref:`numbers`.)  If *radix* is specified and *x*
+   is not a string, :exc:`TypeError` is raised. Otherwise, the argument may be a
+   plain or long integer or a floating point number.  Conversion of floating
+   point numbers to integers truncates (towards zero).  If the argument is
+   outside the integer range a long object will be returned instead.  If no
+   arguments are given, returns ``0``.
+
+   The integer type is described in :ref:`typesnumeric`.
+
+.. function:: iter(o[, sentinel])
+
+   Return an :term:`iterator` object.  The first argument is interpreted very differently
+   depending on the presence of the second argument. Without a second argument, *o*
+   must be a collection object which supports the iteration protocol (the
+   :meth:`__iter__` method), or it must support the sequence protocol (the
+   :meth:`__getitem__` method with integer arguments starting at ``0``).  If it
+   does not support either of those protocols, :exc:`TypeError` is raised. If the
+   second argument, *sentinel*, is given, then *o* must be a callable object.  The
+   iterator created in this case will call *o* with no arguments for each call to
+   its :meth:`next` method; if the value returned is equal to *sentinel*,
+   :exc:`StopIteration` will be raised, otherwise the value will be returned.
+
+   .. versionadded:: 2.2
+
+.. function:: list([iterable])
+
+   Return a list whose items are the same and in the same order as *iterable*'s
+   items.  *iterable* may be either a sequence, a container that supports
+   iteration, or an iterator object.  If *iterable* is already a list, a copy is
+   made and returned, similar to ``iterable[:]``.  For instance, ``list('abc')``
+   returns ``['a', 'b', 'c']`` and ``list( (1, 2, 3) )`` returns ``[1, 2, 3]``.  If
+   no argument is given, returns a new empty list, ``[]``.
+
+   :class:`list` is a mutable sequence type, as documented in
+   :ref:`typesseq`. For other containers see the built in :class:`dict`,
+   :class:`set`, and :class:`tuple` classes, and the :mod:`collections` module.
+
+.. function:: object()
+
+   Return a new featureless object.  :class:`object` is a base for all new style
+   classes.  It has the methods that are common to all instances of new style
+   classes.
+
+   .. versionadded:: 2.2
+
+   .. versionchanged:: 2.3
+      This function does not accept any arguments. Formerly, it accepted arguments but
+      ignored them.
+
+.. function:: open(filename[, mode[, bufsize]])
+
+   Open a file, returning an object of the :class:`file` type described in
+   section :ref:`bltin-file-objects`.  If the file cannot be opened,
+   :exc:`IOError` is raised.  When opening a file, it's preferable to use
+   :func:`open` instead of invoking the :class:`file` constructor directly.
+
+   The first two arguments are the same as for ``stdio``'s :cfunc:`fopen`:
+   *filename* is the file name to be opened, and *mode* is a string indicating how
+   the file is to be opened.
+
+   The most commonly-used values of *mode* are ``'r'`` for reading, ``'w'`` for
+   writing (truncating the file if it already exists), and ``'a'`` for appending
+   (which on *some* Unix systems means that *all* writes append to the end of the
+   file regardless of the current seek position).  If *mode* is omitted, it
+   defaults to ``'r'``.  The default is to use text mode, which may convert
+   ``'\n'`` characters to a platform-specific representation on writing and back
+   on reading.  Thus, when opening a binary file, you should append ``'b'`` to
+   the *mode* value to open the file in binary mode, which will improve
+   portability.  (Appending ``'b'`` is useful even on systems that don't treat
+   binary and text files differently, where it serves as documentation.)  See below
+   for more possible values of *mode*.
+
+   .. index::
+      single: line-buffered I/O
+      single: unbuffered I/O
+      single: buffer size, I/O
+      single: I/O control; buffering
+
+   The optional *bufsize* argument specifies the file's desired buffer size: 0
+   means unbuffered, 1 means line buffered, any other positive value means use a
+   buffer of (approximately) that size.  A negative *bufsize* means to use the
+   system default, which is usually line buffered for tty devices and fully
+   buffered for other files.  If omitted, the system default is used. [#]_
+
+   Modes ``'r+'``, ``'w+'`` and ``'a+'`` open the file for updating (note that
+   ``'w+'`` truncates the file).  Append ``'b'`` to the mode to open the file in
+   binary mode, on systems that differentiate between binary and text files; on
+   systems that don't have this distinction, adding the ``'b'`` has no effect.
+
+   In addition to the standard :cfunc:`fopen` values *mode* may be ``'U'`` or
+   ``'rU'``.  Python is usually built with universal newline support; supplying
+   ``'U'`` opens the file as a text file, but lines may be terminated by any of the
+   following: the Unix end-of-line convention ``'\n'``,  the Macintosh convention
+   ``'\r'``, or the Windows convention ``'\r\n'``. All of these external
+   representations are seen as ``'\n'`` by the Python program. If Python is built
+   without universal newline support a *mode* with ``'U'`` is the same as normal
+   text mode.  Note that file objects so opened also have an attribute called
+   :attr:`newlines` which has a value of ``None`` (if no newlines have yet been
+   seen), ``'\n'``, ``'\r'``, ``'\r\n'``, or a tuple containing all the newline
+   types seen.
+
+   Python enforces that the mode, after stripping ``'U'``, begins with ``'r'``,
+   ``'w'`` or ``'a'``.
+
+   Python provides many file handling modules including
+   :mod:`fileinput`, :mod:`os`, :mod:`os.path`, :mod:`tempfile`, and
+   :mod:`shutil`.
+
+.. function:: ord(c)
+
+   Given a string of length one, return an integer representing the Unicode code
+   point of the character when the argument is a unicode object, or the value of
+   the byte when the argument is an 8-bit string. For example, ``ord('a')`` returns
+   the integer ``97``, ``ord(u'\u2020')`` returns ``8224``.  This is the inverse of
+   :func:`chr` for 8-bit strings and of :func:`unichr` for unicode objects.  If a
+   unicode argument is given and Python was built with UCS2 Unicode, then the
+   character's code point must be in the range [0..65535] inclusive; otherwise the
+   string length is two, and a :exc:`TypeError` will be raised.
+
+
+.. function:: range([start,] stop[, step])
+
+   This is a versatile function to create lists containing arithmetic progressions.
+   It is most often used in :keyword:`for` loops.  
+
+   However, we recommend the use of xrange instead.
+
+   The arguments must be plain
+   integers.  If the *step* argument is omitted, it defaults to ``1``.  If the
+   *start* argument is omitted, it defaults to ``0``.  The full form returns a list
+   of plain integers ``[start, start + step, start + 2 * step, ...]``.  If *step*
+   is positive, the last element is the largest ``start + i * step`` less than
+   *stop*; if *step* is negative, the last element is the smallest ``start + i *
+   step`` greater than *stop*.  *step* must not be zero (or else :exc:`ValueError`
+   is raised).  Example:
+
+      >>> range(10)
+      [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+      >>> range(1, 11)
+      [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
+      >>> range(0, 30, 5)
+      [0, 5, 10, 15, 20, 25]
+      >>> range(0, 10, 3)
+      [0, 3, 6, 9]
+      >>> range(0, -10, -1)
+      [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
+      >>> range(0)
+      []
+      >>> range(1, 0)
+      []
+
+.. function:: set([iterable])
+   :noindex:
+
+   Return a new set, optionally with elements are taken from *iterable*.
+   The set type is described in :ref:`types-set`.
+
+   For other containers see the built in :class:`dict`, :class:`list`, and
+   :class:`tuple` classes, and the :mod:`collections` module.
+
+   .. versionadded:: 2.4
+
+.. function:: str([object])
+
+   Return a string containing a nicely printable representation of an object.  For
+   strings, this returns the string itself.  The difference with ``repr(object)``
+   is that ``str(object)`` does not always attempt to return a string that is
+   acceptable to :func:`eval`; its goal is to return a printable string.  If no
+   argument is given, returns the empty string, ``''``.
+
+   For more information on strings see :ref:`typesseq` which describes sequence
+   functionality (strings are sequences), and also the string-specific methods
+   described in the :ref:`string-methods` section. To output formatted strings
+   use template strings or the ``%`` operator described in the
+   :ref:`string-formatting` section. In addition see the :ref:`stringservices`
+   section. See also :func:`unicode`.
+
+.. function:: tuple([iterable])
+
+   Return a tuple whose items are the same and in the same order as *iterable*'s
+   items.  *iterable* may be a sequence, a container that supports iteration, or an
+   iterator object. If *iterable* is already a tuple, it is returned unchanged.
+   For instance, ``tuple('abc')`` returns ``('a', 'b', 'c')`` and ``tuple([1, 2,
+   3])`` returns ``(1, 2, 3)``.  If no argument is given, returns a new empty
+   tuple, ``()``.
+
+   :class:`tuple` is an immutable sequence type, as documented in
+   :ref:`typesseq`. For other containers see the built in :class:`dict`,
+   :class:`list`, and :class:`set` classes, and the :mod:`collections` module.
+
+.. function:: type(name, bases, dict)
+   :noindex:
+
+   Return a new type object.  This is essentially a dynamic form of the
+   :keyword:`class` statement. The *name* string is the class name and becomes the
+   :attr:`__name__` attribute; the *bases* tuple itemizes the base classes and
+   becomes the :attr:`__bases__` attribute; and the *dict* dictionary is the
+   namespace containing definitions for class body and becomes the :attr:`__dict__`
+   attribute.  For example, the following two statements create identical
+   :class:`type` objects:
+
+      >>> class X(object):
+      ...     a = 1
+      ...
+      >>> X = type('X', (object,), dict(a=1))
+
+   .. versionadded:: 2.2
+
+.. function:: unichr(i)
+
+   Return the Unicode string of one character whose Unicode code is the integer
+   *i*.  For example, ``unichr(97)`` returns the string ``u'a'``.  This is the
+   inverse of :func:`ord` for Unicode strings.  The valid range for the argument
+   depends how Python was configured -- it may be either UCS2 [0..0xFFFF] or UCS4
+   [0..0x10FFFF]. :exc:`ValueError` is raised otherwise. For ASCII and 8-bit
+   strings see :func:`chr`.
+
+   .. versionadded:: 2.0
+
+
+.. function:: unicode([object[, encoding [, errors]]])
+
+   Return the Unicode string version of *object* using one of the following modes:
+
+   If *encoding* and/or *errors* are given, ``unicode()`` will decode the object
+   which can either be an 8-bit string or a character buffer using the codec for
+   *encoding*. The *encoding* parameter is a string giving the name of an encoding;
+   if the encoding is not known, :exc:`LookupError` is raised. Error handling is
+   done according to *errors*; this specifies the treatment of characters which are
+   invalid in the input encoding.  If *errors* is ``'strict'`` (the default), a
+   :exc:`ValueError` is raised on errors, while a value of ``'ignore'`` causes
+   errors to be silently ignored, and a value of ``'replace'`` causes the official
+   Unicode replacement character, ``U+FFFD``, to be used to replace input
+   characters which cannot be decoded.  See also the :mod:`codecs` module.
+
+   If no optional parameters are given, ``unicode()`` will mimic the behaviour of
+   ``str()`` except that it returns Unicode strings instead of 8-bit strings. More
+   precisely, if *object* is a Unicode string or subclass it will return that
+   Unicode string without any additional decoding applied.
+
+   For objects which provide a :meth:`__unicode__` method, it will call this method
+   without arguments to create a Unicode string. For all other objects, the 8-bit
+   string version or representation is requested and then converted to a Unicode
+   string using the codec for the default encoding in ``'strict'`` mode.
+
+   For more information on Unicode strings see :ref:`typesseq` which describes
+   sequence functionality (Unicode strings are sequences), and also the
+   string-specific methods described in the :ref:`string-methods` section. To
+   output formatted strings use template strings or the ``%`` operator described
+   in the :ref:`string-formatting` section. In addition see the
+   :ref:`stringservices` section. See also :func:`str`.
+
+
+Use as decorators:
+classmethod, staticmethod, property
+
+``slice`` is rarely used directly.
+
+super
+type - 3 arg form
+compile
+
+
+Math Builtin Functions
+----------------------
+
+Most math functions are defined in ``math`` (or ``cmath`` for complex math). These are functions that are builtin:
+
+abs, cmp, divmod, pow, round
+
+You may need to use named functions 
+
+
+Functions on Iterables
+----------------------
+
+The next group of builtin functions operate on iterables, which in
+Jython also includes all Java objects that implement the
+``java.util.Iterator`` interface.
+
+In particular,
+
+.. function:: enumerate(iterable)
+
+.. function:: zip([,iterable, ...])
+
+The ``zip`` function creates a list of tuples by stepping through each
+*iterable*. One very common idiom is to use ``zip`` to create a
+``dict`` where one iterable has the keys, and the other the
+values. This is often seen in working with CSV files (from a header
+row) or database cursors (from the ``description``
+attribute). However, you might want to consider using
+``collections.namedtuple`` instead::
+
+ 
+.. function:: sorted(iterable[, cmp[, key[, reverse]]])
+
+The ``sorted`` function returns a sorted list. Use the optional *key*
+argument to specify a key function to control how it's sorted. So for
+example, this will sort the list by the length of the elements in it::