Home | History | Annotate | Download | only in rt
      1 /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
      2  *
      3  * This program and the accompanying materials are made available under
      4  * the terms of the Common Public License v1.0 which accompanies this distribution,
      5  * and is available at http://www.eclipse.org/legal/cpl-v10.html
      6  *
      7  * $Id: ClassPathProcessorST.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $
      8  */
      9 package com.vladium.emma.rt;
     10 
     11 import java.io.File;
     12 import java.io.FileInputStream;
     13 import java.io.FileNotFoundException;
     14 import java.io.IOException;
     15 import java.io.InputStream;
     16 import java.util.Map;
     17 import java.util.jar.JarInputStream;
     18 import java.util.jar.Manifest;
     19 import java.util.zip.ZipEntry;
     20 import java.util.zip.ZipInputStream;
     21 
     22 import com.vladium.jcd.cls.ClassDef;
     23 import com.vladium.jcd.parser.ClassDefParser;
     24 import com.vladium.logging.Logger;
     25 import com.vladium.util.ByteArrayOStream;
     26 import com.vladium.util.Files;
     27 import com.vladium.util.IPathEnumerator;
     28 import com.vladium.util.asserts.$assert;
     29 import com.vladium.emma.IAppErrorCodes;
     30 import com.vladium.emma.EMMARuntimeException;
     31 import com.vladium.emma.data.IMetaData;
     32 import com.vladium.emma.filter.IInclExclFilter;
     33 import com.vladium.emma.instr.InstrVisitor;
     34 
     35 // ----------------------------------------------------------------------------
     36 /**
     37  * @author Vlad Roubtsov, (C) 2003
     38  */
     39 public
     40 final class ClassPathProcessorST implements IPathEnumerator.IPathHandler, IAppErrorCodes
     41 {
     42     // public: ................................................................
     43 
     44     public void run ()
     45     {
     46         long start = System.currentTimeMillis ();
     47 
     48         // construct instr path enumerator [throws on illegal input only]:
     49         final IPathEnumerator enumerator = IPathEnumerator.Factory.create (m_path, m_canonical, this);
     50 
     51         // allocate I/O buffers:
     52         m_readbuf = new byte [BUF_SIZE]; // don't reuse this across run() calls to reset it to the original size
     53         m_readpos = 0;
     54         m_baos = new ByteArrayOStream (BUF_SIZE); // don't reuse this across run() calls to reset it to the original size
     55 
     56         if (m_log.atINFO ())
     57         {
     58             m_log.info ("processing classpath ...");
     59         }
     60 
     61         // actual work is driven by the path enumerator:
     62         try
     63         {
     64             enumerator.enumerate ();
     65         }
     66         catch (IOException ioe)
     67         {
     68             throw new EMMARuntimeException (INSTR_IO_FAILURE, ioe);
     69         }
     70 
     71         if (m_log.atINFO ())
     72         {
     73             final long end = System.currentTimeMillis ();
     74 
     75             m_log.info ("[" + m_classCount + " class(es) processed in " + (end - start) + " ms]");
     76         }
     77     }
     78 
     79     // IPathEnumerator.IPathHandler:
     80 
     81     public void handleArchiveStart (final File parentDir, final File archive, final Manifest manifest)
     82     {
     83         m_archiveFile = Files.newFile (parentDir, archive.getPath ());
     84     }
     85 
     86     public void handleArchiveEntry (final JarInputStream in, final ZipEntry entry)
     87     {
     88         if (m_log.atTRACE2 ()) m_log.trace2 ("handleArchiveEntry", "[" + entry.getName () + "]");
     89 
     90         final String name = entry.getName ();
     91         final String lcName = name.toLowerCase ();
     92 
     93         if (lcName.endsWith (".class"))
     94         {
     95             final String className = name.substring (0, name.length () - 6).replace ('/', '.');
     96 
     97             if ((m_coverageFilter == null) || m_coverageFilter.included (className))
     98             {
     99                 String srcURL = null;
    100                 InputStream clsin = null;
    101                 try
    102                 {
    103                     readZipEntry (in, entry);
    104 
    105                     srcURL = "jar:".concat (m_archiveFile.toURL ().toExternalForm ()).concat ("!/").concat (name);
    106                 }
    107                 catch (FileNotFoundException fnfe)
    108                 {
    109                     // ignore: this should never happen
    110                     if ($assert.ENABLED)
    111                     {
    112                         fnfe.printStackTrace (System.out);
    113                     }
    114                 }
    115                 catch (IOException ioe)
    116                 {
    117                     // TODO: error code
    118                     throw new EMMARuntimeException (ioe);
    119                 }
    120                 finally
    121                 {
    122                     if (clsin != null)
    123                         try
    124                         {
    125                             clsin.close ();
    126                             clsin = null;
    127                         }
    128                         catch (Exception e)
    129                         {
    130                             // TODO: error code
    131                             throw new EMMARuntimeException (e);
    132                         }
    133                 }
    134 
    135                 // [original class def read into m_readbuf]
    136 
    137                 try
    138                 {
    139                     ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
    140                     if (! clsDef.isInterface ()) ++ m_classCount;
    141 
    142                     m_visitor.process (clsDef, false, false, true, m_instrResult); // get metadata only
    143                     clsDef = null;
    144 
    145                     boolean cacheClassDef = true;
    146 
    147                     if (m_instrResult.m_descriptor != null)
    148                     {
    149                         // do not overwrite existing descriptors to support "first
    150                         // in the classpath wins" semantics:
    151 
    152                         if (! m_mdata.add (m_instrResult.m_descriptor, false))
    153                            cacheClassDef = false;
    154                     }
    155 
    156                     if (cacheClassDef && (m_cache != null))
    157                     {
    158                         final byte [] bytes = new byte [m_readpos];
    159                         System.arraycopy (m_readbuf, 0, bytes, 0, m_readpos);
    160 
    161                         m_cache.put (className, new ClassPathCacheEntry (bytes, srcURL));
    162                     }
    163                 }
    164                 catch (IOException ioe)
    165                 {
    166                     // TODO: error code
    167                     throw new EMMARuntimeException (ioe);
    168                 }
    169             }
    170         }
    171     }
    172 
    173     public void handleArchiveEnd (final File parentDir, final File archive)
    174     {
    175         m_archiveFile = null;
    176     }
    177 
    178 
    179     public void handleDirStart (final File pathDir, final File dir)
    180     {
    181         // do nothing
    182     }
    183 
    184     public void handleFile (final File pathDir, final File file)
    185     {
    186         if (m_log.atTRACE2 ()) m_log.trace2 ("handleFile", "[" + pathDir + "] [" + file + "]");
    187 
    188         final String name = file.getPath ();
    189         final String lcName = name.toLowerCase ();
    190 
    191         if (lcName.endsWith (".class"))
    192         {
    193             final String className = name.substring (0, name.length () - 6).replace (File.separatorChar, '.');
    194 
    195             if ((m_coverageFilter == null) || m_coverageFilter.included (className))
    196             {
    197                 String srcURL = null;
    198                 InputStream clsin = null;
    199                 try
    200                 {
    201                     final File inFile = Files.newFile (pathDir, file.getPath ());
    202                     readFile (inFile);
    203 
    204                     srcURL = inFile.toURL ().toExternalForm ();
    205                 }
    206                 catch (FileNotFoundException fnfe)
    207                 {
    208                     // ignore: this should never happen
    209                     if ($assert.ENABLED)
    210                     {
    211                         fnfe.printStackTrace (System.out);
    212                     }
    213                 }
    214                 catch (IOException ioe)
    215                 {
    216                     // TODO: error code
    217                     throw new EMMARuntimeException (ioe);
    218                 }
    219                 finally
    220                 {
    221                     if (clsin != null)
    222                         try
    223                         {
    224                             clsin.close ();
    225                             clsin = null;
    226                         }
    227                         catch (Exception e)
    228                         {
    229                             // TODO: error code
    230                             throw new EMMARuntimeException (e);
    231                         }
    232                 }
    233 
    234                 // [original class def read into m_readbuf]
    235 
    236                 try
    237                 {
    238                     ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
    239                     if (! clsDef.isInterface ()) ++ m_classCount;
    240 
    241                     m_visitor.process (clsDef, false, false, true, m_instrResult); // get metadata only
    242                     clsDef = null;
    243 
    244 
    245                     boolean cacheClassDef = true;
    246 
    247                     if (m_instrResult.m_descriptor != null)
    248                     {
    249                         // do not overwrite existing descriptors to support "first
    250                         // in the classpath wins" semantics:
    251 
    252                         if (! m_mdata.add (m_instrResult.m_descriptor, false))
    253                            cacheClassDef = false;
    254                     }
    255 
    256                     if (cacheClassDef && (m_cache != null))
    257                     {
    258                         final byte [] bytes = new byte [m_readpos];
    259                         System.arraycopy (m_readbuf, 0, bytes, 0, m_readpos);
    260 
    261                         m_cache.put (className, new ClassPathCacheEntry (bytes, srcURL));
    262                     }
    263                 }
    264                 catch (IOException ioe)
    265                 {
    266                     // TODO: error code
    267                     throw new EMMARuntimeException (ioe);
    268                 }
    269             }
    270         }
    271     }
    272 
    273     public void handleDirEnd (final File pathDir, final File dir)
    274     {
    275         // do nothing
    276     }
    277 
    278     // protected: .............................................................
    279 
    280     // package: ...............................................................
    281 
    282 
    283     /*
    284      * null 'cache' indicates to only populate the metadata and not bother with
    285      * caching instrumented class defs
    286      */
    287     ClassPathProcessorST (final File [] path, final boolean canonical,
    288                           final IMetaData mdata, final IInclExclFilter filter,
    289                           final Map cache)
    290     {
    291         if (path == null) throw new IllegalArgumentException ("null input: path");
    292         if (mdata == null) throw new IllegalArgumentException ("null input: mdata");
    293 
    294         m_path = path;
    295         m_canonical = canonical;
    296         m_mdata = mdata;
    297         m_coverageFilter = filter;
    298         m_cache = cache; // can be null
    299         m_visitor = new InstrVisitor (mdata.getOptions ());
    300         m_instrResult = new InstrVisitor.InstrResult ();
    301 
    302         m_log = Logger.getLogger ();
    303     }
    304 
    305     // private: ...............................................................
    306 
    307 
    308     /*
    309      * Reads into m_readbuf (m_readpos is updated correspondingly)
    310      */
    311     private void readFile (final File file)
    312         throws IOException
    313     {
    314         final int length = (int) file.length ();
    315 
    316         ensureReadCapacity (length);
    317 
    318         InputStream in = null;
    319         try
    320         {
    321             in = new FileInputStream (file);
    322 
    323             int totalread = 0;
    324             for (int read;
    325                  (totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
    326                  totalread += read);
    327             m_readpos = totalread;
    328         }
    329         finally
    330         {
    331             if (in != null) try { in.close (); } catch (Exception ignore) {}
    332         }
    333     }
    334 
    335     /*
    336      * Reads into m_readbuf (m_readpos is updated correspondingly)
    337      */
    338     private void readZipEntry (final ZipInputStream in, final ZipEntry entry)
    339         throws IOException
    340     {
    341         final int length = (int) entry.getSize (); // can be -1 if unknown
    342 
    343         if (length >= 0)
    344         {
    345             ensureReadCapacity (length);
    346 
    347             int totalread = 0;
    348             for (int read;
    349                  (totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
    350                  totalread += read);
    351             m_readpos = totalread;
    352         }
    353         else
    354         {
    355             ensureReadCapacity (BUF_SIZE);
    356 
    357             m_baos.reset ();
    358             for (int read; (read = in.read (m_readbuf)) >= 0; m_baos.write (m_readbuf, 0, read));
    359 
    360             m_readbuf = m_baos.copyByteArray ();
    361             m_readpos = m_readbuf.length;
    362         }
    363     }
    364 
    365     private void ensureReadCapacity (final int capacity)
    366     {
    367         if (m_readbuf.length < capacity)
    368         {
    369             final int readbuflen = m_readbuf.length;
    370             m_readbuf = null;
    371             m_readbuf = new byte [Math.max (readbuflen << 1, capacity)];
    372         }
    373     }
    374 
    375 
    376     private final File [] m_path; // never null
    377     private final boolean m_canonical;
    378     private final IMetaData m_mdata; // never null
    379     private final IInclExclFilter m_coverageFilter; // can be null
    380     private final InstrVisitor m_visitor;
    381     private final InstrVisitor.InstrResult m_instrResult;
    382     private final Map /* classJavaName:String -> ClassPathCacheEntry */ m_cache; // can be null
    383 
    384     private final Logger m_log; // this class is instantiated and used on a single thread
    385 
    386     private int m_classCount;
    387 
    388     private byte [] m_readbuf;
    389     private int m_readpos;
    390     private ByteArrayOStream m_baos; // TODO: code to guard this from becoming too large
    391 
    392     private File m_archiveFile;
    393 
    394     private static final int BUF_SIZE = 32 * 1024;
    395 
    396 } // end of class
    397 // ----------------------------------------------------------------------------