Skip to content

Commit

Permalink
LDEV-4739 - change Physical classloading behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeloffner committed Nov 8, 2023
1 parent 417cea9 commit 2074d6a
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 149 deletions.
36 changes: 28 additions & 8 deletions core/src/main/java/lucee/commons/lang/PhysicalClassLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@

import lucee.commons.digest.HashUtil;
import lucee.commons.io.IOUtil;
import lucee.commons.io.SystemUtil;
import lucee.commons.io.log.LogUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.io.res.util.ResourceClassLoader;
import lucee.commons.io.res.util.ResourceUtil;
import lucee.runtime.PageSourcePool;
import lucee.runtime.config.Config;
import lucee.runtime.config.ConfigPro;
import lucee.runtime.exp.ApplicationException;
Expand All @@ -59,6 +60,7 @@ public final class PhysicalClassLoader extends ExtendableClassLoader {
private Map<String, String> unavaiClasses = new ConcurrentHashMap<String, String>();

private Map<String, SoftReference<PhysicalClassLoader>> customCLs;
private PageSourcePool pageSourcePool;

private static long counter = 0L;
private static long _start = 0L;
Expand All @@ -84,14 +86,15 @@ public static String uid() {
* @param parent
* @throws IOException
*/
public PhysicalClassLoader(Config c, Resource directory) throws IOException {
this(c, directory, (ClassLoader[]) null, true);
public PhysicalClassLoader(Config c, Resource directory, PageSourcePool pageSourcePool) throws IOException {
this(c, directory, (ClassLoader[]) null, true, pageSourcePool);
}

public PhysicalClassLoader(Config c, Resource directory, ClassLoader[] parentClassLoaders, boolean includeCoreCL) throws IOException {
public PhysicalClassLoader(Config c, Resource directory, ClassLoader[] parentClassLoaders, boolean includeCoreCL, PageSourcePool pageSourcePool) throws IOException {
super(parentClassLoaders == null || parentClassLoaders.length == 0 ? c.getClassLoader() : parentClassLoaders[0]);
config = (ConfigPro) c;

this.pageSourcePool = pageSourcePool;
// ClassLoader resCL = parent!=null?parent:config.getResourceClassLoader(null);

List<ClassLoader> tmp = new ArrayList<ClassLoader>();
Expand Down Expand Up @@ -122,7 +125,7 @@ public Class<?> loadClass(String name) throws ClassNotFoundException {

@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (SystemUtil.createToken("PhysicalClassLoader", name)) {
synchronized (this) {
return loadClass(name, resolve, true);
}
}
Expand Down Expand Up @@ -150,7 +153,7 @@ private Class<?> loadClass(String name, boolean resolve, boolean loadFromFS) thr

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {// if(name.indexOf("sub")!=-1)print.ds(name);
synchronized (SystemUtil.createToken("PhysicalClassLoader", name)) {
synchronized (this) {
Resource res = directory.getRealResource(name.replace('.', '/').concat(".class"));

ByteArrayOutputStream baos = new ByteArrayOutputStream();
Expand All @@ -172,7 +175,7 @@ private Class<?> loadClass(String name, boolean resolve, boolean loadFromFS) thr
public Class<?> loadClass(String name, byte[] barr) throws UnmodifiableClassException {
Class<?> clazz = null;

synchronized (SystemUtil.createToken("PhysicalClassLoader", name)) {
synchronized (this) {

// new class , not in memory yet
try {
Expand Down Expand Up @@ -274,7 +277,7 @@ public PhysicalClassLoader getCustomClassLoader(Resource[] resources, boolean re
SoftReference<PhysicalClassLoader> tmp = customCLs == null ? null : customCLs.get(key);
PhysicalClassLoader pcl = tmp == null ? null : tmp.get();
if (pcl != null) return pcl;
pcl = new PhysicalClassLoader(config, getDirectory(), new ClassLoader[] { new ResourceClassLoader(resources, getParent()) }, true);
pcl = new PhysicalClassLoader(config, getDirectory(), new ClassLoader[] { new ResourceClassLoader(resources, getParent()) }, true, pageSourcePool);
if (customCLs == null) customCLs = new ConcurrentHashMap<String, SoftReference<PhysicalClassLoader>>();
customCLs.put(key, new SoftReference<PhysicalClassLoader>(pcl));
return pcl;
Expand All @@ -291,6 +294,11 @@ private String hash(Resource[] resources) {
}

public void clear() {
clear(true);
}

public void clear(boolean clearPagePool) {
if (clearPagePool && pageSourcePool != null) pageSourcePool.clearPages(this);
this.loadedClasses.clear();
this.allLoadedClasses.clear();
this.unavaiClasses.clear();
Expand All @@ -313,4 +321,16 @@ public static String substractAppendix(String name) throws ApplicationException
if (name.endsWith("$cf")) return name;
throw new ApplicationException("could not remove appendix from [" + name + "]");
}

@Override
public void finalize() throws Throwable {
try {
clear();
}
catch (Exception e) {
LogUtil.log(config, "classloader", e);
}
super.finalize();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;

import lucee.print;
import lucee.commons.io.IOUtil;
import lucee.commons.io.TemporaryStream;
import lucee.commons.io.log.LogUtil;
Expand Down Expand Up @@ -305,14 +304,11 @@ public static HttpClientBuilder getHttpClientBuilder(boolean pooling, String cli
}

public static void setTimeout(HttpClientBuilder builder, TimeSpan timeout) {
print.e("setTimeout:" + timeout.getMillis());
if (timeout == null || timeout.getMillis() <= 0) return;

int ms = (int) timeout.getMillis();
if (ms < 0) ms = Integer.MAX_VALUE;

// builder.setConnectionTimeToLive(ms, TimeUnit.MILLISECONDS);
print.e("setTimeout2:" + ms);
SocketConfig sc = SocketConfig.custom().setSoTimeout(ms).build();
builder.setDefaultSocketConfig(sc);
}
Expand Down Expand Up @@ -378,7 +374,6 @@ public static void closeIdleConnections() {

private static HTTPResponse invoke(URL url, HttpUriRequest request, String username, String password, long timeout, boolean redirect, String charset, String useragent,
ProxyData proxy, lucee.commons.net.http.Header[] headers, Map<String, String> formfields, boolean pooling) throws IOException, GeneralSecurityException {
print.e("invoke:" + timeout);
CloseableHttpClient client;
proxy = ProxyDataImpl.validate(proxy, url.getHost());

Expand Down
108 changes: 76 additions & 32 deletions core/src/main/java/lucee/runtime/MappingImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.io.Serializable;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
Expand All @@ -34,7 +35,7 @@

import lucee.commons.io.FileUtil;
import lucee.commons.io.IOUtil;
import lucee.commons.io.log.Log;
import lucee.commons.io.log.LogUtil;
import lucee.commons.io.res.Resource;
import lucee.commons.lang.ExceptionUtil;
import lucee.commons.lang.MappingUtil;
Expand All @@ -61,18 +62,14 @@ public final class MappingImpl implements Mapping {

private static final long serialVersionUID = 6431380676262041196L;

private static final int MAX_SIZE_CFC = 3000;// 6783;
private static final int MAX_SIZE_CFM = 2000;// 6783;

private static final Class<PageSource> SUBPAGE_CONSTR = PageSource.class;

private String virtual;
private String lcVirtual;
private boolean topLevel;
private short inspect;
private boolean physicalFirst;
private transient PhysicalClassLoader pclCFM;
private transient PhysicalClassLoader pclCFC;
private transient Map<String, PhysicalClassLoaderReference> loaders = new HashMap<>();
private Resource archive;

private final Config config;
Expand Down Expand Up @@ -156,7 +153,6 @@ public MappingImpl(Config config, String virtual, String strPhysical, String str
private void initPhysical() {
ServletContext cs = (config instanceof ConfigWeb) ? ((ConfigWeb) config).getServletContext() : null;
physical = ConfigWebUtil.getResource(cs, strPhysical, config.getConfigDir(), FileUtil.TYPE_DIR, config, checkPhysicalFromWebroot, false);

if (archive == null) this.physicalFirst = true;
else if (physical == null) this.physicalFirst = false;

Expand All @@ -182,7 +178,7 @@ private void loadArchive() {
catch (Throwable t) {
ExceptionUtil.rethrowIfNecessary(t);
archMod = archive.lastModified();
ThreadLocalPageContext.getLog(config, "application").log(Log.LEVEL_ERROR, "OSGi", t);
LogUtil.log(config, "OSGi", t);
archive = null;
}
}
Expand Down Expand Up @@ -233,25 +229,54 @@ public Class<?> loadClass(String className) {
return null;
}

private PhysicalClassLoader touchPhysicalClassLoader(boolean forComponent) throws IOException {
if (forComponent ? pclCFC == null : pclCFM == null) {
if (forComponent) pclCFC = new PhysicalClassLoader(config, getClassRootDirectory());
else pclCFM = new PhysicalClassLoader(config, getClassRootDirectory());
private Class<?> loadClass(String className, byte[] code) throws IOException, ClassNotFoundException {

PhysicalClassLoaderReference pclr = loaders.get(className);
PhysicalClassLoader pcl = pclr == null ? null : pclr.get();
if (pcl == null || code != null) {// || pcl.getSize(true) > 3
if (pcl != null) {
pcl.clear();
}
pcl = new PhysicalClassLoader(config, getClassRootDirectory(), pageSourcePool);
synchronized (loaders) {
loaders.put(className, new PhysicalClassLoaderReference(pcl));
}
}
else if ((forComponent ? pclCFC : pclCFM).getSize(true) > (forComponent ? MAX_SIZE_CFC : MAX_SIZE_CFM)) {
PhysicalClassLoader pcl = forComponent ? pclCFC : pclCFM;
pageSourcePool.clearPages(pcl);
pcl.clear();
if (forComponent) pclCFC = new PhysicalClassLoader(config, getClassRootDirectory());
else pclCFM = new PhysicalClassLoader(config, getClassRootDirectory());

if (code != null) {
try {
return pcl.loadClass(className, code);
}
catch (UnmodifiableClassException e) {
throw ExceptionUtil.toIOException(e);
}
}
return forComponent ? pclCFC : pclCFM;
return pcl.loadClass(className);
}

public void cleanLoaders() {
pageSourcePool.cleanLoaders();
}

public void clear(String className) {
PhysicalClassLoaderReference ref = loaders.remove(className);
PhysicalClassLoader pcl;
if (ref != null) {
pcl = ref.get();
if (pcl != null) {
pcl.clear(false);
}
}
}

public int getSize() {
return loaders.size();
}

@Override
public Class<?> getPhysicalClass(String className) throws ClassNotFoundException, IOException {
return touchPhysicalClassLoader(className.contains("_cfc$cf")).loadClass(className);
// return touchClassLoader().loadClass(className);
return loadClass(className, null);
// return touchPhysicalClassLoader(className.contains("_cfc$cf")).loadClass(className);
}

public Class<?> getPhysicalClass(String className, Class<?> defaultValue) {
Expand All @@ -266,12 +291,14 @@ public Class<?> getPhysicalClass(String className, Class<?> defaultValue) {
@Override
public Class<?> getPhysicalClass(String className, byte[] code) throws IOException {
try {
return touchPhysicalClassLoader(className.contains("_cfc$cf")).loadClass(className, code);
return loadClass(className, code);
}
catch (UnmodifiableClassException e) {
throw new IOException(e);
catch (Exception e) {
throw ExceptionUtil.toIOException(e);
}

// return touchPhysicalClassLoader(className.contains("_cfc$cf")).loadClass(className, code);

// boolean isCFC = className.indexOf("_cfc$")!=-1;//aaaa ResourceUtil.getExtension(ps.getRealpath(),
// "").equalsIgnoreCase("cfc");
// return touchClassLoader().loadClass(className,code,isCFC);
Expand All @@ -286,8 +313,8 @@ public void clearPages(ClassLoader cl) {
pageSourcePool.clearPages(cl);
}

public void clearUnused(Config config) {
pageSourcePool.clearUnused(config);
public void clearUnused() {
pageSourcePool.cleanLoaders();
}

public void resetPages(ClassLoader cl) {
Expand Down Expand Up @@ -404,7 +431,6 @@ public Array getDisplayPathes(Array arr) throws PageException {
if (ps != null) arr.append(ps.getDisplayPath());
}
return arr;

}

public List<PageSource> getPageSources(boolean loaded) {
Expand Down Expand Up @@ -585,21 +611,39 @@ public Mapping toMapping() {
}
}

// used in transformer
public static CIPage loadCIPage(PageSource ps, String className) {
// TODO check if the sub class itself has changed or not, maybe just the main class has, if there is
// no change there is no need to load it new
try {
MappingImpl m = ((MappingImpl) ps.getMapping());
Resource res = m.getClassRootDirectory().getRealResource(className + ".class");
Class<?> clazz = m.touchPhysicalClassLoader(true).loadClass(className.replace('/', '.').replace('\\', '.'), IOUtil.toBytes(res));

String cn = className.replace('/', '.').replace('\\', '.');
Class<?> clazz = m.loadClass(cn, IOUtil.toBytes(res));
return (CIPage) clazz.getConstructor(SUBPAGE_CONSTR).newInstance(ps);
// return (CIPage) ((Class<?>) ((MappingImpl)
// ps.getMapping()).loadClass(className)).getConstructor(SUBPAGE_CONSTR).newInstance(ps);
}
catch (Exception e) {
throw Caster.toPageRuntimeException(e);
}
}

private static class PhysicalClassLoaderReference extends SoftReference<PhysicalClassLoader> {

private long lastModified;

public PhysicalClassLoaderReference(PhysicalClassLoader pcl) {
super(pcl);
this.lastModified = System.currentTimeMillis();
}

@Override
public PhysicalClassLoader get() {
this.lastModified = System.currentTimeMillis();
return super.get();
}

public long lastModified() {
return lastModified;
}
}

}
1 change: 1 addition & 0 deletions core/src/main/java/lucee/runtime/PageSourceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ public Resource getResourceTranslated(PageContext pc) throws ExpressionException
}

public void clear() {
mapping.clear(pcn.className);
pcn.page = null;
}

Expand Down
Loading

0 comments on commit 2074d6a

Please sign in to comment.