Skip to content

Commit

Permalink
Add Zuul Proxy Rule
Browse files Browse the repository at this point in the history
  • Loading branch information
FloBoJa committed Nov 30, 2023
1 parent f0aa100 commit 79244fa
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
Expand Down Expand Up @@ -69,17 +68,15 @@ public Map<OperationInterface, List<Operation>> simplified() {
.flatMap(x -> x.stream())
.collect(Collectors.toList()));
for (OperationInterface member : groupedRequirements.get(root)) {
simplifiedRoot.addAll(member.simplified()
.values()
.stream()
.flatMap(x -> x.stream())
.collect(Collectors.toList()));
simplifiedRoot.addAll(member.simplified()
.values()
.stream()
.flatMap(x -> x.stream())
.collect(Collectors.toList()));
}
simplifiedInterfaces.add(Map.of(
root,
simplifiedRoot.stream()
.distinct()
.collect(Collectors.toList())));
simplifiedInterfaces.add(Map.of(root, simplifiedRoot.stream()
.distinct()
.collect(Collectors.toList())));
}
return MapMerger.merge(simplifiedInterfaces);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,13 @@
class="org.palladiosimulator.somox.analyzer.rules.impl.SpringRules">
</rule>
</extension>
<extension
id="org.palladiosimulator.somox.analyzer.rules.impl.spring.zuul"
name="Spring Zuul Rules"
point="org.palladiosimulator.somox.analyzer.rule">
<rule
class="org.palladiosimulator.somox.analyzer.rules.impl.SpringZuulRules">
</rule>
</extension>

</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import java.nio.file.Path
import org.eclipse.jdt.core.dom.CompilationUnit
import java.util.Map;
import org.jdom2.Document
import java.util.stream.Collectors
import org.apache.log4j.Logger
import org.eclipse.jdt.core.dom.MethodDeclaration
import org.eclipse.jdt.core.dom.ITypeBinding
Expand All @@ -20,6 +19,7 @@ import org.palladiosimulator.somox.analyzer.rules.model.CompUnitOrName
import java.util.function.Function
import java.util.Set
import org.palladiosimulator.somox.analyzer.rules.engine.Rule
import org.palladiosimulator.somox.analyzer.rules.impl.util.SpringHelper

class SpringRules implements Rule {
static final Logger LOG = Logger.getLogger(SpringRules)
Expand All @@ -30,6 +30,7 @@ class SpringRules implements Rule {
public static final String YAML_MAPPERS_KEY = YAML_DISCOVERER_ID + ".mappers"
public static final String XML_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.xml"
public static final String PROPERTIES_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.properties"
public static final String ZUUL_RULE_ID = "org.palladiosimulator.somox.analyzer.rules.impl.spring.zuul";

override processRules(RuleEngineBlackboard blackboard, Path path) {
val unit = blackboard.getDiscoveredFiles(JAVA_DISCOVERER_ID, typeof(CompilationUnit)).get(path)
Expand All @@ -40,102 +41,31 @@ class SpringRules implements Rule {
val poms = blackboard.getDiscoveredFiles(XML_DISCOVERER_ID, typeof(Document))
val propertyFiles = blackboard.getDiscoveredFiles(PROPERTIES_DISCOVERER_ID, typeof(Properties))

val projectRoot = findProjectRoot(path, poms)
val configRoot = findConfigRoot(poms)
val projectRoot = SpringHelper.findProjectRoot(path, poms)
val configRoot = SpringHelper.findConfigRoot(poms)
val bootstrapYaml = yamlMappers.get(
findFile(yamlMappers.keySet, projectRoot.resolve("src/main/resources"),
SpringHelper.findFile(yamlMappers.keySet, projectRoot.resolve("src/main/resources"),
Set.of("bootstrap.yaml", "bootstrap.yml")))
val applicationProperties = propertyFiles.get(
findFile(propertyFiles.keySet, projectRoot.resolve("src/main/resources"), Set.of("application.properties")))
SpringHelper.findFile(propertyFiles.keySet, projectRoot.resolve("src/main/resources"),
Set.of("application.properties")))

val applicationName = getFromYamlOrProperties("spring.application.name", bootstrapYaml, applicationProperties)
val applicationName = SpringHelper.getFromYamlOrProperties("spring.application.name", bootstrapYaml,
applicationProperties)
val projectConfigYaml = yamlMappers.get(
findFile(yamlMappers.keySet, configRoot, Set.of(applicationName + ".yaml", applicationName + ".yml")))
SpringHelper.findFile(yamlMappers.keySet, configRoot.resolve("src/main/resources/shared"),
Set.of(applicationName + ".yaml", applicationName + ".yml")))
val contextPathOption = Optional.ofNullable(projectConfigYaml).flatMap[x|x.apply("server.servlet.context-path")]
var contextPath = contextPathOption.orElse("/")

val rawApplicationYaml = rawYamls.get(
findFile(yamlMappers.keySet, projectRoot.resolve("src/main/resources"),
SpringHelper.findFile(yamlMappers.keySet, projectRoot.resolve("src/main/resources"),
Set.of("application.yaml", "application.yml")))
val contextVariables = collectContextVariables(rawApplicationYaml)

processRuleForCompUnit(blackboard, unit, applicationName, contextPath, contextVariables)
}

def findProjectRoot(Path currentPath, Map<Path, Document> poms) {
if (currentPath === null || poms === null) {
return null
}
val closestPom = poms.entrySet.stream.map([entry|entry.key]) // Only keep poms above the current compilation unit.
.filter([path|currentPath.startsWith(path.parent)]) // Take the longest path, which is the pom.xml closest to the compilation unit
.max([a, b|a.size.compareTo(b.size)])

if (closestPom.present) {
return closestPom.get.parent
} else {
return null
}
}

def findConfigRoot(Map<Path, Document> poms) {
if (poms === null) {
return null
}
val configRoots = poms.entrySet.stream.map [ entry |
entry.key -> entry.value.rootElement.getChild("dependencies", entry.value.rootElement.namespace)
].filter[entry|entry.value !== null].map [ entry |
entry.key -> entry.value.getChildren("dependency", entry.value.namespace)
].filter[entry|!entry.value.empty].filter [ entry |
entry.value.filter [ dependency |
dependency.getChildTextTrim("groupId", dependency.namespace) == "org.springframework.cloud"
].exists [ dependency |
dependency.getChildTextTrim("artifactId", dependency.namespace) == "spring-cloud-config-server"
]
].collect(Collectors.toList)

if (configRoots.size > 1) {
LOG.warn("Multiple Spring config servers, choosing \"" + configRoots.get(0).key.parent + "\" arbitrarily")
} else if (configRoots.empty) {
return null
}
return configRoots.get(0).key.parent
}

def findFile(Set<Path> paths, Path directory, Set<String> possibleNames) {
if (paths === null || directory === null || possibleNames === null) {
return null
}
val candidates = paths.stream.filter[path|path.parent == directory].filter [ path |
possibleNames.contains(path.fileName.toString)
].collect(Collectors.toList)

if (candidates.size > 1) {
// fileName must exist since candidates were found
val fileName = possibleNames.iterator.next;
LOG.warn(
"Multiple " + fileName + " in " + directory + ", choosing " + directory.relativize(candidates.get(0)) +
" arbitrarily")
} else if (candidates.empty) {
return null
}
return candidates.get(0)
}

def getFromYamlOrProperties(String key, Function<String, Optional<String>> yamlMapper, Properties properties) {
if (yamlMapper !== null) {
val result = yamlMapper.apply(key)
if (result.present) {
return result.get()
}
}

if (properties !== null) {
return properties.getProperty(key)
}

return null
}

def Map<String, String> collectContextVariables(Iterable<Map<String, Object>> applicationYaml) {
val result = new HashMap<String, String>();
if (applicationYaml === null || applicationYaml.empty) {
Expand Down Expand Up @@ -428,7 +358,7 @@ class SpringRules implements Rule {
}

override getRequiredServices() {
return Set.of(JAVA_DISCOVERER_ID, YAML_DISCOVERER_ID, XML_DISCOVERER_ID, PROPERTIES_DISCOVERER_ID)
return Set.of(JAVA_DISCOVERER_ID, YAML_DISCOVERER_ID, XML_DISCOVERER_ID, PROPERTIES_DISCOVERER_ID, ZUUL_RULE_ID)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package org.palladiosimulator.somox.analyzer.rules.impl

import java.nio.file.Path
import java.util.ArrayList
import java.util.HashMap
import java.util.List
import java.util.Map
import java.util.Optional
import java.util.Properties
import java.util.Set
import java.util.function.Function
import org.apache.log4j.Logger
import org.jdom2.Document
import org.palladiosimulator.somox.analyzer.rules.blackboard.RuleEngineBlackboard
import org.palladiosimulator.somox.analyzer.rules.engine.Rule
import org.palladiosimulator.somox.analyzer.rules.impl.util.SpringHelper

class SpringZuulRules implements Rule {
static final Logger LOG = Logger.getLogger(SpringZuulRules)

public static final String RULE_ID = "org.palladiosimulator.somox.analyzer.rules.impl.spring.zuul"
public static final String JAVA_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.java"
public static final String YAML_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.yaml"
public static final String YAML_MAPPERS_KEY = YAML_DISCOVERER_ID + ".mappers"
public static final String XML_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.xml"
public static final String PROPERTIES_DISCOVERER_ID = "org.palladiosimulator.somox.discoverer.properties"

override processRules(RuleEngineBlackboard blackboard, Path path) {
val rawYamls = blackboard.getPartition(YAML_DISCOVERER_ID) as Map<Path, Iterable<Map<String, Object>>>
val yamlMappers = blackboard.getPartition(YAML_MAPPERS_KEY) as Map<Path, Function<String, Optional<String>>>
val poms = blackboard.getDiscoveredFiles(XML_DISCOVERER_ID, typeof(Document))
val propertyFiles = blackboard.getDiscoveredFiles(PROPERTIES_DISCOVERER_ID, typeof(Properties))

val projectRoot = SpringHelper.findProjectRoot(path, poms)
val configRoot = SpringHelper.findConfigRoot(poms)

var routeMap = blackboard.getPartition(RULE_ID) as Map<Path, List<Route>>
if (routeMap === null) {
routeMap = new HashMap<Path, List<Route>>()
}

// Execute only once for each Spring application/service
if(routeMap.containsKey(projectRoot)) return

val bootstrapYaml = yamlMappers.get(
SpringHelper.findFile(yamlMappers.keySet, projectRoot.resolve("src/main/resources"),
Set.of("bootstrap.yaml", "bootstrap.yml")))
val applicationProperties = propertyFiles.get(
SpringHelper.findFile(propertyFiles.keySet, projectRoot.resolve("src/main/resources"),
Set.of("application.properties")))
val applicationName = SpringHelper.getFromYamlOrProperties("spring.application.name", bootstrapYaml,
applicationProperties)
val projectConfigYaml = rawYamls.get(
SpringHelper.findFile(rawYamls.keySet, configRoot.resolve("src/main/resources/shared"),
Set.of(applicationName + ".yaml", applicationName + ".yml")))

// Query zuul.routes in config server only (for now)
val routes = collectRoutes(projectConfigYaml)
for (route : routes) {
LOG.warn("Route in " + applicationName + ": " + route.path + " -> " + route.serviceId)
}
routeMap.put(projectRoot, routes)
blackboard.addPartition(RULE_ID, routeMap)
}

def List<Route> collectRoutes(Iterable<Map<String, Object>> applicationYamlIter) {
val result = new ArrayList()

if(applicationYamlIter === null || applicationYamlIter.empty) return result

val applicationYaml = applicationYamlIter.get(0) as Map<String, Object>
if(applicationYaml === null) return result

val zuulObject = applicationYaml.get("zuul")
if(!(zuulObject instanceof Map)) return result
val zuul = zuulObject as Map<String, Object>

val routesObject = zuul.get("routes")
if(!(routesObject instanceof Map)) return result
val routes = routesObject as Map<String, Map<String, Object>>

for (route : routes.values) {
val pathObject = route.get("path")
val serviceIdObject = route.get("serviceId")
if (pathObject !== null && serviceIdObject !== null && pathObject instanceof String &&
serviceIdObject instanceof String) {
val path = pathObject as String
val serviceId = serviceIdObject as String
result.add(new Route(path, serviceId))
}
}

return result;
}

override isBuildRule() {
return false
}

override getConfigurationKeys() {
return Set.of
}

override getID() {
return RULE_ID
}

override getName() {
return "Spring Zuul Rules"
}

override getRequiredServices() {
return Set.of(JAVA_DISCOVERER_ID, YAML_DISCOVERER_ID, XML_DISCOVERER_ID, PROPERTIES_DISCOVERER_ID)
}

static class Route {
public String path
public String serviceId

new(String path, String serviceId) {
this.path = path
this.serviceId = serviceId
}
}
}
Loading

0 comments on commit 79244fa

Please sign in to comment.