1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package ru.akman.maven.plugins.jpackage;
18
19 import java.io.BufferedWriter;
20 import java.io.File;
21 import java.io.IOException;
22 import java.nio.file.FileSystems;
23 import java.nio.file.Files;
24 import java.nio.file.Path;
25 import java.nio.file.PathMatcher;
26 import java.text.MessageFormat;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Objects;
31 import java.util.Properties;
32 import java.util.Set;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import java.util.stream.Collectors;
36 import java.util.stream.Stream;
37 import org.apache.commons.lang3.JavaVersion;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.maven.artifact.Artifact;
40 import org.apache.maven.plugin.MojoExecutionException;
41 import org.apache.maven.plugins.annotations.Component;
42 import org.apache.maven.plugins.annotations.Execute;
43 import org.apache.maven.plugins.annotations.Mojo;
44 import org.apache.maven.plugins.annotations.Parameter;
45 import org.apache.maven.plugins.annotations.ResolutionScope;
46 import org.apache.maven.shared.model.fileset.FileSet;
47 import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
48 import org.codehaus.plexus.languages.java.jpms.LocationManager;
49 import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
50 import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest;
51 import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult;
52 import org.codehaus.plexus.util.FileUtils;
53 import org.codehaus.plexus.util.cli.CommandLineException;
54 import org.codehaus.plexus.util.cli.Commandline;
55 import ru.akman.maven.plugins.BaseToolMojo;
56 import ru.akman.maven.plugins.CommandLineBuilder;
57 import ru.akman.maven.plugins.CommandLineOption;
58
59
60
61
62
63
64
65
66
67
68 @Mojo(
69 name = "jpackage",
70 requiresDependencyResolution = ResolutionScope.RUNTIME
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 public class JPackageMojo extends BaseToolMojo {
99
100
101
102
103 private static final String TOOL_HOME_BIN = "bin";
104
105
106
107
108 private static final String TOOL_NAME = "jpackage";
109
110
111
112
113 private static final String OPTS_FILE = TOOL_NAME + ".opts";
114
115
116
117
118 private static final String DESCRIPTOR_NAME = "module-info.class";
119
120
121
122
123 private static final String PROPS_PREFIX = "launcher.";
124
125
126
127
128 private static final String PROPS_SUFFIX = ".properties";
129
130
131
132
133 private static final String SPACES_PATTERN = "\\s+";
134
135
136
137
138 private static final char WRAP_CHAR = '\'';
139
140
141
142
143 private static final String ERROR_RESOLVE =
144 "Error: Unable to resolve file path for {0} [{1}]";
145
146
147
148
149 private final List<File> tempFiles = new ArrayList<>();
150
151
152
153
154 private ResolvePathsResult<File> projectDependencies;
155
156
157
158
159 private JavaModuleDescriptor mainModuleDescriptor;
160
161
162
163
164 @Component
165 private LocationManager locationManager;
166
167
168
169
170 @Parameter
171 private File toolhome;
172
173
174
175
176
177
178
179
180
181
182
183
184 @Parameter(
185 defaultValue = "${project.build.directory}/jpackage"
186 )
187 private File dest;
188
189
190
191
192
193
194
195
196
197
198 @Parameter
199 private File temp;
200
201
202
203
204
205
206
207 @Parameter(
208 defaultValue = "PLATFORM"
209 )
210 private PackageType type;
211
212
213
214
215
216
217 @Parameter(
218 defaultValue = "false"
219 )
220 private boolean verbose;
221
222
223
224
225
226
227 @Parameter
228 private String appversion;
229
230
231
232
233
234
235 @Parameter
236 private String copyright;
237
238
239
240
241
242
243 @Parameter
244 private String description;
245
246
247
248
249
250
251
252
253
254
255
256 @Parameter
257 private String name;
258
259
260
261
262
263
264 @Parameter
265 private String vendor;
266
267
268
269
270
271
272
273
274
275
276
277
278 @Parameter
279 private File icon;
280
281
282
283
284
285
286
287
288
289
290 @Parameter
291 private File input;
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311 @Parameter
312 private File runtimeimage;
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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 @Parameter
389 private ModulePath modulepath;
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 @Parameter
409 private List<String> addmodules;
410
411
412
413
414
415
416
417 @Parameter(
418 defaultValue = "false"
419 )
420 private boolean bindservices;
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435 @Parameter
436 private String module;
437
438
439
440
441
442
443
444
445 @Parameter
446 private String mainjar;
447
448
449
450
451
452
453
454 @Parameter
455 private String mainclass;
456
457
458
459
460
461
462
463 @Parameter
464 private String arguments;
465
466
467
468
469
470
471 @Parameter
472 private String javaoptions;
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 @Parameter
500 private List<Launcher> addlaunchers;
501
502
503
504
505
506
507
508
509
510
511
512 @Parameter(
513 defaultValue = "false"
514 )
515 private boolean winconsole;
516
517
518
519
520
521
522
523
524
525
526
527
528
529 @Parameter
530 private File appimage;
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548 @Parameter
549 private List<File> fileassociations;
550
551
552
553
554
555
556
557
558 @Parameter
559 private String installdir;
560
561
562
563
564
565
566
567
568 @Parameter
569 private File licensefile;
570
571
572
573
574
575
576
577
578
579
580
581 @Parameter
582 private File resourcedir;
583
584
585
586
587
588
589
590
591
592
593
594
595 @Parameter(
596 defaultValue = "false"
597 )
598 private boolean windirchooser;
599
600
601
602
603
604
605 @Parameter(
606 defaultValue = "false"
607 )
608 private boolean winmenu;
609
610
611
612
613
614
615 @Parameter
616 private String winmenugroup;
617
618
619
620
621
622
623 @Parameter(
624 defaultValue = "false"
625 )
626 private boolean winperuserinstall;
627
628
629
630
631
632
633 @Parameter(
634 defaultValue = "false"
635 )
636 private boolean winshortcut;
637
638
639
640
641
642
643 @Parameter
644 private String winupgradeuuid;
645
646
647
648
649
650
651
652
653
654
655
656
657
658 @Parameter
659 private String macpackageidentifier;
660
661
662
663
664
665
666
667
668
669
670 @Parameter
671 private String macpackagename;
672
673
674
675
676
677
678
679
680
681 @Parameter
682 private String macpackagesigningprefix;
683
684
685
686
687
688
689 @Parameter
690 private boolean macsign;
691
692
693
694
695
696
697
698
699
700
701 @Parameter
702 private File macsigningkeychain;
703
704
705
706
707
708
709
710 @Parameter
711 private String macsigningkeyusername;
712
713
714
715
716
717
718
719
720
721
722
723 @Parameter
724 private String linuxpackagename;
725
726
727
728
729
730
731 @Parameter
732 private String linuxdebmaintainer;
733
734
735
736
737
738
739 @Parameter
740 private String linuxmenugroup;
741
742
743
744
745
746
747 @Parameter
748 private boolean linuxpackagedeps;
749
750
751
752
753
754
755 @Parameter
756 private String linuxrpmlicensetype;
757
758
759
760
761
762
763
764 @Parameter
765 private String linuxapprelease;
766
767
768
769
770
771
772
773 @Parameter
774 private String linuxappcategory;
775
776
777
778
779
780
781 @Parameter
782 private boolean linuxshortcut;
783
784
785
786
787
788
789
790
791 private void processOptions(final CommandLineBuilder cmdLine)
792 throws MojoExecutionException {
793 CommandLineOption opt = null;
794
795 opt = cmdLine.createOpt();
796 opt.createArg().setValue("--dest");
797 try {
798 opt.createArg().setValue(dest.getCanonicalPath());
799 } catch (IOException ex) {
800 throw new MojoExecutionException(MessageFormat.format(
801 ERROR_RESOLVE,
802 "--dest",
803 dest.toString()), ex);
804 }
805
806 if (temp != null) {
807 opt = cmdLine.createOpt();
808 opt.createArg().setValue("--temp");
809 try {
810 opt.createArg().setValue(temp.getCanonicalPath());
811 } catch (IOException ex) {
812 throw new MojoExecutionException(MessageFormat.format(
813 ERROR_RESOLVE,
814 "--temp",
815 temp.toString()), ex);
816 }
817 }
818
819 if (type != null && !type.equals(PackageType.PLATFORM)) {
820 opt = cmdLine.createOpt();
821 opt.createArg().setValue("--type");
822 switch (type) {
823 case IMAGE:
824 opt.createArg().setValue("app-image");
825 break;
826 case EXE:
827 opt.createArg().setValue("exe");
828 break;
829 case MSI:
830 opt.createArg().setValue("msi");
831 break;
832 case RPM:
833 opt.createArg().setValue("rpm");
834 break;
835 case DEB:
836 opt.createArg().setValue("deb");
837 break;
838 case PKG:
839 opt.createArg().setValue("pkg");
840 break;
841 case DMG:
842 opt.createArg().setValue("dmg");
843 break;
844 default:
845
846 }
847 }
848
849 if (verbose) {
850 opt = cmdLine.createOpt();
851 opt.createArg().setValue("--verbose");
852 }
853
854 if (!StringUtils.isBlank(appversion)) {
855 opt = cmdLine.createOpt();
856 opt.createArg().setValue("--app-version");
857 opt.createArg().setValue(PluginUtils.wrapOpt(
858 appversion, WRAP_CHAR));
859 }
860
861 if (!StringUtils.isBlank(copyright)) {
862 opt = cmdLine.createOpt();
863 opt.createArg().setValue("--copyright");
864 opt.createArg().setValue(PluginUtils.wrapOpt(
865 copyright, WRAP_CHAR));
866 }
867
868 if (!StringUtils.isBlank(description)) {
869 opt = cmdLine.createOpt();
870 opt.createArg().setValue("--description");
871 opt.createArg().setValue(PluginUtils.wrapOpt(
872 description.replaceAll(SPACES_PATTERN, " "), WRAP_CHAR));
873 }
874
875 if (!StringUtils.isBlank(name)) {
876 opt = cmdLine.createOpt();
877 opt.createArg().setValue("--name");
878 opt.createArg().setValue(PluginUtils.wrapOpt(
879 name, WRAP_CHAR));
880 }
881
882 if (!StringUtils.isBlank(vendor)) {
883 opt = cmdLine.createOpt();
884 opt.createArg().setValue("--vendor");
885 opt.createArg().setValue(PluginUtils.wrapOpt(
886 vendor, WRAP_CHAR));
887 }
888
889 if (icon != null) {
890 opt = cmdLine.createOpt();
891 opt.createArg().setValue("--icon");
892 try {
893 opt.createArg().setValue(icon.getCanonicalPath());
894 } catch (IOException ex) {
895 throw new MojoExecutionException(MessageFormat.format(
896 ERROR_RESOLVE,
897 "--icon",
898 icon.toString()), ex);
899 }
900 }
901
902 if (input != null) {
903 opt = cmdLine.createOpt();
904 opt.createArg().setValue("--input");
905 try {
906 opt.createArg().setValue(input.getCanonicalPath());
907 } catch (IOException ex) {
908 throw new MojoExecutionException(MessageFormat.format(
909 ERROR_RESOLVE,
910 "--input",
911 input.toString()), ex);
912 }
913 }
914
915 if (runtimeimage != null) {
916 opt = cmdLine.createOpt();
917 opt.createArg().setValue("--runtime-image");
918 try {
919 opt.createArg().setValue(runtimeimage.getCanonicalPath());
920 } catch (IOException ex) {
921 throw new MojoExecutionException(MessageFormat.format(
922 ERROR_RESOLVE,
923 "--runtime-image",
924 runtimeimage.toString()), ex);
925 }
926 }
927
928 if (!StringUtils.isBlank(module)) {
929 opt = cmdLine.createOpt();
930 opt.createArg().setValue("--module");
931 opt.createArg().setValue(StringUtils.stripToEmpty(
932 module));
933 }
934
935 if (!StringUtils.isBlank(mainjar)) {
936 opt = cmdLine.createOpt();
937 opt.createArg().setValue("--main-jar");
938 opt.createArg().setValue(PluginUtils.wrapOpt(
939 mainjar, WRAP_CHAR));
940 }
941
942 if (!StringUtils.isBlank(mainclass)) {
943 opt = cmdLine.createOpt();
944 opt.createArg().setValue("--main-class");
945 opt.createArg().setValue(StringUtils.stripToEmpty(
946 mainclass));
947 }
948
949
950
951
952
953
954
955 if (!StringUtils.isBlank(arguments)) {
956 opt = cmdLine.createOpt();
957 opt.createArg().setValue("--arguments");
958 opt.createArg().setValue(PluginUtils.wrapOpt(
959 arguments, WRAP_CHAR));
960 }
961
962
963
964
965
966
967
968
969 if (!StringUtils.isBlank(javaoptions)) {
970 opt = cmdLine.createOpt();
971 opt.createArg().setValue("--java-options");
972 opt.createArg().setValue(PluginUtils.wrapOpt(
973 javaoptions, WRAP_CHAR));
974 }
975
976 if (addlaunchers != null && !addlaunchers.isEmpty()) {
977 for (final Launcher addlauncher : addlaunchers) {
978 final String name = StringUtils.stripToEmpty(addlauncher.getName());
979 if (!StringUtils.isBlank(name)) {
980 final File file = processLauncher(addlauncher);
981 tempFiles.add(file);
982 opt = cmdLine.createOpt();
983 opt.createArg().setValue("--add-launcher");
984 try {
985 opt.createArg().setValue(name + "=" + file.getCanonicalPath());
986 } catch (IOException ex) {
987 throw new MojoExecutionException(MessageFormat.format(
988 ERROR_RESOLVE,
989 "--add-launcher",
990 file.toString()), ex);
991 }
992 }
993 }
994 }
995
996 if (winconsole) {
997 opt = cmdLine.createOpt();
998 opt.createArg().setValue("--win-console");
999 }
1000
1001 if (appimage != null) {
1002 opt = cmdLine.createOpt();
1003 opt.createArg().setValue("--app-image");
1004 try {
1005 opt.createArg().setValue(appimage.getCanonicalPath());
1006 } catch (IOException ex) {
1007 throw new MojoExecutionException(MessageFormat.format(
1008 ERROR_RESOLVE,
1009 "--app-image",
1010 appimage.toString()), ex);
1011 }
1012 }
1013
1014 if (fileassociations != null) {
1015 for (final File fileassociation : fileassociations) {
1016 if (fileassociation != null
1017 && !StringUtils.isBlank(fileassociation.toString())) {
1018 opt = cmdLine.createOpt();
1019 opt.createArg().setValue("--file-associations");
1020 try {
1021 opt.createArg().setValue(fileassociation.getCanonicalPath());
1022 } catch (IOException ex) {
1023 throw new MojoExecutionException(MessageFormat.format(
1024 ERROR_RESOLVE,
1025 "--file-associations",
1026 fileassociation.toString()), ex);
1027 }
1028 }
1029 }
1030 }
1031
1032 if (!StringUtils.isBlank(installdir)) {
1033 opt = cmdLine.createOpt();
1034 opt.createArg().setValue("--install-dir");
1035 opt.createArg().setValue(PluginUtils.wrapOpt(
1036 installdir, WRAP_CHAR));
1037 }
1038
1039 if (licensefile != null) {
1040 opt = cmdLine.createOpt();
1041 opt.createArg().setValue("--license-file");
1042 try {
1043 opt.createArg().setValue(licensefile.getCanonicalPath());
1044 } catch (IOException ex) {
1045 throw new MojoExecutionException(MessageFormat.format(
1046 ERROR_RESOLVE,
1047 "--license-file",
1048 licensefile.toString()), ex);
1049 }
1050 }
1051
1052 if (resourcedir != null) {
1053 opt = cmdLine.createOpt();
1054 opt.createArg().setValue("--resource-dir");
1055 try {
1056 opt.createArg().setValue(resourcedir.getCanonicalPath());
1057 } catch (IOException ex) {
1058 throw new MojoExecutionException(MessageFormat.format(
1059 ERROR_RESOLVE,
1060 "--resource-dir",
1061 resourcedir.toString()), ex);
1062 }
1063 }
1064
1065 if (windirchooser) {
1066 opt = cmdLine.createOpt();
1067 opt.createArg().setValue("--win-dir-chooser");
1068 }
1069
1070 if (winmenu) {
1071 opt = cmdLine.createOpt();
1072 opt.createArg().setValue("--win-menu");
1073 }
1074
1075 if (!StringUtils.isBlank(winmenugroup)) {
1076 opt = cmdLine.createOpt();
1077 opt.createArg().setValue("--win-menu-group");
1078 opt.createArg().setValue(PluginUtils.wrapOpt(
1079 winmenugroup, WRAP_CHAR));
1080 }
1081
1082 if (winperuserinstall) {
1083 opt = cmdLine.createOpt();
1084 opt.createArg().setValue("--win-per-user-install");
1085 }
1086
1087 if (winshortcut) {
1088 opt = cmdLine.createOpt();
1089 opt.createArg().setValue("--win-shortcut");
1090 }
1091
1092 if (!StringUtils.isBlank(winupgradeuuid)) {
1093 opt = cmdLine.createOpt();
1094 opt.createArg().setValue("--win-upgrade-uuid");
1095 opt.createArg().setValue(PluginUtils.wrapOpt(
1096 winupgradeuuid, WRAP_CHAR));
1097 }
1098
1099 if (!StringUtils.isBlank(macpackageidentifier)) {
1100 opt = cmdLine.createOpt();
1101 opt.createArg().setValue("--mac-package-identifier");
1102 opt.createArg().setValue(PluginUtils.wrapOpt(
1103 macpackageidentifier, WRAP_CHAR));
1104 }
1105
1106 if (!StringUtils.isBlank(macpackagename)) {
1107 opt = cmdLine.createOpt();
1108 opt.createArg().setValue("--mac-package-name");
1109 opt.createArg().setValue(PluginUtils.wrapOpt(
1110 macpackagename, WRAP_CHAR));
1111 }
1112
1113 if (!StringUtils.isBlank(macpackagesigningprefix)) {
1114 opt = cmdLine.createOpt();
1115 opt.createArg().setValue("--mac-package-signing-prefix");
1116 opt.createArg().setValue(PluginUtils.wrapOpt(
1117 macpackagesigningprefix, WRAP_CHAR));
1118 }
1119
1120 if (macsign) {
1121 opt = cmdLine.createOpt();
1122 opt.createArg().setValue("--mac-sign");
1123 }
1124
1125 if (macsigningkeychain != null) {
1126 opt = cmdLine.createOpt();
1127 opt.createArg().setValue("--mac-signing-keychain");
1128 try {
1129 opt.createArg().setValue(macsigningkeychain.getCanonicalPath());
1130 } catch (IOException ex) {
1131 throw new MojoExecutionException(MessageFormat.format(
1132 ERROR_RESOLVE,
1133 "--mac-signing-keychain",
1134 macsigningkeychain.toString()), ex);
1135 }
1136 }
1137
1138 if (!StringUtils.isBlank(macsigningkeyusername)) {
1139 opt = cmdLine.createOpt();
1140 opt.createArg().setValue("--mac-signing-key-user-name");
1141 opt.createArg().setValue(PluginUtils.wrapOpt(
1142 macsigningkeyusername, WRAP_CHAR));
1143 }
1144
1145 if (!StringUtils.isBlank(linuxpackagename)) {
1146 opt = cmdLine.createOpt();
1147 opt.createArg().setValue("--linux-package-name");
1148 opt.createArg().setValue(PluginUtils.wrapOpt(
1149 linuxpackagename, WRAP_CHAR));
1150 }
1151
1152 if (!StringUtils.isBlank(linuxdebmaintainer)) {
1153 opt = cmdLine.createOpt();
1154 opt.createArg().setValue("--linux-deb-maintainer");
1155 opt.createArg().setValue(PluginUtils.wrapOpt(
1156 linuxdebmaintainer, WRAP_CHAR));
1157 }
1158
1159 if (!StringUtils.isBlank(linuxmenugroup)) {
1160 opt = cmdLine.createOpt();
1161 opt.createArg().setValue("--linux-menu-group");
1162 opt.createArg().setValue(PluginUtils.wrapOpt(
1163 linuxmenugroup, WRAP_CHAR));
1164 }
1165
1166 if (linuxpackagedeps) {
1167 opt = cmdLine.createOpt();
1168 opt.createArg().setValue("--linux-package-deps");
1169 }
1170
1171 if (!StringUtils.isBlank(linuxrpmlicensetype)) {
1172 opt = cmdLine.createOpt();
1173 opt.createArg().setValue("--linux-rpm-license-type");
1174 opt.createArg().setValue(PluginUtils.wrapOpt(
1175 linuxrpmlicensetype, WRAP_CHAR));
1176 }
1177
1178 if (!StringUtils.isBlank(linuxapprelease)) {
1179 opt = cmdLine.createOpt();
1180 opt.createArg().setValue("--linux-app-release");
1181 opt.createArg().setValue(PluginUtils.wrapOpt(
1182 linuxapprelease, WRAP_CHAR));
1183 }
1184
1185 if (!StringUtils.isBlank(linuxappcategory)) {
1186 opt = cmdLine.createOpt();
1187 opt.createArg().setValue("--linux-app-category");
1188 opt.createArg().setValue(PluginUtils.wrapOpt(
1189 linuxappcategory, WRAP_CHAR));
1190 }
1191
1192 if (linuxshortcut) {
1193 opt = cmdLine.createOpt();
1194 opt.createArg().setValue("--linux-shortcut");
1195 }
1196 }
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208 private File processLauncher(final Launcher launcher)
1209 throws MojoExecutionException {
1210 final String name = launcher.getName();
1211
1212 Properties props;
1213 try {
1214 props = launcher.getProperties(getCharset());
1215 } catch (IOException ex) {
1216 throw new MojoExecutionException(MessageFormat.format(
1217 "Error: Unable to read properties for launcher: [{0}]",
1218 name), ex);
1219 }
1220
1221 File file;
1222 try {
1223 file = Files.createTempFile(getBuildDir().toPath(),
1224 PROPS_PREFIX, PROPS_SUFFIX).toFile();
1225 } catch (IOException ex) {
1226 throw new MojoExecutionException(MessageFormat.format(
1227 "Error: Unable to create temporary file for launcher: [{0}]",
1228 name), ex);
1229 }
1230
1231 try (BufferedWriter bw =
1232 Files.newBufferedWriter(file.toPath(), getCharset())) {
1233 props.store(bw, null);
1234 } catch (IOException ex) {
1235 throw new MojoExecutionException(MessageFormat.format(
1236 "Error: Unable to write temporary file for launcher: [{0}]",
1237 name), ex);
1238 }
1239 if (getLog().isDebugEnabled()) {
1240 getLog().debug(MessageFormat.format(
1241 "Found additional launcher: [{0}]", name)
1242 + System.lineSeparator()
1243 + props.toString());
1244 }
1245 return file;
1246 }
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256 private ResolvePathsResult<File> resolveDependencies()
1257 throws MojoExecutionException {
1258
1259
1260
1261 final Set<Artifact> artifacts = getProject().getArtifacts();
1262 if (getLog().isDebugEnabled()) {
1263 getLog().debug(PluginUtils.getArtifactSetDebugInfo(artifacts));
1264 }
1265
1266
1267 final List<File> paths = new ArrayList<>();
1268
1269
1270 paths.add(getOutputDir());
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297 paths.addAll(artifacts.stream()
1298 .filter(a -> a != null && !Artifact.SCOPE_TEST.equals(a.getScope()))
1299 .map(a -> a.getFile())
1300 .collect(Collectors.toList()));
1301
1302
1303
1304 paths.addAll(getProject().getDependencies().stream()
1305 .filter(d -> d != null && !StringUtils.isBlank(d.getSystemPath()))
1306 .map(d -> new File(StringUtils.stripToEmpty(d.getSystemPath())))
1307 .collect(Collectors.toList()));
1308
1309
1310
1311 final ResolvePathsRequest<File> request =
1312 ResolvePathsRequest.ofFiles(paths);
1313
1314
1315 final File descriptorFile =
1316 getOutputDir().toPath().resolve(DESCRIPTOR_NAME).toFile();
1317 if (descriptorFile.exists() && !descriptorFile.isDirectory()) {
1318 request.setMainModuleDescriptor(descriptorFile);
1319 }
1320
1321
1322 if (getToolHomeDirectory() != null) {
1323 request.setJdkHome(getToolHomeDirectory());
1324 }
1325
1326
1327 try {
1328 return locationManager.resolvePaths(request);
1329 } catch (IOException ex) {
1330 throw new MojoExecutionException(
1331 "Error: Unable to resolve project dependencies", ex);
1332 }
1333
1334 }
1335
1336
1337
1338
1339
1340
1341 private JavaModuleDescriptor fetchMainModuleDescriptor() {
1342 final JavaModuleDescriptor descriptor =
1343 projectDependencies.getMainModuleDescriptor();
1344 if (descriptor == null) {
1345
1346 if (getLog().isWarnEnabled()) {
1347 getLog().warn("The main module descriptor not found");
1348 }
1349 } else {
1350 if (getLog().isDebugEnabled()) {
1351 getLog().debug(MessageFormat.format(
1352 "Found the main module descriptor: [{0}]", descriptor.name()));
1353 }
1354 }
1355 return descriptor;
1356 }
1357
1358
1359
1360
1361
1362
1363 private Map<File, String> fetchPathExceptions() {
1364 return projectDependencies.getPathExceptions()
1365 .entrySet().stream()
1366 .filter(entry -> entry != null && entry.getKey() != null)
1367 .collect(Collectors.toMap(
1368 entry -> entry.getKey(),
1369 entry -> PluginUtils.getThrowableCause(entry.getValue())
1370 ));
1371 }
1372
1373
1374
1375
1376
1377
1378 private List<File> fetchClasspathElements() {
1379 final List<File> result = projectDependencies.getClasspathElements()
1380 .stream()
1381 .filter(Objects::nonNull)
1382 .collect(Collectors.toList());
1383 if (getLog().isDebugEnabled()) {
1384 getLog().debug("Found classpath elements: " + result.size()
1385 + System.lineSeparator()
1386 + result.stream()
1387 .map(file -> file.toString())
1388 .collect(Collectors.joining(System.lineSeparator())));
1389 }
1390 return result;
1391 }
1392
1393
1394
1395
1396
1397
1398 private List<File> fetchModulepathElements() {
1399 final List<File> result = projectDependencies.getModulepathElements()
1400 .keySet()
1401 .stream()
1402 .filter(Objects::nonNull)
1403 .collect(Collectors.toList());
1404 if (getLog().isDebugEnabled()) {
1405 getLog().debug("Found modulepath elements: " + result.size()
1406 + System.lineSeparator()
1407 + projectDependencies.getModulepathElements().entrySet().stream()
1408 .filter(entry -> entry != null && entry.getKey() != null)
1409 .map(entry -> entry.getKey().toString()
1410 + (ModuleNameSource.FILENAME.equals(entry.getValue())
1411 ? System.lineSeparator()
1412 + "[!] Detected 'requires' filename based "
1413 + "automatic module"
1414 + System.lineSeparator()
1415 + "[!] Please don't publish this project to "
1416 + "a public artifact repository"
1417 + System.lineSeparator()
1418 + (mainModuleDescriptor != null
1419 && mainModuleDescriptor.exports().isEmpty()
1420 ? "[!] APPLICATION"
1421 : "[!] LIBRARY")
1422 : ""))
1423 .collect(Collectors.joining(System.lineSeparator())));
1424 }
1425 return result;
1426 }
1427
1428
1429
1430
1431
1432
1433 private String getPathElements() {
1434 String result = null;
1435 if (modulepath != null) {
1436 final List<File> pathelements = modulepath.getPathElements();
1437 if (pathelements != null && !pathelements.isEmpty()) {
1438 result = pathelements.stream()
1439 .filter(Objects::nonNull)
1440 .map(file -> file.toString())
1441 .collect(Collectors.joining(File.pathSeparator));
1442 if (getLog().isDebugEnabled()) {
1443 getLog().debug(PluginUtils.getPathElementsDebugInfo("PATHELEMENTS",
1444 pathelements));
1445 getLog().debug(result);
1446 }
1447 }
1448 }
1449 return result;
1450 }
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460 private String getFileSets() throws MojoExecutionException {
1461 String result = null;
1462 if (modulepath != null) {
1463 final List<FileSet> filesets = modulepath.getFileSets();
1464 if (filesets != null && !filesets.isEmpty()) {
1465 for (final FileSet fileSet : filesets) {
1466 final File fileSetDir;
1467 try {
1468 fileSetDir =
1469 PluginUtils.normalizeFileSetBaseDir(getBaseDir(), fileSet);
1470 } catch (IOException ex) {
1471 throw new MojoExecutionException(
1472 "Error: Unable to resolve fileset", ex);
1473 }
1474 result = Stream.of(getFileSetManager().getIncludedFiles(fileSet))
1475 .filter(fileName -> !StringUtils.isBlank(fileName))
1476 .map(fileName -> fileSetDir.toPath().resolve(
1477 StringUtils.stripToEmpty(fileName)).toString())
1478 .collect(Collectors.joining(File.pathSeparator));
1479 if (getLog().isDebugEnabled()) {
1480 getLog().debug(PluginUtils.getFileSetDebugInfo("FILESET",
1481 fileSet, result));
1482 }
1483 }
1484 }
1485 }
1486 return result;
1487 }
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497 private String getDirSets() throws MojoExecutionException {
1498 String result = null;
1499 if (modulepath != null) {
1500 final List<FileSet> dirsets = modulepath.getDirSets();
1501 if (dirsets != null && !dirsets.isEmpty()) {
1502 for (final FileSet dirSet : dirsets) {
1503 final File dirSetDir;
1504 try {
1505 dirSetDir =
1506 PluginUtils.normalizeFileSetBaseDir(getBaseDir(), dirSet);
1507 } catch (IOException ex) {
1508 throw new MojoExecutionException(
1509 "Error: Unable to resolve dirset", ex);
1510 }
1511 result = Stream.of(getFileSetManager().getIncludedDirectories(dirSet))
1512 .filter(dirName -> !StringUtils.isBlank(dirName))
1513 .map(dirName -> dirSetDir.toPath().resolve(
1514 StringUtils.stripToEmpty(dirName)).toString())
1515 .collect(Collectors.joining(File.pathSeparator));
1516 if (getLog().isDebugEnabled()) {
1517 getLog().debug(PluginUtils.getFileSetDebugInfo("DIRSET",
1518 dirSet, result));
1519 }
1520 }
1521 }
1522 }
1523 return result;
1524 }
1525
1526
1527
1528
1529
1530
1531 private String getDependencySets() {
1532 String result = null;
1533 if (modulepath != null) {
1534 final List<DependencySet> dependencysets =
1535 modulepath.getDependencySets();
1536 if (dependencysets != null && !dependencysets.isEmpty()) {
1537 for (final DependencySet dependencySet : dependencysets) {
1538 result = getIncludedDependencies(dependencySet)
1539 .stream()
1540 .collect(Collectors.joining(File.pathSeparator));
1541 if (getLog().isDebugEnabled()) {
1542 getLog().debug(PluginUtils.getDependencySetDebugInfo(
1543 "DEPENDENCYSET", dependencySet, result));
1544 }
1545 }
1546 }
1547 }
1548 return result;
1549 }
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559 private Set<String> getIncludedDependencies(final DependencySet depSet) {
1560 return projectDependencies.getPathElements().entrySet().stream()
1561 .filter(entry -> entry != null
1562 && entry.getKey() != null
1563 && filterDependency(depSet, entry.getKey(), entry.getValue()))
1564 .map(entry -> entry.getKey().toString())
1565 .collect(Collectors.toSet());
1566 }
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576 private Set<String> getExcludedDependencies(final DependencySet depSet) {
1577 return projectDependencies.getPathElements().entrySet().stream()
1578 .filter(entry -> entry != null
1579 && entry.getKey() != null
1580 && !filterDependency(depSet, entry.getKey(), entry.getValue()))
1581 .map(entry -> entry.getKey().toString())
1582 .collect(Collectors.toSet());
1583 }
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598 private boolean filterDependency(final DependencySet depSet, final File file,
1599 final JavaModuleDescriptor descriptor) {
1600
1601 if (descriptor == null) {
1602 if (getLog().isWarnEnabled()) {
1603 getLog().warn("Missing module descriptor: " + file);
1604 }
1605 } else {
1606 if (descriptor.isAutomatic() && getLog().isDebugEnabled()) {
1607 getLog().debug("Found automatic module: " + file);
1608 }
1609 }
1610
1611 boolean isIncluded = false;
1612
1613 if (depSet == null) {
1614
1615 isIncluded = true;
1616
1617 if (descriptor != null && descriptor.isAutomatic()
1618 && getLog().isDebugEnabled()) {
1619 getLog().debug("Included automatic module: " + file);
1620 }
1621
1622 if (file.compareTo(getOutputDir()) == 0) {
1623 isIncluded = false;
1624 if (getLog().isDebugEnabled()) {
1625 getLog().debug("Excluded output module: " + file);
1626 }
1627 }
1628 } else {
1629 if (descriptor != null && descriptor.isAutomatic()
1630 && depSet.isAutomaticExcluded()) {
1631 if (getLog().isDebugEnabled()) {
1632 getLog().debug("Excluded automatic module: " + file);
1633 }
1634 } else {
1635 if (file.compareTo(getOutputDir()) == 0) {
1636 if (depSet.isOutputIncluded()) {
1637 isIncluded = true;
1638 if (getLog().isDebugEnabled()) {
1639 getLog().debug("Included output module: " + file);
1640 }
1641 } else {
1642 if (getLog().isDebugEnabled()) {
1643 getLog().debug("Excluded output module: " + file);
1644 }
1645 }
1646 } else {
1647 isIncluded = matchesIncludes(depSet, file, descriptor)
1648 && !matchesExcludes(depSet, file, descriptor);
1649 }
1650 }
1651 }
1652
1653 if (getLog().isDebugEnabled()) {
1654 getLog().debug(PluginUtils.getDependencyDebugInfo(file, descriptor,
1655 isIncluded));
1656 }
1657
1658 return isIncluded;
1659 }
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672 private boolean matchesIncludes(final DependencySet depSet, final File file,
1673 final JavaModuleDescriptor descriptor) {
1674
1675 final String name = descriptor == null ? "" : descriptor.name();
1676
1677 final List<String> includes = depSet.getIncludes();
1678 final List<String> includenames = depSet.getIncludeNames();
1679
1680 boolean result = true;
1681
1682 if (includenames == null || includenames.isEmpty()) {
1683 if (includes == null || includes.isEmpty()) {
1684 result = true;
1685 } else {
1686 result = pathMatches(includes, file.toPath());
1687 }
1688 } else {
1689 if (includes == null || includes.isEmpty()) {
1690 result = nameMatches(includenames, name);
1691 } else {
1692 result = pathMatches(includes, file.toPath())
1693 || nameMatches(includenames, name);
1694 }
1695 }
1696 return result;
1697 }
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710 private boolean matchesExcludes(final DependencySet depSet, final File file,
1711 final JavaModuleDescriptor descriptor) {
1712
1713 final String name = descriptor == null ? "" : descriptor.name();
1714
1715 final List<String> excludes = depSet.getExcludes();
1716 final List<String> excludenames = depSet.getExcludeNames();
1717
1718 boolean result = false;
1719
1720 if (excludenames == null || excludenames.isEmpty()) {
1721 if (excludes == null || excludes.isEmpty()) {
1722 result = false;
1723 } else {
1724 result = pathMatches(excludes, file.toPath());
1725 }
1726 } else {
1727 if (excludes == null || excludes.isEmpty()) {
1728 result = nameMatches(excludenames, name);
1729 } else {
1730 result = pathMatches(excludes, file.toPath())
1731 || nameMatches(excludenames, name);
1732 }
1733 }
1734 return result;
1735 }
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748 private boolean pathMatches(final List<String> patterns, final Path path) {
1749 for (final String pattern : patterns) {
1750 final PathMatcher pathMatcher =
1751 FileSystems.getDefault().getPathMatcher(pattern);
1752 if (pathMatcher.matches(path)) {
1753 return true;
1754 }
1755 }
1756 return false;
1757 }
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769 private boolean nameMatches(final List<String> patterns, final String name) {
1770 for (final String pattern : patterns) {
1771 final Pattern regexPattern = Pattern.compile(pattern);
1772 final Matcher nameMatcher = regexPattern.matcher(name);
1773 if (nameMatcher.matches()) {
1774 return true;
1775 }
1776 }
1777 return false;
1778 }
1779
1780
1781
1782
1783
1784
1785
1786
1787 private void processModules(final CommandLineBuilder cmdLine)
1788 throws MojoExecutionException {
1789 CommandLineOption opt = null;
1790
1791 if (modulepath != null) {
1792 final StringBuilder path = new StringBuilder();
1793 final String pathElements = getPathElements();
1794 if (!StringUtils.isBlank(pathElements)) {
1795 path.append(StringUtils.stripToEmpty(pathElements));
1796 }
1797 final String fileSets = getFileSets();
1798 if (!StringUtils.isBlank(fileSets)) {
1799 if (path.length() != 0) {
1800 path.append(File.pathSeparator);
1801 }
1802 path.append(StringUtils.stripToEmpty(fileSets));
1803 }
1804 final String dirSets = getDirSets();
1805 if (!StringUtils.isBlank(dirSets)) {
1806 if (path.length() != 0) {
1807 path.append(File.pathSeparator);
1808 }
1809 path.append(StringUtils.stripToEmpty(dirSets));
1810 }
1811 final String dependencySets = getDependencySets();
1812 if (!StringUtils.isBlank(dependencySets)) {
1813 if (path.length() != 0) {
1814 path.append(File.pathSeparator);
1815 }
1816 path.append(StringUtils.stripToEmpty(dependencySets));
1817 }
1818 if (path.length() != 0) {
1819 opt = cmdLine.createOpt();
1820 opt.createArg().setValue("--module-path");
1821 opt.createArg().setValue(path.toString());
1822 }
1823 }
1824
1825 if (addmodules != null && !addmodules.isEmpty()) {
1826 opt = cmdLine.createOpt();
1827 opt.createArg().setValue("--add-modules");
1828 opt.createArg().setValue(
1829 addmodules.stream().collect(Collectors.joining(",")));
1830 }
1831
1832 if (bindservices) {
1833 opt = cmdLine.createOpt();
1834 opt.createArg().setValue("--bind-services");
1835 }
1836 }
1837
1838
1839
1840
1841
1842
1843 @Override
1844 public void execute() throws MojoExecutionException {
1845
1846
1847 init(TOOL_NAME, toolhome, TOOL_HOME_BIN);
1848
1849
1850 final JavaVersion toolJavaVersion = getToolJavaVersion();
1851 if (toolJavaVersion == null
1852 || !toolJavaVersion.atLeast(JavaVersion.JAVA_9)) {
1853 throw new MojoExecutionException(MessageFormat.format(
1854 "Error: At least {0} is required to use [{1}]", JavaVersion.JAVA_9,
1855 TOOL_NAME));
1856 }
1857
1858
1859 if (temp != null) {
1860 if (getLog().isDebugEnabled()) {
1861 getLog().debug(MessageFormat.format(
1862 "Temporary directory: [{0}]", temp));
1863 }
1864 if (temp.exists() && temp.isDirectory()) {
1865 try {
1866 FileUtils.deleteDirectory(temp);
1867 } catch (IOException ex) {
1868 throw new MojoExecutionException(MessageFormat.format(
1869 "Error: Unable to delete temporary directory: [{0}]", temp), ex);
1870 }
1871 }
1872 }
1873
1874
1875 projectDependencies = resolveDependencies();
1876 mainModuleDescriptor = fetchMainModuleDescriptor();
1877
1878
1879 final Map<File, String> pathExceptions = fetchPathExceptions();
1880 if (!pathExceptions.isEmpty() && getLog().isWarnEnabled()) {
1881 getLog().warn("Found path exceptions: " + pathExceptions.size()
1882 + System.lineSeparator()
1883 + pathExceptions.entrySet().stream()
1884 .map(entry -> entry.getKey().toString()
1885 + System.lineSeparator()
1886 + entry.getValue())
1887 .collect(Collectors.joining(System.lineSeparator())));
1888 }
1889
1890
1891 final CommandLineBuilder cmdLineBuilder = new CommandLineBuilder();
1892 cmdLineBuilder.setExecutable(getToolExecutable().toString());
1893 processOptions(cmdLineBuilder);
1894 processModules(cmdLineBuilder);
1895 final List<String> optsLines = new ArrayList<>();
1896 optsLines.add("# " + TOOL_NAME);
1897 optsLines.addAll(cmdLineBuilder.buildOptionList());
1898 if (getLog().isDebugEnabled()) {
1899 getLog().debug(optsLines.stream()
1900 .collect(Collectors.joining(System.lineSeparator(),
1901 System.lineSeparator(), "")));
1902 }
1903
1904
1905
1906 final Path cmdOptsPath = getBuildDir().toPath().resolve(OPTS_FILE);
1907 try {
1908 Files.write(cmdOptsPath, optsLines, getCharset());
1909 } catch (IOException ex) {
1910 throw new MojoExecutionException(MessageFormat.format(
1911 "Error: Unable to write command options to file: [{0}]",
1912 cmdOptsPath), ex);
1913 }
1914 tempFiles.add(cmdOptsPath.toFile());
1915
1916
1917
1918 final Commandline cmdLine = new Commandline();
1919 cmdLine.setExecutable(getToolExecutable().toString());
1920 cmdLine.createArg().setValue("@" + cmdOptsPath.toString());
1921
1922
1923 int exitCode = 0;
1924 try {
1925 exitCode = execCmdLine(cmdLine);
1926 } catch (CommandLineException ex) {
1927 throw new MojoExecutionException(MessageFormat.format(
1928 "Error: Unable to execute [{0}] tool", TOOL_NAME), ex);
1929 }
1930 if (exitCode != 0) {
1931 if (getLog().isErrorEnabled()) {
1932 getLog().error(System.lineSeparator()
1933 + "Command options was: "
1934 + System.lineSeparator()
1935 + optsLines.stream()
1936 .collect(Collectors.joining(System.lineSeparator())));
1937 }
1938 throw new MojoExecutionException(MessageFormat.format(
1939 "Error: Tool execution failed [{0}] with exit code: {1}", TOOL_NAME,
1940 exitCode));
1941 }
1942
1943
1944 for (final File tempFile : tempFiles) {
1945 try {
1946 FileUtils.forceDelete(tempFile);
1947 } catch (IOException ex) {
1948 throw new MojoExecutionException(MessageFormat.format(
1949 "Error: Unable to delete temporary file: [{0}]", tempFile), ex);
1950 }
1951 }
1952
1953 }
1954
1955 }