1 package org
.argeo
.internal
.cms
.jshell
.osgi
;
3 import java
.io
.ByteArrayInputStream
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
6 import java
.net
.MalformedURLException
;
8 import java
.net
.URISyntaxException
;
10 import java
.net
.URLConnection
;
11 import java
.net
.URLStreamHandler
;
12 import java
.security
.CodeSource
;
13 import java
.security
.SecureClassLoader
;
14 import java
.time
.Instant
;
15 import java
.time
.ZoneId
;
16 import java
.time
.ZonedDateTime
;
17 import java
.time
.format
.DateTimeFormatter
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Collections
;
20 import java
.util
.Date
;
21 import java
.util
.Enumeration
;
22 import java
.util
.HashMap
;
23 import java
.util
.LinkedHashMap
;
24 import java
.util
.List
;
27 import jdk
.jshell
.execution
.LoaderDelegate
;
28 import jdk
.jshell
.spi
.ExecutionControl
.ClassBytecodes
;
29 import jdk
.jshell
.spi
.ExecutionControl
.ClassInstallException
;
30 import jdk
.jshell
.spi
.ExecutionControl
.EngineTerminationException
;
32 /** A {@link LoaderDelegate} using a parent {@link ClassLoader}. */
33 class WrappingLoaderDelegate
implements LoaderDelegate
{
34 private final WrappingClassloader loader
;
35 private final Map
<String
, Class
<?
>> klasses
= new HashMap
<>();
37 private static class WrappingClassloader
extends SecureClassLoader
{
39 private final Map
<String
, ClassFile
> classFiles
= new HashMap
<>();
41 public WrappingClassloader(ClassLoader parent
) {
45 private class ResourceURLStreamHandler
extends URLStreamHandler
{
47 private final String name
;
49 ResourceURLStreamHandler(String name
) {
54 protected URLConnection
openConnection(URL u
) throws IOException
{
55 return new URLConnection(u
) {
56 private InputStream in
;
57 private Map
<String
, List
<String
>> fields
;
58 private List
<String
> fieldNames
;
61 public void connect() {
66 ClassFile file
= classFiles
.get(name
);
67 in
= new ByteArrayInputStream(file
.data
);
68 fields
= new LinkedHashMap
<>();
69 fields
.put("content-length", List
.of(Integer
.toString(file
.data
.length
)));
70 Instant instant
= new Date(file
.timestamp
).toInstant();
71 ZonedDateTime time
= ZonedDateTime
.ofInstant(instant
, ZoneId
.of("GMT"));
72 String timeStamp
= DateTimeFormatter
.RFC_1123_DATE_TIME
.format(time
);
73 fields
.put("date", List
.of(timeStamp
));
74 fields
.put("last-modified", List
.of(timeStamp
));
75 fieldNames
= new ArrayList
<>(fields
.keySet());
79 public InputStream
getInputStream() throws IOException
{
85 public String
getHeaderField(String name
) {
87 return fields
.getOrDefault(name
, List
.of()).stream().findFirst().orElse(null);
91 public Map
<String
, List
<String
>> getHeaderFields() {
97 public String
getHeaderFieldKey(int n
) {
98 return n
< fieldNames
.size() ? fieldNames
.get(n
) : null;
102 public String
getHeaderField(int n
) {
103 String name
= getHeaderFieldKey(n
);
105 return name
!= null ?
getHeaderField(name
) : null;
112 void declare(String name
, byte[] bytes
) {
113 classFiles
.put(toResourceString(name
), new ClassFile(bytes
, System
.currentTimeMillis()));
117 protected Class
<?
> findClass(String name
) throws ClassNotFoundException
{
118 ClassFile file
= classFiles
.get(toResourceString(name
));
120 return super.findClass(name
);
122 return super.defineClass(name
, file
.data
, 0, file
.data
.length
, (CodeSource
) null);
126 public URL
findResource(String name
) {
127 URL u
= doFindResource(name
);
128 return u
!= null ? u
: super.findResource(name
);
132 public Enumeration
<URL
> findResources(String name
) throws IOException
{
133 URL u
= doFindResource(name
);
134 Enumeration
<URL
> sup
= super.findResources(name
);
140 List
<URL
> result
= new ArrayList
<>();
142 while (sup
.hasMoreElements()) {
143 result
.add(sup
.nextElement());
148 return Collections
.enumeration(result
);
151 private URL
doFindResource(String name
) {
152 if (classFiles
.containsKey(name
)) {
154 return new URL(null, new URI("jshell", null, "/" + name
, null).toString(),
155 new ResourceURLStreamHandler(name
));
156 } catch (MalformedURLException
| URISyntaxException ex
) {
157 throw new InternalError(ex
);
164 private String
toResourceString(String className
) {
165 return className
.replace('.', '/') + ".class";
168 private static class ClassFile
{
169 public final byte[] data
;
170 public final long timestamp
;
172 ClassFile(byte[] data
, long timestamp
) {
174 this.timestamp
= timestamp
;
180 public WrappingLoaderDelegate(ClassLoader parentClassLoader
) {
181 this.loader
= new WrappingClassloader(parentClassLoader
);
183 Thread
.currentThread().setContextClassLoader(loader
);
187 public void load(ClassBytecodes
[] cbcs
) throws ClassInstallException
, EngineTerminationException
{
188 boolean[] loaded
= new boolean[cbcs
.length
];
190 for (ClassBytecodes cbc
: cbcs
) {
191 loader
.declare(cbc
.name(), cbc
.bytecodes());
193 for (int i
= 0; i
< cbcs
.length
; ++i
) {
194 ClassBytecodes cbc
= cbcs
[i
];
195 Class
<?
> klass
= loader
.loadClass(cbc
.name());
196 klasses
.put(cbc
.name(), klass
);
198 // Get class loaded to the point of, at least, preparation
199 klass
.getDeclaredMethods();
201 } catch (Throwable ex
) {
202 throw new ClassInstallException("load: " + ex
.getMessage(), loaded
);
207 public void classesRedefined(ClassBytecodes
[] cbcs
) {
208 for (ClassBytecodes cbc
: cbcs
) {
209 loader
.declare(cbc
.name(), cbc
.bytecodes());
214 public void addToClasspath(String cp
) {
219 public Class
<?
> findClass(String name
) throws ClassNotFoundException
{
220 Class
<?
> klass
= klasses
.get(name
);
222 throw new ClassNotFoundException(name
+ " not found");