diff --git a/src/share/classes/com/sun/tools/javac/file/ZipFileIndexCache.java b/src/share/classes/com/sun/tools/javac/file/ZipFileIndexCache.java index 3c878d71efa2b42aa0336b2bcbc1b02824760f61..b99558f00667a717bb37fcb3dd730ea8e17ca8fa 100644 --- a/src/share/classes/com/sun/tools/javac/file/ZipFileIndexCache.java +++ b/src/share/classes/com/sun/tools/javac/file/ZipFileIndexCache.java @@ -25,6 +25,8 @@ package com.sun.tools.javac.file; +import com.alibaba.tenant.TenantContainer; +import com.alibaba.tenant.TenantGlobals; import com.sun.tools.javac.file.RelativePath.RelativeDirectory; import com.sun.tools.javac.util.Context; import java.io.File; @@ -34,6 +36,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.zip.ZipFile; /** A cache for ZipFileIndex objects. */ @@ -44,10 +47,17 @@ public class ZipFileIndexCache { /** Get a shared instance of the cache. */ private static ZipFileIndexCache sharedInstance; + public synchronized static ZipFileIndexCache getSharedInstance() { - if (sharedInstance == null) - sharedInstance = new ZipFileIndexCache(); - return sharedInstance; + // sharedInstance was shared by all TenantContainers and never cleared, which would cause HUGE memory footprint. + if (TenantGlobals.isDataIsolationEnabled() && TenantContainer.current() != null) { + return TenantContainer.current() + .getFieldValue(ZipFileIndexCache.class, "sharedInstance", ZipFileIndexCache::new); + } else { + if (sharedInstance == null) + sharedInstance = new ZipFileIndexCache(); + return sharedInstance; + } } /** Get a context-specific instance of a cache. */ diff --git a/src/share/classes/javax/tools/ToolProvider.java b/src/share/classes/javax/tools/ToolProvider.java index b8af9da321af35bf53bf1611b0d4b248b61658ea..bd8e8d242d97348b3b33e550d1409ad5cb52f9f1 100644 --- a/src/share/classes/javax/tools/ToolProvider.java +++ b/src/share/classes/javax/tools/ToolProvider.java @@ -25,6 +25,8 @@ package javax.tools; +import com.alibaba.tenant.TenantContainer; +import com.alibaba.tenant.TenantGlobals; import java.io.File; import java.lang.ref.Reference; import java.lang.ref.WeakReference; @@ -138,9 +140,14 @@ public class ToolProvider { private static ToolProvider instance; private static synchronized ToolProvider instance() { - if (instance == null) - instance = new ToolProvider(); - return instance; + if (TenantGlobals.isDataIsolationEnabled() && TenantContainer.current() != null) { + return TenantContainer.current() + .getFieldValue(ToolProvider.class, "instance", ToolProvider::new); + } else { + if (instance == null) + instance = new ToolProvider(); + return instance; + } } // Cache for tool classes. diff --git a/test/multi-tenant/TestStaticFieldIsolation.java b/test/multi-tenant/TestStaticFieldIsolation.java new file mode 100644 index 0000000000000000000000000000000000000000..695e8459152a0878499892cc9a49e4ad753fc21b --- /dev/null +++ b/test/multi-tenant/TestStaticFieldIsolation.java @@ -0,0 +1,128 @@ + +/* + * @test + * @summary Test isolation of various static fields + * @run main/othervm -XX:+MultiTenant -XX:+TenantDataIsolation TestStaticFieldIsolation + */ + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import com.alibaba.tenant.TenantConfiguration; +import com.alibaba.tenant.TenantContainer; +import com.alibaba.tenant.TenantException; +import javax.tools.ToolProvider; + + +public class TestStaticFieldIsolation { + + public static void main(String[] args) throws TenantException { + TestStaticFieldIsolation test = new TestStaticFieldIsolation(); + test.testIsolation_com_sun_tools_javac_file_ZipFileIndexCache_sharedInstance(); + test.testIsolation_javax_tools_ToolProvider_getSystemJavaCompiler(); + } + + private void testIsolation_com_sun_tools_javac_file_ZipFileIndexCache_sharedInstance() throws TenantException { + // find the class + ClassLoader toolsLoader = ToolProvider.getSystemJavaCompiler().getClass().getClassLoader(); + Class clazz[] = new Class[1]; + try { + clazz[0] = toolsLoader.loadClass("com.sun.tools.javac.file.ZipFileIndexCache"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + fail(); + } finally { + assertNotNull(clazz[0]); + } + + + Runnable task = () -> { + try { + Method f = clazz[0].getDeclaredMethod("getSharedInstance"); + f.setAccessible(true); + f.invoke(null); + } catch (Throwable t) { + fail(); + } + }; + testStaticFieldIsolation(clazz[0], task, "sharedInstance"); + } + + private void testIsolation_javax_tools_ToolProvider_getSystemJavaCompiler() throws TenantException { + Runnable task = () -> { + try { + ToolProvider.getSystemJavaCompiler(); + } catch (Throwable t) { + fail(); + } + }; + testStaticFieldIsolation(ToolProvider.class, task, "instance"); + } + + + /** + * test isolation of {@code classKay}'s static field {@code fieldName} + * @param key The class contains target static field + * @param tenantTask Task to trigger the static filed isolation per tenant, + * which is stored in TenantContainer.tenantData + * @param fieldName Field name + * @throws TenantException + */ + private static void testStaticFieldIsolation(K key, Runnable tenantTask, String fieldName) + throws TenantException { + TenantContainer tenant = createSimpleTenant(); + + assertNull(tenant.getFieldValue(key, fieldName)); + + // run twice to initialize field in ROOT and non-ROOT + tenant.run(tenantTask); + tenantTask.run(); + + Object isolatedFieldValue = tenant.getFieldValue(key, fieldName); + assertNotNull(isolatedFieldValue); + + try { + Class clazz = null; + if (key instanceof Class) { + clazz = (Class)key; + } else { + clazz = key.getClass(); + } + Field field = clazz.getDeclaredField(fieldName); + field.setAccessible(true); + Object rootValue = field.get(null); + + assertTrue(rootValue != isolatedFieldValue); + } catch (NoSuchFieldException | IllegalAccessException e) { + e.printStackTrace(); + fail(); + } + + tenant.destroy(); + } + + // convenient static method to create a simple {@code TenantContainer} object + private static TenantContainer createSimpleTenant() { + TenantConfiguration config = new TenantConfiguration(1024, 64 * 1024 * 1024); + return TenantContainer.create(config); + } + + private static void assertNull(Object o) { + if (o != null) { + throw new RuntimeException("assertNull failed!"); + } + } + + private static void assertNotNull(Object o) { + if (o == null) { + throw new RuntimeException("assertNotNull failed!"); + } + } + + private static void fail() { throw new RuntimeException("Failed!"); } + + private static void assertTrue(Boolean b) { + if (!b.booleanValue()) { + throw new RuntimeException("assertTrue failed!"); + } + } +}