Giter VIP home page Giter VIP logo

jcl's Introduction

Overview

JCL is a configurable, dynamic and extensible custom classloader that loads java classes directly from Jar files and other sources. The motivation was to create isolated classloaders, which can be easily integrated with IoC frameworks like Spring and with web applications.

The entire library, including it’s code-base and documentation is available under the terms and conditions of Apache License 2.0.

Installation

To use JCL, download and build the JCL project and put jcl(-core-2.x).jar and dependencies in the application’s classpath. See the build section below and also check out the downloads section for more details on the available builds and source.

JCL is also available in Maven Central Repository, which makes it very easy to use in maven projects as a dependency.

Building JCL

JCL is a multi-module maven project that has five modules as shown below:


 JCL
  |---> core
  |---> spring
  |---> web
  |---> test-jcl
  |---> test-web

The test-* modules create resources to run the unit tests and a sample web application. The core module builds an artifact with all the main JCL class files. The spring module generates an artifact for spring integration.

To build JCL, check-out the source code of JCL2 from github and run the following command:


 mvn clean install

This command will create jcl-core and jcl-spring artifacts, and will also create a sample war file to test JCL web integration. It will also copy the dependencies of each artifact in the dependecies folder.

Requirements

JCL requires JDK 1.5 or later; and will pull in all its dependencies using maven.

Usage

Below is a little tutorial on JCL v2 with examples. Please also see the overview section for installation.

What’s new in v2

Version 2.x now deprecates version 1.x so we recommend users to switch to version 2, which was a complete refactoring of version 1 and has a better design with a lot more features. Following is a list of some updates and changes in v2.x:

  • Total refactoring of version 1
  • Maven is being used for project management instead of ANT
  • Added more robust spring and web support
  • Added OSGi boot loading
  • JCL 2 is more customizable and configurable
  • etc. etc.

Using JCL

JCL is a light weight API and has only a few but useful classes. Here is a simple example on how to programmatically use JCL. JarClassLoader has an arguments-constructor and add methods that take jar-file/class-folder paths, URLs and InputStreams.


  JarClassLoader jcl = new JarClassLoader();

  //Loading classes from different sources
  jcl.add("myjar.jar");
  jcl.add(new URL("http://myserver.com/myjar.jar"));
  jcl.add(new FileInputStream("myotherjar.jar"));
  jcl.add("myclassfolder/");

  //Recursively load all jar files in the folder/sub-folder(s)
  jcl.add("myjarlib/");

  JclObjectFactory factory = JclObjectFactory.getInstance();

  //Create object of loaded class
  Object obj = factory.create(jcl, "mypack.MyClass");

Now we can use reflection to invoke methods of this object. It is also possible to create object proxies and call methods the normal way, this is done by using cast(able) methods available in JclUtils class explained later.

Creating a JCL Context

In order to access the created JarClassLoader instance from any where in the application a JclContext must be created. The DefaultContextLoader provides a way to create this context for a single programmatically created JarClassLoader instance.


  JarClassLoader jcl = new JarClassLoader();
  jcl.add("myjarlib/");

  DefaultContextLoader context=new DefaultContextLoader(jcl);
  context.loadContext();

Now “jcl” can be accessed from anywhere in the application as follows.


  JarClassLoader jcl=JclContext.get(); // returns the Default JCL instance

It is also possible to create an XML configuration file for JCL and use XmlContextLoader to create the context.

Using JCL in Web Applications

One of the motivations behind JCL was to be able to create multiple isolated classloaders in a single web application hosted on application servers like JBoss, Tomcat etc. This is achieved with JclContextLoaderListener.

The JCL configuration is put in a XML file and then the JclContextLoaderListener is used to load the context. Below is an example of JCL XML configuration.
Note: as of v2.6 jcl-web dependency is required for the following.


<?xml version="1.0" encoding="UTF-8"?>
<jcl-context>
    <jcl name="jcl1">
        <loaders>
            <loader name="jcl.parent">
                <order>3</order>
                <enabled>true</enabled>
            </loader>
            <loader name="jcl.local">
                <order>1</order>
                <enabled>true</enabled>
            </loader>
            <loader name="jcl.current">
                <enabled>false</enabled>
            </loader>
            <loader name="jcl.thread">
                <enabled>true</enabled>
            </loader>
            <loader name="jcl.system">
                <enabled>true</enabled>
            </loader>
            <loader name="jcl.bootosgi">
                <enabled>true</enabled>
                <strict>true</strict>
                <bootDelegation>mypack.system.*</bootDelegation>
            </loader>
            <loader name="custom" class="mypack.MyLoader">
                <order>2</order>
                <enabled>true</enabled>
            </loader>
        </loaders>
        <sources>
            <source>webapp:WEB-INF/mylib/myjar.jar</source>
            <source>webapp:myotherjar.jar</source>
        </sources>
    </jcl>
    <jcl name="jcl2">
        <sources>
            <source>webapp:WEB-INF/myjarlib/</source>
        </sources>
    </jcl>
</jcl-context>

In this example two classloaders are created in a single context. The source paths starting with webapp: are treated as paths to internal web application jar files and folders. After this configuration, the context can be loaded in the web application by adding the JclContextLoaderListener in the application’s web.xml file.


 <context-param>
    <param-name>jcl-context</param-name>
    <param-value>
      classpath:jcl.xml
    </param-value>
  </context-param>

  <listener>
    <listener-class>org.xeustechnologies.jcl.web.JclContextLoaderListener</listener-class>
  </listener>

The JCL instances can then be accessed by name from anywhere in the web application as shown below.


  JarClassLoader jcl1=JclContext.get("jcl1");
  JarClassLoader jcl2=JclContext.get("jcl2");

Sample web application

The JCL project also includes a sample web application that demonstrates JCL web-app integration. The jcltest.war file is created in the test-web module at jcl-project/test-web/target when the project is built. The content of the war file is also under the same module and is located in jcl-project/test-web/src/main folder. The exploded war folder contains all the files that clearly shows the usage of JCL in web applications.

After deploying the sample web application, go to http://server-name:8080/jcltest; this will print “Hello World” on the screen with classloader details. This message is returned from a JCL-loaded object, see jcl-project/test-web/src/main/jcltest.war/index.jsp for more details.

Using JCL with Spring Framework

JCL can be used with spring framework. JCL v2 provides Spring XML extension to load beans via JCL. The JCL bean is created using the new jcl:jcl element and spring beans can reference the jcl bean by using the jcl:jcl-ref element as shown in the example below. These elements are defined in the jcl-schema.xsd. This also works well in a web application using the Spring ContextLoaderListener. In addition to this JCL v2 also supports the older way of Spring integration, please see the spring-test.xml file provided with the source.


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jcl="http://www.xeustechnologies.org/schema/jcl"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.xeustechnologies.org/schema/jcl http://www.xeustechnologies.org/schema/jcl/jcl.xsd">

    <jcl:jcl id="jcl1">
        <constructor-arg>
            <list>
                <value>target/test-jcl.jar</value>
            </list>
        </constructor-arg>
    </jcl:jcl>

    <jcl:jcl id="jcl2">
        <constructor-arg>
            <list>
                <value>target/test-jcl.jar</value>
            </list>
        </constructor-arg>
    </jcl:jcl>

    <bean id="test1" class="org.xeustechnologies.jcl.test.Test">
        <jcl:jcl-ref ref="jcl1" />
        <constructor-arg ref="name"/>
        <property name="lastName">
                <value>Zafar</value>
        </property>
    </bean>

    <bean id="name" class="java.lang.String">
        <jcl:jcl-ref ref="jcl2" />
        <constructor-arg>
            <value>Kamran</value>
        </constructor-arg>
    </bean>
</beans>

Casting objects of types loaded in JCL

Java does not allow casting objects of types loaded in a different classloader; such casting attempt results in a ClassCastException. But sometimes it is necessary to cast objects to obtain interface references loaded in the current classloader. E.g. suppose that an API implementation is loaded using JCL and the API itself is loaded in the current classloader, now it is easy to use the interface reference to invoke methods than using reflection. JCL provides a few ways to obtain interface references and to convert the JCL-loaded objects into castable objects. This is actually done by internally creating proxies for the JCL-loaded objects. JCL, as of version 2.2, can also create cglib dynamic proxies apart from the regular jdk proxies, which makes it possible to create proxies for any class not just interfaces. It is also possible to create auto-proxies/castable-objects from the object factory, which is very handy because then the objects can be used and casted the normal way.

Auto creating castable objects from factory

In version 2.2 and later, the JclObjectFactory can be used to auto-create “castable” objects, by passing true to the getInstance method. Now every time an object is created, it can be casted to references in the current classloader without the need of JclUtils. By default JCL2 uses jdk proxies but that can be changed by specifying the proxy provider, as shown in the example below.


  JarClassLoader jcl = new JarClassLoader();
  jcl.add("myapi-impl.jar"); //Load jar file

  // Set default to cglib (from version 2.2.1)
  ProxyProviderFactory.setDefaultProxyProvider( new CglibProxyProvider() );

  //Create a factory of castable objects/proxies
  JclObjectFactory factory = JclObjectFactory.getInstance(true);

  //Create and cast object of loaded class
  MyInterface mi = (MyInterface) factory.create(jcl,"myapi.impl.MyInterfaceImpl");

Auto proxying can also be dynamically enabled using a jvm command line argument:


  -Djcl.autoProxy=true

Casting objects when required

By default the object factory creates objects of classes loaded in JCL without proxies. So in order to cast/clone the objects, JclUtils class is used as shown in the example below:


  JarClassLoader jcl = new JarClassLoader();
  jcl.add("myapi-impl.jar"); //Load jar file

  //Create default factory
  JclObjectFactory factory = JclObjectFactory.getInstance();

  //Create object of loaded class
  Object obj = factory.create(jcl,"myapi.impl.MyInterfaceImpl");

  //Obtain interface reference in the current classloader
  MyInterface mi = JclUtils.cast(obj, MyInterface.class);

  //Convert the object into a castable object in the current classloader (jdk proxy)
  MyInterface mi1 = (MyInterface) JclUtils.toCastable(obj, MyInterface.class);

  //Clone "Serializable" object into a castable object in the current classloader
  //The JCL-loaded object must implement Serializable
  //This method is now deprecated and the use of deepClone is recommended
  MyInterface mi2 = (MyInterface) JclUtils.clone(obj);

  //Clone any object Serializable and non-Serializable
  MyInterface mi2 = (MyInterface) JclUtils.deepClone(obj);

Manipulating class loading order & adding custom classloaders

JCL v2 by default looks for classes in five places in order, local JCL class loader (order=1), current class loader (order=2), parent class loader (order=3), in thread context class loader (order=4) and then in system class loader (order=5). There is a sixth class loader available for Osgi boot delegation; it is described later in the document. In addition to built-in loaders, more class loaders can be added by extending the org.xeustechnologies.jcl.ProxyClassLoader class. Each loader has a loading order and the default loading orders can be changed. Below is an example that shows how to do this:


  JarClassLoader jcl=new JarClassLoader();
  jcl.add("myjar.jar"); // Add some class source

  jcl.getSystemLoader().setOrder(1); // Look in system class loader first
  jcl.getLocalLoader().setOrder(2); // if not found look in local class loader
  jcl.getParentLoader().setOrder(3); // if not found look in parent class loader
  jcl.getThreadLoader().setOrder(4); // if not found look in thread context class loader
  jcl.getCurrentLoader().setOrder(5); // if not found look in current class loader

  // A custom class loader that extends org.xeustechnologies.jcl.ProxyClassLoader
  MyLoader loader=new MyLoader();
  loader.setOrder(6);

  jcl.addLoader(loader); //Add custom loader

Enable/Disable classloaders

JCL also provides a way to enable/disable classloaders. This can be done both programmetically and by passing a JVM -D arg.


  JarClassLoader jcl=new JarClassLoader();
  jcl.add("myjar.jar"); // Add some class source

  //Disable parent class loader
  jcl.getParentLoader().setEnabled(false);

Similarly a -D argument can be passed to the VM; this is done by passing a boolean values to the loader’s class name.


  -Dorg.xeustechnologies.jcl.AbstractClassLoader$ParentLoader=false\
  -Dmypack.MyLoader=false

Enable OSGi boot delegation

Sometimes it is required to delegate some classes to the parent loader; this is part of the OSGi Spec for boot delegation. This can be enabled by passing the following argument to the JVM.


  -Dosgi.bootdelegation=true

And as spec’d, you can also pass classes and packages as a JVM argument


  -Dorg.osgi.framework.bootdelegation=mypack.*

By default the OSGi boot delegation is not strict and if the class is not found in the parent classloader then JCL will try to find it in other loaders. OSGi boot delegation can be made strict and a ClassNotFoundException is thrown if the class is not found in parent.


  -Dosgi.bootdelegation.strict=true

Note: The order of OSGi boot loader cannot be changed. The osgi boot delegation can also be turned on programetically, please see the LoadTest.java provided with the source.

Other configuration

Some other JCL tweaks.

Class collisions

JCL by default ignore class collisions and the first class that is found gets loaded. It is also possible to throw exception on collision, this is done as follows:


  -Djcl.suppressCollisionException=false

Missing Jars

JCL by default ignores all missing jars/class sources. This default behaviour can be changes both programmatically and on runtime:


  -Djcl.suppressMissingResourceException=false

Using parent log config.

JCL uses an isolated log4j logger, which sometimes spits out a lot of lines on the console. The default log level has now been lowered to INFO but to disable it entirely and use the parent log config, pass the following argument to JVM. Version 2.3 and above does not use Log4J. Version 2.8 and later uses SLF4J.


  -Djcl.isolateLogging=false

Maven Dependency


  <dependency>
 	<groupId>org.xeustechnologies</groupId>
	<artifactId>jcl-core</artifactId> 
	<version>2.8</version>
  </dependency>

jcl's People

Contributors

b4hand avatar donald-w avatar geronimo-iia avatar harbulot avatar kamranzafar avatar mmichalek-micros avatar qxo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jcl's Issues

Support for directory entries from jar files

It would be great if JCL had an ability to load not only the files from .jar, but also the directories. It is now not supported, since there's a line in JarResources class:

                if (jarEntry.isDirectory()) {
                    continue;
                }

Without that ability it's impossible to scan the classpath for the files within directories and use the tools like PathMatchingResourcePatternResolver (from Spring), which is working great with the standard URLClassLoader.

How to install?

mvn clean install
[INFO] Scanning for projects...
Downloading: https://repo.maven.apache.org/maven2/org/sonatype/oss/oss-parent/5/
oss-parent-5.pom
[ERROR] The build could not read 1 project -> [Help 1]
[ERROR]
[ERROR] The project org.xeustechnologies:jcl:2.5 (C:\Users\I302583\Downloads\J
CL-jcl-2.5\JCL2\pom.xml) has 1 error
[ERROR] Non-resolvable parent POM: Could not transfer artifact org.sonatype.
oss:oss-parent:pom:5 from/to central (https://repo.maven.apache.org/maven2): rep
o.maven.apache.org and 'parent.relativePath' points at wrong local POM @ line 13
, column 10: Unknown host repo.maven.apache.org -> [Help 2]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e swit
ch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please rea
d the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/ProjectBuildin
gException
[ERROR] [Help 2] http://cwiki.apache.org/confluence/display/MAVEN/UnresolvableMo
delException

java.lang.LinkageError

  @Test
  public void test() throws Exception {
JarClassLoader jcl = new JarClassLoader();

//adding Test.jar (consist of com.google.at.mortezaadi.Sample)
//to JCL
jcl.add("test/Test.jar");

// insuring JCL can load the class
Class loadedClass = jcl.loadClass(
    "com.google.at.mortezaadi.Sample");
assertNotNull(loadedClass);

// unload class file inside the Test.Jar
jcl.unloadClass("com.google.at.mortezaadi.Sample");

// insuring jcl unloaded the class
boolean classExsist = false;
try{
    jcl.loadClass("com.google.at.mortezaadi.Sample");
    classExsist = true;
}catch(Exception ex) {
}
assertTrue(!classExsist);

/******************************/
//adding jar file again  
jcl.add("test/Test.jar");

   /** loading file again THROWS EXCEPTION
 * java.lang.LinkageError: loader (instance of  org/xeustechnologies/jcl/JarClassLoader): attempted  duplicate    class definition for name: "com/google/at/mortezaadi/Sample"
 */
loadedClass = jcl.loadClass(
    "com.google.at.mortezaadi.Sample");
}

unload whole jarFile

Hey there,
not sure, if this is the right place for my question. I'm currently working on a launcher for my java-app. This launcher loads a jarFile with your JavaClassLoader and starts the main app. Now, I want to update this jarFile, but it is locked (maybe by the ClassLoader?).
Is there a possibility to unload the whole jarFile to replace it with a newer one?

Greetings from Germany
Fabian

Android suporte

Do not know if that library should work on android, but the test did not get good results.

package com.example.JavaDN;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import org.xeustechnologies.jcl.JarClassLoader;
import org.xeustechnologies.jcl.JclObjectFactory;

import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends Activity implements Runnable {

    JarClassLoader mJcl;
    TextView log;

    /**
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mJcl = new JarClassLoader();

        log = (TextView) findViewById(R.id.log);

        new Thread(this).start();
    }

    @Override
    public void run() {
        log.setText("Loading lib...");
        try {
            mJcl.add(new URL("https://dl.dropboxusercontent.com/u/67269258/lib_class.jar"));
        } catch (MalformedURLException e){
            e.printStackTrace();
        }

        JclObjectFactory factory = JclObjectFactory.getInstance();

        //Create object of loaded class
        Object obj = factory.create(mJcl, "Math");

        log.setText("ready! " + obj);
    }
}

Error:

06-08 19:17:15.799    7604-7617/com.example.JavaDN E/dalvikvm﹕ ERROR: defineClass(0x4261d390, Math, 0x42691238, 0, 812)
06-08 19:17:15.799    7604-7617/com.example.JavaDN W/dalvikvm﹕ threadid=11: thread exiting with uncaught exception (group=0x41bef700)
06-08 19:17:15.804    7604-7617/com.example.JavaDN E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-3011
    org.xeustechnologies.jcl.exception.JclException: java.lang.UnsupportedOperationException: can't load this type of class file
            at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:102)
            at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:85)
            at com.example.JavaDN.MainActivity.run(MainActivity.java:44)
            at java.lang.Thread.run(Thread.java:841)
     Caused by: java.lang.UnsupportedOperationException: can't load this type of class file
            at java.lang.VMClassLoader.defineClass(Native Method)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:292)
            at java.lang.ClassLoader.defineClass(ClassLoader.java:256)
            at org.xeustechnologies.jcl.JarClassLoader.access$000(JarClassLoader.java:50)
            at org.xeustechnologies.jcl.JarClassLoader$LocalLoader.loadClass(JarClassLoader.java:254)
            at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:122)
            at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:95)
            at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:100)
            at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:85)
            at com.example.JavaDN.MainActivity.run(MainActivity.java:44)
            at java.lang.Thread.run(Thread.java:841)

Don't close InputStream

Currently if you add InputStream to jcl instance, it will close it.
This is due to the use of a BufferedInputStream which on close, close underlying inputstream.
This is damageable when you use a ZipInputStream which contains many different jars which has to be loaded into jcl.
Jcl should not close resources give to it.
Bypass is to pass a new instance of an InputStream on each ZipEntry which match jar file.

Problematic code:
`
public static void loadJars(final InputStream stream) throws IOException {

final JarClassLoader jcl = new JarClassLoader();
try (ZipInputStream zip = new ZipInputStream(stream)) {
    ZipEntry entry;
    while ((entry = zip.getNextEntry()) != null) {
    final String name = entry.getName();
    if (name.matches(".*\\.jar")) {
        jcl.add(zip);
    }
    }
}

}`

JarClassloader reports incorrect URL's for resources.

Example code:
jarClassLoader.getResource("some-resources.xml");

This method has a high likelihood of return the wrong URL for the requested resource. The URL will reflect the last jar loaded by JarResources and not the actual jar where the resource is located.

This issue results from the fact that JarResources has a single baseUrl but is capable of loading resources from multiple jars. I have a change to track baseUrls of the resources in JarResource by changing from Map<String, byte[]> to Map<String, JclJarResource> and will submit a patch.

getResource in local loader is broken

getResource in the local loader throws an exception if no jar has ever been loaded.
This is incorrect: it should return NULL so other loaders have a chance of returning the resource.

Note that PR #24 fixes this issue as well and I strongly suggest to accept it.

With Java8

Hi,

We have been using this for a while and always thank you !

We tested our code with Java 8 and found an exception, which was never seen before.

Caused by: java.util.ConcurrentModificationException: null

    at java.util.ArrayList.sort(ArrayList.java:1456) ~[na:1.8.0_31]

    at java.util.Collections.sort(Collections.java:141) ~[na:1.8.0_31]

    at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:97) ~[ebinocle-indexer-25.9-jar-with-dependencies.jar:25.9]

    at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:82) ~[ebinocle-indexer-25.9-jar-with-dependencies.jar:25.9]

    at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:95) ~[ebinocle-indexer-25.9-jar-with-dependencies.jar:25.9]

In Java8, Collections.sort is implemented differently from the previous Java.

Someone had a similar issue and wrote about it here:
http://onelineatatime.io/collections-sort-the-java8u20-modification/

I had a look at the relevant code.
Moving 'Collections.sort' into methods modifying the list (addDefaultLoader & addLoader) can be a simple solution but I am unsure if it is okay enough.

I would appreciate it if you have a look at this issue.

Regards,
Youngmi.

Enabling Thread Loader causes stack overflow with spring

Caused by: java.lang.StackOverflowError at java.io.UnixFileSystem.getBooleanAttributes0(Native Method) at java.io.UnixFileSystem.getBooleanAttributes(UnixFileSystem.java:242) at java.io.File.exists(File.java:772) at sun.misc.URLClassPath$FileLoader.getResource(URLClassPath.java:1072) at sun.misc.URLClassPath$FileLoader.findResource(URLClassPath.java:1039) at sun.misc.URLClassPath.findResource(URLClassPath.java:176) at java.net.URLClassLoader$2.run(URLClassLoader.java:551) at java.net.URLClassLoader$2.run(URLClassLoader.java:549) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findResource(URLClassLoader.java:548) at java.lang.ClassLoader.getResource(ClassLoader.java:1139) at java.net.URLClassLoader.getResourceAsStream(URLClassLoader.java:227) at org.xeustechnologies.jcl.AbstractClassLoader$CurrentLoader.loadResource(AbstractClassLoader.java:286) at org.xeustechnologies.jcl.AbstractClassLoader.getResourceAsStream(AbstractClassLoader.java:158) at org.xeustechnologies.jcl.AbstractClassLoader$ThreadContextLoader.loadResource(AbstractClassLoader.java:329) at org.xeustechnologies.jcl.AbstractClassLoader.getResourceAsStream(AbstractClassLoader.java:158) at org.xeustechnologies.jcl.AbstractClassLoader$ThreadContextLoader.loadResource(AbstractClassLoader.java:329) at org.xeustechnologies.jcl.AbstractClassLoader.getResourceAsStream(AbstractClassLoader.java:158) at org.xeustechnologies.jcl.AbstractClassLoader$ThreadContextLoader.loadResource(AbstractClassLoader.java:329)

Struggling with class casting

I have what is a relatively simple/standard setup; a plugin design pattern.

I can create the plugin in the new JCL classloader and get a reference to it with no problems. I create the reference to the interface using:

myPlugin = JclUtils.cast(...)

Now any method that I pass in standard JDK objects; no problems whatsoever. Everything is happy. However the problem is when i am attempting to get at a function that is passing a custom interface to it.

So my main callable interface is:

public IJobResult handle( IJobMessage job )

I create an instance of IJobMessage in the main code and when i attempt to call myPlugin.handle() it keeps

com.sun.proxy.$Proxy0 threw unexpected java.lang.reflect.UndeclaredThrowableException

with no such method. I have tried to JclUtils.cast() on the object that implements IJobMessage but that doesn't work either.

So the question is, how do I create an object in the base code and pass that to my loaded plugin loaded by JCL?

Thank you for assistance you can lend

getResources() does not return all resources in all jars of a given classloader

I'm using JCL in a simple manner, I think. I create a classloader and add() a bunch of jars.

Some of these jars have identically named resources. From this class loader I try getResources() expecting the enumeration to find all the resources. Unfortunately, I only get the first resource.

I debugged the code and it seems that every classloader only returns one resource through findResource(), I think..

Am I doing something really wrong ?

2.8 Release

Are there plans on a 2.8 release? Is this project still active?

Is it work for JCL to load a jar with his dependen jars?

Is it work for JCL to load a jar with his dependen jars? Can you give an example if this is true? Prefer such example in /test. Thanks.

The background is I use JCL to avoid httpclient version conflicts. My jar depends on Rest-Assured jar, and Rest-Assured depends httpclient jar. Can I use JCL to load all 3 jars, so I can avoid confilicts?

ClassLoader order is broken

By default, JCL uses the local loader for loading classes. Hence, JCL will by default always prefer its own loader to load classes from the jar. However, this makes it impossible for the application code to pass arguments to dynamically loaded classes, as the arguments use the application's class loader, where as the dynamic class' method parameter types use the JCL class loader:

Let's assume these classes are both in the application code and the imported Jar:

class Engine { }
interface Car { void setEngine(Engine engine); }

Then this does not work:

JarClassLoader jcl = new JarClassLoader();
jcl.add("fancyCars.jar"); // Contains class FancyCar implements Car {}
Object o = factory.create(jcl, "FancyCar");
Car car = JclUtils.cast(o, Car.class);

Engine engine = new DefaultEngine(); // Create some engine

// This statement breaks:
car.setEngine(engine);

// Reason:
// engine.getClass().getClassLoader() uses the application's class loader, whereas
// the Engine class that appears as the parameter type of FancyCar::setEngine is loaded via JCL

Per documenation, a work around should be to simply use the local class loader last, however this leads to another required workaround:

jcl.getLocalLoader().setOrder(100);

// This is a workaround to force updating the internal order of loaders;
// as of jcl 2.9-SNAPSHOT, there is no e.g. jcl.updateLoaderOrder() method
// I suggest to introduce it.

jcl.addLoader(jcl.getLocalLoader()); // Adding a loader updates the order

JCL to load plugin.jar with driver inside

Hi,
I'm a server side Java app (run with maven jetty) and I need to load, when required, a plugin.jar that is outside the classpath.
I do this with this code:

JarClassLoader jcl = new JarClassLoader();
jcl.add(pluginFile.getPath());
JclObjectFactory factory = JclObjectFactory.getInstance();

            // Create object of loaded class

            JarFile jarFile = new JarFile(pluginFile);
            Enumeration<JarEntry> e = jarFile.entries();
            while (e.hasMoreElements()) {
                JarEntry je = (JarEntry) e.nextElement();
                if (je.isDirectory() || !je.getName().endsWith(".class")) {
                    continue;
                }
                // -6 because of .class
                String className = je.getName().substring(0, je.getName().length() - 6);
                className = className.replace('/', '.');
                Object obj = factory.create(jcl, className);
                log.debug("Classe restituita: "+obj);
                return obj;
            }

then I invoke a method from this plugin class, say doSomething().

My plugin class in short make an access to a datasource (Dmbs) and inside is is included the jar needed to access the db.

in my doSomething() method I do this:

JarClassLoader jcl = new JarClassLoader();
jcl.add(".");
JclObjectFactory factory = JclObjectFactory.getInstance();
// Create object of loaded class
Object obj = factory.create(jcl, "com.mysql.jdbc.Driver");
Class.forName(dataSet.getDriver(), true, jcl);

Unfortunally when my server app try ti invoke doSomething() method I've always this exception:

26/07/2014 10:17:01 ERROR PluginManager:70 -
java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/db
at java.sql.DriverManager.getConnection(DriverManager.java:689)
at java.sql.DriverManager.getConnection(DriverManager.java:247)

Jcl is supposed to solve this situation?

Thanks

JMS client is not working when initial context factory is loaded using JCL class loader

I'm trying to implement a JMS client using Weblogic implementation, but its not working correctly.
The inicial context factory class of Weblogic JMS implementation is weblogic.jndi.WLInitialContextFactory.
I was looking at the code and I found the reason it was not working. The class mentioned above is found by JCL, but for some reason, JCL is creating the instance in a different way of the class loader used by JMS, the context class loader.

// JMS is instantiating the initial context factory this way:
loadClass(className, getContextClassLoader())

// The code above call...
Class.forName(className, true, classLoader)

// Getting class loader:
ClassLoader getContextClassLoader() {
  return AccessController.doPrivileged(
    new PrivilegedAction<ClassLoader>() {
      public ClassLoader run() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
          // Don't use bootstrap class loader directly!
          loader = ClassLoader.getSystemClassLoader();
        }
        return loader;
      }
    }
  );
}

Loading the initial context factory class using Thread.currentThread().getContextClassLoader() works fine. Using JCL class loader, the class is instantiated, but for some reason I could not connect to JMS server.

How to use JCL with JavaConfig in Spring Boot

Hi I am trying to use JCL with an application developed with Spring Boot. Is it possible to integrate an object created with JCL in Spring Boot context? Your documentation uses an xml, is it possible to integrate the object created with JCL with JavaConfig?

Thanks in advance.
Regards,
Giuseppe

Integration Tutorial

Hi,

this looks very interesting, but my programming skills aren't good enough to understand how to use this library from reading the existing documentation.
It would be great if you could write a tutorial on how use this with Spring with minimal added code. (i.e. without explicitly accessing jcl all the time).

getResourceAsStream shouldn't throw Exception?

Great work on this. I'm using it to create an isolated container for running scheduled tasks.

However, I found an issue when using the JarClassLoader to load a spring configuration file.
During the rather convoluted jaxp/xerces validation sequence that occurs at one point the xerces code attempts to load a configuration setting which may or may not exist 'META-INF/services/org.apache.xerces.xni.parser.XMLParserConfiguration'.

The calling xerces code is not expecting an exception to be thrown if the resource does not exist rather it is simply checking for a null value to be returned. Due to the exception the whole sequence fails etc.

I was able to get around it by subclassing the JarClassLoader and simply overriding the getResourceAsStream method to swallow the exception and throw null. But it took some time to figure out this is what was causing the issue.

You may want to consider not throwing the exception so it is more consistent with the standard ClassLoader.( The current java 6 ClassLoader also does not throw an exception but simply returns null).
Thanks.

Required Package Name

This might not be an issue, but I think it requires documentation. JCL2 requires a package name for loading classes whereas it wasn't required in the original JCL. Code at line 64 in JarClassLoader.java breaks when the class being loaded is not part of a package.

Which License?

I was looking at using this library and wanted to understand the licensing. The code itself appears to be marked with Apache 2.0, but the
JCL/JCL2/core/src/main/resources/LICENSE.txt and
JCL2/pom.xml
both indicate GNU LGPL

Could you let me know which it is? I'm happy to put together a PR for full conversion to Apache 2.0, which I'd need to incorporate this into my project.

Thanks
Donald

not support a class's static methods

I try to use JCL to load a class and use its static methods, but I am failed. I found out static methods are supported in Java 1.8 interface. But it seems JCL does not work even I use this new feature.

Can you help confirm this? Thanks.

load jar from jar Manifest

Hi,

Great library !!! 👍
I've a suggestion for this project : add JAR cascade loading support (by using MANIFEST.MF).

// Some possible snippet :
JarClassLoader jcl = new JarClassLoader();

//Loading classes from different sources
// true to allow JCL to load jar defined in manifest
jcl.add("myjar.jar", true);

Cannot load class which is presented in JAR

Hi!

I'm trying to do this

jcl.loadClass("com.bea.xml.stream.MXParserFactory");

But getting ClassNotFound exception. This class is presented in JAR - I can see it in archive. What's wrong with my code?

Heap space

Hi, I've this error after some times my class try to load an external jar file.

java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3230)
at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:113)
at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:140)
at org.xeustechnologies.jcl.JarResources.loadJar(JarResources.java:201)
at org.xeustechnologies.jcl.JarResources.loadJar(JarResources.java:119)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:262)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:285)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:285)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:285)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:285)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:285)
at org.xeustechnologies.jcl.ClasspathResources.loadResource(ClasspathResources.java:246)
at org.xeustechnologies.jcl.JarClassLoader.add(JarClassLoader.java:141)

Loadin Java Service Provider Interface (SPI) from JAR via JCL

I load the "lucene-core-4.10.3.jar" via JCL, it works fine.

Then I create my Class as follows :
Object[] arguments = new Object[] { "Param" };
Class[] parameter = new Class[] { String.class };

    JclObjectFactory factory = JclObjectFactory.getInstance();

    Object obj = factory.create(jcl,
            "my.package.impl",
            arguments, parameter);

The constructor of my implementation creates the Lucene "IndexWriterConfig" object which uses the SPI "META-INF/services/org.apache.lucene.codecs.Codec" of the "lucene-core-4.10.3.jar".

The problem is that the SPI - classes were not loaded.
Is this a problem of my coding, has anyone a suggestion?

Tagging the releases

Would it be possible to tag the commits that correspond to a release (for example v2.3, ...)?

Some suggestions

Hi kamranzafar!

First, JCL is really great! I made some small modifications to JCL for my Webstart project. I don't know if they could be useful to others so I post them here

The first one is some kind of destroy method for JarClassLoader. It's useful for me because I have some kind of multigame and use this method to unload claseses and resources from JarClassLoader when not in use. (after seeing the unloadClass of JarClassLoader, maybe the unloading of resources could be done with the unloadClass(key) method too).

    public void destroy() {
      for (String key : getLoadedClasses().keySet())
        unloadClass(key);
      for (String key : getLoadedResources().keySet())
        classpathResources.unload(key);
    }

The second one is more related to the webstart and the ProtectionDomain/CodeSource. For that working I had to update public Class loadClass(String className, boolean resolveIt) of LocalLoader, to include in the defineClass method the protectionDomain (Maybe this is related to #1 issue Set ProtectionDomain/CodeSource??.

result = defineClass( className, classBytes, 0, classBytes.length, getClass().getProtectionDomain());

I think it could also be helpful a constructor for JarClassLoader that receives a parent class loader, for example to do something like this.

jcl = new JarClassLoader(new SecureClassLoader() {
      @Override
      protected PermissionCollection getPermissions(CodeSource codesource) {
        PermissionCollection pcol = super.getPermissions(codesource);
        pcol.add(new AllPermission());
        return (pcol);
      }
  });

The last one, I don't implement it yet, but is a public Enumeration<URL> getResources(String name) throws IOException method for each of the ProxyClassLoader implementations. I think the implementations could be something like:

  • SystemLoader
public Enumeration<URL> getResources(String name) throws IOException 
{
    return getSystemResources(name);
}
  • ParentLoader
public Enumeration<URL> getResources(String name) throws IOException 
{
     return getParent().getResources(name);
}
  • CurrentLoader
public Enumeration<URL> getResources(String name) throws IOException 
{
    return getClass().getClassLoader().getResources(name);
}
  • ThreadContextLoader
public Enumeration<URL> getResources(String name) throws IOException 
{
    return Thread.currentThread().getContextClassLoader().getResources(name);
}

Tell me what you think about them as I can learn from your knowledge in classloading!!!

New SPI issues in 1.7

I originally posted this under the closed #32 but it should live in its own issue since it's a different but related bug.

I just jumped from 2.4 to 2.7 (to fix the intermittent Java8 concurrent modification exception), and now I can no longer create an Elasticsearch (1.7 aka Lucene 2.10.4) Client object, eg

Thread.currentThread().setContextClassLoader(jcl_classloader);

                final ImmutableSettings.Builder test_settings = 
                        ImmutableSettings.settingsBuilder()
                            .put("cluster.name", "aleph2")
                            .put("node.gateway.type", "none")
                            .put("index.store.type", "memory")
                            .put("index.number_of_replicas", 0)
                            .put("index.number_of_shards", 1)
                            .put("node.http.enabled", false);                                       
                NodeBuilder.nodeBuilder().settings(test_settings).loadConfigSettings(false).node();             

will exception out with

An SPI class of type org.apache.lucene.codecs.Codec with name 'Lucene410' does not exist.  You need to add the corresponding JAR file supporting this SPI to your classpath.  The current classpath supports the following names: []: IllegalArgumentException]:[NamedSPILoader.java:109:org.apache.lucene.util.NamedSPILoader:lookup]

Identical code works with 2.4, 2.5, 2.6

(EDIT: obviously I can provide more details, just wanted to give an overview of the issue to start with)

Any chance to dual-license JCL?

I wonder if you have any plans for dual-licensing the JCL library? I would like to use it for an open source project (Micro), project licensed under the Apache 2.

Thank you,
-florin

Suggestion

Is there a possibility to retrieve the Class object without instantiate the Object :

Here's an example
`
JarClassLoader jcl = new JarClassLoader();
JclObjectFactory factory = JclObjectFactory.getInstance();
Class<?> clazz = factory.classOf(jcl,"com.myclass");

`
Thanks in advance

2.7 release?

Wondering when we can expect the 2.7 release. The improvements to resource handling would be nice :)

Test failure on mvn clean install


T E S T S

Running org.xeustechnologies.jcl.LoadTest
Tests run: 14, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.403 sec <<< F
AILURE!
testDefaultContextLoader(org.xeustechnologies.jcl.LoadTest) Time elapsed: 0.006
sec <<< FAILURE!
junit.framework.AssertionFailedError: Expected JclContextException
at org.xeustechnologies.jcl.LoadTest.testDefaultContextLoader(LoadTest.j
ava:246)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.
java:57)

ClassCast exception even when using JclUtils.cast()

Thank you so much for making JCL available to us, it is a real lifesaver for my project!

I'm trying to implement dynamic loading of alert plugins for my babysitter monitor service:
https://github.com/massenz/babysitter
(relevant classes are in the com.rivermeadow.babysitter.alerts package)

In the final version, the JAR would uploaded via a POST specifying the main class implementing the plugin API.

To simplifiy a bit, an AlertPlugin implements the lifecycle methods, with the activate() method returning a Pager class, which will actually respond to the alerts.

However, when I try to implement the dynamic loading using JCL (loaded via Maven, jcl-core, version 2.5) it does work by getting me a valid AlertPlugin class, but I get a ClassCastException when I try to load the Pager via the activate() method, even if I use JclUtils.cast().

If I modify the API to return me the name of the class implementing the Pager interface, and load that one via JCL then it all works just fine: so I guess I have a workaround, but I'm wondering what am I doing wrong, of if it's a genuine bug.

BTW, using ObjectFactory.getInstance(true) doesn't work at all.

See an example here:
https://gist.github.com/massenz/7402453

More than happy to contribute to the project, if I can help implementing this.

How to fully release/close a loaded jar ?

Hi

I'm looking for a solution to release a loaded jar from the memory.
The following code shows how I tried it so far.
Any better ideas?

thx

`import org.xeustechnologies.jcl.JarClassLoader;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import java.util.jar.JarFile;

public class JarLoader extends JarClassLoader{

protected HashSet<String> setJarFileNames2Close = new HashSet<String>();

public void release(){
	close();
	loaders.clear();
	classes.clear();
}

public void close() {
	setJarFileNames2Close.clear();
	closeClassLoader(this);
	finalizeNativeLibs(this);
	cleanupJarFileFactory();
}

/**
 * cleanup jar file factory cache
 */
@SuppressWarnings({ "nls", "unchecked" })
public boolean cleanupJarFileFactory()
{
	boolean res = false;
	Class classJarURLConnection = null;
	try {
		classJarURLConnection = Class.forName("sun.net.www.protocol.jar.JarURLConnection");
	} catch (ClassNotFoundException e) {
		//ignore
	}
	if (classJarURLConnection == null) {
		return res;
	}
	Field f = null;
	try {
		f = classJarURLConnection.getDeclaredField("factory");
	} catch (NoSuchFieldException e) {
		//ignore
	}
	if (f == null) {
		return res;
	}
	f.setAccessible(true);
	Object obj = null;
	try {
		obj = f.get(null);
	} catch (IllegalAccessException e) {
		//ignore
	}
	if (obj == null) {
		return res;
	}
	Class classJarFileFactory = obj.getClass();
	//
	HashMap fileCache = null;
	try {
		f = classJarFileFactory.getDeclaredField("fileCache");
		f.setAccessible(true);
		obj = f.get(null);
		if (obj instanceof HashMap) {
			fileCache = (HashMap)obj;
		}
	} catch (NoSuchFieldException e) {
	} catch (IllegalAccessException e) {
		//ignore
	}
	HashMap urlCache = null;
	try {
		f = classJarFileFactory.getDeclaredField("urlCache");
		f.setAccessible(true);
		obj = f.get(null);
		if (obj instanceof HashMap) {
			urlCache = (HashMap)obj;
		}
	} catch (NoSuchFieldException e) {
	} catch (IllegalAccessException e) {
		//ignore
	}
	if (urlCache != null) {
		HashMap urlCacheTmp = (HashMap)urlCache.clone();
		Iterator it = urlCacheTmp.keySet().iterator();
		while (it.hasNext()) {
			obj = it.next();
			if (!(obj instanceof JarFile)) {
				continue;
			}
			JarFile jarFile = (JarFile)obj;
			if (setJarFileNames2Close.contains(jarFile.getName())) {
				try {
					jarFile.close();
				} catch (IOException e) {
					//ignore
				}
				if (fileCache != null) {
					fileCache.remove(urlCache.get(jarFile));
				}
				urlCache.remove(jarFile);
			}
		}
		res = true;
	} else if (fileCache != null) {
		// urlCache := null
		HashMap fileCacheTmp = (HashMap)fileCache.clone();
		Iterator it = fileCacheTmp.keySet().iterator();
		while (it.hasNext()) {
			Object key = it.next();
			obj = fileCache.get(key);
			if (!(obj instanceof JarFile)) {
				continue;
			}
			JarFile jarFile = (JarFile)obj;
			if (setJarFileNames2Close.contains(jarFile.getName())) {
				try {
					jarFile.close();
				} catch (IOException e) {
					//ignore
				}
				fileCache.remove(key);
			}
		}
		res = true;
	}
	setJarFileNames2Close.clear();
	return res;
}

/**
 * close jar files of cl
 * @param cl
 * @return
 */
@SuppressWarnings( { "nls", "unchecked" })
public boolean closeClassLoader(ClassLoader cl) {
	boolean res = false;
	if (cl == null) {
		return res;
	}
	Class classURLClassLoader = JarLoader.class;
	Field f = null;
	try {
		f = classURLClassLoader.getDeclaredField("ucp");
	} catch (NoSuchFieldException e1) {
		//ignore
	}
	if (f != null) {
		f.setAccessible(true);
		Object obj = null;
		try {
			obj = f.get(cl);
		} catch (IllegalAccessException e1) {
			//ignore
		}
		if (obj != null) {
			final Object ucp = obj;
			f = null;
			try {
				f = ucp.getClass().getDeclaredField("loaders");
			} catch (NoSuchFieldException e1) {
				//ignore
			}
			if (f != null) {
				f.setAccessible(true);
				ArrayList loaders = null;
				try {
					loaders = (ArrayList) f.get(ucp);
					res = true;
				} catch (IllegalAccessException e1) {
					//ignore
				}
				for (int i = 0; loaders != null && i < loaders.size(); i++) {
					obj = loaders.get(i);
					f = null;
					try {
						f = obj.getClass().getDeclaredField("jar");
					} catch (NoSuchFieldException e) {
						//ignore
					}
					if (f != null) {
						f.setAccessible(true);
						try {
							obj = f.get(obj);
						} catch (IllegalAccessException e1) {
							// ignore
						}
						if (obj instanceof JarFile) {
							final JarFile jarFile = (JarFile)obj;
							setJarFileNames2Close.add(jarFile.getName());
							//try {
							//	jarFile.getManifest().clear();
							//} catch (IOException e) {
							//	// ignore
							//}
							try {
								jarFile.close();
							} catch (IOException e) {
								// ignore
							}
						}
					}
				}
			}
		}
	}
	return res;
}

/**
 * finalize native libraries
 * @param cl
 * @return
 */
@SuppressWarnings({ "nls", "unchecked" })
public boolean finalizeNativeLibs(ClassLoader cl) {
	boolean res = false;
	Class classClassLoader = ClassLoader.class;
	java.lang.reflect.Field nativeLibraries = null;
	try {
		nativeLibraries = classClassLoader.getDeclaredField("nativeLibraries");
	} catch (NoSuchFieldException e1) {
		//ignore
	}
	if (nativeLibraries == null) {
		return res;
	}
	nativeLibraries.setAccessible(true);
	Object obj = null;
	try {
		obj = nativeLibraries.get(cl);
	} catch (IllegalAccessException e1) {
		//ignore
	}
	if (!(obj instanceof Vector)) {
		return res;
	}
	res = true;
	Vector java_lang_ClassLoader_NativeLibrary = (Vector)obj;
	for (Object lib : java_lang_ClassLoader_NativeLibrary) {
		java.lang.reflect.Method finalize = null;
		try {
			finalize = lib.getClass().getDeclaredMethod("finalize", new Class[0]);
		} catch (NoSuchMethodException e) {
			//ignore
		}
		if (finalize != null) {
			finalize.setAccessible(true);
			try {
				finalize.invoke(lib, new Object[0]);
			} catch (IllegalAccessException e) {
			} catch (InvocationTargetException e) {
				//ignore
			}
		}
	}
	return res;
}

}
`

There isn't any support of getPackage() method

I loaded dynamically JDBC driver jar and called acceptsURL method (with MethodHandle): as I got a NPE with MySQL Driver, I read the MySQL code and noticed the NPE occurs because of invoking method getPackage() (in com.mysql.jdbc.Util), that always returns a null reference.

Can't download JCL dependency using Ivy/Ant.

I attempted to include the JCL library using Ivy/Ant, with the following include:

The Ivy portion of the build doesn't complain, but it also doesn't download any jar files. Looking at the Maven repos, JCL seems to be missing some references (ie there are no jar files references, and no source code either). Apologies if I'm missing something, I'm not a Maven expert. Does including JCL in a Maven project work as expected?

Deadlock in version 2.7

thread busy-map-server-莲花洞-6-0:
java.lang.Thread.State: BLOCKED
at java.lang.ClassLoader.checkCerts(ClassLoader.java:884)
- waiting to lock <13bdd6ad> (a org.xeustechnologies.jcl.JarClassLoader) owned by "login-executor-0" t@301
at java.lang.ClassLoader.preDefineClass(ClassLoader.java:665)
at java.lang.ClassLoader.defineClass(ClassLoader.java:758)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at org.xeustechnologies.jcl.JarClassLoader.access$000(JarClassLoader.java:42)
at org.xeustechnologies.jcl.JarClassLoader$LocalLoader.loadClass(JarClassLoader.java:246)
at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:120)
- locked <789824e1> (a java.util.Collections$SynchronizedRandomAccessList)
at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:94)
at org.reflections.ReflectionUtils.forName(ReflectionUtils.java:376)
at org.reflections.ReflectionUtils.forNames(ReflectionUtils.java:397)
at org.reflections.Reflections.getSubTypesOf(Reflections.java:367)

Locked ownable synchronizers:
- locked <12e5f7cb> (a java.util.concurrent.ThreadPoolExecutor$Worker)

thread login-executor-0:
java.lang.Thread.State: BLOCKED
at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:117)
- waiting to lock <789824e1> (a java.util.Collections$SynchronizedRandomAccessList) owned by "busy-map-server-莲花洞-6-0" t@205
at org.xeustechnologies.jcl.AbstractClassLoader.loadClass(AbstractClassLoader.java:94)

JNI locked monitors:
- locked <13bdd6ad> (a org.xeustechnologies.jcl.JarClassLoader)

Locked ownable synchronizers:
- locked <33c6c4f> (a java.util.concurrent.ThreadPoolExecutor$Worker)

any suggestion?

getResource() fails for resources that are not within a jar

Say you have a directory structure like this:

    someDirectory/
           someFile

You can have jars or classes inside someDirectory too, it doesn't matter.
If you call:

    jcl.add("someDirectory")

And then try to call jcl.getResource("someFile"), JCL throws an exception:

    org.xeustechnologies.jcl.exception.JclException: java.net.MalformedURLException: no protocol: /home/whoever/file/path/someDirectory/someFilesomeFile
    at org.xeustechnologies.jcl.JarResources.getResourceURL(JarResources.java:74)
    at org.xeustechnologies.jcl.JarClassLoader$LocalLoader.findResource(JarClassLoader.java:285)
    at org.xeustechnologies.jcl.AbstractClassLoader.getResource(AbstractClassLoader.java:150)

Note that the filename (someFile) is actually repeated twice in the returned filename.
I'll submit a PR shortly.

Set ProtectionDomain/CodeSource

Some code as equinox for example makes all sort of usage of ProtectionDomain/CodeSource.
So, can you please set those while defining classes? I know it cannot be done in all cases but at least for loading a jar from an URL it can be done.
Take for example how to do it http://www.docjar.com/html/api/java/security/SecureClassLoader.java.html

Maybe even think on having an addition optional param on add() for specifing the code source url.

BTW, why not using github? is such an easier and better way to contribute. I could have done that and only asked you to pull.

license

all the source code only include the LGPL license, however the main page says i can use apache..

do i have permission to use this under the apache license instead of lgpl?

Auto-proxying does not recursively search for all interfaces

Auto-proxying does not recursively search for interfaces, so this small example unexpectedly does not work:

interface I {}
class A implements I{}
class B extends A {}
JarClassLoader jcl = new JarClassLoader();
JclObjectFactory factory = JclObjectFactory.getInstance(true);
Object o = factory.create(jcl, "B");

Results in:

Exception in thread "main" org.xeustechnologies.jcl.exception.JclException: org.xeustechnologies.jcl.exception.JclException: Neither the class [B] nor all the implemented interfaces found in the current classloader
	at org.xeustechnologies.jcl.JclObjectFactory.create(JclObjectFactory.java:93)

The fix would be to change JclObjectFactory.java with:

//            Class[] interfaces = object.getClass().getInterfaces(); // Remove this line
            List<Class> il = new ArrayList<Class>();


// Use this util e.g. from org.apache.commons:commons-lang3 instead
            List<Class<?>> interfaces = ClassUtils.getAllInterfaces(object.getClass());

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.