2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.debugger.jpda.ant;
45
import java.io.FileNotFoundException;
46
import java.io.IOException;
47
import java.io.InputStream;
48
import java.util.ArrayList;
49
import java.util.HashMap;
50
import java.util.Iterator;
51
import java.util.List;
53
import java.util.logging.Level;
54
import java.util.logging.Logger;
56
import org.apache.tools.ant.BuildException;
57
import org.apache.tools.ant.DirectoryScanner;
58
import org.apache.tools.ant.Project;
59
import org.apache.tools.ant.Task;
60
import org.apache.tools.ant.types.FileSet;
61
import org.apache.tools.ant.util.FileUtils;
63
import org.openide.filesystems.FileObject;
64
import org.openide.filesystems.FileStateInvalidException;
65
import org.openide.filesystems.FileUtil;
67
import org.netbeans.api.debugger.DebuggerEngine;
68
import org.netbeans.api.debugger.DebuggerManager;
69
import org.netbeans.api.debugger.jpda.JPDADebugger;
70
import org.netbeans.api.java.classpath.ClassPath;
71
import org.netbeans.api.java.queries.SourceForBinaryQuery;
72
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
73
import org.netbeans.spi.debugger.jpda.EditorContext;
77
* Ant task to reload classes in VM for running debugging session.
79
* @author David Konecny
81
public class JPDAReload extends Task {
83
private static final Logger logger = Logger.getLogger("org.netbeans.modules.debugger.jpda.ant"); // NOI18N
85
private List filesets = new ArrayList ();
88
* FileSet with .class files to reload. The base dir of the fileset is expected
89
* to be classpath root for these classes.
91
public void addFileset (FileSet fileset) {
92
filesets.add (fileset);
95
public void execute() throws BuildException {
96
if (logger.isLoggable(Level.FINE)) {
97
logger.fine("JPDAReload.execute(), filesets = "+filesets);
99
if (filesets.size() == 0) {
100
throw new BuildException ("A nested fileset with class to refresh in VM must be specified.");
103
// check debugger state
104
DebuggerEngine debuggerEngine = DebuggerManager.getDebuggerManager ().
106
if (debuggerEngine == null) {
107
throw new BuildException ("No debugging sessions was found.");
109
JPDADebugger debugger = (JPDADebugger) debuggerEngine.lookupFirst
110
(null, JPDADebugger.class);
111
if (debugger == null) {
112
throw new BuildException("Current debugger is not JPDA one.");
114
if (!debugger.canFixClasses ()) {
115
throw new BuildException("The debugger does not support Fix action.");
117
if (debugger.getState () == JPDADebugger.STATE_DISCONNECTED) {
118
throw new BuildException ("The debugger is not running");
121
System.out.println ("Classes to be reloaded:");
123
FileUtils fileUtils = FileUtils.newFileUtils ();
124
Map map = new HashMap ();
125
EditorContext editorContext = (EditorContext) DebuggerManager.
126
getDebuggerManager ().lookupFirst (null, EditorContext.class);
128
Iterator it = filesets.iterator ();
129
while (it.hasNext ()) {
130
FileSet fs = (FileSet) it.next ();
131
DirectoryScanner ds = fs.getDirectoryScanner (getProject ());
132
String fileNames[] = ds.getIncludedFiles ();
133
File baseDir = fs.getDir (getProject ());
134
int i, k = fileNames.length;
135
for (i = 0; i < k; i++) {
136
File f = fileUtils.resolveFile (baseDir, fileNames [i]);
138
FileObject fo = FileUtil.toFileObject(f);
141
String url = classToSourceURL (fo);
143
editorContext.updateTimeStamp (debugger, url);
144
InputStream is = fo.getInputStream ();
145
long fileSize = fo.getSize ();
146
byte[] bytecode = new byte [(int) fileSize];
148
// remove ".class" from and use dots for for separator
149
String className = fileNames [i].substring (
151
fileNames [i].length () - 6
152
).replace (File.separatorChar, '.');
157
System.out.println (" " + className);
158
} catch (IOException ex) {
159
ex.printStackTrace ();
165
if (logger.isLoggable(Level.FINE)) {
166
logger.fine("Reloaded classes: "+map.keySet());
168
if (map.size () == 0) {
169
System.out.println (" No class to reload");
174
debugger.fixClasses (map);
175
} catch (UnsupportedOperationException uoex) {
176
error = "The virtual machine does not support this operation: "+uoex.getLocalizedMessage();
177
} catch (NoClassDefFoundError ncdfex) {
178
error = "The bytes don't correspond to the class type (the names don't match): "+ncdfex.getLocalizedMessage();
179
} catch (VerifyError ver) {
180
error = "A \"verifier\" detects that a class, though well formed, contains an internal inconsistency or security problem: "+ver.getLocalizedMessage();
181
} catch (UnsupportedClassVersionError ucver) {
182
error = "The major and minor version numbers in bytes are not supported by the VM. "+ucver.getLocalizedMessage();
183
} catch (ClassFormatError cfer) {
184
error = "The bytes do not represent a valid class. "+cfer.getLocalizedMessage();
185
} catch (ClassCircularityError ccer) {
186
error = "A circularity has been detected while initializing a class: "+ccer.getLocalizedMessage();
189
getProject().log(error, Project.MSG_ERR);
190
throw new BuildException(error);
194
private String classToSourceURL (FileObject fo) {
196
ClassPath cp = ClassPath.getClassPath (fo, ClassPath.EXECUTE);
197
FileObject root = cp.findOwnerRoot (fo);
198
String resourceName = cp.getResourceName (fo, '/', false);
199
if (resourceName == null) {
200
getProject().log("Can not find classpath resource for "+fo+", skipping...", Project.MSG_ERR);
203
int i = resourceName.indexOf ('$');
205
resourceName = resourceName.substring (0, i);
206
FileObject[] sRoots = SourceForBinaryQuery.findSourceRoots
207
(root.getURL ()).getRoots ();
208
ClassPath sourcePath = ClassPathSupport.createClassPath (sRoots);
209
FileObject rfo = sourcePath.findResource (resourceName + ".java");
210
if (rfo == null) return null;
211
return rfo.getURL ().toExternalForm ();
212
} catch (FileStateInvalidException ex) {
213
ex.printStackTrace ();