/*
* 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 [EMAIL PROTECTED]
*
* 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:[EMAIL PROTECTED]">[EMAIL PROTECTED]</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:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public void setHostname( String hostname ) {
this.hostname = hostname;
}
/**
*
* Get the value of <code>hostname</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public String getHostname() {
return this.hostname;
}
/**
*
* Get the value of <code>port</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public String getPort() {
return this.port;
}
/**
*
* Set the value of <code>port</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public void setPort( String port ) {
this.port = port;
}
/**
*
* Get the value of <code>debug</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public boolean getDebug() {
return this.debug;
}
/**
*
* Set the value of <code>debug</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public void setDebug( boolean debug ) {
this.debug = debug;
}
/**
*
* Get the value of <code>verbose</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public boolean getVerbose() {
return this.verbose;
}
/**
*
* Set the value of <code>verbose</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public void setVerbose( boolean verbose ) {
this.verbose = verbose;
}
/**
*
* Get the value of <code>continue</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public boolean getContinue() {
return this._continue;
}
/**
*
* Set the value of <code>continue</code>.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Kevin A. Burton</a>
*/
public void setContinue( boolean _continue ) {
this._continue = _continue;
}
/**
* Reload given classes.
*
* @author <a href="mailto:[EMAIL PROTECTED]">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:[EMAIL PROTECTED]">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:[EMAIL PROTECTED]">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 ( [EMAIL PROTECTED], [EMAIL PROTECTED], [EMAIL PROTECTED] )
Location - San Francisco, CA, Cell - 415.595.9965
Jabber - [EMAIL PROTECTED], 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-----
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>