| /* |
| 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 source |
| |
| import ( |
| "fmt" |
| "regexp" |
| "strings" |
| |
| v1 "github.com/apache/camel-k/pkg/apis/camel/v1" |
| "github.com/apache/camel-k/pkg/util" |
| "github.com/apache/camel-k/pkg/util/camel" |
| ) |
| |
| type catalog2deps func(*camel.RuntimeCatalog) []string |
| |
| const ( |
| defaultJsonDataformat = "json-jackson" |
| ) |
| |
| var ( |
| singleQuotedFrom = regexp.MustCompile(`from\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`) |
| doubleQuotedFrom = regexp.MustCompile(`from\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`) |
| singleQuotedTo = regexp.MustCompile(`\.to\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`) |
| singleQuotedToD = regexp.MustCompile(`\.toD\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`) |
| singleQuotedToF = regexp.MustCompile(`\.toF\s*\(\s*'([a-zA-Z0-9-]+:[^']+)'`) |
| doubleQuotedTo = regexp.MustCompile(`\.to\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`) |
| doubleQuotedToD = regexp.MustCompile(`\.toD\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`) |
| doubleQuotedToF = regexp.MustCompile(`\.toF\s*\(\s*"([a-zA-Z0-9-]+:[^"]+)"`) |
| languageRegexp = regexp.MustCompile(`language\s*\(\s*["|']([a-zA-Z0-9-]+[^"|']+)["|']\s*,.*\)`) |
| camelTypeRegexp = regexp.MustCompile(`.*(org.apache.camel.*Component|DataFormat|Language)`) |
| jsonLibraryRegexp = regexp.MustCompile(`.*JsonLibrary\.Jackson.*`) |
| jsonLanguageRegexp = regexp.MustCompile(`.*\.json\(\).*`) |
| circuitBreakerRegexp = regexp.MustCompile(`.*\.circuitBreaker\(\).*`) |
| restConfigurationRegexp = regexp.MustCompile(`.*restConfiguration\(\).*`) |
| 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*\(.*\).*`) |
| mvelRegexp = regexp.MustCompile(`.*\.mvel\s*\(.*\).*`) |
| xqueryRegexp = regexp.MustCompile(`.*\.xquery\s*\(.*\).*`) |
| xpathRegexp = regexp.MustCompile(`.*\.?xpath\s*\(.*\).*`) |
| xtokenizeRegexp = regexp.MustCompile(`.*\.xtokenize\s*\(.*\).*`) |
| |
| sourceCapabilities = map[*regexp.Regexp][]string{ |
| circuitBreakerRegexp: {v1.CapabilityCircuitBreaker}, |
| } |
| |
| sourceDependencies = map[*regexp.Regexp]catalog2deps{ |
| jsonLibraryRegexp: func(catalog *camel.RuntimeCatalog) []string { |
| res := make([]string, 0) |
| if jsonDF := catalog.GetArtifactByDataFormat(defaultJsonDataformat); jsonDF != nil { |
| res = append(res, jsonDF.GetDependencyID()) |
| } |
| return res |
| }, |
| jsonLanguageRegexp: func(catalog *camel.RuntimeCatalog) []string { |
| res := make([]string, 0) |
| if jsonDF := catalog.GetArtifactByDataFormat(defaultJsonDataformat); jsonDF != nil { |
| res = append(res, jsonDF.GetDependencyID()) |
| } |
| return res |
| }, |
| 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{} |
| }, |
| xtokenizeRegexp: func(catalog *camel.RuntimeCatalog) []string { |
| if dependency, ok := catalog.GetLanguageDependency("xtokenize"); ok { |
| return []string{dependency} |
| } |
| |
| return []string{} |
| }, |
| } |
| ) |
| |
| // Inspector -- |
| type Inspector interface { |
| Extract(v1.SourceSpec, *Metadata) error |
| } |
| |
| // InspectorForLanguage -- |
| func InspectorForLanguage(catalog *camel.RuntimeCatalog, language v1.Language) Inspector { |
| switch language { |
| case v1.LanguageJavaSource: |
| return &JavaSourceInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| case v1.LanguageXML: |
| return &XMLInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| case v1.LanguageGroovy: |
| return &GroovyInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| case v1.LanguageJavaScript: |
| return &JavaScriptInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| case v1.LanguageKotlin: |
| return &KotlinInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| case v1.LanguageYaml: |
| return &YAMLInspector{ |
| baseInspector: baseInspector{ |
| catalog: catalog, |
| }, |
| } |
| } |
| return &baseInspector{} |
| } |
| |
| type baseInspector struct { |
| catalog *camel.RuntimeCatalog |
| } |
| |
| func (i baseInspector) Extract(v1.SourceSpec, *Metadata) error { |
| return nil |
| } |
| |
| // discoverDependencies returns a list of dependencies required by the given source code |
| func (i *baseInspector) discoverCapabilities(source v1.SourceSpec, meta *Metadata) { |
| uris := util.StringSliceJoin(meta.FromURIs, meta.ToURIs) |
| |
| for _, uri := range uris { |
| if i.getURIPrefix(uri) == "platform-http" { |
| meta.RequiredCapabilities.Add(v1.CapabilityPlatformHTTP) |
| } |
| } |
| |
| for pattern, capabilities := range sourceCapabilities { |
| if !pattern.MatchString(source.Content) { |
| continue |
| } |
| |
| for _, capability := range capabilities { |
| meta.RequiredCapabilities.Add(capability) |
| } |
| } |
| } |
| |
| // discoverDependencies returns a list of dependencies required by the given source code |
| func (i *baseInspector) discoverDependencies(source v1.SourceSpec, meta *Metadata) { |
| uris := util.StringSliceJoin(meta.FromURIs, meta.ToURIs) |
| |
| for _, uri := range uris { |
| candidateComp := i.decodeComponent(uri) |
| if candidateComp != "" { |
| i.addDependency(candidateComp, meta) |
| } |
| } |
| |
| for pattern, supplier := range sourceDependencies { |
| if !pattern.MatchString(source.Content) { |
| continue |
| } |
| |
| for _, dep := range supplier(i.catalog) { |
| i.addDependency(dep, meta) |
| } |
| } |
| |
| for _, match := range languageRegexp.FindAllStringSubmatch(source.Content, -1) { |
| if len(match) > 1 { |
| if dependency, ok := i.catalog.GetLanguageDependency(match[1]); ok { |
| i.addDependency(dependency, meta) |
| } |
| } |
| } |
| |
| for _, match := range camelTypeRegexp.FindAllStringSubmatch(source.Content, -1) { |
| if len(match) > 1 { |
| if dependency, ok := i.catalog.GetJavaTypeDependency(match[1]); ok { |
| i.addDependency(dependency, meta) |
| } |
| } |
| } |
| } |
| |
| func (i *baseInspector) addDependency(dependency string, meta *Metadata) { |
| if i.catalog.Runtime.Provider == v1.RuntimeProviderQuarkus { |
| if strings.HasPrefix(dependency, "camel:") { |
| dependency = "camel-quarkus:" + strings.TrimPrefix(dependency, "camel:") |
| } |
| } |
| meta.Dependencies.Add(dependency) |
| } |
| |
| func (i *baseInspector) decodeComponent(uri string) string { |
| uriSplit := strings.SplitN(uri, ":", 2) |
| if len(uriSplit) < 2 { |
| return "" |
| } |
| uriStart := uriSplit[0] |
| if component := i.catalog.GetArtifactByScheme(uriStart); component != nil { |
| return component.GetDependencyID() |
| } |
| 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 |
| } |