34
34
35
35
package org .jruby .runtime .load ;
36
36
37
+ import java .io .ByteArrayOutputStream ;
37
38
import java .io .File ;
38
39
import java .io .FileNotFoundException ;
39
40
import java .io .IOException ;
41
+ import java .io .InputStream ;
40
42
import java .net .MalformedURLException ;
41
43
import java .net .URISyntaxException ;
42
44
import java .net .URI ;
43
45
import java .net .URL ;
44
46
import java .security .AccessControlException ;
45
47
import java .util .HashMap ;
48
+ import java .util .HashSet ;
46
49
import java .util .List ;
47
50
import java .util .Map ;
51
+ import java .util .Set ;
48
52
import java .util .concurrent .ConcurrentHashMap ;
49
53
import java .util .concurrent .atomic .AtomicInteger ;
50
54
import java .util .concurrent .locks .ReentrantLock ;
@@ -192,6 +196,8 @@ public String[] getSuffixes() {
192
196
protected final Ruby runtime ;
193
197
protected final LibrarySearcher librarySearcher ;
194
198
199
+ protected final Set <LoadHook > loadHooks = new HashSet <>();
200
+
195
201
public LoadService (Ruby runtime ) {
196
202
this .runtime = runtime ;
197
203
if (RubyInstanceConfig .DEBUG_LOAD_TIMINGS ) {
@@ -1084,7 +1090,7 @@ protected LoadServiceResource tryResourceFromCWD(SearchState state, String baseN
1084
1090
debugLogTry ("resourceFromCWD" , file .toString ());
1085
1091
if (file .isFile () && file .isAbsolute () && file .canRead ()) {
1086
1092
boolean absolute = true ;
1087
- foundResource = new LoadServiceResource (file , getFileName (file , namePlusSuffix ), absolute );
1093
+ foundResource = new LoadServiceResource (this , file , getFileName (file , namePlusSuffix ), absolute );
1088
1094
debugLogFound (foundResource );
1089
1095
state .setLoadName (resolveLoadName (foundResource , namePlusSuffix ));
1090
1096
break ;
@@ -1138,7 +1144,7 @@ protected LoadServiceResource tryResourceFromHome(SearchState state, String base
1138
1144
boolean absolute = true ;
1139
1145
1140
1146
state .setLoadName (file .getPath ());
1141
- foundResource = new LoadServiceResource (file , state .loadName , absolute );
1147
+ foundResource = new LoadServiceResource (this , file , state .loadName , absolute );
1142
1148
debugLogFound (foundResource );
1143
1149
break ;
1144
1150
}
@@ -1164,7 +1170,7 @@ protected LoadServiceResource tryResourceFromJarURL(SearchState state, String ba
1164
1170
URL url = resourceUri .toURL ();
1165
1171
debugLogTry ("resourceFromJarURL" , url .toString ());
1166
1172
if (url .openStream () != null ) {
1167
- foundResource = new LoadServiceResource (url , namePlusSuffix );
1173
+ foundResource = new LoadServiceResource (this , url , namePlusSuffix );
1168
1174
debugLogFound (foundResource );
1169
1175
}
1170
1176
} catch (FileNotFoundException e ) {
@@ -1191,7 +1197,7 @@ protected LoadServiceResource tryResourceFromJarURL(SearchState state, String ba
1191
1197
debugLogTry ("resourceFromJarURL" , expandedFilename );
1192
1198
if (file .getJarEntry (expandedFilename ) != null ) {
1193
1199
URI resourceUri = new URI ("jar" , "file:" + jarFile + "!/" + expandedFilename , null );
1194
- foundResource = new LoadServiceResource (resourceUri .toURL (), namePlusSuffix );
1200
+ foundResource = new LoadServiceResource (this , resourceUri .toURL (), namePlusSuffix );
1195
1201
debugLogFound (foundResource );
1196
1202
}
1197
1203
} catch (URISyntaxException e ) {
@@ -1316,7 +1322,7 @@ protected LoadServiceResource tryResourceFromJarURLWithLoadPath(String namePlusS
1316
1322
if (current .getJarEntry (canonicalEntry ) != null ) {
1317
1323
try {
1318
1324
URI resourceUri = new URI ("jar" , "file:" + jarFileName + "!/" + canonicalEntry , null );
1319
- foundResource = new LoadServiceResource (resourceUri .toURL (), resourceUri .getSchemeSpecificPart ());
1325
+ foundResource = new LoadServiceResource (this , resourceUri .toURL (), resourceUri .getSchemeSpecificPart ());
1320
1326
debugLogFound (foundResource );
1321
1327
} catch (URISyntaxException e ) {
1322
1328
throw runtime .newIOError (e .getMessage ());
@@ -1405,7 +1411,7 @@ protected LoadServiceResource tryResourceFromLoadPath( String namePlusSuffix,Str
1405
1411
debugLogTry ("resourceFromLoadPath" , "'" + actualPath .toString () + "' " + actualPath .isFile () + " " + actualPath .canRead ());
1406
1412
}
1407
1413
if (actualPath .canRead ()) {
1408
- foundResource = new LoadServiceResource (actualPath , reportedPath , absolute );
1414
+ foundResource = new LoadServiceResource (this , actualPath , reportedPath , absolute );
1409
1415
debugLogFound (foundResource );
1410
1416
}
1411
1417
}
@@ -1452,7 +1458,7 @@ protected LoadServiceResource tryResourceAsIs(String namePlusSuffix, String debu
1452
1458
}
1453
1459
1454
1460
if (actualPath .isFile () && actualPath .canRead ()) {
1455
- foundResource = new LoadServiceResource (actualPath , reportedPath );
1461
+ foundResource = new LoadServiceResource (this , actualPath , reportedPath );
1456
1462
debugLogFound (foundResource );
1457
1463
}
1458
1464
}
@@ -1597,7 +1603,7 @@ public LoadServiceResource getClassPathResource(ClassLoader classLoader, String
1597
1603
1598
1604
if (loc != null ) { // got it
1599
1605
String path = classpathFilenameFromURL (name , loc , isClasspathScheme );
1600
- LoadServiceResource foundResource = new LoadServiceResource (loc , path );
1606
+ LoadServiceResource foundResource = new LoadServiceResource (this , loc , path );
1601
1607
debugLogFound (foundResource );
1602
1608
return foundResource ;
1603
1609
}
@@ -1655,4 +1661,61 @@ protected String resolveLoadName(LoadServiceResource foundResource, String previ
1655
1661
protected String getFileName (JRubyFile file , String namePlusSuffix ) {
1656
1662
return file .getAbsolutePath ();
1657
1663
}
1664
+
1665
+ public interface LoadHook {
1666
+ RubyString callback (String file , RubyString src );
1667
+ }
1668
+
1669
+ public void addLoadHook (LoadHook loadHook ) {
1670
+ synchronized (loadHooks ) {
1671
+ loadHooks .add (loadHook );
1672
+ }
1673
+ }
1674
+
1675
+ public void removeLoadHook (LoadHook loadHook ) {
1676
+ synchronized (loadHooks ) {
1677
+ loadHooks .remove (loadHook );
1678
+ }
1679
+ }
1680
+
1681
+ public RubyString runLoadHooks (String file , byte [] src ) {
1682
+ if (loadHooks .size () > 0 ) {
1683
+ synchronized (loadHooks ) {
1684
+ RubyString string = RubyString .newString (runtime , src );
1685
+ for (LoadHook loadHook : loadHooks ) {
1686
+ string = loadHook .callback (file , string );
1687
+ }
1688
+ return string ;
1689
+ }
1690
+ }
1691
+
1692
+ return null ;
1693
+ }
1694
+
1695
+ public RubyString runLoadHooks (String file , InputStream src ) throws IOException {
1696
+ if (loadHooks .size () > 0 ) {
1697
+ synchronized (loadHooks ) {
1698
+
1699
+ byte [] bytes = new byte [8192 ];
1700
+
1701
+ ByteArrayOutputStream baos = new ByteArrayOutputStream (8192 );
1702
+
1703
+ int len ;
1704
+ while ((len = src .read (bytes )) != -1 ) {
1705
+ baos .write (bytes , 0 , len );
1706
+ }
1707
+ bytes = baos .toByteArray ();
1708
+
1709
+ RubyString string = RubyString .newString (runtime , bytes );
1710
+
1711
+ for (LoadHook loadHook : loadHooks ) {
1712
+ string = loadHook .callback (file , string );
1713
+ }
1714
+
1715
+ return string ;
1716
+ }
1717
+ }
1718
+
1719
+ return null ;
1720
+ }
1658
1721
}