ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bur...@openprivacy.org (Kevin A. Burton)
Subject [CONTRIB] Redefine task.
Date Mon, 04 Feb 2002 06:06:29 GMT
/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Ant", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

package org.apache.tools.ant.taskdefs.optional;

import org.apache.tools.ant.*;

import java.io.*;
import java.util.*;

import com.sun.jdi.*;
import com.sun.jdi.connect.*;

import org.apache.tools.ant.taskdefs.*;
import org.apache.tools.ant.types.*;

/*

TODO:

- compute stats at the end

    - total files
    - (DONE) redefined classes
    - (DONE) failed redefined classes

- modularization of code.  Right now execute and getClassMap methods are too
  long,

*/

/**
 * <p> Task to support the ability to redefine classes on a target VM.  This *
 * requires JPDA 1.4 which is available in JDK 1.4.
 *
 * <p> This task supports the following attributes:
 * 
 * <li>hostname - host name of server running a JPDA 
 * <li>port - port on this host name
 * <li>continue - continue loading classes even if some fail
 * <li>verbose - output verbose information
 * <li>debug - more information
 * 
 * @author Kevin Burton <a href="mailto:burton@openprivacy.org">burton@openprivacy.org</a>
 */
public class Redefine extends MatchingTask {
    
    public static final String CONNECTOR_NAME = "dt_socket";
    
    private boolean verbose = false;
    
    private boolean debug = false;

    private String port = null;
    
    private String hostname = null;

    private List filesets = new ArrayList();

    private boolean _continue = false;

    /**
     * List of all classnames that failed to be defined.
     */
    private List succeeded = new ArrayList();
    
    /**
     * List of all classnames that failed to be defined.
     */
    private List failed = new ArrayList();
    
    /**
     * Adds a set of files (nested fileset attribute).
     */
    public void addFileset( FileSet set ) {
        filesets.add(set);
    }
    
    /**
     * 
     * Set the value of <code>hostname</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void setHostname( String hostname ) { 
        
        this.hostname = hostname;
        
    }

    /**
     * 
     * Get the value of <code>hostname</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public String getHostname() { 
        
        return this.hostname;
        
    }

    /**
     * 
     * Get the value of <code>port</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public String getPort() { 
        
        return this.port;
        
    }

    /**
     * 
     * Set the value of <code>port</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void setPort( String port ) { 
        
        this.port = port;
        
    }

    /**
     * 
     * Get the value of <code>debug</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public boolean getDebug() { 
        
        return this.debug;
        
    }

    /**
     * 
     * Set the value of <code>debug</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void setDebug( boolean debug ) { 
        
        this.debug = debug;
        
    }

    /**
     * 
     * Get the value of <code>verbose</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public boolean getVerbose() { 
        
        return this.verbose;
        
    }

    /**
     * 
     * Set the value of <code>verbose</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void setVerbose( boolean verbose ) { 
        
        this.verbose = verbose;
        
    }

    /**
     * 
     * Get the value of <code>continue</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public boolean getContinue() { 
        
        return this._continue;
        
    }

    /**
     * 
     * Set the value of <code>continue</code>.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void setContinue( boolean _continue ) { 
        
        this._continue = _continue;
        
    }

    /**
     * Reload given classes.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void execute() throws BuildException {

        try {
            
            VirtualMachineManager vmm = Bootstrap.virtualMachineManager();
            
            AttachingConnector conn = null;
            
            //getting the dt_socket connector
            Iterator i = vmm.attachingConnectors().iterator();
            
            while ( i.hasNext() ) {
                
                Connector current = (Connector)i.next();
                
                if ( current.transport().name().equals( CONNECTOR_NAME ) ) {
                    
                    conn = (AttachingConnector)current;
                    
                } 
                
            } 
            
            if ( conn == null ) 
                throw new Exception( "Could not find transport -> " + CONNECTOR_NAME );
            
            //attach to the remote vm.
            
            Map arguments = conn.defaultArguments();
            
            //set hostname and port arguments.
            
            ((Connector.Argument)arguments.get( "hostname" )).setValue( hostname );
            ((Connector.Argument)arguments.get( "port" )).setValue( port );
            
            System.out.println( "Using connector arguments:" );
            
            Iterator i_args = arguments.keySet().iterator();
            
            while ( i_args.hasNext() ) {
                
                String name = (String)i_args.next();
                
                String value = ((Connector.Argument)arguments.get( name )).value();
                
                System.out.println( "    " + name + " -- " + value );
                
            } 

            VirtualMachine vm = conn.attach( arguments );

            //display VM status

            if ( debug || verbose ) {

                System.out.println( "Target VM canAddMethod : " + vm.canAddMethod() );

                System.out.println( "Target VM canUnrestrictedlyRedefineClasses : " + vm.canUnrestrictedlyRedefineClasses()
);

                System.out.println( vm.description() );
                
            } 

            if ( vm.canRedefineClasses()  == false)
                throw new BuildException( "The target VM can not redefine classes." );

            System.out.println( "Suspending target VM..." );
            
            //suspend the VM
            vm.suspend();

            try { 
                
                Map classmap = this.getClassMap( vm );

                if ( classmap.size() > 0 ) {

                    System.out.println( "Redefining " + classmap.size() + " files." );

                    //if we are in debug... define one class at a time..

                    //define one class at a time... this way we can figure out
                    //where any problems like if a class can't verify

                    Iterator iterator = classmap.keySet().iterator();

                    while ( iterator.hasNext() ) {

                        ReferenceType rt = (ReferenceType)iterator.next();

                        Map currentmap = new HashMap();
                        currentmap.put( rt, classmap.get( rt ) );
                        
                        try { 
                            
                            vm.redefineClasses( currentmap );

                            succeeded.add( rt.name() );
                            
                        } catch ( Throwable t ) {

                            if ( _continue ) {

                                failed.add( rt.name() );
                                
                            } else {

                                throw new Exception( "Couldn't redefine " + rt.name() + "
- " + t.getMessage() );

                            }
                            
                        }
                        
                    } 

                } 

            } catch ( Throwable t ) {

                //catch all issues with classloading...
                
                if ( debug ) 
                    t.printStackTrace();
                
                throw new BuildException( t );
                
            } finally {

                //resume and dispose the VM even if we have any issuss.
                System.out.println( "Resuming target VM..." );

                //resume the VM
                vm.resume();

                //necessary to cleanly close JPDA connection.
                vm.dispose();

                this.showStatus();

            }

        } catch ( Throwable t ) {

            //should catch all JPDA issues
            
            if ( debug ) 
                t.printStackTrace();

            throw new BuildException( t );
            
        }

    }

    /**
     * Get a mapping of classname -> filename for all files.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public Map getClassMap( VirtualMachine vm ) throws Exception {

        HashMap classmap = new  HashMap();
        
        FileSet fs = null;

        Iterator fsi = this.filesets.iterator();

        while ( fsi.hasNext() ) {

            fs = (FileSet)fsi.next();

            //work out these classes.

            DirectoryScanner ds = fs.getDirectoryScanner( this.getProject() );

            String[] files = ds.getIncludedFiles();

            File dir = fs.getDir( this.getProject() );

            String dirname = dir.getAbsolutePath();
            
            if ( debug || verbose )
                System.out.println( "Found " + files.length + " within " + dirname );

            String classname = "";
            
            for (int j=0; j < files.length; j++) {

                File f = new File( dir, files[j] );
                String absolutePath = f.getAbsolutePath();

                //compute the classname from the dir name

                classname = absolutePath.substring( dirname.length() + 1, absolutePath.length()
);

                //trim the .class extension

                classname = classname.substring( 0, classname.length() - ".class".length()
);

                //replace the '/' with a '.'
                StringBuffer buff = new StringBuffer( classname );
                int index;
                while ( ( index = buff.toString().indexOf( '/' ) ) != -1 ) {

                    buff.setCharAt( index, '.' );
                    
                } 

                classname = buff.toString();

                //ok... now we need to get a ReferenceType for this classname.

                List cresults = vm.classesByName( classname );

                ReferenceType rt = null;
                
                if ( cresults.size() == 0 ) {
                    
                    if ( debug )
                        System.out.println( "WARNING: " + classname + " hasn't been loaded.
 Can't redefine." );

                } else if ( cresults.size() > 1 ) {

                    throw new Exception( "Too many classes loaded.  We don't handle this yet."
);
                    
                } else {

                    rt = (ReferenceType)cresults.get( 0 );

                    if ( verbose || debug )
                        System.out.println( "Redefining class -> " + classname + "( " +
f.length() + " bytes )" );

                    //NOTE: technically File.length() is a double but we don't need
                    //class files that big.
                
                    byte[] bytes = new byte[ (int)f.length() ];

                    FileInputStream fis = new FileInputStream( f );
                    
                    int read_count = fis.read( bytes, 0, (int)f.length() );

                    fis.close();

                    //make sure that we read the correct number of bytes.

                    if ( read_count != f.length() ) 
                        throw new Exception( "Didn't read required byte count: " + read_count
);

                    classmap.put( rt, bytes );

                }
                
            }
                
        } 

        return classmap;
        
    }

    /**
     * Show the number of classes we loaded and failed to load.
     *
     * @author <a href="mailto:burton@openprivacy.org">Kevin A. Burton</a>
     */
    public void showStatus() {

        System.out.println( "Loaded " + succeeded.size() + " classes" );
        System.out.println( "Failed to load  " + failed.size() + " classes" );
        
    }
    
}




- -- 
Kevin A. Burton ( burton@apache.org, burton@openprivacy.org, burtonator@acm.org )
             Location - San Francisco, CA, Cell - 415.595.9965
        Jabber - burtonator@jabber.org,  Web - http://relativity.yi.org/

I'm intercontinental when I eat french toast...
    - Beck 

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: Get my public key at: http://relativity.yi.org/pgpkey.txt

iD8DBQE8XiTlAwM6xb2dfE0RArC4AKCByG/dIL7ShGyrieZg0vGafNgK+gCgnlw7
+/VRaFCGKtZYh1PIPGFmk7k=
=dXOm
-----END PGP SIGNATURE-----


Mime
View raw message