Skip to content

Commit 6d4fa01

Browse files
committed
OpenAPI: Initial implementation
1 parent bf4cafb commit 6d4fa01

23 files changed

+1070
-0
lines changed

modules/jooby-openapi/pom.xml

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5+
6+
<parent>
7+
<groupId>io.jooby</groupId>
8+
<artifactId>modules</artifactId>
9+
<version>2.6.1-SNAPSHOT</version>
10+
</parent>
11+
12+
<modelVersion>4.0.0</modelVersion>
13+
<artifactId>jooby-openapi</artifactId>
14+
15+
<dependencies>
16+
<dependency>
17+
<groupId>com.google.code.findbugs</groupId>
18+
<artifactId>jsr305</artifactId>
19+
<scope>provided</scope>
20+
</dependency>
21+
22+
<dependency>
23+
<groupId>io.jooby</groupId>
24+
<artifactId>jooby</artifactId>
25+
<version>${jooby.version}</version>
26+
</dependency>
27+
28+
<!-- https://mvnrepository.com/artifact/io.github.classgraph/classgraph -->
29+
<dependency>
30+
<groupId>io.github.classgraph</groupId>
31+
<artifactId>classgraph</artifactId>
32+
<version>4.8.60</version>
33+
</dependency>
34+
35+
<!-- ASM -->
36+
<dependency>
37+
<groupId>org.ow2.asm</groupId>
38+
<artifactId>asm</artifactId>
39+
<optional>true</optional>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.ow2.asm</groupId>
44+
<artifactId>asm-util</artifactId>
45+
<optional>true</optional>
46+
</dependency>
47+
48+
<!-- Test dependencies -->
49+
<dependency>
50+
<groupId>org.junit.jupiter</groupId>
51+
<artifactId>junit-jupiter-engine</artifactId>
52+
<scope>test</scope>
53+
</dependency>
54+
55+
<dependency>
56+
<groupId>org.jacoco</groupId>
57+
<artifactId>org.jacoco.agent</artifactId>
58+
<classifier>runtime</classifier>
59+
<scope>test</scope>
60+
</dependency>
61+
62+
<dependency>
63+
<groupId>org.slf4j</groupId>
64+
<artifactId>slf4j-simple</artifactId>
65+
<version>${slf4j.version}</version>
66+
<scope>test</scope>
67+
</dependency>
68+
69+
<!-- kotlin -->
70+
<dependency>
71+
<groupId>org.jetbrains.kotlin</groupId>
72+
<artifactId>kotlin-stdlib-jdk8</artifactId>
73+
<scope>test</scope>
74+
</dependency>
75+
76+
<dependency>
77+
<groupId>org.jetbrains.kotlin</groupId>
78+
<artifactId>kotlin-reflect</artifactId>
79+
<scope>test</scope>
80+
</dependency>
81+
82+
<dependency>
83+
<groupId>org.jetbrains.kotlinx</groupId>
84+
<artifactId>kotlinx-coroutines-core</artifactId>
85+
<scope>test</scope>
86+
</dependency>
87+
</dependencies>
88+
89+
<build>
90+
<plugins>
91+
<plugin>
92+
<artifactId>kotlin-maven-plugin</artifactId>
93+
<groupId>org.jetbrains.kotlin</groupId>
94+
<executions>
95+
<execution>
96+
<id>compile</id>
97+
<goals>
98+
<goal>compile</goal>
99+
</goals>
100+
<configuration>
101+
<sourceDirs>
102+
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
103+
<sourceDir>${project.basedir}/src/main/java</sourceDir>
104+
</sourceDirs>
105+
</configuration>
106+
</execution>
107+
<execution>
108+
<id>test-compile</id>
109+
<goals>
110+
<goal>test-compile</goal>
111+
</goals>
112+
<configuration>
113+
<sourceDirs>
114+
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
115+
<sourceDir>${project.basedir}/src/test/java</sourceDir>
116+
</sourceDirs>
117+
</configuration>
118+
</execution>
119+
</executions>
120+
</plugin>
121+
122+
<plugin>
123+
<groupId>org.apache.maven.plugins</groupId>
124+
<artifactId>maven-compiler-plugin</artifactId>
125+
<configuration>
126+
<proc>none</proc>
127+
</configuration>
128+
<executions>
129+
<execution>
130+
<id>default-compile</id>
131+
<phase>none</phase>
132+
</execution>
133+
<execution>
134+
<id>default-testCompile</id>
135+
<phase>none</phase>
136+
</execution>
137+
<execution>
138+
<id>java-compile</id>
139+
<phase>compile</phase>
140+
<goals>
141+
<goal>compile</goal>
142+
</goals>
143+
</execution>
144+
<execution>
145+
<id>java-test-compile</id>
146+
<phase>test-compile</phase>
147+
<goals>
148+
<goal>testCompile</goal>
149+
</goals>
150+
</execution>
151+
</executions>
152+
</plugin>
153+
</plugins>
154+
</build>
155+
</project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.jooby.internal.openapi;
2+
3+
import io.jooby.SneakyThrows;
4+
5+
import java.io.IOException;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
9+
public class ClassSource {
10+
private Path basedir;
11+
12+
public ClassSource(Path basedir) {
13+
this.basedir = basedir;
14+
}
15+
16+
public byte[] byteCode(String classname) {
17+
try {
18+
Path classpath = classpath(classname);
19+
return Files.readAllBytes(classpath);
20+
} catch (IOException x) {
21+
throw SneakyThrows.propagate(x);
22+
}
23+
}
24+
25+
private Path classpath(String classname) {
26+
Path path = basedir;
27+
String[] segments = classname.split("\\.");
28+
for (int i = 0; i < segments.length - 1; i++) {
29+
path = path.resolve(segments[i]);
30+
}
31+
path = path.resolve(segments[segments.length - 1] + ".class");
32+
return path;
33+
}
34+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package io.jooby.internal.openapi;
2+
3+
import org.objectweb.asm.ClassReader;
4+
import org.objectweb.asm.ClassVisitor;
5+
import org.objectweb.asm.MethodVisitor;
6+
import org.objectweb.asm.Opcodes;
7+
import org.objectweb.asm.Type;
8+
import org.objectweb.asm.tree.AbstractInsnNode;
9+
import org.objectweb.asm.tree.ClassNode;
10+
import org.objectweb.asm.util.ASMifier;
11+
import org.objectweb.asm.util.Printer;
12+
import org.objectweb.asm.util.TraceClassVisitor;
13+
14+
import java.io.PrintWriter;
15+
import java.util.HashMap;
16+
import java.util.HashSet;
17+
import java.util.Map;
18+
import java.util.Set;
19+
import java.util.function.Function;
20+
import java.util.stream.Stream;
21+
22+
public class ExecutionContext {
23+
public final ClassNode root;
24+
private final Type mainType;
25+
private final Map<Type, ClassNode> nodes = new HashMap<>();
26+
private final ClassSource source;
27+
private final Set<Object> instructions = new HashSet<>();
28+
private final boolean debug;
29+
30+
public ExecutionContext(ClassSource source, Type mainType, boolean debug) {
31+
this.mainType = mainType;
32+
this.source = source;
33+
this.debug = debug;
34+
this.root = newClassNode(mainType);
35+
}
36+
37+
public ClassNode classNode(Type type) {
38+
if (type.equals(mainType)) {
39+
return root;
40+
}
41+
return nodes.computeIfAbsent(type, this::newClassNode);
42+
}
43+
44+
private ClassNode newClassNode(Type type) {
45+
ClassReader reader = new ClassReader(source.byteCode(type.getClassName()));
46+
if (debug) {
47+
Printer printer = new ASMifier();
48+
PrintWriter output = new PrintWriter(System.out);
49+
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, printer, output);
50+
reader.accept(traceClassVisitor, 0);
51+
}
52+
ClassNode node = createClassVisitor(ClassNode::new);
53+
reader.accept(node, 0);
54+
return node;
55+
}
56+
57+
public Type getMainType() {
58+
return mainType;
59+
}
60+
61+
public <T extends ClassVisitor> T createClassVisitor(Function<Integer, T> factory) {
62+
return factory.apply(Opcodes.ASM7);
63+
}
64+
65+
public boolean isRouter(Type type) {
66+
return Stream.of(mainType, TypeFactory.JOOBY, TypeFactory.KOOBY, TypeFactory.ROUTER)
67+
.anyMatch(it -> it.equals(type));
68+
}
69+
70+
public boolean process(AbstractInsnNode instruction) {
71+
return instructions.add(instruction);
72+
}
73+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package io.jooby.internal.openapi;
2+
3+
import org.objectweb.asm.Handle;
4+
import org.objectweb.asm.tree.AbstractInsnNode;
5+
import org.objectweb.asm.tree.InvokeDynamicInsnNode;
6+
import org.objectweb.asm.tree.MethodInsnNode;
7+
import org.objectweb.asm.tree.VarInsnNode;
8+
9+
import java.util.Iterator;
10+
import java.util.Spliterator;
11+
import java.util.Spliterators;
12+
import java.util.function.Function;
13+
import java.util.function.Predicate;
14+
import java.util.stream.Stream;
15+
import java.util.stream.StreamSupport;
16+
17+
public class InsnNode {
18+
19+
private static class NodeIterator implements Iterator<org.objectweb.asm.tree.AbstractInsnNode> {
20+
21+
private AbstractInsnNode node;
22+
private Function<AbstractInsnNode, AbstractInsnNode> next;
23+
24+
public NodeIterator(final org.objectweb.asm.tree.AbstractInsnNode node,
25+
final Function<AbstractInsnNode, AbstractInsnNode> next) {
26+
this.node = node;
27+
this.next = next;
28+
}
29+
30+
@Override
31+
public boolean hasNext() {
32+
return node != null;
33+
}
34+
35+
@Override
36+
public org.objectweb.asm.tree.AbstractInsnNode next() {
37+
org.objectweb.asm.tree.AbstractInsnNode it = node;
38+
node = next.apply(node);
39+
return it;
40+
}
41+
}
42+
43+
public static <N extends AbstractInsnNode> Predicate<N> opcode(int opcode) {
44+
return n -> n.getOpcode() == opcode;
45+
}
46+
47+
public static <N extends AbstractInsnNode> Predicate<N> varInsn(int opcode, int var) {
48+
return n -> n.getOpcode() == opcode && ((VarInsnNode) n).var == var;
49+
}
50+
51+
public static Stream<AbstractInsnNode> prev(AbstractInsnNode node) {
52+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new NodeIterator(node,
53+
AbstractInsnNode::getPrevious), Spliterator.ORDERED), false);
54+
}
55+
56+
public static Stream<AbstractInsnNode> next(AbstractInsnNode node) {
57+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(new NodeIterator(node,
58+
AbstractInsnNode::getNext), Spliterator.ORDERED), false);
59+
}
60+
61+
public static String toString(InvokeDynamicInsnNode node) {
62+
Handle handle = (Handle) node.bsmArgs[1];
63+
return handle.getOwner() + "." + node.name + node.desc;
64+
}
65+
66+
public static String toString(MethodInsnNode node) {
67+
return node.owner + "." + node.name + node.desc;
68+
}
69+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.jooby.internal.openapi;
2+
3+
import org.objectweb.asm.tree.MethodNode;
4+
5+
public class RouteDescriptor {
6+
7+
private String pattern;
8+
9+
private MethodNode node;
10+
11+
public RouteDescriptor(String pattern, MethodNode node) {
12+
this.pattern = pattern;
13+
this.node = node;
14+
}
15+
16+
public MethodNode getNode() {
17+
return node;
18+
}
19+
20+
public String getPattern() {
21+
return pattern;
22+
}
23+
24+
public String toString() {
25+
return pattern;
26+
}
27+
}

0 commit comments

Comments
 (0)