Search code examples
javasocketsclassloaderurlclassloader

How to use classloader to load class file from server to client


I have nio channel that my client should load a class file from server computer. They are in the same range of IP. I have two interface that are common on server and client machine. And a class which implements the interface on server machine. I use the following code on my client machine, but ClassNotFoundException will appear when I run it.

URL url = new URL("file:///E:/Computing/Master/classes/" );
URLClassLoader ucl = new URLClassLoader(new URL[]{url});
Class clazz = ucl.loadClass("com.counter.controller.Action");
ProcessAlgorithm iAction = (ProcessAlgorithm) clazz.newInstance();

What is the complete process of class loading in this case?


Solution

  • I found the solution and like to share it here. First, this job is network class loading. It can be find in javadoc with this name. Actually there is no way to load a class file from remote computers by using the following code:

    URL url = new URL("file:///E:/Computing/Master/classes/" );
    URLClassLoader ucl = new URLClassLoader(new URL[]{url});
    Class clazz = ucl.loadClass("com.counter.controller.Action");
    ProcessAlgorithm iAction = (ProcessAlgorithm) clazz.newInstance();
    

    Even when you change its URL to "http" while there is no http protocol between two separate computers. Well lets start the correct way.

    Suppose you have two computers with 192.168.10.1(server) and 192.168.10.2(client) IPs. There is a class file which the client should not copy from server disk to its disk. So, firstly, start defining the same interface on both JVM (server and client). with the same package like the following interface:

    package org.counter.biz;
    
    public interface ProcessAlgorithm {
    
        int doProcess() ;
    
    }
    

    So, this interface is common on server and client. Secondly, your main class should be defined on server and implements the interface :

    package org.counter.biz;
    
    public class Action implements ProcessAlgorithm {
    
        @Override
        public int doProcess() {
    
           /* something to do */
    
        }
    }
    

    And finally the class file should be sent to the client on socket or socket channel. Here, I use Socketchannel on my server and Socket on my client. (Actually, you have to know how to connect two remote computers via socket first.)

    Server side code of sending bytes of class file to client :

    private void sendAlgorithmFile(SocketChannel client, String filePath) throws IOException {
    
            ByteBuffer buffer = ByteBuffer.allocate(8192);
            buffer.clear();
    
           /*file path like E:\\classes\\Action.class*/
            Path path = Paths.get(filePath);
            FileChannel fileChannel = FileChannel.open(path);
            int bytes = 0;
            int counter = 0;
    
    
            do {
                bytes = fileChannel.read(buffer);
                if (bytes <= 0)
                    break;
                counter += bytes;
                buffer.flip();
                do {
                    bytes -= client.write(buffer);
                } while (bytes > 0);
                buffer.clear();
            } while (true);
    
    
            fileChannel.close();
    
        }
    

    There is much way to send a file via socket. It is my code and its correctness has been examined.

    Client side to receive the file and change it to a class which is not saved on client's disk.

    package org.counter.biz;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.Socket;
    
    public class MyClassLoader extends ClassLoader {
    
        private Socket clientChannel;
        private int count = 0;
    
        public MyClassLoader(Socket channel){
            this.clientChannel = channel;
        }
    
        @Override
        protected Class findClass(String className){
    
            Class myClass = null;
    
            InputStream inputStream = null;
    
            try {
                inputStream = clientChannel.getInputStream();
            }catch (IOException e){e.printStackTrace();}
    
    
            byte[] bytes = new byte[8192];
            byte[] myBytes = null;
    
            try {
                while ((count = inputStream.read(bytes)) > 0){
                    myBytes = new byte[count];
                    System.arraycopy(bytes, 0, myBytes, 0, count);
                    myClass = defineClass(className, myBytes, 0, myBytes.length);
                }
                inputStream.close();
            }catch (IOException io){}
    
    
            return myClass;
    
        }
    
    }
    

    Then:

    public class Client {
      public static void main(String[] args) throws Exception{
        MyClassLoader myClassLoader = new MyClassLoader(clientSocket);
        Class clazz = myClassLoader.findClass(null);
        ProcessAlgorithm iAction = (ProcessAlgorithm) clazz.newInstance();
       }
    }
    

    Then you can use the class like this

    iAction.doProcess();
    

    If there is any question, I'm here to answer. :)