136
137 /**
138 * Defines the storage options that SimpleLibrary supports.
139 */
140 public static enum StorageOption {
141 DEFLATED,
142 }
143
144 private static final class Header
145 extends MetaData
146 {
147 private static final String FILE
148 = FileConstants.META_PREFIX + "jigsaw-library";
149
150 private static final int MAJOR_VERSION = 0;
151 private static final int MINOR_VERSION = 1;
152
153 private static final int DEFLATED = 1 << 0;
154
155 private File parent;
156 private Set<StorageOption> opts;
157
158 public File parent() { return parent; }
159 public boolean isDeflated() {
160 return opts.contains(StorageOption.DEFLATED);
161 }
162
163 private Header(File root, File p, Set<StorageOption> opts) {
164 super(MAJOR_VERSION, MINOR_VERSION,
165 FileConstants.Type.LIBRARY_HEADER,
166 new File(root, FILE));
167 this.parent = p;
168 this.opts = new HashSet<>(opts);
169 }
170
171 private Header(File root) {
172 this(root, null, Collections.<StorageOption>emptySet());
173 }
174
175 protected void storeRest(DataOutputStream out)
176 throws IOException
177 {
178 int flags = 0;
179 if (isDeflated())
180 flags |= DEFLATED;
181 out.writeShort(flags);
182 out.writeByte((parent != null) ? 1 : 0);
183 if (parent != null)
184 out.writeUTF(parent.toString());
185 }
186
187 protected void loadRest(DataInputStream in)
188 throws IOException
189 {
190 opts = new HashSet<StorageOption>();
191 int flags = in.readShort();
192 if ((flags & DEFLATED) == DEFLATED)
193 opts.add(StorageOption.DEFLATED);
194 int b = in.readByte();
195 if (b != 0)
196 parent = new File(in.readUTF());
197 }
198
199 private static Header load(File f)
200 throws IOException
201 {
202 Header h = new Header(f);
203 h.load();
204 return h;
205 }
206
207 }
208
209 private final File root;
210 private final File canonicalRoot;
211 private File parentPath = null;
212 private SimpleLibrary parent = null;
213 private final Header hd;
214
215 public String name() { return root.toString(); }
216 public File root() { return canonicalRoot; }
217 public int majorVersion() { return hd.majorVersion; }
218 public int minorVersion() { return hd.minorVersion; }
219 public SimpleLibrary parent() { return parent; }
220 public boolean isDeflated() { return hd.isDeflated(); }
221
222 private URI location = null;
223 public URI location() {
224 if (location == null)
225 location = root().toURI();
226 return location;
227 }
228
229 @Override
230 public String toString() {
231 return (this.getClass().getName()
232 + "[" + canonicalRoot
233 + ", v" + hd.majorVersion + "." + hd.minorVersion + "]");
234 }
235
236 private SimpleLibrary(File path, boolean create, File parentPath, Set<StorageOption> opts)
237 throws IOException
238 {
239 root = path;
240 canonicalRoot = root.getCanonicalFile();
241 if (root.exists()) {
242 if (!root.isDirectory())
243 throw new IOException(root + ": Exists but is not a directory");
244 hd = Header.load(root);
245 if (hd.parent() != null) {
246 parent = open(hd.parent());
247 parentPath = hd.parent();
248 }
249 return;
250 }
251 if (!create)
252 throw new FileNotFoundException(root.toString());
253 if (parentPath != null) {
254 this.parent = open(parentPath);
255 this.parentPath = this.parent.root();
256 }
257 if (!root.mkdirs())
258 throw new IOException(root + ": Cannot create library directory");
259 hd = new Header(canonicalRoot, this.parentPath, opts);
260 hd.store();
261 }
262
263 public static SimpleLibrary create(File path, File parent, Set<StorageOption> opts)
264 throws IOException
265 {
266 return new SimpleLibrary(path, true, parent, opts);
267 }
268
269 public static SimpleLibrary create(File path, File parent)
270 throws IOException
271 {
272 return new SimpleLibrary(path, true, parent, Collections.<StorageOption>emptySet());
273 }
274
275 public static SimpleLibrary create(File path, Set<StorageOption> opts)
276 throws IOException
277 {
278 // ## Should default parent to $JAVA_HOME/lib/modules
279 return new SimpleLibrary(path, true, null, opts);
280 }
281
282 public static SimpleLibrary open(File path)
283 throws IOException
284 {
285 return new SimpleLibrary(path, false, null, Collections.<StorageOption>emptySet());
286 }
287
288 private static final JigsawModuleSystem jms
289 = JigsawModuleSystem.instance();
290
291 private static final class Index
292 extends MetaData
293 {
294
295 private static String FILE = "index";
296
297 private static int MAJOR_VERSION = 0;
298 private static int MINOR_VERSION = 1;
299
300 private Set<String> publicClasses;
301 public Set<String> publicClasses() { return publicClasses; }
302
303 private Set<String> otherClasses;
304 public Set<String> otherClasses() { return otherClasses; }
305
952 {
953 install(mfs, root, strip);
954 configure(null);
955 }
956
957 @Override
958 public void installFromManifests(Collection<Manifest> mfs)
959 throws ConfigurationException, IOException
960 {
961 installFromManifests(mfs, false);
962 }
963
964 private ModuleFileVerifier.Parameters mfvParams;
965
966 private ModuleId install(InputStream is, boolean verifySignature, boolean strip)
967 throws ConfigurationException, IOException, SignatureException
968 {
969 BufferedInputStream bin = new BufferedInputStream(is);
970 DataInputStream in = new DataInputStream(bin);
971 File md = null;
972 try (ModuleFile.Reader mr = new ModuleFile.Reader(in)) {
973 byte[] mib = mr.readStart();
974 ModuleInfo mi = jms.parseModuleInfo(mib);
975 md = moduleDir(mi.id());
976 ModuleId mid = mi.id();
977 if (md.exists())
978 throw new ConfigurationException(mid + ": Already installed");
979 if (!md.mkdirs())
980 throw new IOException(md + ": Cannot create");
981
982 if (verifySignature && mr.hasSignature()) {
983 ModuleFileVerifier mfv = new SignedModule.PKCS7Verifier(mr);
984 if (mfvParams == null) {
985 mfvParams = new SignedModule.VerifierParameters();
986 }
987 // Verify the module signature and validate the signer's
988 // certificate chain
989 Set<CodeSigner> signers = mfv.verifySignature(mfvParams);
990
991 // Verify the module header hash and the module info hash
992 mfv.verifyHashesStart(mfvParams);
995 // ## should be granted?
996
997 // Store signer info
998 new Signers(md, signers).store();
999
1000 // Read and verify the rest of the hashes
1001 mr.readRest(md, isDeflated());
1002 mfv.verifyHashesRest(mfvParams);
1003 } else {
1004 mr.readRest(md, isDeflated());
1005 }
1006
1007 if (strip)
1008 strip(md);
1009 reIndex(mid); // ## Could do this while reading module file
1010 return mid;
1011
1012 } catch (IOException | SignatureException x) {
1013 if (md != null && md.exists()) {
1014 try {
1015 Files.deleteTree(md);
1016 } catch (IOException y) {
1017 y.initCause(x);
1018 throw y;
1019 }
1020 }
1021 throw x;
1022 }
1023 }
1024
1025 private ModuleId installFromJarFile(File mf, boolean verifySignature, boolean strip)
1026 throws ConfigurationException, IOException, SignatureException
1027 {
1028 File md = null;
1029 try (JarFile jf = new JarFile(mf, verifySignature)) {
1030 ModuleInfo mi = jf.getModuleInfo();
1031 if (mi == null)
1032 throw new ConfigurationException(mf + ": not a modular JAR file");
1033
1034 md = moduleDir(mi.id());
1035 ModuleId mid = mi.id();
1169 entry.setSize(size);
1170 entry.setCrc(je.getCrc());
1171 entry.setCompressedSize(size);
1172 }
1173 jos.putNextEntry(entry);
1174 if (baos.size() > 0)
1175 baos.writeTo(jos);
1176 jos.closeEntry();
1177 } catch (SecurityException se) {
1178 throw new SignatureException(se);
1179 }
1180 }
1181
1182 private ModuleId install(File mf, boolean verifySignature, boolean strip)
1183 throws ConfigurationException, IOException, SignatureException
1184 {
1185 ModuleId mid;
1186 if (mf.getName().endsWith(".jar"))
1187 mid = installFromJarFile(mf, verifySignature, strip);
1188 else {
1189 try (FileInputStream in = new FileInputStream(mf)) {
1190 mid = install(in, verifySignature, strip);
1191 }
1192 }
1193 return mid;
1194 }
1195
1196 public void install(Collection<File> mfs, boolean verifySignature, boolean strip)
1197 throws ConfigurationException, IOException, SignatureException
1198 {
1199 List<ModuleId> mids = new ArrayList<>();
1200 boolean complete = false;
1201 Throwable ox = null;
1202 try {
1203 for (File mf : mfs)
1204 mids.add(install(mf, verifySignature, strip));
1205 configure(mids);
1206 complete = true;
1207 } catch (IOException|ConfigurationException x) {
1208 ox = x;
1321 throws ConfigurationException, IOException
1322 {
1323 // ## mids not used yet
1324 for (ModuleInfo mi : listLocalRootModuleInfos()) {
1325 // ## We could be a lot more clever about this!
1326 Configuration<Context> cf
1327 = Configurator.configure(this, mi.id().toQuery());
1328 new StoredConfiguration(moduleDir(mi.id()), cf).store();
1329 }
1330 }
1331
1332 public URI findLocalResource(ModuleId mid, String name)
1333 throws IOException
1334 {
1335 return locateContent(mid, name);
1336 }
1337
1338 public File findLocalNativeLibrary(ModuleId mid, String name)
1339 throws IOException
1340 {
1341 File md = findModuleDir(mid);
1342 if (md == null)
1343 return null;
1344 File f = new File(new File(md, "lib"), name);
1345 if (!f.exists())
1346 return null;
1347 return f;
1348 }
1349
1350 public File classPath(ModuleId mid)
1351 throws IOException
1352 {
1353 File md = findModuleDir(mid);
1354 if (md == null) {
1355 if (parent != null)
1356 return parent.classPath(mid);
1357 return null;
1358 }
1359 // ## Check for other formats here
1360 return new File(md, "classes");
1361 }
1362
1363 /**
1364 * <p> Re-index the classes of the named previously-installed modules, and
|
136
137 /**
138 * Defines the storage options that SimpleLibrary supports.
139 */
140 public static enum StorageOption {
141 DEFLATED,
142 }
143
144 private static final class Header
145 extends MetaData
146 {
147 private static final String FILE
148 = FileConstants.META_PREFIX + "jigsaw-library";
149
150 private static final int MAJOR_VERSION = 0;
151 private static final int MINOR_VERSION = 1;
152
153 private static final int DEFLATED = 1 << 0;
154
155 private File parent;
156 // location of native libs for this library (may be outside the library)
157 // null:default, to use a per-module 'lib' directory
158 private File natlibs;
159 // location of native cmds for this library (may be outside the library)
160 // null:default, to use a per-module 'bin' directory
161 private File natcmds;
162
163 private Set<StorageOption> opts;
164
165 public File parent() { return parent; }
166 public File natlibs() { return natlibs; }
167 public File natcmds() { return natcmds; }
168 public boolean isDeflated() {
169 return opts.contains(StorageOption.DEFLATED);
170 }
171
172 private Header(File root, File p, File natlibs, File natcmds,
173 Set<StorageOption> opts) {
174 super(MAJOR_VERSION, MINOR_VERSION,
175 FileConstants.Type.LIBRARY_HEADER,
176 new File(root, FILE));
177 this.parent = p;
178 this.natlibs = natlibs;
179 this.natcmds = natcmds;
180 this.opts = new HashSet<>(opts);
181 }
182
183 private Header(File root) {
184 this(root, null, null, null, Collections.<StorageOption>emptySet());
185 }
186
187 protected void storeRest(DataOutputStream out)
188 throws IOException
189 {
190 int flags = 0;
191 if (isDeflated())
192 flags |= DEFLATED;
193 out.writeShort(flags);
194 out.writeByte((parent != null) ? 1 : 0);
195 if (parent != null)
196 out.writeUTF(parent.toString());
197 out.writeByte((natlibs != null) ? 1 : 0);
198 if (natlibs != null)
199 out.writeUTF(natlibs.toString());
200 out.writeByte((natcmds != null) ? 1 : 0);
201 if (natcmds != null)
202 out.writeUTF(natcmds.toString());
203 }
204
205 protected void loadRest(DataInputStream in)
206 throws IOException
207 {
208 opts = new HashSet<StorageOption>();
209 int flags = in.readShort();
210 if ((flags & DEFLATED) == DEFLATED)
211 opts.add(StorageOption.DEFLATED);
212 int b = in.readByte();
213 if (b != 0)
214 parent = new File(in.readUTF());
215 b = in.readByte();
216 if (b != 0)
217 natlibs = new File(in.readUTF());
218 b = in.readByte();
219 if (b != 0)
220 natcmds = new File(in.readUTF());
221 }
222
223 private static Header load(File f)
224 throws IOException
225 {
226 Header h = new Header(f);
227 h.load();
228 return h;
229 }
230
231 }
232
233 private final File root;
234 private final File canonicalRoot;
235 private File parentPath;
236 private File natlibs;
237 private File natcmds;
238 private SimpleLibrary parent;
239 private final Header hd;
240
241 public String name() { return root.toString(); }
242 public File root() { return canonicalRoot; }
243 public int majorVersion() { return hd.majorVersion; }
244 public int minorVersion() { return hd.minorVersion; }
245 public SimpleLibrary parent() { return parent; }
246 public File natlibs() { return natlibs; }
247 public File natcmds() { return natcmds; }
248 public boolean isDeflated() { return hd.isDeflated(); }
249
250 private URI location = null;
251 public URI location() {
252 if (location == null)
253 location = root().toURI();
254 return location;
255 }
256
257 @Override
258 public String toString() {
259 return (this.getClass().getName()
260 + "[" + canonicalRoot
261 + ", v" + hd.majorVersion + "." + hd.minorVersion + "]");
262 }
263
264 private static void ensureDirectory(File dir) throws IOException {
265 if (!dir.isDirectory())
266 throw new IOException(dir + ": Not a directory");
267 }
268
269 private static void warnWriteable(File dir) {
270 if (!dir.canWrite())
271 // TODO: where to write the warning message???
272 System.out.println("Warning: " + dir + " is not writeable.");
273 }
274
275 private SimpleLibrary(File path, boolean create, File parentPath,
276 File natlibs, File natcmds, Set<StorageOption> opts)
277 throws IOException
278 {
279 root = path;
280 canonicalRoot = root.getCanonicalFile();
281 boolean exists = root.exists();
282 boolean directory = root.isDirectory();
283 if (!create) {
284 if (!exists)
285 throw new FileNotFoundException(root.toString());
286 if (!directory)
287 throw new IOException(root + ": Exists but is not a directory");
288 hd = Header.load(root);
289 if (hd.parent() != null) {
290 parent = open(hd.parent());
291 this.parentPath = hd.parent();
292 }
293 if (hd.natlibs() != null)
294 this.natlibs = hd.natlibs();
295 if (hd.natcmds() != null)
296 this.natcmds = hd.natcmds();
297 return;
298 }
299 // create
300 if (exists) {
301 if (!directory)
302 throw new IOException(root + ": Not a directory");
303 else if (root.list().length != 0)
304 throw new IOException(root + ": Already Exists");
305 } else if (!root.mkdirs())
306 throw new IOException(root + ": Cannot create library directory");
307 if (parentPath != null) {
308 this.parent = open(parentPath);
309 this.parentPath = this.parent.root();
310 }
311 if (natlibs != null) {
312 // resolve against the working dir, and store the absolute path
313 this.natlibs = natlibs.getCanonicalFile();
314 if (!this.natlibs.exists()) {
315 if (!this.natlibs.mkdir())
316 throw new IOException(this.natlibs + ": Cannot create lib directory");
317 } else {
318 ensureDirectory(this.natlibs);
319 warnWriteable(this.natlibs);
320 }
321 }
322 if (natcmds != null) {
323 this.natcmds = natcmds.getCanonicalFile();
324 if (!this.natcmds.exists()) {
325 if (!this.natcmds.mkdir())
326 throw new IOException(this.natcmds + ": Cannot create cmd directory");
327 } else {
328 ensureDirectory(this.natcmds);
329 warnWriteable(this.natcmds);
330 }
331 }
332 hd = new Header(canonicalRoot, this.parentPath, this.natlibs,
333 this.natcmds, opts);
334 hd.store();
335 }
336
337 public static SimpleLibrary create(File path, File parent, File natlibs,
338 File natcmds, Set<StorageOption> opts)
339 throws IOException
340 {
341 return new SimpleLibrary(path, true, parent, natlibs, natcmds, opts);
342 }
343
344 public static SimpleLibrary create(File path, File parent,
345 Set<StorageOption> opts)
346 throws IOException
347 {
348 return new SimpleLibrary(path, true, parent, null, null, opts);
349 }
350
351 public static SimpleLibrary create(File path, File parent)
352 throws IOException
353 {
354 return new SimpleLibrary(path, true, parent, null, null,
355 Collections.<StorageOption>emptySet());
356 }
357
358 public static SimpleLibrary create(File path, Set<StorageOption> opts)
359 throws IOException
360 {
361 // ## Should default parent to $JAVA_HOME/lib/modules
362 return new SimpleLibrary(path, true, null, null, null, opts);
363 }
364
365 public static SimpleLibrary open(File path)
366 throws IOException
367 {
368 return new SimpleLibrary(path, false, null, null, null,
369 Collections.<StorageOption>emptySet());
370 }
371
372 private static final JigsawModuleSystem jms
373 = JigsawModuleSystem.instance();
374
375 private static final class Index
376 extends MetaData
377 {
378
379 private static String FILE = "index";
380
381 private static int MAJOR_VERSION = 0;
382 private static int MINOR_VERSION = 1;
383
384 private Set<String> publicClasses;
385 public Set<String> publicClasses() { return publicClasses; }
386
387 private Set<String> otherClasses;
388 public Set<String> otherClasses() { return otherClasses; }
389
1036 {
1037 install(mfs, root, strip);
1038 configure(null);
1039 }
1040
1041 @Override
1042 public void installFromManifests(Collection<Manifest> mfs)
1043 throws ConfigurationException, IOException
1044 {
1045 installFromManifests(mfs, false);
1046 }
1047
1048 private ModuleFileVerifier.Parameters mfvParams;
1049
1050 private ModuleId install(InputStream is, boolean verifySignature, boolean strip)
1051 throws ConfigurationException, IOException, SignatureException
1052 {
1053 BufferedInputStream bin = new BufferedInputStream(is);
1054 DataInputStream in = new DataInputStream(bin);
1055 File md = null;
1056 try (ModuleFile.Reader mr = new ModuleFile.Reader(in, this)) {
1057 byte[] mib = mr.readStart();
1058 ModuleInfo mi = jms.parseModuleInfo(mib);
1059 md = moduleDir(mi.id());
1060 ModuleId mid = mi.id();
1061 if (md.exists())
1062 throw new ConfigurationException(mid + ": Already installed");
1063 if (!md.mkdirs())
1064 throw new IOException(md + ": Cannot create");
1065
1066 if (verifySignature && mr.hasSignature()) {
1067 ModuleFileVerifier mfv = new SignedModule.PKCS7Verifier(mr);
1068 if (mfvParams == null) {
1069 mfvParams = new SignedModule.VerifierParameters();
1070 }
1071 // Verify the module signature and validate the signer's
1072 // certificate chain
1073 Set<CodeSigner> signers = mfv.verifySignature(mfvParams);
1074
1075 // Verify the module header hash and the module info hash
1076 mfv.verifyHashesStart(mfvParams);
1079 // ## should be granted?
1080
1081 // Store signer info
1082 new Signers(md, signers).store();
1083
1084 // Read and verify the rest of the hashes
1085 mr.readRest(md, isDeflated());
1086 mfv.verifyHashesRest(mfvParams);
1087 } else {
1088 mr.readRest(md, isDeflated());
1089 }
1090
1091 if (strip)
1092 strip(md);
1093 reIndex(mid); // ## Could do this while reading module file
1094 return mid;
1095
1096 } catch (IOException | SignatureException x) {
1097 if (md != null && md.exists()) {
1098 try {
1099 ModuleFile.Reader.remove(md);
1100 } catch (IOException y) {
1101 y.initCause(x);
1102 throw y;
1103 }
1104 }
1105 throw x;
1106 }
1107 }
1108
1109 private ModuleId installFromJarFile(File mf, boolean verifySignature, boolean strip)
1110 throws ConfigurationException, IOException, SignatureException
1111 {
1112 File md = null;
1113 try (JarFile jf = new JarFile(mf, verifySignature)) {
1114 ModuleInfo mi = jf.getModuleInfo();
1115 if (mi == null)
1116 throw new ConfigurationException(mf + ": not a modular JAR file");
1117
1118 md = moduleDir(mi.id());
1119 ModuleId mid = mi.id();
1253 entry.setSize(size);
1254 entry.setCrc(je.getCrc());
1255 entry.setCompressedSize(size);
1256 }
1257 jos.putNextEntry(entry);
1258 if (baos.size() > 0)
1259 baos.writeTo(jos);
1260 jos.closeEntry();
1261 } catch (SecurityException se) {
1262 throw new SignatureException(se);
1263 }
1264 }
1265
1266 private ModuleId install(File mf, boolean verifySignature, boolean strip)
1267 throws ConfigurationException, IOException, SignatureException
1268 {
1269 ModuleId mid;
1270 if (mf.getName().endsWith(".jar"))
1271 mid = installFromJarFile(mf, verifySignature, strip);
1272 else {
1273 // ## Assume jmod file, should we check extension?
1274 try (FileInputStream in = new FileInputStream(mf)) {
1275 mid = install(in, verifySignature, strip);
1276 }
1277 }
1278 return mid;
1279 }
1280
1281 public void install(Collection<File> mfs, boolean verifySignature, boolean strip)
1282 throws ConfigurationException, IOException, SignatureException
1283 {
1284 List<ModuleId> mids = new ArrayList<>();
1285 boolean complete = false;
1286 Throwable ox = null;
1287 try {
1288 for (File mf : mfs)
1289 mids.add(install(mf, verifySignature, strip));
1290 configure(mids);
1291 complete = true;
1292 } catch (IOException|ConfigurationException x) {
1293 ox = x;
1406 throws ConfigurationException, IOException
1407 {
1408 // ## mids not used yet
1409 for (ModuleInfo mi : listLocalRootModuleInfos()) {
1410 // ## We could be a lot more clever about this!
1411 Configuration<Context> cf
1412 = Configurator.configure(this, mi.id().toQuery());
1413 new StoredConfiguration(moduleDir(mi.id()), cf).store();
1414 }
1415 }
1416
1417 public URI findLocalResource(ModuleId mid, String name)
1418 throws IOException
1419 {
1420 return locateContent(mid, name);
1421 }
1422
1423 public File findLocalNativeLibrary(ModuleId mid, String name)
1424 throws IOException
1425 {
1426 File f = natlibs();
1427 if (f == null) {
1428 f = findModuleDir(mid);
1429 if (f == null)
1430 return null;
1431 f = new File(f, "lib");
1432 }
1433 f = new File(f, name);
1434 if (!f.exists())
1435 return null;
1436 return f;
1437 }
1438
1439 public File classPath(ModuleId mid)
1440 throws IOException
1441 {
1442 File md = findModuleDir(mid);
1443 if (md == null) {
1444 if (parent != null)
1445 return parent.classPath(mid);
1446 return null;
1447 }
1448 // ## Check for other formats here
1449 return new File(md, "classes");
1450 }
1451
1452 /**
1453 * <p> Re-index the classes of the named previously-installed modules, and
|