Skip to content

Commit

Permalink
Softreference add to CacheClassLoader for class code
Browse files Browse the repository at this point in the history
  • Loading branch information
richard committed Jul 10, 2020
1 parent 5dc047f commit 6753f88
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 26 deletions.
29 changes: 19 additions & 10 deletions src/cz/b2b/jcl/DB/JdbcClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public void close() throws IOException {

/**
Set Pool settings.
@param minPoolSize minimal pool size
@param maxPoolSize maximal pool size
@param maxIdleTime maximal idle timeout
Expand Down Expand Up @@ -217,22 +217,31 @@ private class JdbcURLStreamHandler extends URLStreamHandler {

@Override
protected URLConnection openConnection(URL url) throws IOException {
return new JdbcURLConnection(url);
}

}

private class JdbcURLConnection extends URLConnection {

public JdbcURLConnection(URL url) {
super(url);
}

@Override
public void connect() throws IOException {
}

@Override
public InputStream getInputStream() throws IOException {
Map cols = parseURL(url);

final byte[] data = class_code(cols);
if (data == null) {
throw new FileNotFoundException(url.getFile());
}
return new URLConnection(url) {
@Override
public void connect() throws IOException {
}

@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
};
return new ByteArrayInputStream(data);
}

private byte[] class_code(Map cols) {
Expand Down
43 changes: 27 additions & 16 deletions src/cz/b2b/jcl/RAM/CacheClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@
import java.util.*;
import java.util.jar.*;
import java.io.*;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.*;
import cz.b2b.jcl.util.CONST;
import cz.b2b.jcl.util.ConcurrentSoftHashMap;

/**
The CacheClassLoader class implements a class loader that loads classes from
Expand Down Expand Up @@ -89,7 +89,7 @@ public class CacheClassLoader extends URLClassLoader {
private final static String protocol = "x-mem-cache";
private static final Logger logger = LoggerFactory.getLogger(CacheClassLoader.class);

private final Map<String, byte[]> CACHE = new ConcurrentHashMap<>();
private Map<String, byte[]> CACHE = null;
private final URL cacheURL = new URL(protocol, CONST.host, CONST.port, CONST.baseURI, new CacheURLStreamHandler());

/**
Expand All @@ -104,12 +104,16 @@ public class CacheClassLoader extends URLClassLoader {
resources. The URLs will be searched in the order specified for classes and
resources after first searching in the specified parent class loader.
@param parent the parent class loader for delegation
@param hardSize The number of "hard" references of class code to hold
internally.
@throws MalformedURLException Thrown to indicate that a malformed URL has
occurred. Either no legal protocol could be found in a specification string
or the string could not be parsed.
*/
public CacheClassLoader(URL[] urls, ClassLoader parent) throws MalformedURLException {
public CacheClassLoader(URL[] urls, ClassLoader parent, int hardSize) throws MalformedURLException {
super(urls, parent);

CACHE = new ConcurrentSoftHashMap<>(hardSize);
super.addURL(cacheURL);
}

Expand All @@ -127,11 +131,12 @@ public CacheClassLoader(URL[] urls, ClassLoader parent) throws MalformedURLExcep
or the string could not be parsed.
*/
public CacheClassLoader(ClassLoader parent) throws MalformedURLException {
this(new URL[]{}, parent);
this(new URL[]{}, parent, 100);
}

@Override
public void close() throws IOException {

CACHE.clear();
super.close();

Expand Down Expand Up @@ -172,7 +177,6 @@ public void addJAR(String jar) throws IOException {
logger.debug("Class/Resource " + jarEntry.getName() + " already loaded; ignoring entry...");
continue;
}

out = new ByteArrayOutputStream();

while ((len = jis.read(b)) > 0) {
Expand All @@ -182,7 +186,6 @@ public void addJAR(String jar) throws IOException {
logger.debug("Jar entry = " + name);

CACHE.put(CONST.baseURI + name, out.toByteArray());

out.close();
}
} finally {
Expand Down Expand Up @@ -314,20 +317,29 @@ private class CacheURLStreamHandler extends URLStreamHandler {

@Override
protected URLConnection openConnection(URL url) throws IOException {
return new CacheURLConnection(url);
}

}

private class CacheURLConnection extends URLConnection {

public CacheURLConnection(URL url) {
super(url);
}

@Override
public void connect() throws IOException {
}

@Override
public InputStream getInputStream() throws IOException {
final byte[] data = CACHE.get(url.getFile());
if (data == null) {
throw new FileNotFoundException(url.getFile());
}
return new URLConnection(url) {
@Override
public void connect() throws IOException {
}

@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(data);
}
};
return new ByteArrayInputStream(data);
}

}
Expand Down Expand Up @@ -360,7 +372,6 @@ private void add_class(String class_name, String packageName, String className)
}

CACHE.put(CONST.baseURI + name, out.toByteArray());

out.close();
} finally {
if (bis != null) {
Expand Down
159 changes: 159 additions & 0 deletions src/cz/b2b/jcl/util/ConcurrentSoftHashMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
https://www.javaspecialists.eu/archive/Issue015.html
*/
package cz.b2b.jcl.util;

//: ConcurrentSoftHashMap.java
import java.util.*;
import java.lang.ref.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

public class ConcurrentSoftHashMap<K,V> extends AbstractMap {

/**
The internal HashMap that will hold the SoftReference.
*/
private final Map<Object, SoftReference> hash = new ConcurrentHashMap<>();
/**
The number of "hard" references to hold internally.
*/
private final int HARD_SIZE;
/**
The FIFO list of hard references, order of last access.
*/
private final ConcurrentLinkedSetQueue hardCache = new ConcurrentLinkedSetQueue();
/**
Reference queue for cleared SoftReference objects.
*/
private final ReferenceQueue queue = new ReferenceQueue();


public ConcurrentSoftHashMap(int hardSize) {
HARD_SIZE = hardSize;
}

@Override
public Object get(Object key) {
Object result = null;
// We get the SoftReference represented by that key
SoftReference soft_ref = hash.get(key);
if (soft_ref != null) {
// From the SoftReference we get the value, which can be
// null if it was not in the map, or it was removed in
// the processQueue() method defined below
result = soft_ref.get();
if (result == null) {
// If the value has been garbage collected, remove the
// entry from the HashMap.
hash.remove(key);
} else {
// We now add this object to the beginning of the hard
// reference queue. One reference can occur more than
// once, because lookups of the FIFO queue are slow, so
// we don't want to search through it each time to remove
// duplicates.
hardCache.enqueue(result);
if (hardCache.size() > HARD_SIZE) {
// Remove the last entry if list longer than HARD_SIZE
hardCache.dequeue();
}
}
}
return result;
}

/**
Here we put the key, value pair into the HashMap using
a SoftValue object.
* @param key
* @param value
* @return
*/
@Override
public Object put(Object key, Object value) {
processQueue(); // throw out garbage collected values first
return hash.put(key, new SoftValue(value, key, queue));
}

@Override
public Object remove(Object key) {
processQueue(); // throw out garbage collected values first
return hash.remove(key);
}

@Override
public void clear() {
hardCache.clear();
processQueue(); // throw out garbage collected values
hash.clear();
}

@Override
public int size() {
processQueue(); // throw out garbage collected values first
return hash.size();
}


@Override
public boolean containsKey(Object key) {
processQueue(); // throw out garbage collected values first
return hash.containsKey(key);
}

@Override
public Set entrySet() {
throw new UnsupportedOperationException();
}

private class ConcurrentLinkedSetQueue<E> extends ConcurrentLinkedQueue<E> {

public void enqueue(E o) {
if (!contains(o)) {
add(o);
}
}

public E dequeue() {
return poll();
}

}

/**
We define our own subclass of SoftReference which contains
not only the value but also the key to make it easier to find
the entry in the HashMap after it's been garbage collected.
*/
private static class SoftValue extends SoftReference {

private final Object key; // always make data member final

/**
Did you know that an outer class can access private data
members and methods of an inner class? I didn't know that!
I thought it was only the inner class who could access the
outer class's private information. An outer class can also
access private members of an inner class inside its inner
class.
*/
private SoftValue(Object k, Object key, ReferenceQueue q) {
super(k, q);
this.key = key;
}
}

/**
Here we go through the ReferenceQueue and remove garbage
collected SoftValue objects from the HashMap by looking them
up using the SoftValue.key data member.
*/
private void processQueue() {
SoftValue sv;
while ((sv = (SoftValue) queue.poll()) != null) {
hash.remove(sv.key); // we can access private data!
}
}

}

0 comments on commit 6753f88

Please sign in to comment.