Repository: apache/camel-k
Pull request #1334 (2020-03-11 19:33:45)

refactor source code inspectors to avoid using redgexp for markup languages
<!-- Description -->




<!--
Enter your extended release note in the below block. If the PR requires
additional action from users switching to the new release, include the string
"action required". If no release note is required, write "NONE". 

You can (optionally) mark this PR with labels "kind/bug" or "kind/feature" to make sure
the text is added to the right section of the release notes. 
-->

**Release Note**
```release-note
NONE
```


Patch:
diff --git a/examples/RestWithUndertow.java b/examples/RestDSL.java
similarity index 84%
rename from examples/RestWithUndertow.java
rename to examples/RestDSL.java
index 63090a624..10cff7388 100644
--- a/examples/RestWithUndertow.java
+++ b/examples/RestDSL.java
@@ -18,15 +18,14 @@
 //
 // To run this integrations use:
 //
-//     kamel run --name=rest-with-undertow --dependency=camel-rest --dependency=camel-undertow examples/RestWithUndertow.java
+//     kamel run examples/RestDSL.java
 //
 import org.apache.camel.Exchange;
 
-public class RestWithUndertow extends org.apache.camel.builder.RouteBuilder {
+public class RestDSL extends org.apache.camel.builder.RouteBuilder {
     @Override
     public void configure() throws Exception {
         restConfiguration()
-            .component("undertow")
             .host("0.0.0.0")
             .port("8080");
 
diff --git a/pkg/metadata/http.go b/pkg/metadata/http.go
deleted file mode 100644
index be568083c..000000000
--- a/pkg/metadata/http.go
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-Licensed to the Apache Software Foundation (ASF) under one or more
-contributor license agreements.  See the NOTICE file distributed with
-this work for additional information regarding copyright ownership.
-The ASF licenses this file to You under the Apache License, Version 2.0
-(the "License"); you may not use this file except in compliance with
-the License.  You may obtain a copy of the License at
-
-   http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package metadata
-
-import (
-	"regexp"
-	"strings"
-
-	"github.com/apache/camel-k/pkg/util/camel"
-
-	v1 "github.com/apache/camel-k/pkg/apis/camel/v1"
-)
-
-var restIndicator = regexp.MustCompile(`.*rest\s*\([^)]*\).*`)
-var xmlRestIndicator = regexp.MustCompile(`.*<\s*rest\s+[^>]*>.*`)
-
-// requiresHTTPService returns true if the integration needs to expose itself through HTTP
-func requiresHTTPService(catalog *camel.RuntimeCatalog, source v1.SourceSpec, fromURIs []string) bool {
-	if hasRestIndicator(source) {
-		return true
-	}
-	return containsHTTPURIs(catalog, fromURIs)
-}
-
-// hasOnlyPassiveEndpoints returns true if the integration has no endpoint that needs to remain always active
-func hasOnlyPassiveEndpoints(catalog *camel.RuntimeCatalog, _ v1.SourceSpec, fromURIs []string) bool {
-	passivePlusHTTP := make(map[string]bool)
-	catalog.VisitSchemes(func(id string, scheme v1.CamelScheme) bool {
-		if scheme.HTTP || scheme.Passive {
-			passivePlusHTTP[id] = true
-		}
-
-		return true
-	})
-
-	return containsOnlyURIsIn(fromURIs, passivePlusHTTP)
-}
-
-func containsHTTPURIs(catalog *camel.RuntimeCatalog, fromURI []string) bool {
-	for _, uri := range fromURI {
-		prefix := getURIPrefix(uri)
-		scheme, ok := catalog.GetScheme(prefix)
-
-		if !ok {
-			// scheme dees not exists
-			continue
-		}
-
-		if scheme.HTTP {
-			return true
-		}
-	}
-
-	return false
-}
-
-func containsOnlyURIsIn(fromURI []string, allowed map[string]bool) bool {
-	for _, uri := range fromURI {
-		prefix := getURIPrefix(uri)
-		if enabled, ok := allowed[prefix]; !ok || !enabled {
-			return false
-		}
-	}
-	return true
-}
-
-func getURIPrefix(uri string) string {
-	parts := strings.SplitN(uri, ":", 2)
-	if len(parts) > 0 {
-		return parts[0]
-	}
-	return ""
-}
-
-func hasRestIndicator(source v1.SourceSpec) bool {
-	pat := getRestIndicatorRegexpsForLanguage(source.InferLanguage())
-	return pat.MatchString(source.Content)
-}
-
-func getRestIndicatorRegexpsForLanguage(language v1.Language) *regexp.Regexp {
-	switch language {
-	case v1.LanguageXML:
-		return xmlRestIndicator
-	default:
-		return restIndicator
-	}
-}
diff --git a/pkg/metadata/metadata.go b/pkg/metadata/metadata.go
index 9f9b53ea5..2c8dd8e88 100644
--- a/pkg/metadata/metadata.go
+++ b/pkg/metadata/metadata.go
@@ -31,17 +31,19 @@ import (
 // ExtractAll returns metadata information from all listed source codes
 func ExtractAll(catalog *camel.RuntimeCatalog, sources []v1.SourceSpec) IntegrationMetadata {
 	// neutral metadata
-	meta := NewIntegrationMetadata()
+	meta := src.NewMetadata()
 	meta.PassiveEndpoints = true
-	meta.RequiresHTTPService = false
+	meta.ExposesHTTPServices = false
 
 	for _, source := range sources {
-		meta = merge(meta, Extract(catalog, source))
+		meta = merge(meta, Extract(catalog, source).Metadata)
+	}
+	return IntegrationMetadata{
+		Metadata: meta,
 	}
-	return meta
 }
 
-func merge(m1 IntegrationMetadata, m2 IntegrationMetadata) IntegrationMetadata {
+func merge(m1 src.Metadata, m2 src.Metadata) src.Metadata {
 	d := strset.Union(m1.Dependencies, m2.Dependencies)
 
 	f := make([]string, 0, len(m1.FromURIs)+len(m2.FromURIs))
@@ -52,13 +54,11 @@ func merge(m1 IntegrationMetadata, m2 IntegrationMetadata) IntegrationMetadata {
 	t = append(t, m1.ToURIs...)
 	t = append(t, m2.ToURIs...)
 
-	return IntegrationMetadata{
-		Metadata: src.Metadata{
-			FromURIs:     f,
-			ToURIs:       t,
-			Dependencies: d,
-		},
-		RequiresHTTPService: m1.RequiresHTTPService || m2.RequiresHTTPService,
+	return src.Metadata{
+		FromURIs:            f,
+		ToURIs:              t,
+		Dependencies:        d,
+		ExposesHTTPServices: m1.ExposesHTTPServices || m2.ExposesHTTPServices,
 		PassiveEndpoints:    m1.PassiveEndpoints && m2.PassiveEndpoints,
 	}
 }
@@ -73,15 +73,16 @@ func Extract(catalog *camel.RuntimeCatalog, source v1.SourceSpec) IntegrationMet
 
 	language := source.InferLanguage()
 
-	m := NewIntegrationMetadata()
+	meta := src.NewMetadata()
+	meta.PassiveEndpoints = true
+	meta.ExposesHTTPServices = false
 
 	// TODO: handle error
-	_ = src.InspectorForLanguage(catalog, language).Extract(source, &m.Metadata)
+	_ = src.InspectorForLanguage(catalog, language).Extract(source, &meta)
 
-	m.RequiresHTTPService = requiresHTTPService(catalog, source, m.FromURIs)
-	m.PassiveEndpoints = hasOnlyPassiveEndpoints(catalog, source, m.FromURIs)
-
-	return m
+	return IntegrationMetadata{
+		Metadata: meta,
+	}
 }
 
 // Each --
diff --git a/pkg/metadata/types.go b/pkg/metadata/types.go
index 934da277c..66565cf6e 100644
--- a/pkg/metadata/types.go
+++ b/pkg/metadata/types.go
@@ -19,26 +19,9 @@ package metadata
 
 import (
 	"github.com/apache/camel-k/pkg/util/source"
-	"github.com/scylladb/go-set/strset"
 )
 
 // IntegrationMetadata contains aggregate metadata about all Camel routes in a integrations
 type IntegrationMetadata struct {
 	source.Metadata
-	// RequiresHTTPService indicates if the integration needs to be invoked through HTTP
-	RequiresHTTPService bool
-	// PassiveEndpoints indicates that the integration contains only passive endpoints that are activated from
-	// external calls, including HTTP (useful to determine if the integration can scale to 0)
-	PassiveEndpoints bool
-}
-
-// NewIntegrationMetadata --
-func NewIntegrationMetadata() IntegrationMetadata {
-	return IntegrationMetadata{
-		Metadata: source.Metadata{
-			FromURIs:     make([]string, 0),
-			ToURIs:       make([]string, 0),
-			Dependencies: strset.New(),
-		},
-	}
 }
diff --git a/pkg/trait/knative_service.go b/pkg/trait/knative_service.go
index 9c9e671e1..8a46b9209 100644
--- a/pkg/trait/knative_service.go
+++ b/pkg/trait/knative_service.go
@@ -158,7 +158,7 @@ func (t *knativeServiceTrait) Configure(e *Environment) (bool, error) {
 			}
 
 			meta := metadata.ExtractAll(e.CamelCatalog, sources)
-			if !meta.RequiresHTTPService || !meta.PassiveEndpoints {
+			if !meta.ExposesHTTPServices || !meta.PassiveEndpoints {
 				single := 1
 				t.MinScale = &single
 			}
@@ -256,7 +256,7 @@ func (t *knativeServiceTrait) SelectControllerStrategy(e *Environment) (*Control
 	}
 
 	meta := metadata.ExtractAll(e.CamelCatalog, sources)
-	if meta.RequiresHTTPService {
+	if meta.ExposesHTTPServices {
 		return &knativeServiceStrategy, nil
 	}
 	return nil, nil
diff --git a/pkg/trait/service.go b/pkg/trait/service.go
index 6257e9e1c..882cd48d7 100644
--- a/pkg/trait/service.go
+++ b/pkg/trait/service.go
@@ -88,7 +88,7 @@ func (t *serviceTrait) Configure(e *Environment) (bool, error) {
 		}
 
 		meta := metadata.ExtractAll(e.CamelCatalog, sources)
-		if !meta.RequiresHTTPService {
+		if !meta.ExposesHTTPServices {
 			e.Integration.Status.SetCondition(
 				v1.IntegrationConditionServiceAvailable,
 				corev1.ConditionFalse,
diff --git a/pkg/util/source/inspector.go b/pkg/util/source/inspector.go
index 934525c9c..a8b690a63 100644
--- a/pkg/util/source/inspector.go
+++ b/pkg/util/source/inspector.go
@@ -18,6 +18,7 @@ limitations under the License.
 package source
 
 import (
+	"fmt"
 	"regexp"
 	"strings"
 
@@ -26,6 +27,8 @@ import (
 	"github.com/apache/camel-k/pkg/util/camel"
 )
 
+type catalog2deps func(*camel.RuntimeCatalog) []string
+
 var (
 	singleQuotedFrom        = regexp.MustCompile(`from\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`)
 	doubleQuotedFrom        = regexp.MustCompile(`from\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`)
@@ -41,8 +44,8 @@ var (
 	jsonLanguageRegexp      = regexp.MustCompile(`.*\.json\(\).*`)
 	circuitBreakerRegexp    = regexp.MustCompile(`.*\.circuitBreaker\(\).*`)
 	restConfigurationRegexp = regexp.MustCompile(`.*restConfiguration\(\).*`)
-	restRegexp              = regexp.MustCompile(`.*rest\(("[a-zA-Z0-9-/]+")*\).*`)
-	restXMLRegexp           = regexp.MustCompile(`^\s*rest\s*{.*`)
+	restRegexp              = regexp.MustCompile(`.*rest\s*\([^)]*\).*`)
+	restClosureRegexp       = regexp.MustCompile(`.*rest\s*{\s*`)
 	groovyLanguageRegexp    = regexp.MustCompile(`.*\.groovy\s*\(.*\).*`)
 	jsonPathLanguageRegexp  = regexp.MustCompile(`.*\.?(jsonpath|jsonpathWriteAsString)\s*\(.*\).*`)
 	ognlRegexp              = regexp.MustCompile(`.*\.ognl\s*\(.*\).*`)
@@ -51,27 +54,91 @@ var (
 	xpathRegexp             = regexp.MustCompile(`.*\.?xpath\s*\(.*\).*`)
 	xtokenizeRegexp         = regexp.MustCompile(`.*\.xtokenize\s*\(.*\).*`)
 
-	sourceDependencies = struct {
-		main    map[*regexp.Regexp]string
-		quarkus map[*regexp.Regexp]string
-	}{
-		main: map[*regexp.Regexp]string{
-			jsonLibraryRegexp:       "camel:jackson",
-			jsonLanguageRegexp:      "camel:jackson",
-			circuitBreakerRegexp:    "camel:hystrix",
-			restConfigurationRegexp: "camel:rest",
-			restRegexp:              "camel:rest",
-			restXMLRegexp:           "camel:rest",
-			groovyLanguageRegexp:    "camel:groovy",
-			jsonPathLanguageRegexp:  "camel:jsonpath",
-			ognlRegexp:              "camel:ognl",
-			mvelRegexp:              "camel:mvel",
-			xqueryRegexp:            "camel:saxon",
-			xpathRegexp:             "camel:xpath",
-			xtokenizeRegexp:         "camel:jaxp",
+	sourceDependencies = map[*regexp.Regexp]catalog2deps{
+		jsonLibraryRegexp: func(_ *camel.RuntimeCatalog) []string {
+			return []string{"camel:jackson"}
+		},
+		jsonLanguageRegexp: func(_ *camel.RuntimeCatalog) []string {
+			return []string{"camel:jackson"}
+		},
+		circuitBreakerRegexp: func(_ *camel.RuntimeCatalog) []string {
+			return []string{"camel:hystrix"}
+		},
+		restConfigurationRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			deps := make([]string, 0)
+			if c, ok := catalog.CamelCatalogSpec.Runtime.Capabilities["rest"]; ok {
+				for _, d := range c.Dependencies {
+					deps = append(deps, fmt.Sprintf("mvn:%s/%s", d.GroupID, d.ArtifactID))
+				}
+			}
+			return deps
+		},
+		restRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			deps := make([]string, 0)
+			if c, ok := catalog.CamelCatalogSpec.Runtime.Capabilities["rest"]; ok {
+				for _, d := range c.Dependencies {
+					deps = append(deps, fmt.Sprintf("mvn:%s/%s", d.GroupID, d.ArtifactID))
+				}
+			}
+			return deps
+		},
+		restClosureRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			deps := make([]string, 0)
+			if c, ok := catalog.CamelCatalogSpec.Runtime.Capabilities["rest"]; ok {
+				for _, d := range c.Dependencies {
+					deps = append(deps, fmt.Sprintf("mvn:%s/%s", d.GroupID, d.ArtifactID))
+				}
+			}
+			return deps
+		},
+		groovyLanguageRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("groovy"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
+		},
+		jsonPathLanguageRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("jsonpath"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
+		},
+		ognlRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("ognl"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
+		},
+		mvelRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("mvel"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
+		},
+		xqueryRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("xquery"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
+		},
+		xpathRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("xpath"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
 		},
-		quarkus: map[*regexp.Regexp]string{
-			xtokenizeRegexp: "camel-quarkus:core-xml",
+		xtokenizeRegexp: func(catalog *camel.RuntimeCatalog) []string {
+			if dependency, ok := catalog.GetLanguageDependency("xtokenize"); ok {
+				return []string{dependency}
+			}
+
+			return []string{}
 		},
 	}
 )
@@ -143,14 +210,12 @@ func (i *baseInspector) discoverDependencies(source v1.SourceSpec, meta *Metadat
 		}
 	}
 
-	for pattern, dep := range sourceDependencies.main {
-		if i.catalog.Runtime.Provider == v1.RuntimeProviderQuarkus {
-			// Check whether quarkus has its own artifact that differs from the standard one
-			if _, ok := sourceDependencies.quarkus[pattern]; ok {
-				dep = sourceDependencies.quarkus[pattern]
-			}
+	for pattern, supplier := range sourceDependencies {
+		if !pattern.MatchString(source.Content) {
+			continue
 		}
-		if pattern.MatchString(source.Content) {
+
+		for _, dep := range supplier(i.catalog) {
 			i.addDependency(dep, meta)
 		}
 	}
@@ -192,3 +257,53 @@ func (i *baseInspector) decodeComponent(uri string) string {
 	}
 	return ""
 }
+
+// hasOnlyPassiveEndpoints returns true if the source has no endpoint that needs to remain always active
+func (i *baseInspector) hasOnlyPassiveEndpoints(fromURIs []string) bool {
+	passivePlusHTTP := make(map[string]bool)
+	i.catalog.VisitSchemes(func(id string, scheme v1.CamelScheme) bool {
+		if scheme.HTTP || scheme.Passive {
+			passivePlusHTTP[id] = true
+		}
+
+		return true
+	})
+
+	return i.containsOnlyURIsIn(fromURIs, passivePlusHTTP)
+}
+
+func (i *baseInspector) containsOnlyURIsIn(fromURI []string, allowed map[string]bool) bool {
+	for _, uri := range fromURI {
+		prefix := i.getURIPrefix(uri)
+		if enabled, ok := allowed[prefix]; !ok || !enabled {
+			return false
+		}
+	}
+	return true
+}
+
+func (i *baseInspector) getURIPrefix(uri string) string {
+	parts := strings.SplitN(uri, ":", 2)
+	if len(parts) > 0 {
+		return parts[0]
+	}
+	return ""
+}
+
+func (i *baseInspector) containsHTTPURIs(fromURI []string) bool {
+	for _, uri := range fromURI {
+		prefix := i.getURIPrefix(uri)
+		scheme, ok := i.catalog.GetScheme(prefix)
+
+		if !ok {
+			// scheme does not exists
+			continue
+		}
+
+		if scheme.HTTP {
+			return true
+		}
+	}
+
+	return false
+}
diff --git a/pkg/util/source/inspector_groovy.go b/pkg/util/source/inspector_groovy.go
index 8ec882335..a37bfc087 100644
--- a/pkg/util/source/inspector_groovy.go
+++ b/pkg/util/source/inspector_groovy.go
@@ -49,5 +49,10 @@ func (i GroovyInspector) Extract(source v1.SourceSpec, meta *Metadata) error {
 
 	i.discoverDependencies(source, meta)
 
+	hasRest := restRegexp.MatchString(source.Content) || restClosureRegexp.MatchString(source.Content)
+
+	meta.ExposesHTTPServices = hasRest || i.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = i.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
diff --git a/pkg/util/source/inspector_java_script.go b/pkg/util/source/inspector_java_script.go
index 60e68f3b5..f79dd3a7f 100644
--- a/pkg/util/source/inspector_java_script.go
+++ b/pkg/util/source/inspector_java_script.go
@@ -49,5 +49,8 @@ func (i JavaScriptInspector) Extract(source v1.SourceSpec, meta *Metadata) error
 
 	i.discoverDependencies(source, meta)
 
+	meta.ExposesHTTPServices = restRegexp.MatchString(source.Content) || i.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = i.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
diff --git a/pkg/util/source/inspector_java_source.go b/pkg/util/source/inspector_java_source.go
index 7b79198e3..32bd0b044 100644
--- a/pkg/util/source/inspector_java_source.go
+++ b/pkg/util/source/inspector_java_source.go
@@ -45,5 +45,8 @@ func (i JavaSourceInspector) Extract(source v1.SourceSpec, meta *Metadata) error
 
 	i.discoverDependencies(source, meta)
 
+	meta.ExposesHTTPServices = restRegexp.MatchString(source.Content) || i.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = i.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
diff --git a/pkg/util/source/inspector_kotlin.go b/pkg/util/source/inspector_kotlin.go
index 44efd099d..b949318b9 100644
--- a/pkg/util/source/inspector_kotlin.go
+++ b/pkg/util/source/inspector_kotlin.go
@@ -45,5 +45,10 @@ func (i KotlinInspector) Extract(source v1.SourceSpec, meta *Metadata) error {
 
 	i.discoverDependencies(source, meta)
 
+	hasRest := restRegexp.MatchString(source.Content) || restClosureRegexp.MatchString(source.Content)
+
+	meta.ExposesHTTPServices = hasRest || i.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = i.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
diff --git a/pkg/util/source/inspector_xml.go b/pkg/util/source/inspector_xml.go
index 5ec7b9907..48777de19 100644
--- a/pkg/util/source/inspector_xml.go
+++ b/pkg/util/source/inspector_xml.go
@@ -45,6 +45,7 @@ func (i XMLInspector) Extract(source v1.SourceSpec, meta *Metadata) error {
 			switch se.Name.Local {
 			case "rest", "restConfiguration":
 				i.addDependency("camel:rest", meta)
+				meta.ExposesHTTPServices = true
 			case "circuitBreaker":
 				i.addDependency("camel:hystrix", meta)
 			case "language":
@@ -77,5 +78,8 @@ func (i XMLInspector) Extract(source v1.SourceSpec, meta *Metadata) error {
 
 	i.discoverDependencies(source, meta)
 
+	meta.ExposesHTTPServices = meta.ExposesHTTPServices || i.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = i.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
diff --git a/pkg/util/source/inspector_yaml.go b/pkg/util/source/inspector_yaml.go
index 06a1ce39f..e42eb38bc 100644
--- a/pkg/util/source/inspector_yaml.go
+++ b/pkg/util/source/inspector_yaml.go
@@ -49,6 +49,9 @@ func (inspector YAMLInspector) Extract(source v1.SourceSpec, meta *Metadata) err
 
 	inspector.discoverDependencies(source, meta)
 
+	meta.ExposesHTTPServices = meta.ExposesHTTPServices || inspector.containsHTTPURIs(meta.FromURIs)
+	meta.PassiveEndpoints = inspector.hasOnlyPassiveEndpoints(meta.FromURIs)
+
 	return nil
 }
 
@@ -56,6 +59,7 @@ func (inspector YAMLInspector) parseStep(key string, content interface{}, meta *
 	switch key {
 	case "rest":
 		inspector.addDependency("camel:rest", meta)
+		meta.ExposesHTTPServices = true
 	case "circuitBreaker":
 		inspector.addDependency("camel:hystrix", meta)
 	}
@@ -126,6 +130,5 @@ func (inspector YAMLInspector) parseStep(key string, content interface{}, meta *
 			meta.ToURIs = append(meta.ToURIs, maybeURI)
 		}
 	}
-
 	return nil
 }
diff --git a/pkg/util/source/types.go b/pkg/util/source/types.go
index 09f727a4f..180291744 100644
--- a/pkg/util/source/types.go
+++ b/pkg/util/source/types.go
@@ -27,4 +27,20 @@ type Metadata struct {
 	ToURIs []string
 	// All inferred dependencies required to run the integration
 	Dependencies *strset.Set
+	// ExposesHTTPServices indicates if a route defined by the source is exposed
+	// through HTTP
+	ExposesHTTPServices bool
+	// PassiveEndpoints indicates that the source contains only passive endpoints that
+	// are activated from external calls, including HTTP (useful to determine if the
+	// integration can scale to 0)
+	PassiveEndpoints bool
+}
+
+// NewMetadata --
+func NewMetadata() Metadata {
+	return Metadata{
+		FromURIs:     make([]string, 0),
+		ToURIs:       make([]string, 0),
+		Dependencies: strset.New(),
+	}
 }


Test patch:
diff --git a/pkg/metadata/metadata_dependencies_test.go b/pkg/metadata/metadata_dependencies_test.go
index 1a9627671..fe5c0e990 100644
--- a/pkg/metadata/metadata_dependencies_test.go
+++ b/pkg/metadata/metadata_dependencies_test.go
@@ -48,7 +48,17 @@ func TestDependenciesJavaSource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:activemq", "camel:amqp", "camel:log", "camel:telegram", "camel:timer", "camel:twitter"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:activemq",
+			"camel:amqp",
+			"camel:log",
+			"camel:telegram",
+			"camel:timer",
+			"camel:twitter",
+		},
+		meta.Dependencies.List())
 }
 
 func TestDependenciesJavaScript(t *testing.T) {
@@ -72,7 +82,16 @@ func TestDependenciesJavaScript(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:activemq", "camel:amqp", "camel:log", "camel:telegram", "camel:timer"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:activemq",
+			"camel:amqp",
+			"camel:log",
+			"camel:telegram",
+			"camel:timer",
+		},
+		meta.Dependencies.List())
 }
 
 func TestDependenciesGroovy(t *testing.T) {
@@ -98,7 +117,17 @@ func TestDependenciesGroovy(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:activemq", "camel:amqp", "camel:log", "camel:telegram", "camel:timer", "camel:twitter"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:activemq",
+			"camel:amqp",
+			"camel:log",
+			"camel:telegram",
+			"camel:timer",
+			"camel:twitter",
+		},
+		meta.Dependencies.List())
 }
 
 func TestDependencies(t *testing.T) {
@@ -180,7 +209,14 @@ func TestJacksonDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:jackson", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:http",
+			"camel:jackson",
+			"camel:log",
+		},
+		meta.Dependencies.List())
 }
 
 func TestJacksonImplicitDependency(t *testing.T) {
@@ -201,7 +237,14 @@ func TestJacksonImplicitDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:jackson", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:http",
+			"camel:jackson",
+			"camel:log",
+		},
+		meta.Dependencies.List())
 }
 
 func TestLanguageDependencies(t *testing.T) {
@@ -227,8 +270,19 @@ func TestLanguageDependencies(t *testing.T) {
 	assert.Nil(t, err)
 
 	meta := Extract(catalog, code)
-	assert.ElementsMatch(t, []string{"camel:direct", "camel:ognl", "camel:saxon", "camel:xpath",
-		"camel:jsonpath", "camel:groovy", "camel:jaxp", "camel:mvel"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:direct",
+			"camel:ognl",
+			"camel:saxon",
+			"camel:xpath",
+			"camel:jsonpath",
+			"camel:groovy",
+			"camel:xml-jaxp",
+			"camel:mvel",
+		},
+		meta.Dependencies.List())
 }
 
 func TestLanguageDependenciesTransformExpression(t *testing.T) {
@@ -250,7 +304,15 @@ func TestLanguageDependenciesTransformExpression(t *testing.T) {
 	assert.Nil(t, err)
 
 	meta := Extract(catalog, code)
-	assert.ElementsMatch(t, []string{"camel:direct", "camel:ognl", "camel:xpath", "camel:jsonpath"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:direct",
+			"camel:ognl",
+			"camel:xpath",
+			"camel:jsonpath",
+		},
+		meta.Dependencies.List())
 }
 
 func TestHystrixDependency(t *testing.T) {
@@ -273,7 +335,13 @@ func TestHystrixDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:hystrix", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(t,
+		[]string{
+			"camel:http",
+			"camel:hystrix",
+			"camel:log",
+		},
+		meta.Dependencies.List())
 }
 
 func TestRestDependency(t *testing.T) {
@@ -296,7 +364,16 @@ func TestRestDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:rest", "camel:direct", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:http",
+			"camel:direct",
+			"camel:log",
+			"mvn:org.apache.camel/camel-rest",
+			"mvn:org.apache.camel/camel-undertow",
+		},
+		meta.Dependencies.List())
 }
 
 func TestRestWithPathDependency(t *testing.T) {
@@ -319,7 +396,15 @@ func TestRestWithPathDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:rest", "camel:direct", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t, []string{
+			"camel:http",
+			"camel:direct",
+			"camel:log",
+			"mvn:org.apache.camel/camel-rest",
+			"mvn:org.apache.camel/camel-undertow",
+		},
+		meta.Dependencies.List())
 }
 
 func TestRestConfigurationDependency(t *testing.T) {
@@ -341,10 +426,17 @@ func TestRestConfigurationDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:rest", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t, []string{
+			"camel:http",
+			"camel:log",
+			"mvn:org.apache.camel/camel-rest",
+			"mvn:org.apache.camel/camel-undertow",
+		},
+		meta.Dependencies.List())
 }
 
-func TestRestClosureDependency(t *testing.T) {
+func TestRestClosureDependencyGroovy(t *testing.T) {
 	code := v1.SourceSpec{
 		DataSpec: v1.DataSpec{
 			Name: "Request.groovy",
@@ -363,7 +455,45 @@ func TestRestClosureDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:http", "camel:rest", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:http",
+			"camel:log",
+			"mvn:org.apache.camel/camel-rest",
+			"mvn:org.apache.camel/camel-undertow",
+		},
+		meta.Dependencies.List())
+}
+
+func TestRestClosureDependencyKotlin(t *testing.T) {
+	code := v1.SourceSpec{
+		DataSpec: v1.DataSpec{
+			Name: "Request.groovy",
+			Content: `
+                rest {
+                }
+			    from("http:test")
+                    .to("log:info")
+		    `,
+		},
+		Language: v1.LanguageKotlin,
+	}
+
+	catalog, err := camel.DefaultCatalog()
+	assert.Nil(t, err)
+
+	meta := Extract(catalog, code)
+
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:http",
+			"camel:log",
+			"mvn:org.apache.camel/camel-rest",
+			"mvn:org.apache.camel/camel-undertow",
+		},
+		meta.Dependencies.List())
 }
 
 func TestXMLHystrixDependency(t *testing.T) {
@@ -389,7 +519,15 @@ func TestXMLHystrixDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:direct", "camel:hystrix", "camel:kafka", "camel:log"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:direct",
+			"camel:hystrix",
+			"camel:kafka",
+			"camel:log",
+		},
+		meta.Dependencies.List())
 }
 
 func TestXMLRestDependency(t *testing.T) {
@@ -419,7 +557,14 @@ func TestXMLRestDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:direct", "camel:rest", "camel:mock"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:direct",
+			"camel:rest",
+			"camel:mock",
+		},
+		meta.Dependencies.List())
 }
 
 func TestXMLLanguageDependencies(t *testing.T) {
@@ -556,7 +701,13 @@ func TestYAMLHystrixDependency(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.ElementsMatch(t, []string{"camel:direct", "camel:hystrix"}, meta.Dependencies.List())
+	assert.ElementsMatch(
+		t,
+		[]string{
+			"camel:direct",
+			"camel:hystrix",
+		},
+		meta.Dependencies.List())
 }
 
 func TestYAMLLanguageDependencies(t *testing.T) {
diff --git a/pkg/metadata/metadata_http_test.go b/pkg/metadata/metadata_http_test.go
index e89365ebd..97b4aeab2 100644
--- a/pkg/metadata/metadata_http_test.go
+++ b/pkg/metadata/metadata_http_test.go
@@ -44,7 +44,7 @@ func TestHttpJavaSource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.False(t, meta.PassiveEndpoints)
 }
 
@@ -67,7 +67,7 @@ func TestHttpOnlyJavaSource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -88,7 +88,7 @@ func TestHttpOnlyJavaSourceRest(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -109,7 +109,7 @@ func TestHttpOnlyJavaSourceRest2(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -131,7 +131,7 @@ func TestNoHttpGroovySource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.False(t, meta.RequiresHTTPService)
+	assert.False(t, meta.ExposesHTTPServices)
 	assert.False(t, meta.PassiveEndpoints)
 }
 
@@ -153,7 +153,7 @@ func TestHttpOnlyGroovySource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -175,7 +175,7 @@ func TestHttpXMLSource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.False(t, meta.PassiveEndpoints)
 }
 
@@ -198,7 +198,7 @@ func TestHttpOnlyXMLSource(t *testing.T) {
 
 	meta := Extract(catalog, code)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -232,7 +232,7 @@ func TestMultilangHTTPOnlySource(t *testing.T) {
 
 	meta := ExtractAll(catalog, codes)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.True(t, meta.PassiveEndpoints)
 }
 
@@ -268,6 +268,6 @@ func TestMultilangHTTPSource(t *testing.T) {
 
 	meta := ExtractAll(catalog, codes)
 
-	assert.True(t, meta.RequiresHTTPService)
+	assert.True(t, meta.ExposesHTTPServices)
 	assert.False(t, meta.PassiveEndpoints)
 }