Solved

Loading a jar file

Posted on 2001-06-04
17
451 Views
Last Modified: 2013-11-23
Is it possible to load a jar file from the server side on click of some event in the client side applet? I am using jdk1.1.8 and want to load the jar file once the application starts executing. ie, I don't want to load the full class file(jar) initially as it takes too much of time to show the first page.
0
Comment
Question by:tonus
17 Comments
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
You can write your own ClassLoader implementation to do that.

Then when you do a Class.forName("com.foo.X") have the class loader doanload the latest distribution of the jar and use it.
0
 
LVL 1

Author Comment

by:tonus
Comment Utility
Hi Chrisos,
    Could you please explain this in detail?
0
 
LVL 92

Expert Comment

by:objects
Comment Utility
This is supported in 1.2, but as chrisos states, in 1.1 you'll have to build it yourself.
0
 
LVL 3

Expert Comment

by:rkenworthy
Comment Utility
Just my two cents:

You will have to build it yourself, but I think I read somewhere that unless you use the default class loader (URLClassLoader I think) applets in an IE and Netscape browser will throw a security exception. This means you will have to sign the applet.

Then again, I am not 100% sure about that - can anyone confirm?

Rob
0
 
LVL 92

Expert Comment

by:objects
Comment Utility
You can load individual classes dynamically, but not a jar.
To do this you need to remove all references to the 'extra' classes from the startup applet. Then when you want to load a class use the following:

Class c = Class.forName(classname);
Object o = c.newInstance();
...        

As class loader doesn't know about the class being loaded (until now) it will pull it from the server (at runtime).

But this will only work for .class files. This technique will not work for a jar.
0
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
OK, bring on the pain!

There follows three files:

1. URLClassLoaderServlet.java is a Servlet, which allows you to serve whatever classes or resources are in the local class path.

2. NetworkClassLoader.java is a ClassLoader that loads from the URLClassLoader what it cannot load locally, it then caches the results so that the download need not be done next time.

3. TestHarness.java is an example of how to use it.

The transmission protocol converts the data to a hex string which is converted back at the other end.

It works, it probably isn't robust, and I have to say that I have learned a new hate for class loaders and streams.

Files to follow...
0
 
LVL 3

Accepted Solution

by:
chrisos earned 50 total points
Comment Utility
package com.wintermuteis.classloader;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.util.zip.*;

public class URLClassLoaderServlet extends HttpServlet {

      private boolean quit = false;

      /**
       * Initialize global variables
       */
      public void init(ServletConfig config) throws ServletException {
            super.init(config);
      }

      /**
       * Process the HTTP Get request
       */
      public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // Result members
            String contentType = null;
            Object send = null;

            // Process input
            String command = "";
            try {
                  command = request.getParameter("command");
            }
            catch( Exception e ) {
                  e.printStackTrace();
            }
            if( command == null ) {
                  command = "";
            }
            System.out.println("Command is: "+command);

            // Get the command type and parameter
            String commandType = "";
            String commandParameter = "";
            if( command.indexOf(":") == -1 ) {
                  commandType = "INVALID";
            }
            else {
                  commandType = command.substring(0, command.indexOf(":")).toUpperCase();
                  commandParameter = command.substring(command.indexOf(":")+1);
            }

            if( commandType.equals("CLASS") || commandType.equals("RESOURCE") ) {
                  System.out.println("Processing a class request for: "+commandParameter);
                  try {
                        // Check that it is available first!
                        Class def = Class.forName(commandParameter);

                        // Determine the name of the source to get
                        String resourceLocation = null;
                        if( commandType.equals("CLASS") ) {
                              resourceLocation = commandParameter.replace('.', File.separatorChar)+".class";
                        }
                        else {
                              resourceLocation = commandParameter;
                              char opposite = File.separatorChar == '/' ? '\\' : '/';
                              resourceLocation = resourceLocation.replace(opposite, File.separatorChar);
                        }

                        // Build a stack indicating where the file should be
                        System.out.println("Get resource "+resourceLocation);
                        Vector pathStack = new Vector();
                        StringTokenizer locations = new StringTokenizer(resourceLocation, File.separator);
                        while( locations.hasMoreTokens() ) {
                              pathStack.addElement(locations.nextToken());
                        }

                        // Locate the file
                        String classPaths = System.getProperty("java.class.path");
                        StringTokenizer paths = new StringTokenizer(classPaths, ";");
                        String currentPath = null;
                        InputStream resource = null;
                        while( resource == null && paths.hasMoreTokens() ) {
                              currentPath = paths.nextToken();
                              resource = locateResourceInPath(currentPath, pathStack);
                        }

                        // Get the contents of the file in byte arrays of 1Kand add each array to a Vector
                        System.out.println("Get resource bytes: "+resource.available());
                        Vector byteArrays = new Vector();
                        byte[] bytes = new byte[resource.available()];
                        int length = resource.read(bytes);

                        // Convert the byte array into one big string
                        String asciiTemp = null;
                        int asciiTempLength = 0;
                        StringBuffer hexEncoded = new StringBuffer();
                        try {
                              for( int byteCounter = 0; byteCounter < bytes.length; byteCounter++ ) {
                                    asciiTemp = Integer.toHexString((int)bytes[byteCounter]);
                                    asciiTempLength = asciiTemp.length();
                                    if( asciiTempLength > 2 ) {
                                          asciiTemp = asciiTemp.substring(asciiTemp.length()-2);
                                    }
                                    else if( asciiTempLength == 1 ) {
                                          asciiTemp = "0"+asciiTemp;
                                    }
                                    hexEncoded.append(asciiTemp+":");
                              }
                        }
                        catch( Exception e ) {
                              e.printStackTrace();
                        }

                        contentType = "text/plain";
                        send = hexEncoded.toString();
                        //System.out.println("Data: "+hexEncoded.toString());
                        System.out.println("Chars in hex: "+hexEncoded.length());
                  }
                  catch( ClassNotFoundException cnfe ) {
                        cnfe.printStackTrace();
                        contentType = "text/html";
                        send = cnfe.toString();
                  }
            }
            else {
                  // INVALID or unrecognised, send back an exception
                  System.out.println("Processing an invalid request");
                  contentType = "text/plain";
                  send = "request must start with CLASS: or RESOURCE:";
            }

            // send the result back
            System.out.println("Content type of response: "+contentType);
            System.out.println("Object type of response: "+send.getClass().getName());
            response.setContentType(contentType);

            // Write a text stream
            System.out.println("Writing output");
            OutputStream outStream = response.getOutputStream();
            PrintWriter out = new PrintWriter(outStream);
            out.write((String)send);
            out.flush();
            outStream.flush();
            out.close();
            outStream.close();

            System.out.println("Request complete");
      }

      private static InputStream locateResourceInPath(String currentPath, Vector pathStack) {
            InputStream result = null;
            //System.out.println("Processing: "+currentPath);
            if( currentPath.toUpperCase().endsWith(".ZIP") || currentPath.toUpperCase().endsWith(".JAR") ) {
                  // The current path is an archive
                  try {
                        String targetFile = "";
                        for( int i = 0; i < pathStack.size(); i++ ) {
                              targetFile += (String)pathStack.elementAt(i)+"/";
                        }
                        // remove the extra slash at the end
                        targetFile = targetFile.substring(0, targetFile.length()-1);

                        // System.out.println("Searching for File: "+targetFile);

                        ZipFile zipFile = new ZipFile(currentPath);
                        Enumeration entries = zipFile.entries();
                        ZipEntry entry = null;
                        String name = null;
                        boolean found = false;
                        while( !found && entries.hasMoreElements() ) {
                              entry = (ZipEntry)entries.nextElement();
                              name = entry.getName();
                              //System.out.println("File: "+name);
                              if( name.equals(targetFile) ) {
                                    System.out.println("Found File: "+name+" in "+currentPath);
                                    result = zipFile.getInputStream(entry);
                                    found = true;
                              }
                        }
                  }
                  catch( IOException ioe ) {
                        ioe.printStackTrace();
                  }
            }
            else {
                  // the current path is a file path
                  boolean found = false;
                  int counter = 0;
                  File current = new File(currentPath);
                  //System.out.println("Root: "+current.getAbsolutePath());
                  String subPath = null;
                  if( current.isDirectory() ) {
                        while( !found && current.exists() && counter < pathStack.size() ) {
                              subPath = (String)pathStack.elementAt(counter);
                              current = new File(current, subPath);
                              //System.out.println("Sub dir: "+current.getAbsolutePath());
                              if(current.isFile() && counter == pathStack.size()-1){
                                    System.out.println("Found file in "+current.getAbsolutePath());
                                    found = true;
                              }
                              counter++;
                        }
                  }
                  if( found ) {
                        try {
                              result = new FileInputStream(current);
                        }
                        catch( FileNotFoundException fnfe ) {
                              //TOSH! this should never happen
                              fnfe.printStackTrace();
                        }
                  }
            }
            return result;
      }


      /**
       * Get Servlet information
       * @return java.lang.String
       */
      public String getServletInfo() {
            return "com.wintermuteis.classloader.URLClassLoaderServlet Information";
      }
}
0
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
package com.wintermuteis.classloader;

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

public class NetworkClassLoader extends ClassLoader {

      private static String rootName = System.getProperty("user.dir");
      private static File root = null;

      private URL source = null;
      private Hashtable loaded = new Hashtable();

      public NetworkClassLoader(URL source) {
            super();
            this.source = source;

            // Set the root of the local cache
            //System.out.println("Setting cache root");
            root = new File(rootName);
            String[] children = root.list();
            File child = null;
            boolean cacheFound = false;
            for( int i = 0; !cacheFound && i < children.length; i++ ) {
                  child = new File(children[i]);
                  if( child.isDirectory() && child.getName().equals("NetworkClassCache") ) {
                        root = child;
                        cacheFound = true;
                  }
            }
            if( !cacheFound ) {
                  File cache = new File(root, "NetworkClassCache");
                  cache.mkdir();
                  root = cache;
            }
            System.out.println("Processing cache root: "+root.getAbsolutePath());
            processCache("", root);
      }


      private void processCache(String path, File root) {
            //System.out.println("Request to process: "+root.getAbsolutePath());
            String[] children = root.list();
            File child = null;
            String name = null;
            for( int i = 0; i < children.length; i++ ) {
                  child = new File(root, children[i]);
                  //System.out.println("Cache processing: "+child.getPath());
                  name = path+File.separator+child.getName();
                  if( child.isDirectory() ) {
                        //System.out.println("Cache found dir: "+name);
                        processCache(name, child);
                  }
                  else {
                        name = name.substring(1);
                        System.out.println("Cache loaded: "+name);
                        loaded.put(name, child);
                  }
            }
      }


      public Class loadClass(String name) throws ClassNotFoundException {
            return super.loadClass(name);
      }

      protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
            System.out.println("NetworkClassLoader: loadClass request for "+name+" resolve is: "+resolve);
            //Try to load the class locally to avoid getting file unneccesarily
            try {
                  ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
                  Class local = systemClassLoader.loadClass(name);
                  if( resolve ) {
                        resolveClass(local);
                  }
                  System.out.println("NetworkClassLoader: class found by system class loader");
                  return local;
            }
            catch( ClassNotFoundException cnfe ) {
            // ignore
                  // System.out.println("NetworkClassLoader: class not found by system class loader");
            }

            Class result = null;
            File localSource = null;
            byte[] bytes = null;;

            // try the cache
            String fileKey = name.replace('.', File.separatorChar);

            if( loaded.containsKey(fileKey+".class") ) {
                  // load from cache
                  localSource = (File)loaded.get(fileKey+".class");

                  try {
                        FileInputStream fis = new FileInputStream(localSource);
                        long length = localSource.length();
                        bytes = new byte[(int)length];
                        int byteCount = 0;
                        long readLength = fis.read(bytes);
                        System.out.println("NetworkClassLoader: Class found in cache");
                  }
                  catch( FileNotFoundException fnfe ) {
                        fnfe.printStackTrace();
                  }
                  catch( IOException ioe ) {
                        ioe.printStackTrace();
                  }
            }
            else {
                  try {
                        // load from URL

                        // Send request
                        String newURL = source.toString()+"?command=CLASS:"+name;
                        System.out.println("Request = "+newURL);
                        URL request = new URL(newURL);
                        URLConnection connection = request.openConnection();

                        // Get the response
                        connection.setDoInput(true);
                        InputStream inStream = connection.getInputStream();
                        InputStreamReader inr = new InputStreamReader(inStream);
                        BufferedReader br = new BufferedReader(inr);
                        int size = 0;
                        String data = br.readLine();
                        //System.out.println("Data: "+data);
                        br.close();
                        inr.close();
                        inStream.close();

                        // Reconstruct the byte array from the string
                        StringTokenizer byteTokenizer = new StringTokenizer(data, ":");
                        bytes = new byte[byteTokenizer.countTokens()];
                        int byteCount = 0;
                        while( byteTokenizer.hasMoreTokens() ) {
                              //bytes[byteCount] = Byte.parseByte(byteTokenizer.nextToken());
                              bytes[byteCount] = (byte)Integer.parseInt(byteTokenizer.nextToken(), 16);
                              byteCount++;
                        }
                        System.out.println("Data retrieved: "+byteCount+" bytes");

                        // Save the file
                        String fileDir = fileKey.substring(0, fileKey.lastIndexOf(File.separator));
                        String fileName = fileKey.substring(fileKey.lastIndexOf(File.separator)+1)+".class";
                        File newDir = new File(root, fileDir);
                        newDir.mkdirs();
                        File newFile = new File(newDir, fileName);
                        FileOutputStream fos = new FileOutputStream(newFile);
                        fos.write(bytes);
                        fos.flush();
                        fos.close();


                        // Add the file to the cache
                        loaded.put(fileKey, newFile);
                        localSource = newFile;
                        System.out.println("NetworkClassLoader: Class retrieved over network");
                  }
                  catch( IOException ioe ) {
                        System.out.println("NetworkClassLoader: Exception");
                        ioe.printStackTrace();
                  }
                  catch( Exception e ) {
                        System.out.println("NetworkClassLoader: Exception");
                        e.printStackTrace();
                  }
            }

            // Create a class from the file
            result = defineClass(name, bytes, 0, bytes.length);

            // Resolve if requested
            if( result != null && resolve ) {
                  System.out.println("Resolving class");
                  resolveClass(result);
            }

            // return the class
            return result;
      }


      public InputStream getResourceAsStream(String name) {
            // Load from parent class loader
            InputStream result = super.getResourceAsStream(name);
            if( result == null ) {
                  // try to load from URL
            }
            return result;
      }

      public URL getResource(String name) {
            // Load from parent class loader
            URL result = super.getResource(name);
            if( result == null ) {
                  // try to load from URL
            }
            return result;
      }

}

/////////////////////////////////////////////////////////

package com.wintermuteis.classloader;

import java.net.MalformedURLException;
import java.net.URL;
import java.sql.*;

public class TestHarness extends Object {

      public TestHarness() {
      }

      public static void main(String[] args) {
            //TestHarness testHarness = new TestHarness();
            try {
                  ClassLoader loader = new NetworkClassLoader(new URL("http://localhost:7070/LServer_html/URLClassLoaderServlet"));
                  Class driver = loader.loadClass("oracle.jdbc.driver.OracleDriver");
                  Class fileDinder = loader.loadClass("com.mpowereurope.utils.FileFinder");

            }
            catch( MalformedURLException murle ) {
                  System.out.println("Exception in test harness");
                  murle.printStackTrace();
            }
            catch( ClassNotFoundException cnfe ) {
                  System.out.println("Exception in test harness");
                  cnfe.printStackTrace();
            }
      }
}
0
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
LVL 3

Expert Comment

by:chrisos
Comment Utility
I'll let somebody else add the resource handling to the NetworkClassLoader code.

Chrisos.
0
 
LVL 1

Expert Comment

by:jimsims
Comment Utility
just a tweak to chrisos' post - you should do the jar loading in a separate thread while the first page displays to minimize lag (as it sounds like that's why you're going to all this pain)
0
 
LVL 92

Expert Comment

by:objects
Comment Utility
chrisos,

Not sure if i missed the point somewhere, but doesn't the URLClassLoader already do what your new class loader does?
0
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
Its not available in JDK 1.1, it is 1.2+

Chrisos
0
 
LVL 92

Expert Comment

by:objects
Comment Utility
But isn't your code running as a servlet?
Just cause the client is running in 1.1, doesn't mean the same restriction is on the server side.
0
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
The server side can be anything as you say, but the client is JDK 1.1 so the ClassLoader has to be extended.

The servlet is just an example that I knocked up, as I don't like Sockets :)

Chrisos
0
 
LVL 92

Expert Comment

by:objects
Comment Utility
Untrusted code aint allowed to create a ClassLoader I thought that's what you were using the servlet for.
0
 
LVL 3

Expert Comment

by:chrisos
Comment Utility
Yep, the applet will have to be signed
0
 
LVL 5

Expert Comment

by:vemul
Comment Utility
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

- points to chrisos

Please leave any comments here within the
next seven days.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER !

vemul
Cleanup Volunteer
0

Featured Post

Free Trending Threat Insights Every Day

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

For customizing the look of your lightweight component and making it look lucid like it was made of glass. Or: how to make your component more Apple-ish ;) This tip assumes your component to be of rectangular shape and completely opaque. (COD…
Introduction This article is the first of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article explains our test automation goals. Then rationale is given for the tools we use to a…
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
Viewers will learn about the regular for loop in Java and how to use it. Definition: Break the for loop down into 3 parts: Syntax when using for loops: Example using a for loop:

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

8 Experts available now in Live!

Get 1:1 Help Now