11package io .quarkus .devui .deployment ;
22
3+ import java .io .BufferedReader ;
34import java .io .ByteArrayInputStream ;
45import java .io .IOException ;
56import java .io .InputStream ;
1920import java .util .LinkedHashMap ;
2021import java .util .List ;
2122import java .util .Map ;
23+ import java .util .Properties ;
2224import java .util .Scanner ;
2325import java .util .regex .Matcher ;
2426import java .util .regex .Pattern ;
3638import io .quarkus .arc .deployment .AdditionalBeanBuildItem ;
3739import io .quarkus .arc .deployment .BeanContainerBuildItem ;
3840import io .quarkus .arc .processor .BuiltinScope ;
41+ import io .quarkus .bootstrap .BootstrapConstants ;
3942import io .quarkus .deployment .Capabilities ;
4043import io .quarkus .deployment .Capability ;
4144import io .quarkus .deployment .IsLocalDevelopment ;
7275import io .quarkus .devui .spi .page .PageBuilder ;
7376import io .quarkus .devui .spi .page .QuteDataPageBuilder ;
7477import io .quarkus .devui .spi .page .WebComponentPageBuilder ;
78+ import io .quarkus .maven .dependency .ArtifactKey ;
79+ import io .quarkus .maven .dependency .DependencyFlags ;
7580import io .quarkus .maven .dependency .GACT ;
7681import io .quarkus .maven .dependency .GACTV ;
7782import io .quarkus .maven .dependency .ResolvedDependency ;
@@ -101,8 +106,6 @@ public class DevUIProcessor {
101106 private static final String DEVUI = "dev-ui" ;
102107 private static final String SLASH = "/" ;
103108 private static final String DOT = "." ;
104- private static final String DOUBLE_POINT = ":" ;
105- private static final String DASH_DEPLOYMENT = "-deployment" ;
106109 private static final String SLASH_ALL = SLASH + "*" ;
107110 private static final String JSONRPC = "json-rpc-ws" ;
108111
@@ -112,7 +115,6 @@ public class DevUIProcessor {
112115
113116 private static final String JAR = "jar" ;
114117 private static final GACT UI_JAR = new GACT ("io.quarkus" , "quarkus-vertx-http-dev-ui-resources" , null , JAR );
115- private static final String YAML_FILE = "/META-INF/quarkus-extension.yaml" ;
116118 private static final String NAME = "name" ;
117119 private static final String DESCRIPTION = "description" ;
118120 private static final String ARTIFACT = "artifact" ;
@@ -519,17 +521,27 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
519521 Map <String , CardPageBuildItem > cardPagesMap = getCardPagesMap (curateOutcomeBuildItem , cardPageBuildItems );
520522 Map <String , MenuPageBuildItem > menuPagesMap = getMenuPagesMap (curateOutcomeBuildItem , menuPageBuildItems );
521523 Map <String , List <FooterPageBuildItem >> footerPagesMap = getFooterPagesMap (curateOutcomeBuildItem , footerPageBuildItems );
522- try {
523- final Yaml yaml = new Yaml ();
524- List <Extension > activeExtensions = new ArrayList <>();
525- List <Extension > inactiveExtensions = new ArrayList <>();
526- List <Extension > sectionMenuExtensions = new ArrayList <>();
527- List <Extension > footerTabExtensions = new ArrayList <>();
528- ClassPathUtils .consumeAsPaths (YAML_FILE , (var p ) -> {
524+
525+ final Yaml yaml = new Yaml ();
526+ List <Extension > activeExtensions = new ArrayList <>();
527+ List <Extension > inactiveExtensions = new ArrayList <>();
528+ List <Extension > sectionMenuExtensions = new ArrayList <>();
529+ List <Extension > footerTabExtensions = new ArrayList <>();
530+
531+ for (ResolvedDependency runtimeExt : curateOutcomeBuildItem .getApplicationModel ()
532+ .getDependencies (DependencyFlags .RUNTIME_EXTENSION_ARTIFACT )) {
533+ runtimeExt .getContentTree ().accept (BootstrapConstants .EXTENSION_METADATA_PATH , extYamlVisit -> {
534+ if (extYamlVisit == null ) {
535+ // this could be an exception but previously the code was simply looking for this resource on the classpath
536+ log .error ("Failed to locate " + BootstrapConstants .EXTENSION_METADATA_PATH + " in "
537+ + runtimeExt .toCompactCoords ());
538+ return ;
539+ }
540+ final Path extYaml = extYamlVisit .getPath ();
529541 try {
530542 Extension extension = new Extension ();
531543 final String extensionYaml ;
532- try (Scanner scanner = new Scanner (Files .newBufferedReader (p , StandardCharsets .UTF_8 ))) {
544+ try (Scanner scanner = new Scanner (Files .newBufferedReader (extYaml , StandardCharsets .UTF_8 ))) {
533545 scanner .useDelimiter ("\\ A" );
534546 extensionYaml = scanner .hasNext () ? scanner .next () : null ;
535547 }
@@ -551,8 +563,7 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
551563 extension .setNamespace (namespace );
552564 extension .setName ((String ) extensionMap .get (NAME ));
553565 extension .setDescription ((String ) extensionMap .getOrDefault (DESCRIPTION , null ));
554- String artifactId = (String ) extensionMap .getOrDefault (ARTIFACT , null );
555- extension .setArtifact (artifactId );
566+ extension .setArtifact ((String ) extensionMap .getOrDefault (ARTIFACT , null ));
556567
557568 extension .setKeywords ((List <String >) metaData .getOrDefault (KEYWORDS , null ));
558569 extension .setShortName ((String ) metaData .getOrDefault (SHORT_NAME , null ));
@@ -614,14 +625,12 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
614625 addLibraryLinks (extension , cardPageBuildItem , curateOutcomeBuildItem , metaData );
615626
616627 // Also make sure the static resources for that static resource is available
617- produceResources (artifactId , webJarBuildProducer ,
618- devUIWebJarProducer );
628+ produceResources (runtimeExt , webJarBuildProducer , devUIWebJarProducer );
619629 activeExtensions .add (extension );
620630 } else { // Inactive
621631 if (addLogo (extension , cardPagesMap .get (namespace ), metaData )) {
622632 // Also make sure the static resources for that static resource is available
623- produceResources (artifactId , webJarBuildProducer ,
624- devUIWebJarProducer );
633+ produceResources (runtimeExt , webJarBuildProducer , devUIWebJarProducer );
625634 }
626635
627636 addLibraryLinks (extension , cardPagesMap .get (namespace ), curateOutcomeBuildItem ,
@@ -643,8 +652,7 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
643652 }
644653 }
645654 // Also make sure the static resources for that static resource is available
646- produceResources (artifactId , webJarBuildProducer ,
647- devUIWebJarProducer );
655+ produceResources (runtimeExt , webJarBuildProducer , devUIWebJarProducer );
648656 sectionMenuExtensions .add (extension );
649657 }
650658
@@ -663,8 +671,7 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
663671 }
664672 }
665673 // Also make sure the static resources for that static resource is available
666- produceResources (artifactId , webJarBuildProducer ,
667- devUIWebJarProducer );
674+ produceResources (runtimeExt , webJarBuildProducer , devUIWebJarProducer );
668675 footerTabExtensions .add (extension );
669676 }
670677 }
@@ -676,41 +683,39 @@ void getAllExtensions(List<CardPageBuildItem> cardPageBuildItems,
676683 }
677684 } catch (IOException | RuntimeException e ) {
678685 // don't abort, just log, to prevent a single extension from breaking entire dev ui
679- log .error ("Failed to process extension descriptor " + p .toUri (), e );
686+ log .error ("Failed to process extension descriptor " + extYaml .toUri (), e );
680687 }
681688 });
689+ }
682690
683- // Also add footers for extensions that might not have a runtime
684- if (!footerPagesMap .isEmpty ()) {
685- for (Map .Entry <String , List <FooterPageBuildItem >> footer : footerPagesMap .entrySet ()) {
686- List <FooterPageBuildItem > fbis = footer .getValue ();
687- for (FooterPageBuildItem footerPageBuildItem : fbis ) {
688- if (footerPageBuildItem .isInternal ()) {
689- Extension deploymentOnlyExtension = new Extension ();
690- deploymentOnlyExtension .setName (footer .getKey ());
691- deploymentOnlyExtension .setNamespace (FOOTER_LOG_NAMESPACE );
692-
693- List <PageBuilder > footerPageBuilders = footerPageBuildItem .getPages ();
694-
695- for (PageBuilder pageBuilder : footerPageBuilders ) {
696- pageBuilder .namespace (deploymentOnlyExtension .getNamespace ());
697- pageBuilder .extension (deploymentOnlyExtension .getName ());
698- pageBuilder .internal ();
699- Page page = pageBuilder .build ();
700- deploymentOnlyExtension .addFooterPage (page );
701- }
702-
703- footerTabExtensions .add (deploymentOnlyExtension );
691+ // Also add footers for extensions that might not have a runtime
692+ if (!footerPagesMap .isEmpty ()) {
693+ for (Map .Entry <String , List <FooterPageBuildItem >> footer : footerPagesMap .entrySet ()) {
694+ List <FooterPageBuildItem > fbis = footer .getValue ();
695+ for (FooterPageBuildItem footerPageBuildItem : fbis ) {
696+ if (footerPageBuildItem .isInternal ()) {
697+ Extension deploymentOnlyExtension = new Extension ();
698+ deploymentOnlyExtension .setName (footer .getKey ());
699+ deploymentOnlyExtension .setNamespace (FOOTER_LOG_NAMESPACE );
700+
701+ List <PageBuilder > footerPageBuilders = footerPageBuildItem .getPages ();
702+
703+ for (PageBuilder pageBuilder : footerPageBuilders ) {
704+ pageBuilder .namespace (deploymentOnlyExtension .getNamespace ());
705+ pageBuilder .extension (deploymentOnlyExtension .getName ());
706+ pageBuilder .internal ();
707+ Page page = pageBuilder .build ();
708+ deploymentOnlyExtension .addFooterPage (page );
704709 }
710+
711+ footerTabExtensions .add (deploymentOnlyExtension );
705712 }
706713 }
707714 }
708-
709- extensionsProducer .produce (
710- new ExtensionsBuildItem (activeExtensions , inactiveExtensions , sectionMenuExtensions , footerTabExtensions ));
711- } catch (IOException ex ) {
712- throw new RuntimeException (ex );
713715 }
716+
717+ extensionsProducer .produce (
718+ new ExtensionsBuildItem (activeExtensions , inactiveExtensions , sectionMenuExtensions , footerTabExtensions ));
714719 }
715720
716721 private void addLibraryLinks (Extension extension , CardPageBuildItem cardPageBuildItem ,
@@ -805,19 +810,19 @@ private List<String> listOrString(Map<String, Object> metaData, String key) {
805810 return List .of (String .valueOf (value ));
806811 }
807812
808- private void produceResources (String artifactId ,
813+ private void produceResources (ResolvedDependency runtimeExt ,
809814 BuildProducer <WebJarBuildItem > webJarBuildProducer ,
810815 BuildProducer <DevUIWebJarBuildItem > devUIWebJarProducer ) {
811816
812- GACT gact = getGACT (artifactId );
813- String namespace = getNamespace (gact );
817+ String namespace = getNamespace (runtimeExt .getKey ());
814818 if (namespace .isEmpty ()) {
815819 namespace = "devui" ;
816820 }
817821 String buildTimeDataImport = namespace + "-data" ;
818822
823+ final GACT deploymentKey = getDeploymentKey (runtimeExt );
819824 webJarBuildProducer .produce (WebJarBuildItem .builder ()
820- .artifactKey (gact )
825+ .artifactKey (deploymentKey )
821826 .root (DEVUI + SLASH )
822827 .filter (new WebJarResourcesFilter () {
823828 @ Override
@@ -834,9 +839,29 @@ public WebJarResourcesFilter.FilterResult apply(String fileName, InputStream fil
834839 })
835840 .build ());
836841
837- devUIWebJarProducer .produce (
838- new DevUIWebJarBuildItem (gact ,
839- DEVUI ));
842+ devUIWebJarProducer .produce (new DevUIWebJarBuildItem (deploymentKey , DEVUI ));
843+ }
844+
845+ private static GACT getDeploymentKey (ResolvedDependency runtimeExt ) {
846+ return runtimeExt .getContentTree ().apply (BootstrapConstants .DESCRIPTOR_PATH , extPropsVisit -> {
847+ if (extPropsVisit == null ) {
848+ throw new RuntimeException ("Failed to locate " + BootstrapConstants .DESCRIPTOR_PATH
849+ + " in " + runtimeExt .toCompactCoords ());
850+ }
851+ final Properties props = new Properties ();
852+ try (BufferedReader reader = Files .newBufferedReader (extPropsVisit .getPath ())) {
853+ props .load (reader );
854+ } catch (IOException e ) {
855+ throw new RuntimeException ("Failed to read " + extPropsVisit .getUrl (), e );
856+ }
857+ final String deploymentCoords = props .getProperty (BootstrapConstants .PROP_DEPLOYMENT_ARTIFACT );
858+ if (deploymentCoords == null ) {
859+ throw new RuntimeException (
860+ "Failed to locate " + BootstrapConstants .PROP_DEPLOYMENT_ARTIFACT + " in " + extPropsVisit .getUrl ());
861+ }
862+ var coords = GACTV .fromString (deploymentCoords );
863+ return new GACT (coords .getGroupId (), coords .getArtifactId (), coords .getClassifier (), coords .getType ());
864+ });
840865 }
841866
842867 @ BuildStep (onlyIf = IsLocalDevelopment .class )
@@ -860,7 +885,7 @@ void createAllRoutes(WebJarResultsBuildItem webJarResultsBuildItem,
860885 }
861886 }
862887
863- private String getNamespace (GACT artifactKey ) {
888+ private String getNamespace (ArtifactKey artifactKey ) {
864889 String namespace = artifactKey .getGroupId () + "." + artifactKey .getArtifactId ();
865890
866891 if (namespace .equals ("io.quarkus.quarkus-vertx-http-dev-ui-resources" )) {
@@ -905,11 +930,6 @@ private Page buildQutePage(PageBuilder pageBuilder, Extension extension, Map<Str
905930 return pageBuilder .build ();
906931 }
907932
908- private GACT getGACT (String artifactKey ) {
909- String [] split = artifactKey .split (DOUBLE_POINT );
910- return new GACT (split [0 ], split [1 ] + DASH_DEPLOYMENT , null , JAR );
911- }
912-
913933 private Class toClass (Type type ) {
914934 if (type .kind ().equals (Type .Kind .PRIMITIVE )) {
915935 return JandexReflection .loadRawType (type );
0 commit comments