title: Classloading Using Different Versions Of The Same Class : java.lang.LinkageError : attempted duplicate class definition for name
body: <p>I have a working code that loads dynamically different Class Implementations with different Class Names. The class files are loaded into the <strong>in-memory database</strong> (<em>Apache Derby Db</em>), and classloader retrieve the <strong>.class</strong> file from the <strong>BLOB</strong> columns.</p>
  
  <p>What I want to do is, inserting the <strong>.class</strong> files as binary BLOB with version column and <code>IS_ENABLED</code> flags, then classloader will load the class for different versions on run-time. There will be db entries same amount of the compiled class versions and there will only one class with <code>IS_ENABLED</code> flag set to <strong>TRUE</strong>.</p>
  
  <p>Because that I try to load the same class name with custom classloader, I get the following Exception;</p>
  
  <pre><code>Exception in thread "main" java.lang.LinkageError: loader (instance of  com/levent/classloader/DerbyServerClassLoader): attempted  duplicate class definition for name: "com/levent/greeter/Greeter"
      at java.lang.ClassLoader.defineClass1(Native Method)
      at java.lang.ClassLoader.defineClass(Unknown Source)
      at java.lang.ClassLoader.defineClass(Unknown Source)
      at com.levent.classloader.DerbyServerClassLoader.findClass(DerbyServerClassLoader.java:38)
      at com.levent.example.ClientClassLoaderDBVersionDemo.main(ClientClassLoaderDBVersionDemo.java:43)
  </code></pre>
  
  <p>There are two different <strong>.class</strong> files (<strong>Greeter.class.v1</strong>, <strong>Greeter.class.v2</strong>) (inserted at the begining of the code) for the same Interface (<strong>Greeter.java</strong>)</p>
  
  <p>In the beginning of test code, class files are retrieved from the lib/classes/ folder and inserted as blob binary data to the in-memory db, after then, .class files are sequentially retrieved by the database and loaded. While loading the class with same name, Exception occurs.</p>
  
  <p>How can I solve this problem? Is there any way to unload a class, or else, anyway to reload a class with same name?</p>
  
  <h1>Project Tree</h1>
  
  <p><a href="https://i.stack.imgur.com/WF5SK.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/WF5SK.png" alt="enter image description here"></a></p>
  
  <h1>DerbyServerClassLoader.java - Class Loader</h1>
  
  <pre><code>package com.levent.classloader;
  
  import java.sql.Blob;
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.PreparedStatement;
  import java.sql.ResultSet;
  import java.sql.SQLException;
  
  public class DerbyServerClassLoader extends ClassLoader {
  
      private ClassLoader parent;
      private String connectionString;
  
      public DerbyServerClassLoader(String connectionString) {
          this(ClassLoader.getSystemClassLoader(), connectionString);
      }
  
      public DerbyServerClassLoader(ClassLoader parent, String connectionString) {
          super(parent);
          this.parent = parent;
          this.connectionString = connectionString;
      }
  
      @Override
      public Class&lt;?&gt; findClass(String name) throws ClassNotFoundException {
          Class cls = null;
  
          try {
              cls = parent.loadClass(name);           // Delegate to the parent Class Loader
          } catch (ClassNotFoundException clnfE) {    // If parent fails, try to locate and load the class
              byte[] bytes = new byte[0];
              try {
                  bytes = loadClassFromDatabase(name);
              } catch (SQLException sqlE) {
                  throw new ClassNotFoundException("Unable to load class", sqlE);
              }
              return defineClass(name, bytes, 0, bytes.length);
          }
  
          return cls;
      }
  
      private byte[] loadClassFromDatabase(String name) throws SQLException {
          PreparedStatement pstmt = null;
          Connection connection = null;
  
          try {
              connection = DriverManager.getConnection(connectionString);
  
              String sql = "SELECT CLASS FROM CLASSES WHERE CLASS_NAME = ? AND IS_ENABLED = ?";
              pstmt = connection.prepareStatement(sql);
              pstmt.setString(1, name);
              pstmt.setBoolean(2, true);
              ResultSet rs = pstmt.executeQuery();
  
              if (rs.next()) {
                  Blob blob = rs.getBlob(1);
                  byte[] data = blob.getBytes(1, (int) blob.length());
                  return data;
              }
          } catch (SQLException e) {
              System.out.println("Unexpected exception: " + e.toString());
          } catch (Exception e) {
              System.out.println("Unexpected exception: " + e.toString());
          } finally {
              if (pstmt != null) {
                  pstmt.close();
              }
  
              if(connection != null) {
                  connection.close();
              }
          }
  
          return null;
      }
  
  }
  </code></pre>
  
  <h1>Greet.java - Greeter Interface</h1>
  
  <pre><code>package com.levent.greeter;
  
  public interface Greet {
  
      public String getGreetMessage();
  
  }
  </code></pre>
  
  <h1>DbSingleton.java - Utility Class to connect DerbyDb</h1>
  
  <pre><code>package com.levent.derbyutility;
  
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.SQLException;
  
  public class DbSingleton {
  
      private static DbSingleton instance = null;
  
      private Connection conn = null;
  
      private DbSingleton() {
          try{
              DriverManager.registerDriver(new org.apache.derby.jdbc.EmbeddedDriver());
          } catch (SQLException e) {
              e.printStackTrace();
          }
      }
  
      public static DbSingleton getInstance() {
          if(instance == null) {
              synchronized(DbSingleton.class) {
                  if(instance == null) {
                      instance = new DbSingleton();
                  }
              }
          }
  
          return instance;
      }
  
      public Connection getConnection() throws SQLException {
          if(conn == null || conn.isClosed()) {
              synchronized (DbSingleton.class) {
                  if(conn == null || conn.isClosed()) {
                      try{
                          //String dbUrl = "jdbc:derby://localhost:1527/myDB;create=true;user=me;password=mine";
                          String dbUrl = "jdbc:derby://localhost:1527/memory:myDB;create=true";
  
                          conn = DriverManager.getConnection(dbUrl);
                      } catch (SQLException e) {
                          e.printStackTrace();
                      }
                  }
              }
          }
  
          return conn;
      }
  
  }
  </code></pre>
  
  <h1>ClientClassLoaderDBVersionDemo.java - Test Code</h1>
  
  <pre><code>package com.levent.example;
  
  import java.io.ByteArrayInputStream;
  import java.io.DataInputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.io.InputStream;
  import java.nio.file.Path;
  import java.nio.file.Paths;
  import java.sql.Connection;
  import java.sql.PreparedStatement;
  import java.sql.SQLException;
  import java.sql.Statement;
  
  import com.levent.classloader.DerbyServerClassLoader;
  import com.levent.derbyutility.DbSingleton;
  import com.levent.greeter.Greet;
  
  public class ClientClassLoaderDBVersionDemo {
  
      // apache derby in-memory db
      private static final String connectionString = "jdbc:derby://localhost:1527/memory:myDB;create=true";
      private static final String classFileName1 = "Greeter.class.v1";
      private static final String classFileName2 = "Greeter.class.v2";
      private static final String className = "com.levent.greeter.Greeter";
  
      public static void main(String[] args) {
          prepareClass();
  
          try {
              Greet greet = null;
  
              DerbyServerClassLoader cl = new DerbyServerClassLoader(connectionString);
  
              updateVersion(className, "v1");
              Class clazz1 = cl.findClass(className);
              greet = (Greet) clazz1.newInstance();
              System.out.println("Version 1 Greet.getGreetMessage() : " + greet.getGreetMessage());
  
              updateVersion(className, "v2");
              Class clazz2 = cl.findClass(className);
              greet = (Greet) clazz2.newInstance();
              System.out.println("Version 2 Greet.getGreetMessage() : " + greet.getGreetMessage());           
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
          } catch (InstantiationException e) {
              e.printStackTrace();
          } catch (IllegalAccessException e) {
              e.printStackTrace();
          }
      }
  
      private static void prepareClass() {
          DbSingleton instance = DbSingleton.getInstance();
  
          Connection conn = null;
  
          try {
              conn = instance.getConnection();
          } catch (SQLException e1) {
              e1.printStackTrace();
          }
  
          Statement sta;
  
          if (conn != null) {
              try {
                  sta = conn.createStatement();
                  int count = sta
                          .executeUpdate("CREATE TABLE CLASSES (CLASS_NAME VARCHAR(50), CLASS BLOB, IS_ENABLED BOOLEAN, VERSION VARCHAR(10) )");
                  System.out.println("CLASSES Table created");
                  sta.close();
  
                  sta = conn.createStatement();
  
                  PreparedStatement psta = conn.prepareStatement("INSERT INTO CLASSES (CLASS_NAME, CLASS, IS_ENABLED, VERSION) values (?, ?, ?, ?)");
                  byte[] bytes = null;
                  InputStream blobObject = null;
  
                  psta.setString(1, className);
                  bytes = readJarFileAsByteArray(classFileName1);
                  blobObject = new ByteArrayInputStream(bytes); 
                  psta.setBlob(2, blobObject, bytes.length);
                  psta.setBoolean(3, false);
                  psta.setString(4, "v1");
                  count = psta.executeUpdate();
  
                  psta.setString(1, className);
                  bytes = readJarFileAsByteArray(classFileName2);
                  blobObject = new ByteArrayInputStream(bytes); 
                  psta.setBlob(2, blobObject, bytes.length);
                  psta.setBoolean(3, false);
                  psta.setString(4, "v2");
                  count += psta.executeUpdate();
  
                  System.out.println(count + " record(s) created.");
                  sta.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
  
      private static byte[] readJarFileAsByteArray(String classFileName) {
          Path currentRelativePath = Paths.get("");
          String s = currentRelativePath.toAbsolutePath().toString();
  
          File file = new File(s + "/lib/classes/" + classFileName);
          byte[] fileData = new byte[(int) file.length()];
          DataInputStream dis;
          try {
              dis = new DataInputStream(new FileInputStream(file));
              dis.readFully(fileData);
              dis.close();
          } catch (FileNotFoundException e) {
              e.printStackTrace();
          } catch (IOException e) {
              e.printStackTrace();
          }
  
          return fileData;
      }
  
      private static void updateVersion(String className, String version) {
          DbSingleton instance = DbSingleton.getInstance();
  
          Connection conn = null;
  
          try {
              conn = instance.getConnection();
          } catch (SQLException e1) {
              e1.printStackTrace();
          }
  
          Statement sta;
  
          if (conn != null) {
              try {
                  int count = 0;
                  sta = conn.createStatement();
                  PreparedStatement psta = conn.prepareStatement("UPDATE CLASSES SET IS_ENABLED = ? WHERE CLASS_NAME = ?");
                  psta.setBoolean(1, false);
                  psta.setString(2, className);
                  count = psta.executeUpdate();
                  System.out.println(count + " record(s) updated.");
  
                  psta = conn.prepareStatement("UPDATE CLASSES SET IS_ENABLED = ? WHERE CLASS_NAME = ? AND VERSION = ?");
  
                  psta.setBoolean(1, true);
                  psta.setString(2, className);
                  psta.setString(3, version);
  
                  count = psta.executeUpdate();
  
                  System.out.println(count + " record(s) updated.");
                  sta.close();
              } catch (SQLException e) {
                  e.printStackTrace();
              }
          }
      }
  
  }
  </code></pre>
  
  <h1>Output</h1>
  
  <pre><code>CLASSES Table created
  2 record(s) created.
  2 record(s) updated.
  1 record(s) updated.
  Version 1 Greet.getGreetMessage() : Hail to the King Baby!
  2 record(s) updated.
  1 record(s) updated.
  Exception in thread "main" java.lang.LinkageError: loader (instance of  com/levent/classloader/DerbyServerClassLoader): attempted  duplicate class definition for name: "com/levent/greeter/Greeter"
      at java.lang.ClassLoader.defineClass1(Native Method)
      at java.lang.ClassLoader.defineClass(Unknown Source)
      at java.lang.ClassLoader.defineClass(Unknown Source)
      at com.levent.classloader.DerbyServerClassLoader.findClass(DerbyServerClassLoader.java:38)
      at com.levent.example.ClientClassLoaderDBVersionDemo.main(ClientClassLoaderDBVersionDemo.java:43)
  </code></pre>