[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 503
  • Last Modified:

Loading a jar file

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
tonus
Asked:
tonus
1 Solution
 
chrisosCommented:
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
 
tonusAuthor Commented:
Hi Chrisos,
    Could you please explain this in detail?
0
 
objectsCommented:
This is supported in 1.2, but as chrisos states, in 1.1 you'll have to build it yourself.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
rkenworthyCommented:
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
 
objectsCommented:
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
 
chrisosCommented:
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
 
chrisosCommented:
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
 
chrisosCommented:
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
 
chrisosCommented:
I'll let somebody else add the resource handling to the NetworkClassLoader code.

Chrisos.
0
 
jimsimsCommented:
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
 
objectsCommented:
chrisos,

Not sure if i missed the point somewhere, but doesn't the URLClassLoader already do what your new class loader does?
0
 
chrisosCommented:
Its not available in JDK 1.1, it is 1.2+

Chrisos
0
 
objectsCommented:
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
 
chrisosCommented:
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
 
objectsCommented:
Untrusted code aint allowed to create a ClassLoader I thought that's what you were using the servlet for.
0
 
chrisosCommented:
Yep, the applet will have to be signed
0
 
vemulCommented:
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 Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now