├── README.md ├── build.xml ├── manifest.mf ├── nbproject ├── build-impl.xml ├── genfiles.properties ├── project.properties └── project.xml └── src └── injectmoduleinfo ├── ModuleInfoClass.java ├── ModuleInfoForm.form ├── ModuleInfoForm.java ├── TextDisplayForm.form └── TextDisplayForm.java /README.md: -------------------------------------------------------------------------------- 1 | # Module-Info-Inject 2 | This injects custom module-info.class into non-modular java libraries, allowing them to be used with jlink 3 | 4 | This is written in Java 8 and is itself non-modular, mostly because I think that is kind of funny. 5 | 6 | Why this exists: 7 | Java archives and libraries which were created in a non-modular way cannot be linked into runnable images. You get an auto-module error, which is super annoying. This utility alleviates the problem and allows you to inject a module class into the jar file, allowing it to be linked properly. 8 | 9 | ### Usage 10 | - Run `ModuleInfoForm.java`. A FileExplorer GUI will pop up. 11 | - Navigate to the jar you want to inject module-info.class into. 12 | - Click inject. If the jar itself has no dependencies, you are good to go. (a backup will be created in the same directory. 13 | - It is likely that there will be dependencies that the jar requires before you can compile a module-info.class to inject into it... 14 | - If this is the case, look at the namespaces and classes that the injector lists for you. You will have to track down the packages that contain them, download them, then add *those* jars as dependencies. 15 | - If dependencies themselves have dependencies, you will not be alerted until you try to use jlink. Please be aware of this and only add one module injected jar to your project at a time (otherwise tracking this down can be a nightmare). 16 | - The module-info.java file will also be injected into the jar for reference. 17 | - There are edge cases I have not figured out yet that lead to the injector not catching some dependencies. Again, these will make themselves known when you go to use jlink. 18 | - Go to the generated `module-info.class` file to see what's the name of the module. Add `requires ` to your 19 | project's `module-info.java` file. 20 | 21 | Enjoy. 22 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project InjectModuleInfo. 12 | 13 | 73 | 74 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /nbproject/build-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Must set platform.home 148 | Must set platform.bootcp 149 | Must set platform.java 150 | Must set platform.javac 151 | 152 | The J2SE Platform is not correctly set up. 153 | Your active platform is: ${platform.active}, but the corresponding property "platforms.${platform.active}.home" is not found in the project's properties files. 154 | Either open the project in the IDE and setup the Platform with the same name or add it manually. 155 | For example like this: 156 | ant -Duser.properties.file=<path_to_property_file> jar (where you put the property "platforms.${platform.active}.home" in a .properties file) 157 | or ant -Dplatforms.${platform.active}.home=<path_to_JDK_home> jar (where no properties file is used) 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | Must set src.dir 326 | Must set test.src.dir 327 | Must set build.dir 328 | Must set dist.dir 329 | Must set build.classes.dir 330 | Must set dist.javadoc.dir 331 | Must set build.test.classes.dir 332 | Must set build.test.results.dir 333 | Must set build.classes.excludes 334 | Must set dist.jar 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | Must set javac.includes 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | No tests executed. 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | Must set JVM to use for profiling in profiler.info.jvm 868 | Must set profiler agent JVM arguments in profiler.info.jvmargs.agent 869 | 870 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | Must select some files in the IDE or set javac.includes 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | To run this application from the command line without Ant, try: 1224 | 1225 | ${platform.java} -jar "${dist.jar.resolved}" 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | Must select one file in the IDE or set run.class 1364 | 1365 | 1366 | 1367 | Must select one file in the IDE or set run.class 1368 | 1369 | 1370 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | Must select one file in the IDE or set debug.class 1395 | 1396 | 1397 | 1398 | 1399 | Must select one file in the IDE or set debug.class 1400 | 1401 | 1402 | 1403 | 1404 | Must set fix.includes 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1416 | 1419 | 1420 | This target only works when run from inside the NetBeans IDE. 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | Must select one file in the IDE or set profile.class 1430 | This target only works when run from inside the NetBeans IDE. 1431 | 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | 1438 | 1439 | This target only works when run from inside the NetBeans IDE. 1440 | 1441 | 1442 | 1443 | 1444 | 1445 | 1446 | 1447 | 1448 | 1449 | 1450 | 1451 | 1452 | This target only works when run from inside the NetBeans IDE. 1453 | 1454 | 1455 | 1456 | 1457 | 1458 | 1459 | 1460 | 1461 | 1462 | 1463 | 1464 | 1465 | 1466 | 1467 | 1468 | 1469 | 1470 | 1473 | 1474 | 1475 | 1476 | 1477 | 1478 | 1479 | 1480 | 1481 | 1482 | 1483 | 1484 | 1485 | 1486 | Must select one file in the IDE or set run.class 1487 | 1488 | 1489 | 1490 | 1491 | 1492 | Must select some files in the IDE or set test.includes 1493 | 1494 | 1495 | 1496 | 1497 | Must select one file in the IDE or set run.class 1498 | 1499 | 1500 | 1501 | 1502 | Must select one file in the IDE or set applet.url 1503 | 1504 | 1505 | 1506 | 1511 | 1512 | 1513 | 1514 | 1515 | 1516 | 1517 | 1518 | 1519 | 1520 | 1521 | 1522 | 1523 | 1524 | 1525 | 1526 | 1527 | 1528 | 1529 | 1530 | 1531 | 1532 | 1533 | 1534 | 1535 | 1536 | 1537 | 1538 | 1539 | 1540 | 1541 | 1542 | 1543 | 1544 | 1545 | 1546 | 1547 | 1548 | 1549 | 1550 | 1551 | 1552 | 1553 | 1554 | 1555 | 1556 | 1557 | 1558 | 1559 | 1560 | 1565 | 1566 | 1567 | 1568 | 1569 | 1570 | 1571 | 1572 | 1573 | 1574 | 1575 | 1576 | 1577 | 1578 | 1579 | 1580 | 1581 | 1582 | 1583 | 1584 | 1585 | 1586 | 1587 | 1588 | 1589 | 1590 | 1591 | 1592 | 1593 | 1594 | 1595 | 1596 | 1597 | 1598 | 1599 | 1600 | 1601 | 1602 | 1603 | 1604 | 1605 | 1606 | 1607 | 1608 | 1609 | 1610 | 1611 | 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1621 | 1622 | 1623 | 1624 | 1625 | 1626 | 1627 | 1628 | 1629 | 1630 | 1631 | 1632 | 1633 | 1634 | 1635 | 1636 | 1637 | 1638 | 1639 | 1640 | 1641 | 1642 | 1643 | Must select some files in the IDE or set javac.includes 1644 | 1645 | 1646 | 1647 | 1648 | 1649 | 1650 | 1651 | 1652 | 1653 | 1654 | 1655 | 1656 | 1657 | 1658 | 1659 | 1664 | 1665 | 1666 | 1667 | 1668 | 1669 | 1670 | 1671 | Some tests failed; see details above. 1672 | 1673 | 1674 | 1675 | 1676 | 1677 | 1678 | 1679 | 1680 | Must select some files in the IDE or set test.includes 1681 | 1682 | 1683 | 1684 | Some tests failed; see details above. 1685 | 1686 | 1687 | 1688 | Must select some files in the IDE or set test.class 1689 | Must select some method in the IDE or set test.method 1690 | 1691 | 1692 | 1693 | Some tests failed; see details above. 1694 | 1695 | 1696 | 1701 | 1702 | Must select one file in the IDE or set test.class 1703 | 1704 | 1705 | 1706 | Must select one file in the IDE or set test.class 1707 | Must select some method in the IDE or set test.method 1708 | 1709 | 1710 | 1711 | 1712 | 1713 | 1714 | 1715 | 1716 | 1717 | 1718 | 1719 | 1724 | 1725 | Must select one file in the IDE or set applet.url 1726 | 1727 | 1728 | 1729 | 1730 | 1731 | 1732 | 1737 | 1738 | Must select one file in the IDE or set applet.url 1739 | 1740 | 1741 | 1742 | 1743 | 1744 | 1745 | 1746 | 1751 | 1752 | 1753 | 1754 | 1755 | 1756 | 1757 | 1758 | 1759 | 1760 | 1761 | 1762 | 1763 | 1764 | 1765 | 1766 | 1767 | 1768 | 1769 | 1770 | 1771 | 1772 | 1773 | 1774 | 1775 | 1776 | 1777 | 1778 | 1779 | 1780 | 1781 | 1782 | 1783 | 1784 | 1785 | 1786 | 1787 | 1788 | 1789 | 1790 | 1791 | 1792 | 1793 | 1794 | 1795 | 1796 | 1797 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=ed33abb5 2 | build.xml.script.CRC32=9d1a5b15 3 | build.xml.stylesheet.CRC32=f85dc8f2@1.91.1.48 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=ed33abb5 7 | nbproject/build-impl.xml.script.CRC32=7ccb81e2 8 | nbproject/build-impl.xml.stylesheet.CRC32=3a2fa800@1.91.1.48 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 6 | application.title=InjectModuleInfo 7 | application.vendor=draque 8 | build.classes.dir=${build.dir}/classes 9 | build.classes.excludes=**/*.java,**/*.form 10 | # This directory is removed when the project is cleaned: 11 | build.dir=build 12 | build.generated.dir=${build.dir}/generated 13 | build.generated.sources.dir=${build.dir}/generated-sources 14 | # Only compile against the classpath explicitly listed here: 15 | build.sysclasspath=ignore 16 | build.test.classes.dir=${build.dir}/test/classes 17 | build.test.results.dir=${build.dir}/test/results 18 | # Uncomment to specify the preferred debugger connection transport: 19 | #debug.transport=dt_socket 20 | debug.classpath=\ 21 | ${run.classpath} 22 | debug.modulepath=\ 23 | ${run.modulepath} 24 | debug.test.classpath=\ 25 | ${run.test.classpath} 26 | debug.test.modulepath=\ 27 | ${run.test.modulepath} 28 | # Files in build.classes.dir which should be excluded from distribution jar 29 | dist.archive.excludes= 30 | # This directory is removed when the project is cleaned: 31 | dist.dir=dist 32 | dist.jar=${dist.dir}/InjectModuleInfo.jar 33 | dist.javadoc.dir=${dist.dir}/javadoc 34 | dist.jlink.dir=${dist.dir}/jlink 35 | dist.jlink.output=${dist.jlink.dir}/InjectModuleInfo 36 | endorsed.classpath= 37 | excludes= 38 | includes=** 39 | jar.compress=false 40 | javac.classpath= 41 | # Space-separated list of extra javac options 42 | javac.compilerargs= 43 | javac.deprecation=false 44 | javac.external.vm=true 45 | javac.modulepath= 46 | javac.processormodulepath= 47 | javac.processorpath=\ 48 | ${javac.classpath} 49 | javac.source=1.8 50 | javac.target=1.8 51 | javac.test.classpath=\ 52 | ${javac.classpath}:\ 53 | ${build.classes.dir} 54 | javac.test.modulepath=\ 55 | ${javac.modulepath} 56 | javac.test.processorpath=\ 57 | ${javac.test.classpath} 58 | javadoc.additionalparam= 59 | javadoc.author=false 60 | javadoc.encoding=${source.encoding} 61 | javadoc.html5=false 62 | javadoc.noindex=false 63 | javadoc.nonavbar=false 64 | javadoc.notree=false 65 | javadoc.private=false 66 | javadoc.splitindex=true 67 | javadoc.use=true 68 | javadoc.version=false 69 | javadoc.windowtitle= 70 | # The jlink additional root modules to resolve 71 | jlink.additionalmodules= 72 | # The jlink additional command line parameters 73 | jlink.additionalparam= 74 | jlink.launcher=true 75 | jlink.launcher.name=InjectModuleInfo 76 | main.class=injectmoduleinfo.ModuleInfoForm 77 | manifest.file=manifest.mf 78 | meta.inf.dir=${src.dir}/META-INF 79 | mkdist.disabled=false 80 | platform.active=JDK_1.8 81 | run.classpath=\ 82 | ${javac.classpath}:\ 83 | ${build.classes.dir} 84 | # Space-separated list of JVM arguments used when running the project. 85 | # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. 86 | # To set system properties for unit tests define test-sys-prop.name=value: 87 | run.jvmargs= 88 | run.modulepath=\ 89 | ${javac.modulepath} 90 | run.test.classpath=\ 91 | ${javac.test.classpath}:\ 92 | ${build.test.classes.dir} 93 | run.test.modulepath=\ 94 | ${javac.test.modulepath} 95 | source.encoding=UTF-8 96 | src.dir=src 97 | test.src.dir=test 98 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | InjectModuleInfo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/injectmoduleinfo/ModuleInfoClass.java: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | * Copyright 2019 Draque Thompson 3 | * 4 | * Module Injector is a module injection tool used for 5 | * modularizing jar files. This allows them to be 6 | * build into runnable images via jlink. 7 | * 8 | * No guarantees about anything. Use with caution. 9 | * This thing is very much a hack, and I hope that all 10 | * dependencies will be made modular so that no one 11 | * has to ever use it again.. 12 | * 13 | *******************************************************/ 14 | 15 | package injectmoduleinfo; 16 | 17 | import java.awt.HeadlessException; 18 | import java.io.BufferedReader; 19 | import java.io.BufferedWriter; 20 | import java.io.File; 21 | import java.io.FileInputStream; 22 | import java.io.FileNotFoundException; 23 | import java.io.FileOutputStream; 24 | import java.io.FileReader; 25 | import java.io.FileWriter; 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.io.InputStreamReader; 29 | import java.net.URI; 30 | import java.nio.file.FileSystem; 31 | import java.nio.file.FileSystems; 32 | import java.nio.file.Files; 33 | import java.nio.file.Path; 34 | import java.util.ArrayList; 35 | import java.util.Enumeration; 36 | import java.util.HashMap; 37 | import java.util.List; 38 | import java.util.Map; 39 | import java.util.zip.ZipEntry; 40 | import java.util.zip.ZipFile; 41 | import java.util.zip.ZipInputStream; 42 | import java.util.zip.ZipOutputStream; 43 | import javax.swing.JOptionPane; 44 | 45 | /** 46 | * 47 | * @author draque 48 | */ 49 | public class ModuleInfoClass { 50 | 51 | private final File target; 52 | private final List dependencies; 53 | private String tmpModulePath = ""; 54 | private final String javaStr = ".java"; 55 | private final String classStr = ".class"; 56 | private final String moduleInfo = "module-info"; 57 | private final String tmpClassPath = "tmpClassPath"; 58 | // windows uses a different module separator character for some reason... 59 | private final String moduleSeparator = System.getProperties().getProperty("os.name").toLowerCase().contains("win") ? ";" : ":"; 60 | 61 | /** 62 | * Use publicly facing inject() method 63 | */ 64 | private ModuleInfoClass(File _target, List _dependencies) { 65 | target = _target; 66 | dependencies = _dependencies; 67 | } 68 | 69 | private static String runAtConsole(String command) throws InterruptedException, IOException { 70 | String ret = ""; 71 | Runtime run = Runtime.getRuntime(); 72 | Process p = run.exec(command); 73 | System.out.println(command); 74 | 75 | // get general output 76 | InputStream is = p.getInputStream(); 77 | BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 78 | String line; 79 | while ((line = reader.readLine()) != null) { 80 | ret += line; 81 | } 82 | 83 | // get error output 84 | is = p.getErrorStream(); 85 | reader = new BufferedReader(new InputStreamReader(is)); 86 | while ((line = reader.readLine()) != null) { 87 | ret += line; 88 | } 89 | 90 | return ret; 91 | } 92 | 93 | /** 94 | * creates temporary module (must be run AFTER target jar extracted) 95 | * 96 | * @throws IOException 97 | */ 98 | private void createTmpModule() throws IOException, InterruptedException, DependancyException { 99 | String command = ""; 100 | String targetModulePath = target.getParent(); 101 | String targetJar = target.getAbsolutePath(); 102 | 103 | command += "jdeps -verbose:class"; 104 | 105 | // if dependencies exist, build proper path for them... 106 | if (!dependencies.isEmpty()) { 107 | command += " --module-path "; 108 | 109 | for (File dependency : dependencies) { 110 | command += "" + dependency.getAbsolutePath() + moduleSeparator; 111 | } 112 | 113 | // remove trailing comma... 114 | command = command.substring(0, command.length() - 1); 115 | } 116 | command += " --add-modules=ALL-MODULE-PATH"; 117 | command += " --generate-module-info"; 118 | command += " " + targetModulePath + ""; 119 | command += " " + targetJar + ""; 120 | 121 | String result = runAtConsole(command); // TODO: HANDLE ERROR RESULT OF A DEPENDANCY ITSELF HAVING A DEPENDANCY THAT IS UNRESOLVED 122 | 123 | if (result.contains("Missing dependen")) { 124 | String commaListDeps = result.replaceAll("(^.*?->)", ""); 125 | commaListDeps = commaListDeps.replaceAll("^\\s*", ""); 126 | commaListDeps = commaListDeps.replaceAll("\\s*not found.*?->\\s*", ","); 127 | commaListDeps = commaListDeps.replaceAll("\\s*not found.*$", ""); 128 | String error = "The following dependencies are missing. Please provide the jars containing them:\n"; 129 | List deps = new ArrayList<>(); 130 | String[] arrayDeps = commaListDeps.split(","); 131 | 132 | for(String dep : arrayDeps) { 133 | // use list to add speed for very long series of depdndencies... 134 | if (!deps.contains(dep)) { 135 | deps.add(dep); 136 | } 137 | } 138 | 139 | for (String dep : deps) { 140 | error += dep + "\n"; 141 | } 142 | 143 | throw new DependancyException(error); 144 | } else if (!result.contains("writing to")) { 145 | throw new IOException("Something's gone wrong in the module.info creation:\n" + result); 146 | } 147 | 148 | // presumes that jdeps still returns "writing to " as success string... 149 | tmpModulePath = result.substring("writing to ".length()); 150 | } 151 | 152 | /** 153 | * creates backup of target jar 154 | */ 155 | private void backupTarget() throws FileNotFoundException, IOException { 156 | File copyTo = new File(target.getAbsolutePath() + ".bak"); 157 | copyFile(target, copyTo, true); 158 | } 159 | 160 | private void copyFile(File source, File copyTo, boolean backup) throws FileNotFoundException, IOException { 161 | int count = 0; 162 | 163 | // prevent backups from being overwritten 164 | if (backup) { 165 | while (copyTo.exists()) { 166 | count++; 167 | copyTo = new File(source.getAbsolutePath() + count + ".bak"); 168 | } 169 | } else if (copyTo.exists()) { 170 | copyTo.delete(); 171 | } 172 | 173 | try (FileInputStream is = new FileInputStream(source)) { 174 | try (FileOutputStream os = new FileOutputStream(copyTo)) { 175 | byte[] buffer = new byte[1024]; 176 | int length; 177 | while ((length = is.read(buffer)) > 0) { 178 | os.write(buffer, 0, length); 179 | } 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * removes remaining temporary files 186 | */ 187 | private void cleanUp() { 188 | deleteFile(new File(tmpModulePath).getParentFile()); 189 | deleteFile(new File(target.getParent() + File.separator + tmpClassPath)); 190 | } 191 | 192 | private void deleteFile(File file) { 193 | if (file != null) { 194 | if (file.isDirectory()) { 195 | for (String subFileName : file.list()) { 196 | String path = file.getAbsolutePath(); 197 | String subFilePath = path + File.separator + subFileName; 198 | File subFile = new File(subFilePath); 199 | deleteFile(subFile); 200 | } 201 | } 202 | 203 | file.delete(); 204 | } 205 | } 206 | 207 | /** 208 | * Tests whether java archive already has a module and/or should inject 209 | * 210 | * @return 211 | */ 212 | private boolean shouldInject() throws IOException { 213 | boolean ret = true; 214 | boolean moduleFound = false; 215 | 216 | if (target.exists()) { 217 | try (FileInputStream iStream = new FileInputStream(target)) { 218 | try (ZipInputStream zin = new ZipInputStream(iStream)) { 219 | ZipEntry entry = zin.getNextEntry(); 220 | while (entry != null) { 221 | if (entry.getName().equals(moduleInfo + classStr)) { 222 | moduleFound = true; 223 | break; 224 | } 225 | 226 | entry = zin.getNextEntry(); 227 | } 228 | } 229 | } 230 | 231 | if (moduleFound) { 232 | String moduleInfoContents = moduleContents(); 233 | String message; 234 | 235 | if (moduleInfoContents.isEmpty()) { 236 | message = "Package contains existing module (not written by this utility). Overwrite?"; 237 | } else { 238 | message = "Package contains existing module (written by this utility)\nOverwrite?"; 239 | } 240 | 241 | int dialogResult = JOptionPane.showConfirmDialog(null, message, "Warning", JOptionPane.YES_NO_OPTION); 242 | ret = dialogResult == JOptionPane.YES_OPTION; 243 | 244 | if (ret) { 245 | removeClassFromTarget(moduleInfo + classStr); 246 | } 247 | } 248 | } else { 249 | ret = false; 250 | JOptionPane.showMessageDialog(null, "Target jar file does not exist."); 251 | } 252 | 253 | return ret; 254 | } 255 | 256 | private void removeClassFromTarget(String targetDelete) throws IOException { 257 | /* Define ZIP File System Properies in HashMap */ 258 | Map zip_properties = new HashMap<>(); 259 | /* We want to read an existing ZIP File, so we set this to False */ 260 | zip_properties.put("create", "false"); 261 | 262 | /* Specify the path to the ZIP File that you want to read as a File System */ 263 | URI zip_disk = URI.create("jar:file:" + target.getAbsolutePath());//("jar:file:/my_zip_file.zip"); 264 | 265 | /* Create ZIP file System */ 266 | try (FileSystem zipfs = FileSystems.newFileSystem(zip_disk, zip_properties)) { 267 | Path pathInZipfile = zipfs.getPath(targetDelete); 268 | Files.delete(pathInZipfile); 269 | } 270 | } 271 | 272 | /** 273 | * compiles java file to class file 274 | * 275 | * @throws IOException 276 | */ 277 | private void compileModule() throws InterruptedException, IOException { 278 | String command = ""; 279 | String compileToPath = target.getParent() + File.separator + tmpClassPath; 280 | 281 | command += "javac"; 282 | if (!dependencies.isEmpty()) { 283 | 284 | command += " --module-path "; 285 | for (File dependency : dependencies) { 286 | command += dependency.getAbsolutePath() + moduleSeparator; 287 | } 288 | 289 | // remove trailing separator character... 290 | command = command.substring(0, command.length() - 1); 291 | } 292 | command += " -d " + compileToPath + " " + tmpModulePath; 293 | 294 | String result = runAtConsole(command); 295 | 296 | File classFile = new File(compileToPath + File.separator + moduleInfo + classStr); 297 | if (result.contains("package is empty or does not exist")) { 298 | // detects empty packages which cause the compiler to throw up, then removes them from the module-info file and recursively recompiles 299 | String emptyExport = result.replaceAll("^.*exports\\s*", ""); 300 | 301 | emptyExport = emptyExport.replaceAll(";.*$", ""); 302 | removeExportFromModule(emptyExport); 303 | JOptionPane.showMessageDialog(null, "Empty export: " + emptyExport + " removed from path to compile. Continuing."); 304 | compileModule(); 305 | } else if (!classFile.exists()) { 306 | throw new IOException("Class file not compiled: " + result); 307 | } 308 | } 309 | 310 | /** 311 | * Eliminates an export from a module-info.java instance (in case it's an empty export) 312 | * @param export 313 | * @throws IOException 314 | */ 315 | private void removeExportFromModule(String export) throws IOException{ 316 | File f1 = new File(tmpModulePath); 317 | String line; 318 | String moduleText = ""; 319 | try (FileReader fr = new FileReader(f1); BufferedReader br = new BufferedReader(fr)) { 320 | while ((line = br.readLine()) != null) { 321 | moduleText += line + "\n"; 322 | } 323 | } 324 | 325 | String regex = "\\s*exports\\s*" + export + ";"; 326 | moduleText = moduleText.replaceAll(regex, ""); 327 | 328 | try (BufferedWriter out = new BufferedWriter(new FileWriter(f1))) { 329 | out.write(moduleText); 330 | out.flush(); 331 | } 332 | } 333 | 334 | /** 335 | * reads java archive and returns existing module info (as string) if it 336 | * exists 337 | */ 338 | private String moduleContents() { 339 | String moduleString = ""; 340 | 341 | try { 342 | ZipFile zip = new ZipFile(target); 343 | 344 | for (Enumeration e = zip.entries(); e.hasMoreElements();) { 345 | ZipEntry entry = (ZipEntry) e.nextElement(); 346 | 347 | if (entry.getName().equals(moduleInfo + javaStr)) { 348 | InputStream is = zip.getInputStream(entry); 349 | InputStreamReader isr = new InputStreamReader(is); 350 | 351 | char[] buffer = new char[1024]; 352 | while (isr.read(buffer, 0, buffer.length) != -1) { 353 | moduleString += new String(buffer); 354 | } 355 | break; 356 | } 357 | } 358 | } catch (HeadlessException | IOException e) { 359 | JOptionPane.showMessageDialog(null, "IO error while reading module info: " + e.getLocalizedMessage()); 360 | } 361 | 362 | return moduleString; 363 | } 364 | 365 | public static void inject(File target, List dependencies){ 366 | new ModuleInfoClass(target, dependencies).doInject(); 367 | } 368 | 369 | private void doInject() { 370 | try { 371 | if (shouldInject()) { 372 | createTmpModule(); 373 | extractTmpClasspath(); 374 | compileModule(); 375 | backupTarget(); 376 | archiveTmpModulePath(); 377 | JOptionPane.showMessageDialog(null, "Archive successfully modularized. (module-info.java added to archive for reference)\nTHERE MIGHT BE ADDITIONAL DEPENDENCIES FOR THIS MODULE. Please pay attention to error messages when you build your image with jlink."); 378 | } 379 | } catch (IOException | InterruptedException e) { 380 | JOptionPane.showMessageDialog(null, "Problems encountered: " + e.getLocalizedMessage()); 381 | } catch (DependancyException e ) { 382 | JOptionPane.showMessageDialog(null, "Problems encountered: Missing Dependencies (text window)"); 383 | TextDisplayForm.run("Missing Dependencies", e.getLocalizedMessage()); 384 | } finally { 385 | cleanUp(); 386 | } 387 | } 388 | 389 | private void extractTmpClasspath() throws IOException { 390 | String destDir = target.getParent() + File.separator + tmpClassPath; 391 | File dir = new File(destDir); 392 | // create output directory if it doesn't exist 393 | if (!dir.exists()) { 394 | dir.mkdirs(); 395 | } 396 | 397 | //buffer for read and write data to file 398 | byte[] buffer = new byte[1024]; 399 | 400 | System.out.println("Unzipping " + target.getAbsolutePath()); 401 | try (FileInputStream fis = new FileInputStream(target); 402 | ZipInputStream zis = new ZipInputStream(fis)) { 403 | ZipEntry ze = zis.getNextEntry(); 404 | while (ze != null) { 405 | String fileName = ze.getName(); 406 | File newFile = new File(destDir + File.separator + fileName); 407 | System.out.println("."); 408 | //create directories for sub directories in zip 409 | File parent = new File(newFile.getParent()); 410 | if (!parent.exists()) { 411 | parent.mkdirs(); 412 | } 413 | 414 | if (!ze.isDirectory()) { 415 | try (FileOutputStream fos = new FileOutputStream(newFile)) { 416 | int len; 417 | while ((len = zis.read(buffer)) > 0) { 418 | fos.write(buffer, 0, len); 419 | } 420 | } 421 | } 422 | //close this ZipEntry 423 | zis.closeEntry(); 424 | ze = zis.getNextEntry(); 425 | } 426 | //close last ZipEntry 427 | zis.closeEntry(); 428 | } 429 | } 430 | 431 | public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { 432 | File destFile = new File(destinationDir, zipEntry.getName()); 433 | 434 | String destDirPath = destinationDir.getCanonicalPath(); 435 | String destFilePath = destFile.getCanonicalPath(); 436 | 437 | if (!destFilePath.startsWith(destDirPath + File.separator)) { 438 | throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); 439 | } 440 | 441 | return destFile; 442 | } 443 | 444 | private void archiveTmpModulePath() throws IOException { 445 | target.delete(); 446 | copyFile(new File(tmpModulePath), 447 | new File(target.getParent() + File.separator + tmpClassPath + File.separator + moduleInfo + javaStr), false); 448 | zipDir(target.getAbsolutePath(), target.getParent() + File.separator + tmpClassPath); 449 | } 450 | 451 | private static void zipDir(String zipFileName, String dir) throws FileNotFoundException, IOException { 452 | File dirObj = new File(dir); 453 | try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName))) { 454 | System.out.println("Creating : " + zipFileName); 455 | addDir(dirObj, out, dir.length() + 1); 456 | } 457 | } 458 | 459 | private static void addDir(File dirObj, ZipOutputStream out, int pathTrim) throws IOException { 460 | File[] files = dirObj.listFiles(); 461 | byte[] tmpBuf = new byte[1024]; 462 | 463 | for (File file : files) { 464 | if (file.isDirectory()) { 465 | addDir(file, out, pathTrim); 466 | continue; 467 | } 468 | try (final FileInputStream in = new FileInputStream(file.getAbsolutePath())) { 469 | String absolutePath = file.getAbsolutePath(); 470 | String trimmedPath = absolutePath.substring(pathTrim); 471 | System.out.print("."); 472 | out.putNextEntry(new ZipEntry(trimmedPath)); 473 | int len; 474 | while ((len = in.read(tmpBuf)) > 0) { 475 | out.write(tmpBuf, 0, len); 476 | } 477 | out.closeEntry(); 478 | } 479 | } 480 | } 481 | 482 | public class DependancyException extends Exception { 483 | public DependancyException(String message) { 484 | super(message); 485 | } 486 | } 487 | } 488 | -------------------------------------------------------------------------------- /src/injectmoduleinfo/ModuleInfoForm.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 |
189 | -------------------------------------------------------------------------------- /src/injectmoduleinfo/ModuleInfoForm.java: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | * Copyright 2019 Draque Thompson 3 | * 4 | * Module Injector is a module injection tool used for 5 | * modularizing jar files. This allows them to be 6 | * build into runnable images via jlink. 7 | * 8 | * No guarantees about anything. Use with caution. 9 | * This thing is very much a hack, and I hope that all 10 | * dependencies will be made modular so that no one 11 | * has to ever use it again.. 12 | * 13 | *******************************************************/ 14 | 15 | package injectmoduleinfo; 16 | 17 | import java.awt.Cursor; 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | import java.util.Arrays; 21 | import java.util.List; 22 | import javax.swing.JFileChooser; 23 | import javax.swing.JOptionPane; 24 | import javax.swing.filechooser.FileNameExtensionFilter; 25 | 26 | /** 27 | * 28 | * @author draque 29 | */ 30 | public final class ModuleInfoForm extends javax.swing.JFrame { 31 | 32 | private File target = null; 33 | private String lastFilePath = null; 34 | private final List dependencies = new ArrayList<>(); 35 | 36 | /** 37 | * Creates new form TheForm 38 | */ 39 | public ModuleInfoForm() { 40 | initComponents(); 41 | this.setLocationRelativeTo(null); 42 | } 43 | 44 | /** 45 | * Selects jar archive 46 | */ 47 | private void selectTarget() { 48 | File[] newTarget = selectJarFiles("Select Java Arhcive to Modularize", false); 49 | 50 | if (newTarget != null && newTarget.length > 0) { 51 | target = newTarget[0]; 52 | txtTargetJar.setText(target.getAbsolutePath()); 53 | lastFilePath = target.getParent(); 54 | clearDependencies(); 55 | } 56 | } 57 | 58 | private void setUIEnabled(boolean enable) { 59 | btnInject.setEnabled(enable); 60 | btnSelect.setEnabled(enable); 61 | btnAddDep.setEnabled(enable); 62 | btnClearDep.setEnabled(enable); 63 | } 64 | 65 | private void clearDependencies() { 66 | dependencies.clear(); 67 | updateDependencyDisplay(); 68 | } 69 | 70 | private void updateDependencyDisplay() { 71 | String depText = ""; 72 | 73 | for (File dep : dependencies) { 74 | depText += dep.getName() + "\n"; 75 | } 76 | 77 | txtDependencies.setText(depText); 78 | } 79 | 80 | private void addDependency() { 81 | File[] newDependencies = selectJarFiles("Select Java Archive Dependency", true); 82 | 83 | if (newDependencies != null && newDependencies.length > 0) { 84 | lastFilePath = newDependencies[0].getParent(); 85 | 86 | dependencies.addAll(Arrays.asList(newDependencies)); 87 | updateDependencyDisplay(); 88 | } 89 | } 90 | 91 | private File[] selectJarFiles(String title, boolean multiSelect) { 92 | File[] ret = null; 93 | final JFileChooser fc = new JFileChooser(lastFilePath); 94 | 95 | fc.setFileFilter(new FileNameExtensionFilter("Java Archive", "jar")); 96 | fc.setDialogTitle(title); 97 | fc.setMultiSelectionEnabled(multiSelect); 98 | 99 | int choice = fc.showOpenDialog(this); 100 | 101 | if (choice == JFileChooser.APPROVE_OPTION) { 102 | ret = fc.getSelectedFiles(); 103 | 104 | if (ret == null || ret.length == 0) { 105 | ret = new File[]{fc.getSelectedFile()}; 106 | } 107 | } 108 | 109 | return ret; 110 | } 111 | 112 | private void inject() { 113 | if (target != null) { 114 | target = new File(txtTargetJar.getText()); // ensures out of date files never used 115 | setUIEnabled(false); 116 | setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 117 | try { 118 | ModuleInfoClass.inject(target, dependencies); 119 | } catch (Exception e) { 120 | JOptionPane.showMessageDialog(null, "Something went wrong:\n" + e.getLocalizedMessage()); 121 | } 122 | setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 123 | setUIEnabled(true); 124 | } else { 125 | JOptionPane.showMessageDialog(null, "Please select target Java archive."); 126 | } 127 | } 128 | 129 | /** 130 | * This method is called from within the constructor to initialize the form. 131 | * WARNING: Do NOT modify this code. The content of this method is always 132 | * regenerated by the Form Editor. 133 | */ 134 | @SuppressWarnings("unchecked") 135 | // //GEN-BEGIN:initComponents 136 | private void initComponents() { 137 | 138 | txtTargetJar = new javax.swing.JTextField(); 139 | btnSelect = new javax.swing.JButton(); 140 | jPanel1 = new javax.swing.JPanel(); 141 | jLabel3 = new javax.swing.JLabel(); 142 | jScrollPane1 = new javax.swing.JScrollPane(); 143 | txtDependencies = new javax.swing.JTextArea(); 144 | jLabel4 = new javax.swing.JLabel(); 145 | btnAddDep = new javax.swing.JButton(); 146 | btnClearDep = new javax.swing.JButton(); 147 | btnInject = new javax.swing.JButton(); 148 | 149 | setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); 150 | setTitle("Module Info Injector"); 151 | 152 | txtTargetJar.setEditable(false); 153 | 154 | btnSelect.setText("Select Jar"); 155 | btnSelect.setToolTipText("Java archive to inject dummy module info into"); 156 | btnSelect.addActionListener(new java.awt.event.ActionListener() { 157 | public void actionPerformed(java.awt.event.ActionEvent evt) { 158 | btnSelectActionPerformed(evt); 159 | } 160 | }); 161 | 162 | jPanel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0))); 163 | 164 | jLabel3.setForeground(new java.awt.Color(153, 153, 153)); 165 | jLabel3.setText("Author: Draque Thompson"); 166 | jLabel3.setToolTipText(""); 167 | 168 | txtDependencies.setEditable(false); 169 | txtDependencies.setColumns(20); 170 | txtDependencies.setRows(5); 171 | jScrollPane1.setViewportView(txtDependencies); 172 | 173 | jLabel4.setText("Dependencies required"); 174 | jLabel4.setToolTipText(""); 175 | 176 | btnAddDep.setText("Add Dependency"); 177 | btnAddDep.addActionListener(new java.awt.event.ActionListener() { 178 | public void actionPerformed(java.awt.event.ActionEvent evt) { 179 | btnAddDepActionPerformed(evt); 180 | } 181 | }); 182 | 183 | btnClearDep.setText("Clear All"); 184 | btnClearDep.addActionListener(new java.awt.event.ActionListener() { 185 | public void actionPerformed(java.awt.event.ActionEvent evt) { 186 | btnClearDepActionPerformed(evt); 187 | } 188 | }); 189 | 190 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); 191 | jPanel1.setLayout(jPanel1Layout); 192 | jPanel1Layout.setHorizontalGroup( 193 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 194 | .addGroup(jPanel1Layout.createSequentialGroup() 195 | .addContainerGap() 196 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 197 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 444, Short.MAX_VALUE) 198 | .addGroup(jPanel1Layout.createSequentialGroup() 199 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 200 | .addComponent(jLabel3) 201 | .addComponent(jLabel4)) 202 | .addGap(0, 0, Short.MAX_VALUE))) 203 | .addContainerGap()) 204 | .addGroup(jPanel1Layout.createSequentialGroup() 205 | .addComponent(btnAddDep) 206 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 207 | .addComponent(btnClearDep)) 208 | ); 209 | jPanel1Layout.setVerticalGroup( 210 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 211 | .addGroup(jPanel1Layout.createSequentialGroup() 212 | .addContainerGap() 213 | .addComponent(jLabel4) 214 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 215 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 263, Short.MAX_VALUE) 216 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 217 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 218 | .addComponent(btnAddDep) 219 | .addComponent(btnClearDep)) 220 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 221 | .addComponent(jLabel3) 222 | .addContainerGap()) 223 | ); 224 | 225 | btnInject.setText("Inject"); 226 | btnInject.setToolTipText("Inject module into selected jar"); 227 | btnInject.addActionListener(new java.awt.event.ActionListener() { 228 | public void actionPerformed(java.awt.event.ActionEvent evt) { 229 | btnInjectActionPerformed(evt); 230 | } 231 | }); 232 | 233 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 234 | getContentPane().setLayout(layout); 235 | layout.setHorizontalGroup( 236 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 237 | .addGroup(layout.createSequentialGroup() 238 | .addContainerGap() 239 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 240 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 241 | .addGroup(layout.createSequentialGroup() 242 | .addComponent(txtTargetJar, javax.swing.GroupLayout.DEFAULT_SIZE, 351, Short.MAX_VALUE) 243 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 244 | .addComponent(btnSelect))) 245 | .addContainerGap()) 246 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() 247 | .addGap(0, 0, Short.MAX_VALUE) 248 | .addComponent(btnInject)) 249 | ); 250 | layout.setVerticalGroup( 251 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 252 | .addGroup(layout.createSequentialGroup() 253 | .addContainerGap() 254 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 255 | .addComponent(txtTargetJar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 256 | .addComponent(btnSelect)) 257 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 258 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 259 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 260 | .addComponent(btnInject)) 261 | ); 262 | 263 | pack(); 264 | }// //GEN-END:initComponents 265 | 266 | private void btnSelectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnSelectActionPerformed 267 | selectTarget(); 268 | }//GEN-LAST:event_btnSelectActionPerformed 269 | 270 | private void btnInjectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnInjectActionPerformed 271 | inject(); 272 | }//GEN-LAST:event_btnInjectActionPerformed 273 | 274 | private void btnClearDepActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnClearDepActionPerformed 275 | clearDependencies(); 276 | }//GEN-LAST:event_btnClearDepActionPerformed 277 | 278 | private void btnAddDepActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAddDepActionPerformed 279 | addDependency(); 280 | }//GEN-LAST:event_btnAddDepActionPerformed 281 | 282 | /** 283 | * @param args the command line arguments 284 | */ 285 | public static void main(String args[]) { 286 | /* Set the Nimbus look and feel */ 287 | // 288 | /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel. 289 | * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 290 | */ 291 | try { 292 | for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) { 293 | if ("Nimbus".equals(info.getName())) { 294 | javax.swing.UIManager.setLookAndFeel(info.getClassName()); 295 | break; 296 | } 297 | } 298 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | javax.swing.UnsupportedLookAndFeelException ex) { 299 | java.util.logging.Logger.getLogger(ModuleInfoForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); 300 | } 301 | // 302 | // 303 | 304 | // 305 | // 306 | 307 | /* Create and display the form */ 308 | java.awt.EventQueue.invokeLater(new Runnable() { 309 | @Override 310 | public void run() { 311 | new ModuleInfoForm().setVisible(true); 312 | } 313 | }); 314 | } 315 | 316 | // Variables declaration - do not modify//GEN-BEGIN:variables 317 | private javax.swing.JButton btnAddDep; 318 | private javax.swing.JButton btnClearDep; 319 | private javax.swing.JButton btnInject; 320 | private javax.swing.JButton btnSelect; 321 | private javax.swing.JLabel jLabel3; 322 | private javax.swing.JLabel jLabel4; 323 | private javax.swing.JPanel jPanel1; 324 | private javax.swing.JScrollPane jScrollPane1; 325 | private javax.swing.JTextArea txtDependencies; 326 | private javax.swing.JTextField txtTargetJar; 327 | // End of variables declaration//GEN-END:variables 328 | } 329 | -------------------------------------------------------------------------------- /src/injectmoduleinfo/TextDisplayForm.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 |
55 | -------------------------------------------------------------------------------- /src/injectmoduleinfo/TextDisplayForm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package injectmoduleinfo; 7 | 8 | /** 9 | * 10 | * @author draque 11 | */ 12 | public final class TextDisplayForm extends javax.swing.JFrame { 13 | 14 | /** 15 | * Creates new form TextDisplayForm 16 | * @param title 17 | * @param message 18 | */ 19 | public TextDisplayForm(String title, String message) { 20 | initComponents(); 21 | 22 | this.setTitle(title); 23 | txtDisplay.setText(message); 24 | } 25 | 26 | public static void run(String title, String message) { 27 | new TextDisplayForm(title, message).setVisible(true); 28 | } 29 | 30 | /** 31 | * This method is called from within the constructor to initialize the form. 32 | * WARNING: Do NOT modify this code. The content of this method is always 33 | * regenerated by the Form Editor. 34 | */ 35 | @SuppressWarnings("unchecked") 36 | // //GEN-BEGIN:initComponents 37 | private void initComponents() { 38 | 39 | jScrollPane1 = new javax.swing.JScrollPane(); 40 | txtDisplay = new javax.swing.JTextArea(); 41 | 42 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 43 | 44 | txtDisplay.setColumns(20); 45 | txtDisplay.setLineWrap(true); 46 | txtDisplay.setRows(5); 47 | txtDisplay.setWrapStyleWord(true); 48 | jScrollPane1.setViewportView(txtDisplay); 49 | 50 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 51 | getContentPane().setLayout(layout); 52 | layout.setHorizontalGroup( 53 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 54 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) 55 | ); 56 | layout.setVerticalGroup( 57 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 58 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) 59 | ); 60 | 61 | pack(); 62 | }// //GEN-END:initComponents 63 | // Variables declaration - do not modify//GEN-BEGIN:variables 64 | private javax.swing.JScrollPane jScrollPane1; 65 | private javax.swing.JTextArea txtDisplay; 66 | // End of variables declaration//GEN-END:variables 67 | } 68 | --------------------------------------------------------------------------------