[Prévia] [Próxima] [Prévia por assunto] [Próxima por assunto]
[Índice cronológico] [Índice de assunto]

RE: Core Java Technologies Tech Tips, August, 19, 2003 (Formatting Messages, Unloading/Reloading Classes)



Veja que interessante. Será que agora isso já funciona realmente em Java?

[]s,
Fabio.

SDN - Core Java Technologies Tech Tips writes:
 > Core Java Technologies Tech Tips
 > 
 > - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
 > UNLOADING AND RELOADING CLASSES
 > 
 > The July 22, 2003 Tech Tip titled "Compiling Source Directly From 
 > a Program" (http://bulkmail2.sun.com/CTServlet?id=40407956805922094:1061310102516) 
 > offered a way to compile source files directly from your Java 
 > program. It presented a simple editor in which you can name a 
 > class and provide the source. That approach works fine, provided 
 > the class is not one that you already compiled and loaded during 
 > the current execution run. If indeed you previously compiled and
 > loaded the class, you can't then edit it and recompile it using 
 > the approach covered in the previous Tech Tip. The issue here is 
 > the underlying class loader. All classes loaded through the 
 > system class loader will never be unloaded. To allow the program 
 > to let you reedit the source for a class, and then recompile and 
 > reload it, you need to work with a different class loader. In 
 > this tip, you'll learn how to create a custom class loader, one 
 > that lets you load your newly compiled class (or any class). 
 > You'll also learn how to discard the class such that the next time 
 > you compile the class, the newly compiled class is loaded.
 > 
 > An instance of the ClassLoader class of the java.lang package is 
 > responsible for loading all classes. For system classes, that 
 > class loader is available through the getSystemClassLoader method 
 > of ClassLoader. For user classes, if that class is already loaded, 
 > you can ask for the ClassLoader with the getClassLoader method of 
 > Class.
 > 
 > In the RunIt program from the earlier Tech Tip, the forName 
 > method of Class was used to load the class data (that is, to get 
 > an instance of Class). If you want to use a different class 
 > loader, you have to use the loadClass method of ClassLoader to 
 > load the class data. In other words, you need to code this:
 > 
 >    String className = ...;
 >    Class aClass = loader.loadClass(className);   
 > 
 > instead of this:
 > 
 >    String className = ...;
 >    Class aClass = Class.forName(className);   
 >    
 > Functionally, the two code blocks above are identical when 
 > loading through the same class loader. The first block loads the 
 > class through the same loader as where the code is found. The 
 > second loads the class through the class loader specified by the 
 > loader variable. In both cases, you would then call something like 
 > newInstance to create an instance of the class.
 > 
 > Repeated calls to Class.forName load the same class (assuming the
 > class loader of the calling class doesn't change). Repeated calls 
 > to loader.loadClass load the same class. However, the second 
 > block allows you to reload a class. To do that, you create a new 
 > ClassLoader:
 > 
 >    String className = ...;
 >    // create new loader instance
 >    ClassLoader loader = ...; 
 >    Class aClass = loader.loadClass(className);
 > 
 > In this particular code block, a new loader is created between 
 > calls to loadClass. If the class definition for className changes 
 > between calls, the new version of the class is loaded the second 
 > time through.
 > 
 > If you change the RunIt program to use this mechanism, you can
 > edit the source after compiling and running the program. The 
 > program should generate output appropriate to the new source, not 
 > the old.
 > 
 > The only thing left is where to get the ClassLoader. The 
 > ClassLoader class is itself an abstract class. Predefined loaders 
 > include the SecureClassLoader of java.security, which adds 
 > permission support, and the URLClassLoader of java.net. Of the 
 > two predefined loaders, only URLClassLoader offers support for
 > public construction through either its constructors or static 
 > newInstance methods. See the documentation for URLClassLoader 
 > (http://bulkmail2.sun.com/CTServlet?id=40407956805922094:1061310102531)
 > for further details.
 > 
 > Creating a URLClassLoader involves creating an array of URL 
 > objects. These URL objects serve as the locations that the custom 
 > class loader uses to find  classes. You specify the elements of
 > the array similarly to the way you specify path elements for the 
 > CLASSPATH environment variable, where the path elements are 
 > separated by a ; on Windows and a : on Unix machines. Each 
 > element in the URL array can be a located locally or on a remote 
 > host. Anything ending in a "/" is presumed to be a directory. 
 > Anything else is presumed to be a JAR file.
 > 
 > For instance, if you want to create a ClassLoader that works like 
 > the default classpath, that is, only searching in the current 
 > directory, you can code the following:
 > 
 >    File file = new File(".");
 >    ClassLoader loader = new URLClassLoader(
 >      new URL[] {file.toURL()}
 >    );
 > 
 > The first line creates a File object, referencing the current 
 > directory. The second line calls the URLClassLoader constructor. 
 > Passed into the constructor is an array of one URL object: the 
 > URL to the File.
 > 
 > If you change the RunIt program to include the following code,
 > recompile it, and run it, the program will discard the loaded 
 > classes between runs and reload them. Notice that unlike the 
 > RunIt program in the earlier Tech Tip, the following code does 
 > not create an instance of the class to invoke the main method. 
 > There is no need to create an instance of the class because the 
 > main method is static, 
 >  
 >    // Create new class loader 
 >    // with current dir as CLASSPATH
 >    File file = new File(".");
 >    ClassLoader loader = new URLClassLoader(
 >      new URL[] {file.toURL()}
 >    );
 >    // load class through new loader
 >    Class aClass = loader.loadClass(className.getText());
 >    // run it
 >    Object objectParameters[] = {new String[]{}};
 >    Class classParameters[] =
 >      {objectParameters[0].getClass()};
 >    Method theMethod = aClass.getDeclaredMethod(
 >      "main", classParameters);
 >    // Static method, no instance needed
 >    theMethod.invoke(null, objectParameters);
 > 
 > Here's the complete code example, with the RunIt program renamed 
 > to RunItReload.
 > 
 >    import java.awt.*;
 >    import java.awt.event.*;
 >    import javax.swing.*;
 >    import java.io.*;
 >    import java.net.*;
 >    import java.lang.reflect.*;
 > 
 >    public class RunItReload extends JFrame {
 >      JPanel contentPane;
 >      JScrollPane jScrollPane1 = new JScrollPane();
 >      JTextArea source = new JTextArea();
 >      JPanel jPanel1 = new JPanel();
 >      JLabel classNameLabel = new JLabel("Class Name");
 >      GridLayout gridLayout1 = new GridLayout(2,1);
 >      JTextField className = new JTextField();
 >      JButton compile = new JButton("Go");
 >      Font boldFont = new java.awt.Font(
 >                                   "SansSerif", 1, 11);
 > 
 >      public RunItReload() {
 >        super("Editor");
 >        setDefaultCloseOperation(EXIT_ON_CLOSE);
 >        contentPane = (JPanel) this.getContentPane();
 >        this.setSize(400, 300);
 >        classNameLabel.setFont(boldFont);
 >        jPanel1.setLayout(gridLayout1);
 >        compile.setFont(boldFont);
 >        compile.setForeground(Color.black);
 >        compile.addActionListener(new ActionListener() {
 >          public void actionPerformed(ActionEvent e) {
 >            try {
 >              doCompile();
 >            } catch (Exception ex) {
 >              System.err.println(
 >                    "Error during save/compile: " + ex);
 >              ex.printStackTrace();
 >            }
 >          }
 >        });
 >        contentPane.add(
 >                     jScrollPane1, BorderLayout.CENTER);
 >        contentPane.add(jPanel1, BorderLayout.NORTH);
 >        jPanel1.add(classNameLabel);
 >        jPanel1.add(className);
 >        jScrollPane1.getViewport().add(source);
 >        contentPane.add(compile, BorderLayout.SOUTH);
 >      }
 >      public static void main(String[] args) {
 >        Frame frame = new RunItReload();
 >        // Center screen
 >        Dimension screenSize =
 >          Toolkit.getDefaultToolkit().getScreenSize();
 >        Dimension frameSize = frame.getSize();
 >        if (frameSize.height > screenSize.height) {
 >          frameSize.height = screenSize.height;
 >        }
 >        if (frameSize.width > screenSize.width) {
 >          frameSize.width = screenSize.width;
 >        }
 >        frame.setLocation(
 >          (screenSize.width - frameSize.width) / 2,
 >          (screenSize.height - frameSize.height) / 2);
 >        frame.show();
 >      }
 >      private void doCompile() throws Exception {
 >        // write source to file
 >        String sourceFile = className.getText() + ".java";
 >        FileWriter fw = new FileWriter(sourceFile);
 >        fw.write(source.getText());
 >        fw.close();
 >        // compile it
 >        int compileReturnCode =
 >          com.sun.tools.javac.Main.compile(
 >              new String[] {sourceFile});
 >        if (compileReturnCode == 0) {
 >          // Create new class loader 
 >          // with current dir as CLASSPATH
 >          File file = new File(".");
 >          ClassLoader loader = 
 >          new URLClassLoader(new URL[] {file.toURL()});
 >          // load class through new loader
 >          Class aClass = loader.loadClass(
 >                                   className.getText());
 >          // run it
 >          Object objectParameters[] = {new String[]{}};
 >          Class classParameters[] =
 >                      {objectParameters[0].getClass()};
 >          Method theMethod = aClass.getDeclaredMethod(
 >                               "main", classParameters);
 >          // Static method, no instance needed
 >          theMethod.invoke(null, objectParameters);
 >        }
 >      }
 >    }
 > 
 > You need to compile and execute this program in a slightly 
 > different way than you did for the RunIt program in the earlier 
 > Tech Tip. Because the custom class loader is using the current 
 > directory as the place where reloadable classes need to come 
 > from, you can't load the actual RunItReload class from the same
 > classpath. Otherwise, the system class loader will load the
 > compiled class from the same location (and class loader). You 
 > need to tell the compiler to send the compiled classed for 
 > RunItReload to a different location. You run the program with 
 > that other location in the classpath, not with "." in it. 
 > Remember that you need to include tools.jar in the classpath to 
 > compile. The following command sends the newly compiled .class 
 > files for RunItReload to the XYZ subdirectory. Feel free to pick 
 > a different subdirectory name. (Although the command is shown on 
 > multiple lines it needs to go on one line):
 > 
 > In Windows: 
 > 
 >     mkdir XYZ
 >     javac -d XYZ -classpath 
 >      c:\j2sdk1.4.2\lib\tools.jar RunItReload.java
 > 
 > In Unix: 
 > 
 >     mkdir XYZ
 >     javac -d XYZ -classpath 
 >      /homedir/jdk14/j2sdk1.4.2/lib/tools.jar 
 >      RunItReload.java
 > 
 > Replace homedir with your actual home directory.
 > 
 > If you get an error that the system cannot find the path 
 > specified, be sure to create the XYZ directory before compilation.
 > 
 > As before, you need to include the tools.jar file in your runtime 
 > classpath, and you need to include the XYZ directory for the 
 > actual RunItReload program. To run the program, issue the 
 > following command. (Again, although the command is shown on 
 > multiple lines, it needs to go on one line). 
 > 
 > In Windows: 
 > 
 >     java -classpath 
 >      c:\j2sdk1.4.2\lib\tools.jar;XYZ RunItReload
 > 
 > In Unix: 
 > 
 >    java -classpath 
 >       /homedir/jdk14/j2sdk1.4.2/lib/tools.jar:
 >       XYZ RunItReload
 > 
 > The XYZ here is carried over from the prior javac step. The
 > target directory for compilation (specified after -d) must match 
 > the runtime classpath.
 > 
 > Running the program displays the GUI. Then you can: 
 > 
 > 1. Enter the name of the class, such as Sample2, to be compiled 
 >    in the JTextField.
 > 
 > 2. Enter the source code in the JTextArea. Here's the source code 
 >    for Sample2: 
 > 
 >     public class Sample2 {
 >       public static void main(String args[]) {
 >         System.out.println(new java.util.Date());
 >         // System.out.println("Hello, World!");
 >       }
 >     }
 > 
 > 3. Click the Go button. 
 > 
 > Output is sent to the console. For example, Sample2 should 
 > produce output that looks something like this: 
 > 
 >   Tue Aug 19 11:25:16 PDT 2003
 > 
 > Comment out the line that prints the date, and uncomment the line 
 > that prints "Hello World". Click the Go button. You should now 
 > see the following in your console:
 > 
 >   Hello, World!
 > 
 > You see a different line displayed because a new class loader was
 > created, one that unloaded previously loaded classes.
 > 
 > .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .  .
 > 
 > IMPORTANT: Please read our Terms of Use, Privacy, and Licensing 
 > policies:
 > http://www.sun.com/share/text/termsofuse.html
 > http://www.sun.com/privacy/
 > http://developer.java.sun.com/berkeley_license.html
 > 
 > * FEEDBACK
 >   Comments? Please enter your feedback on the Tech Tips at: 
 >   
 >      http://developers.sun.com/contact/feedback.jsp?category=sdn