Skip to content
This repository has been archived by the owner on Dec 18, 2022. It is now read-only.

Commit

Permalink
[#31] resolve dynamic url mapping
Browse files Browse the repository at this point in the history
- find the full url in static map if not try to find dynamic one with parsing url with regEx pattern
  • Loading branch information
ztkmkoo committed Apr 18, 2020
1 parent 0353052 commit 4c326c5
Show file tree
Hide file tree
Showing 8 changed files with 507 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private DssRestPathResolver dssRestPathResolver(List<DssRestActorService> servic
final DssRestPathResolver.Builder builder = DssRestPathResolver.builder();
serviceList.forEach(service -> {
final ActorRef<DssRestServiceActorCommand> serviceActor = context.spawn(DssRestServiceActor.create(service), service.getName());
builder.addServiceActor(service.getMethodType(), service.getPath(), serviceActor);
builder.addService(service, serviceActor);
});
return builder.build();
}
Expand All @@ -54,7 +54,7 @@ private Behavior<DssRestMasterActorCommand> handlingDssRestMasterActorCommandReq

context.getLog().info("DssRestMasterActorCommandRequest: {}", request);

final Optional<ActorRef<DssRestServiceActorCommand>> optional = dssRestPathResolver.getStaticServiceActor(request.getMethodType(), request.getPath());
final Optional<ActorRef<DssRestServiceActorCommand>> optional = dssRestPathResolver.getPathServiceActor(request.getMethodType(), request.getPath());
if (optional.isPresent()) {
optional.get().tell(new DssRestServiceActorCommandRequest(request));
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package com.ztkmkoo.dss.core.actor.rest;

import akka.actor.typed.ActorRef;
import com.ztkmkoo.dss.core.actor.rest.entity.DssRestPath;
import com.ztkmkoo.dss.core.actor.rest.entity.DssRestPathComposition;
import com.ztkmkoo.dss.core.actor.rest.service.DssRestActorService;
import com.ztkmkoo.dss.core.actor.rest.util.DssRestPathUtils;
import com.ztkmkoo.dss.core.exception.DssRestServiceMappingException;
import com.ztkmkoo.dss.core.message.rest.DssRestServiceActorCommand;
import com.ztkmkoo.dss.core.network.rest.enumeration.DssRestMethodType;
Expand All @@ -17,19 +21,56 @@
public class DssRestPathResolver {

private static final Logger logger = LoggerFactory.getLogger(DssRestPathResolver.class);
private final Map<DssRestMethodType, Map<String, ActorRef<DssRestServiceActorCommand>>> staticServiceActorMap;

private final Map<DssRestMethodType, Map<String, DssRestPathComposition>> staticPathCompositionMap;
private final Map<DssRestMethodType, List<DssRestPathComposition>> dynamicPathCompositionMap;

private DssRestPathResolver(Builder builder) {
this.staticServiceActorMap = Collections.unmodifiableMap(builder.staticServiceActorMap);
this.staticPathCompositionMap = Collections.unmodifiableMap(builder.staticPathCompositionMap);
this.dynamicPathCompositionMap = Collections.unmodifiableMap(builder.dynamicPathCompositionMap);
}

public Optional<ActorRef<DssRestServiceActorCommand>> getPathServiceActor(DssRestMethodType methodType, String path) {
final String fixedPath = DssRestPathUtils.getFixedPath(path);
final Optional<ActorRef<DssRestServiceActorCommand>> fixedOptional = getStaticPathServiceActor(methodType, fixedPath);
if (fixedOptional.isPresent()) {
return fixedOptional;
}

return getDynamicPathServiceActor(methodType, fixedPath);
}

private Optional<ActorRef<DssRestServiceActorCommand>> getStaticPathServiceActor(DssRestMethodType methodType, String fixedPath) {
if (!staticPathCompositionMap.containsKey(methodType)) {
return Optional.empty();
}

final Map<String, DssRestPathComposition> map = staticPathCompositionMap.get(methodType);

if (!map.containsKey(fixedPath)) {
return Optional.empty();
}

return Optional.of(map.get(fixedPath).getServiceActor());
}

public Optional<ActorRef<DssRestServiceActorCommand>> getStaticServiceActor(DssRestMethodType methodType, String path) {
return Optional
.ofNullable(
staticServiceActorMap
.getOrDefault(methodType, Collections.emptyMap())
.get(path)
);
private Optional<ActorRef<DssRestServiceActorCommand>> getDynamicPathServiceActor(DssRestMethodType methodType, String fixedPath) {
if (!dynamicPathCompositionMap.containsKey(methodType)) {
return Optional.empty();
}

final List<DssRestPathComposition> list = dynamicPathCompositionMap.get(methodType);
if (list.isEmpty()) {
return Optional.empty();
}

for (DssRestPathComposition composition : list) {
if(composition.getDssRestPath().match(fixedPath)) {
return Optional.of(composition.getServiceActor());
}
}

return Optional.empty();
}

public static Builder builder() {
Expand All @@ -38,33 +79,104 @@ public static Builder builder() {

public static class Builder {

private Map<DssRestMethodType, Map<String, ActorRef<DssRestServiceActorCommand>>> staticServiceActorMap = new EnumMap<>(DssRestMethodType.class);
private final Map<DssRestMethodType, Map<String, DssRestPathComposition>> staticPathCompositionMap = new EnumMap<>(DssRestMethodType.class);
private final Map<DssRestMethodType, List<DssRestPathComposition>> dynamicPathCompositionMap = new EnumMap<>(DssRestMethodType.class);

public Builder addServiceActor(DssRestMethodType methodType, String path, ActorRef<DssRestServiceActorCommand> serviceActor) {
Objects.requireNonNull(methodType);
Objects.requireNonNull(path);
public Builder addService(DssRestActorService service, ActorRef<DssRestServiceActorCommand> serviceActor) {
Objects.requireNonNull(service);
Objects.requireNonNull(service.getMethodType());
Objects.requireNonNull(service.getPath());
Objects.requireNonNull(serviceActor);

if (!staticServiceActorMap.containsKey(methodType)) {
staticServiceActorMap.put(methodType, new HashMap<>());
final String fixedPath = DssRestPathUtils.getFixedPath(service.getPath());
if (DssRestPathUtils.isDynamicPath(fixedPath)) {
addDynamicPathService(service, serviceActor);
} else {
addStaticPathService(service, serviceActor);
}

return this;
}

private void addStaticPathService(DssRestActorService service, ActorRef<DssRestServiceActorCommand> serviceActor) {
if(!staticPathCompositionMap.containsKey(service.getMethodType())) {
staticPathCompositionMap.put(service.getMethodType(), new HashMap<>());
}

if (staticServiceActorMap.get(methodType).containsKey(path)) {
final Map<String, DssRestPathComposition> map = staticPathCompositionMap.get(service.getMethodType());
final String path = service.getPath();
if (map.containsKey(path)) {
throw new DssRestServiceMappingException("Cannot map same path: " + path);
}
staticServiceActorMap.get(methodType).put(path, serviceActor);
return this;

map.put(
service.getPath(),
DssRestPathComposition
.builder()
.serviceActor(serviceActor)
.dssRestPath(
DssRestPath
.builder()
.path(path)
.build()
)
.build()
);
}

public DssRestPathResolver build() {
if (!staticServiceActorMap.isEmpty()) {
staticServiceActorMap.forEach((methodType, map) -> map.forEach((path, actorRef) ->
logger.info("Add mapping {} {} to {}",
methodType.name(), path, actorRef.path().name())
));
private void addDynamicPathService(DssRestActorService service, ActorRef<DssRestServiceActorCommand> serviceActor) {
if(!dynamicPathCompositionMap.containsKey(service.getMethodType())) {
dynamicPathCompositionMap.put(service.getMethodType(), new ArrayList<>());
}

final List<DssRestPathComposition> list = dynamicPathCompositionMap.get(service.getMethodType());
final String path = service.getPath();

list.add(
DssRestPathComposition
.builder()
.serviceActor(serviceActor)
.dssRestPath(
DssRestPath
.builder()
.path(path)
.build()
)
.build()
);
}

public DssRestPathResolver build() {
logStaticMappingInfo();
logDynamicMappingInfo();

return new DssRestPathResolver(this);
}

private void logStaticMappingInfo() {
if (staticPathCompositionMap.isEmpty()) {
return;
}

staticPathCompositionMap.forEach((methodType, map) ->
map.forEach((path, composition) ->
logger.info("Add static mapping: [{}] {} to {}",
methodType, path, composition.getServiceActor().path().name())
)
);
}

private void logDynamicMappingInfo() {
if (dynamicPathCompositionMap.isEmpty()) {
return;
}

dynamicPathCompositionMap.forEach((methodType, map) ->
map.forEach(composition ->
logger.info("Add static mapping: [{}] {} to {}", methodType,
composition.getDssRestPath().getRowPath(), composition.getServiceActor().path().name())
)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.ztkmkoo.dss.core.actor.rest.entity;

import lombok.Builder;
import lombok.Getter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Project: dss
* Created by: @ztkmkoo(ztkmkoo@gmail.com)
* Date: 20. 4. 13. 오전 2:06
*/
@Getter
public class DssRestPath implements Serializable {

private static final long serialVersionUID = 8287023454874773276L;

private final transient Logger logger = LoggerFactory.getLogger(DssRestPath.class);

private static final String PATH_SEPARATOR_REG_EX = "\\/";
private static final String PATH_NORMAL_REG_EX = "[^\\/]+";
private static final String PATH_DIGITAL_REG_EX = "(\\d+)";
private static final String PATH_VARIABLE_REG_EX = "\\{[^\\/]+\\}";
private static final Pattern PATH_NORMAL_PATTERN = Pattern.compile(PATH_NORMAL_REG_EX);
private static final Pattern PATH_VARIABLE_PATTERN = Pattern.compile(PATH_VARIABLE_REG_EX);

private final Pattern pattern;
private final String rowPath;
private final Map<Integer, String> staticPathMap = new HashMap<>();

@Builder
private DssRestPath(String path, Map<String, Class<? extends Object>> classMap) {
final String regex = getRegEx(path, Objects.nonNull(classMap) ? classMap : Collections.emptyMap());
this.pattern = Pattern.compile(regex);
this.rowPath = path;
logger.info("Regex: {}", regex);
}

private String getRegEx(String path, Map<String, Class<? extends Object>> classMap) {
final StringBuilder sb = new StringBuilder();

final List<String> pathElements = pathElements(path);
for (int i = 0; i < pathElements.size(); i++) {
final String pathElement = pathElements.get(i);
if (isPathVariablePattern(pathElement)) {
final String pathVariableName = pathElement.substring(1, pathElement.length() - 1);

final Class<? extends Object> tClass = classMap.getOrDefault(pathVariableName, String.class);
final String regEx = regExFromClassType(tClass);
sb.append(PATH_SEPARATOR_REG_EX).append(regEx);
} else {
sb
.append(PATH_SEPARATOR_REG_EX)
.append(PATH_NORMAL_REG_EX);
staticPathMap.put(i, pathElement);
}
}

return sb.append("$").toString();
}

public boolean match(String path) {
Objects.requireNonNull(pattern);
final String fixedPath = getFixedPath(path);
final Matcher matcher = pattern.matcher(fixedPath);
if (!matcher.find()) {
logger.debug("Path regEx not matched: {}", path);
return false;
}

logger.debug("Path regEx matched, try to check static path element: {}", path);
return matchStaticPath(fixedPath, staticPathMap);
}

private boolean matchStaticPath(String path, Map<Integer, String> staticPathMap) {
final List<String> pathElements = pathElements(path);
logger.debug("user request path: {}, expected path: {}", pathElements, rowPath);

for (Map.Entry<Integer, String> entry : staticPathMap.entrySet()) {
final int index = entry.getKey();
final String staticPathElement = entry.getValue();

if (index >= pathElements.size()) {
return false;
}

final String pathElement = pathElements.get(index);
if (!staticPathElement.equals(pathElement)) {
return false;
}
}

return true;
}

private static List<String> pathElements(String path) {
final List<String> list = new ArrayList<>();

final String fixedPath = getFixedPath(path);

final Matcher matcher = PATH_NORMAL_PATTERN.matcher(fixedPath);
while (matcher.find()) {
list.add(matcher.group());
}

return list;
}

private static boolean isPathVariablePattern(String pathElement) {
final Matcher matcher = PATH_VARIABLE_PATTERN.matcher(pathElement);
return matcher.find();
}

private static String regExFromClassType(Class<? extends Object> tClass) {
if (Number.class.isAssignableFrom(tClass)) {
return PATH_DIGITAL_REG_EX;
} else {
return PATH_NORMAL_REG_EX;
}
}

public static String getFixedPath(String path) {
if (path.contains("?")) {
return path.substring(0, path.indexOf('?'));
} else {
return path;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.ztkmkoo.dss.core.actor.rest.entity;

import akka.actor.typed.ActorRef;
import com.ztkmkoo.dss.core.message.rest.DssRestServiceActorCommand;
import lombok.Builder;
import lombok.Getter;

import java.io.Serializable;

/**
* Project: dss
* Created by: @ztkmkoo(ztkmkoo@gmail.com)
* Date: 20. 4. 18. 오전 4:09
*/
@Getter
public class DssRestPathComposition implements Serializable {
private static final long serialVersionUID = 6447860854358860483L;

private final DssRestPath dssRestPath;
private final ActorRef<DssRestServiceActorCommand> serviceActor;

@Builder
private DssRestPathComposition(DssRestPath dssRestPath, ActorRef<DssRestServiceActorCommand> serviceActor) {
this.dssRestPath = dssRestPath;
this.serviceActor = serviceActor;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.ztkmkoo.dss.core.actor.rest.util;

/**
* Project: dss
* Created by: @ztkmkoo(ztkmkoo@gmail.com)
* Date: 20. 4. 18. 오전 4:11
*/
public class DssRestPathCompositionUtils {

private DssRestPathCompositionUtils() {}
}
Loading

0 comments on commit 4c326c5

Please sign in to comment.