├── docs ├── data │ └── .gitkeep ├── content │ ├── .gitkeep │ ├── index.md │ └── tutorial │ │ ├── index.md │ │ ├── integration.md │ │ ├── implementation.md │ │ ├── helloworld.md │ │ ├── setup.md │ │ └── merge-schema.md ├── layouts │ └── .gitkeep ├── static │ └── .gitkeep ├── themes │ └── .gitkeep ├── archetypes │ └── .gitkeep ├── config.toml └── deploy.sh ├── rpc-router ├── src │ ├── test │ │ ├── resources │ │ │ ├── config │ │ │ │ ├── rpc-router-empty.yml │ │ │ │ ├── client.keystore │ │ │ │ ├── server.keystore │ │ │ │ ├── client.truststore │ │ │ │ ├── server.truststore │ │ │ │ ├── rpc-router-true.yml │ │ │ │ ├── rpc-router-false-package.yml │ │ │ │ ├── rpc-router-multi-package.yml │ │ │ │ ├── values.yml │ │ │ │ └── handler.yml │ │ │ ├── logback-test.xml │ │ │ └── spec.yaml │ │ └── java │ │ │ └── com │ │ │ ├── networknt │ │ │ └── rpc │ │ │ │ └── router │ │ │ │ ├── SchemaHandlerTest.java │ │ │ │ ├── RpcStartupHookProviderTest.java │ │ │ │ ├── DeleteRuleHybridHandler.java │ │ │ │ ├── JsonHybridHandlerTest.java │ │ │ │ ├── TestServiceHybridHandler.java │ │ │ │ ├── TestServer.java │ │ │ │ ├── RpcRouterConfigTest.java │ │ │ │ └── RpcRouterTest.java │ │ │ └── other │ │ │ └── handler │ │ │ └── OtherHybridHandler.java │ └── main │ │ ├── java │ │ └── com │ │ │ └── networknt │ │ │ └── rpc │ │ │ ├── router │ │ │ ├── ServiceHandler.java │ │ │ ├── JsonHandler.java │ │ │ ├── RpcStartupHookProvider.java │ │ │ ├── RpcRouterConfig.java │ │ │ └── SchemaHandler.java │ │ │ └── HybridHandler.java │ │ └── resources │ │ └── config │ │ ├── rpc-router.yml │ │ ├── rpc-router.yaml │ │ └── rpc-router-schema.json └── pom.xml ├── .travis.yml ├── rpc-security ├── src │ ├── test │ │ ├── resources │ │ │ ├── config │ │ │ │ ├── rpc-router-empty.yml │ │ │ │ ├── client.keystore │ │ │ │ ├── client.truststore │ │ │ │ ├── server.keystore │ │ │ │ ├── server.truststore │ │ │ │ ├── rpc-router-true.yml │ │ │ │ ├── rpc-router-false-package.yml │ │ │ │ ├── values.yml │ │ │ │ └── handler.yml │ │ │ ├── logback-test.xml │ │ │ └── spec.yaml │ │ └── java │ │ │ └── com │ │ │ └── networknt │ │ │ └── rpc │ │ │ └── security │ │ │ ├── TestServiceHybridHandler.java │ │ │ ├── TestServer.java │ │ │ └── HybridUnifiedSecurityTest.java │ └── main │ │ └── java │ │ └── com │ │ └── networknt │ │ └── rpc │ │ └── security │ │ └── HybridJwtVerifyHandler.java └── pom.xml ├── .gitignore ├── .pre-commit-config.yaml ├── README.md ├── LICENSE ├── pom.xml └── CHANGELOG.md /docs/data/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/content/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/layouts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/themes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/archetypes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/rpc-router-empty.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: [] 3 | jsonPath: /api/json 4 | formPath: /api/form 5 | -------------------------------------------------------------------------------- /docs/content/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-06-04T09:34:18-04:00 3 | title: Introduction 4 | type: index 5 | --- 6 | 7 | This is the entry point. 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | cache: 3 | directories: 4 | - $HOME/.m2 5 | jdk: 6 | - openjdk11 7 | branches: 8 | only: 9 | - master 10 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/client.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-router/src/test/resources/config/client.keystore -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/server.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-router/src/test/resources/config/server.keystore -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/rpc-router-empty.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: [] 3 | jsonPath: /api/json 4 | formPath: /api/form 5 | resourcesBasePath: / 6 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/client.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-router/src/test/resources/config/client.truststore -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/server.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-router/src/test/resources/config/server.truststore -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/client.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-security/src/test/resources/config/client.keystore -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/client.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-security/src/test/resources/config/client.truststore -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/server.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-security/src/test/resources/config/server.keystore -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/server.truststore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/networknt/light-hybrid-4j/HEAD/rpc-security/src/test/resources/config/server.truststore -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/rpc-router-true.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: 3 | - com.networknt 4 | jsonPath: /api/json 5 | formPath: /api/form 6 | registerService: true 7 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/rpc-router-false-package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: 3 | - com.networknt 4 | jsonPath: /api/json 5 | formPath: /api/form 6 | registerService: false 7 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/rpc-router-true.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: com.networknt 3 | jsonPath: /api/json 4 | formPath: /api/form 5 | resourcesBasePath: / 6 | registerService: true 7 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/rpc-router-false-package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: com.networknt 3 | jsonPath: /api/json 4 | formPath: /api/form 5 | resourcesBasePath: / 6 | registerService: false 7 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/rpc-router-multi-package.yml: -------------------------------------------------------------------------------- 1 | --- 2 | handlerPackages: 3 | - com.networknt 4 | - com.other.handler 5 | jsonPath: /api/json 6 | formPath: /api/form 7 | registerService: true 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | bower_components/ 3 | node_modules/ 4 | dist/ 5 | .idea/ 6 | .tmp/ 7 | .project 8 | .classpath 9 | .settings 10 | .metadata/ 11 | *.iml 12 | *.log 13 | *.tmp 14 | *.zip 15 | *.bak 16 | *.versionsBackup 17 | 18 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 19 | hs_err_pid* 20 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/SchemaHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class SchemaHandlerTest { 7 | @Test 8 | public void testLoadSpecs() { 9 | SchemaHandler schemaHandler = new SchemaHandler(); 10 | Assert.assertNotNull(schemaHandler); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/router/ServiceHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | 6 | /** 7 | * This annotation is used to mark a class as a service handler. The id is used to identify the handler 8 | * 9 | * @author Steve Hu 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | public @interface ServiceHandler { 13 | String id() default ""; 14 | } 15 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v6.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-yaml 8 | - id: check-added-large-files 9 | - repo: https://github.com/networknt/pre-commit-hook-keyword 10 | rev: f17c4de14fc24420f6768c19cad06ba03af06d86 11 | hooks: 12 | - id: keywordscan 13 | args: ["--keywords=c3VubGlmZQ==,Y2liYw==,c3VuIGxpZmU="] 14 | types: ["text"] 15 | -------------------------------------------------------------------------------- /rpc-router/src/main/resources/config/rpc-router.yml: -------------------------------------------------------------------------------- 1 | # rpc-router configuration 2 | # The hybrid handler package names that is used for scanner during server startup. The more specific of package names, the faster to start 3 | # List the package prefixes for all handlers used. Leave an empty array to indicate wildcard (all packages) 4 | handlerPackages: ${rpc-router.handlerPackages:} 5 | # The JSON RPC API path 6 | jsonPath: ${rpc-router.jsonPath:/api/json} 7 | # The form RPC API path 8 | formPath: ${rpc-router.formPath:/api/form} 9 | # if we want to register all handlers as services to the Consul for client to discover 10 | registerService: ${rpc-router.registerService:false} 11 | -------------------------------------------------------------------------------- /rpc-router/src/main/resources/config/rpc-router.yaml: -------------------------------------------------------------------------------- 1 | # rpc-router configuration 2 | # The hybrid handler package names that is used for scanner during server startup. The more specific of package names, the faster to start 3 | # List the package prefixes for all handlers used. Leave an empty array to indicate wildcard (all packages) 4 | handlerPackages: ${rpc-router.handlerPackages:} 5 | # The JSON RPC API path 6 | jsonPath: ${rpc-router.jsonPath:/api/json} 7 | # The form RPC API path 8 | formPath: ${rpc-router.formPath:/api/form} 9 | # if we want to register all handlers as services to the Consul for client to discover 10 | registerService: ${rpc-router.registerService:false} 11 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/RpcStartupHookProviderTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.rpc.HybridHandler; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.Map; 8 | 9 | /** 10 | * Created by steve on 20/02/17. 11 | */ 12 | public class RpcStartupHookProviderTest { 13 | @Test 14 | public void testStartup() { 15 | RpcStartupHookProvider provider = new RpcStartupHookProvider(); 16 | provider.onStartup(); 17 | Map servicesMap = provider.serviceMap; 18 | Assert.assertTrue(servicesMap.size() > 0); 19 | System.out.println("serviceMap size = " + servicesMap.size()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/DeleteRuleHybridHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.rpc.HybridHandler; 4 | import io.undertow.server.HttpServerExchange; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | /** 11 | * Created by steve on 12/04/17. 12 | */ 13 | @ServiceHandler(id="lightapi.net/rule/deleteRule/0.1.0") 14 | public class DeleteRuleHybridHandler implements HybridHandler { 15 | static private final Logger logger = LoggerFactory.getLogger(DeleteRuleHybridHandler.class); 16 | 17 | @Override 18 | public ByteBuffer handle(HttpServerExchange exchange, Object input) { 19 | System.out.println("DeleteRuleHybridHandler is called"); 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/content/tutorial/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-04-27T09:38:08-04:00 3 | title: Tutorial 4 | --- 5 | 6 | 7 | 8 | The following tutorials will help users to get started on hybrid service and more will be added later. 9 | 10 | * [How to generate and initial hybrid microservice project](https://networknt.github.io/light-hybrid-4j/tutorial/setup/) 11 | 12 | * [How to develop, setup hybrid microservice](https://networknt.github.io/light-hybrid-4j/tutorial/implementation/) 13 | 14 | * [How to run and do integration test for hybrid microservice](https://networknt.github.io/light-hybrid-4j/tutorial/integration/) 15 | 16 | * [Hello World Example](https://networknt.github.io/light-hybrid-4j/tutorial/helloworld/) 17 | 18 | * [Multiple Services Example](https://networknt.github.io/light-hybrid-4j/tutorial/merge-schema/) 19 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/JsonHybridHandlerTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.utility.Util; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Created by steve on 12/04/17. 12 | */ 13 | public class JsonHybridHandlerTest { 14 | @Test 15 | public void testJsonServiceId() { 16 | Map map = new HashMap<>(); 17 | map.put("host", "www.networknt.com"); 18 | map.put("service", "account"); 19 | map.put("action", "retrieve"); 20 | map.put("version", "1.0.3"); 21 | 22 | SchemaHandler handler = new SchemaHandler(); 23 | String serviceId = Util.getServiceId(map); 24 | Assert.assertEquals("www.networknt.com/account/retrieve/1.0.3", serviceId); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/other/handler/OtherHybridHandler.java: -------------------------------------------------------------------------------- 1 | package com.other.handler; 2 | 3 | import com.networknt.rpc.HybridHandler; 4 | import com.networknt.rpc.router.ServiceHandler; 5 | import io.undertow.server.HttpServerExchange; 6 | import java.nio.ByteBuffer; 7 | import java.nio.charset.StandardCharsets; 8 | 9 | /** 10 | * This test handler exists to test the multiple package handler configuration. 11 | */ 12 | @ServiceHandler(id="lightapi.net/rule/deleteRule/0.1.0") 13 | public class OtherHybridHandler implements HybridHandler { 14 | 15 | @Override 16 | public ByteBuffer handle(HttpServerExchange exchange, Object input) { 17 | System.out.println("OtherHybridHandler is called with " + input); 18 | String message = "OK"; 19 | ByteBuffer buffer = ByteBuffer.allocateDirect(message.length()); 20 | buffer.put(message.getBytes(StandardCharsets.US_ASCII)); 21 | buffer.flip(); 22 | return buffer; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /rpc-router/src/main/resources/config/rpc-router-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema" : "http://json-schema.org/draft-07/schema#", 3 | "type" : "object", 4 | "required" : [ "handlerPackages", "jsonPath", "formPath", "registerService" ], 5 | "properties" : { 6 | "handlerPackages" : { 7 | "type" : "array", 8 | "description" : "The hybrid handler package names that is used for scanner during server startup. The more specific of package names, the faster to start\nList the package prefixes for all handlers used. Leave an empty array to indicate wildcard (all packages)", 9 | "items" : { 10 | "type" : "string" 11 | } 12 | }, 13 | "jsonPath" : { 14 | "type" : "string", 15 | "description" : "The JSON RPC API path", 16 | "default" : "/api/json" 17 | }, 18 | "formPath" : { 19 | "type" : "string", 20 | "description" : "The form RPC API path", 21 | "default" : "/api/form" 22 | }, 23 | "registerService" : { 24 | "type" : "boolean", 25 | "description" : "if we want to register all handlers as services to the Consul for client to discover" 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /rpc-security/src/test/java/com/networknt/rpc/security/TestServiceHybridHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.security; 2 | 3 | import com.networknt.rpc.HybridHandler; 4 | import com.networknt.rpc.router.ServiceHandler; 5 | import io.undertow.server.HttpServerExchange; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.UnsupportedEncodingException; 10 | import java.nio.ByteBuffer; 11 | import java.nio.charset.StandardCharsets; 12 | 13 | /** 14 | * Created by steve on 20/02/17. 15 | */ 16 | @ServiceHandler(id="lightapi.net/rule/deleteRule/0.1.0") 17 | public class TestServiceHybridHandler implements HybridHandler { 18 | static private final Logger logger = LoggerFactory.getLogger(TestServiceHybridHandler.class); 19 | 20 | @Override 21 | public ByteBuffer handle(HttpServerExchange exchange, Object input) { 22 | System.out.println("TestServiceHandler is called with " + input); 23 | String message = "OK"; 24 | ByteBuffer buffer = ByteBuffer.allocateDirect(message.length()); 25 | buffer.put(message.getBytes(StandardCharsets.US_ASCII)); 26 | buffer.flip(); 27 | return buffer; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/TestServiceHybridHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.rpc.HybridHandler; 4 | import io.undertow.server.HttpServerExchange; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import java.io.UnsupportedEncodingException; 9 | import java.nio.ByteBuffer; 10 | 11 | /** 12 | * Created by steve on 20/02/17. 13 | */ 14 | @ServiceHandler(id="lightapi.net/rule/deleteRule/0.1.0") 15 | public class TestServiceHybridHandler implements HybridHandler { 16 | static private final Logger logger = LoggerFactory.getLogger(TestServiceHybridHandler.class); 17 | 18 | @Override 19 | public ByteBuffer handle(HttpServerExchange exchange, Object input) { 20 | System.out.println("TestServiceHandler is called with " + input); 21 | String message = "OK"; 22 | ByteBuffer buffer = ByteBuffer.allocateDirect(message.length()); 23 | try { 24 | buffer.put(message.getBytes("US-ASCII")); 25 | } catch (UnsupportedEncodingException e) { 26 | logger.error("Exception:" + e.getMessage(), e); 27 | throw new RuntimeException(e); 28 | } 29 | buffer.flip(); 30 | return buffer; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/TestServer.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.server.Server; 4 | import com.networknt.server.ServerConfig; 5 | import org.junit.rules.ExternalResource; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | public class TestServer extends ExternalResource { 12 | static final Logger logger = LoggerFactory.getLogger(TestServer.class); 13 | 14 | private static final AtomicInteger refCount = new AtomicInteger(0); 15 | private static Server server; 16 | 17 | private static final TestServer instance = new TestServer(); 18 | 19 | public static TestServer getInstance () { 20 | return instance; 21 | } 22 | 23 | private TestServer() { 24 | 25 | } 26 | 27 | public ServerConfig getServerConfig() { 28 | return ServerConfig.getInstance(); 29 | } 30 | 31 | @Override 32 | protected void before() { 33 | try { 34 | if (refCount.get() == 0) { 35 | Server.start(); 36 | } 37 | } 38 | finally { 39 | refCount.getAndIncrement(); 40 | } 41 | } 42 | 43 | @Override 44 | protected void after() { 45 | refCount.getAndDecrement(); 46 | if (refCount.get() == 0) { 47 | Server.stop(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rpc-security/src/test/java/com/networknt/rpc/security/TestServer.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.security; 2 | 3 | import com.networknt.server.Server; 4 | import com.networknt.server.ServerConfig; 5 | import org.junit.rules.ExternalResource; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.util.concurrent.atomic.AtomicInteger; 10 | 11 | public class TestServer extends ExternalResource { 12 | static final Logger logger = LoggerFactory.getLogger(TestServer.class); 13 | 14 | private static final AtomicInteger refCount = new AtomicInteger(0); 15 | private static Server server; 16 | 17 | private static final TestServer instance = new TestServer(); 18 | 19 | public static TestServer getInstance () { 20 | return instance; 21 | } 22 | 23 | private TestServer() { 24 | 25 | } 26 | 27 | public ServerConfig getServerConfig() { 28 | return ServerConfig.getInstance(); 29 | } 30 | 31 | @Override 32 | protected void before() { 33 | try { 34 | if (refCount.get() == 0) { 35 | Server.start(); 36 | } 37 | } 38 | finally { 39 | refCount.getAndIncrement(); 40 | } 41 | } 42 | 43 | @Override 44 | protected void after() { 45 | refCount.getAndDecrement(); 46 | if (refCount.get() == 0) { 47 | Server.stop(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/values.yml: -------------------------------------------------------------------------------- 1 | # server.yml 2 | server.httpsPort: 49588 3 | 4 | # rpc-router.yml 5 | rpc-router.handlerPackages: 6 | - com.networknt 7 | - net.lightapi 8 | rpc-router.jsonPath: /portal/query 9 | 10 | # hybrid-security.yml 11 | hybrid-security.keyResolver: JsonWebKeySet 12 | 13 | # client.yml 14 | client.tokenKeyServerUrl: http://localhost:7082 15 | client.tokenKeyUri: /oauth2/N2CMw0HGQXeLvC1wBfln2A/keys 16 | 17 | # service.yml 18 | service.singletons: 19 | - com.networknt.registry.URL: 20 | - com.networknt.registry.URLImpl 21 | - com.networknt.consul.client.ConsulClient: 22 | - com.networknt.consul.client.ConsulClientImpl 23 | - com.networknt.registry.Registry: 24 | - com.networknt.consul.ConsulRegistry 25 | - com.networknt.balance.LoadBalance: 26 | - com.networknt.balance.RoundRobinLoadBalance 27 | - com.networknt.cluster.Cluster: 28 | - com.networknt.cluster.LightCluster 29 | # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. 30 | - com.networknt.server.StartupHookProvider: 31 | # registry all service handlers by from annotations 32 | - com.networknt.rpc.router.RpcStartupHookProvider 33 | # ShutdownHookProvider implementations, there are one to many and they are called in the same sequence defined. 34 | # - com.networknt.server.ShutdownHookProvider: 35 | # - com.networknt.server.Test1ShutdownHook 36 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/values.yml: -------------------------------------------------------------------------------- 1 | # server.yml 2 | server.httpsPort: 49588 3 | 4 | # rpc-router.yml 5 | rpc-router.handlerPackages: com.networknt 6 | 7 | # hybrid-security.yml 8 | hybrid-security.keyResolver: JsonWebKeySet 9 | 10 | # client.yml 11 | client.tokenKeyServerUrl: http://localhost:7082 12 | client.tokenKeyUri: /oauth2/N2CMw0HGQXeLvC1wBfln2A/keys 13 | 14 | # service.yml 15 | service.singletons: 16 | - com.networknt.registry.URL: 17 | - com.networknt.registry.URLImpl 18 | - com.networknt.consul.client.ConsulClient: 19 | - com.networknt.consul.client.ConsulClientImpl 20 | - com.networknt.registry.Registry: 21 | - com.networknt.consul.ConsulRegistry 22 | - com.networknt.balance.LoadBalance: 23 | - com.networknt.balance.RoundRobinLoadBalance 24 | - com.networknt.cluster.Cluster: 25 | - com.networknt.cluster.LightCluster 26 | # StartupHookProvider implementations, there are one to many and they are called in the same sequence defined. 27 | - com.networknt.server.StartupHookProvider: 28 | # registry all service handlers by from annotations 29 | - com.networknt.rpc.router.RpcStartupHookProvider 30 | # ShutdownHookProvider implementations, there are one to many and they are called in the same sequence defined. 31 | # - com.networknt.server.ShutdownHookProvider: 32 | # - com.networknt.server.Test1ShutdownHook 33 | 34 | # unified-security.yml 35 | unified-security.pathPrefixAuths: 36 | - prefix: /api/json 37 | basic: true 38 | jwt: true 39 | apikey: true 40 | -------------------------------------------------------------------------------- /docs/content/tutorial/integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-10-12T17:06:30-04:00 3 | title: Integration Test 4 | --- 5 | 6 | ## Integration Test 7 | 8 | 1. Copy hybrid serivce jar files into docker service folder 9 | 10 | 2. Go to light-eventuate-4j root folder, run docker-compose for event store: 11 | 12 | cd light-eventuate-4j 13 | docker-compose up 14 | 15 | The docker compose will start docker images for eventuate event store: 16 | -- zookeeper 17 | --kafka 18 | --mysql 19 | 20 | 3. Run services: 21 | 22 | docker-compose -f docker-compose-service.yml up 23 | 24 | This command will start command and query side hybrid services 25 | 26 | 27 | 4. Test on command side to create and publish events: 28 | 29 | Use postmand, set post request: 30 | 31 | URL: http://localhost:8083/api/json 32 | 33 | content: 34 | { 35 | "host": "lightapi.net", 36 | "service":"todo", 37 | "action":"create", 38 | "version":"0.1.0", 39 | "title": " this is the test event from postman ", 40 | "completed": false, 41 | "order": 0 42 | } 43 | 44 | 5. Test on query side to subscribe and process events: 45 | 46 | Use postmand, set post request: 47 | 48 | URL: http://localhost:8082/api/json 49 | 50 | content: 51 | 52 | { 53 | "host": "lightapi.net", 54 | "service":"todo", 55 | "action":"gettodos", 56 | "version":"0.1.0" 57 | 58 | } 59 | 60 | The event will be subscrible and processed by saving to local TODO table and will get by query side service and send back in the resposne: 61 | 62 | { 63 | "message": [ 64 | { 65 | "0000015c7e109e77-acde480011220000": { 66 | "title": " this is the test event from postman", 67 | "completed": false, 68 | "order": 0 69 | } 70 | } 71 | 72 | 73 | 74 | 75 | # End 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A hybrid between monolith and microservices to take advantages of both. 2 | 3 | [Stack Overflow](https://stackoverflow.com/questions/tagged/light-4j) | 4 | [Google Group](https://groups.google.com/forum/#!forum/light-4j) | 5 | [Gitter Chat](https://gitter.im/networknt/light-hybrid-4j) | 6 | [Subreddit](https://www.reddit.com/r/lightapi/) | 7 | [Youtube Channel](https://www.youtube.com/channel/UCHCRMWJVXw8iB7zKxF55Byw) | 8 | [Documentation](https://doc.networknt.com/style/light-hybrid-4j/) | 9 | [Contribution Guide](https://doc.networknt.com/contribute/) | 10 | 11 | [![Build Status](https://travis-ci.org/networknt/light-hybrid-4j.svg?branch=master)](https://travis-ci.org/networknt/light-hybrid-4j) 12 | 13 | ## Why Hybrid 14 | 15 | ### Switching to Microservices from Java EE is hard 16 | 17 | I've been working with my clients on microservies for several years and 18 | I feel the pain for Java EE developers to switch to microservice architecture. 19 | Almost everything they've learned in Java EE is anti-pattern in Microservices 20 | and it is a big paradigm shift. It would be easy for them to move step by step 21 | to fully adopt microservices. 22 | 23 | ### Most companies don't need Internet scale 24 | 25 | For most companies, pure microservices might not be suitable as the 26 | infrastructure and management overhead is too high initially. For most of 27 | organizations, they don't need Internet scale and they are not in a size to adopt 28 | microservices before their application grows to the right size. For startup companies, 29 | it is wise to build something still monolithic but can be split up when the application 30 | grows bigger. Most companies won't even need Internet scale for the entire life cycle 31 | of their application so they can enjoy the smaller foot print as well as low supporting 32 | cost with all the benefits of microserivces architecture. 33 | 34 | ### Restful API is not efficient 35 | 36 | REST was introduced before Single Page Application on the browser and it is heavily rely 37 | on the URI schema for client/server communication. In URI, all query parameters and path 38 | parameters will be converted to string and then converted back to the right type based on 39 | swagger specification. This is very time consuming and make the swagger spec. so complicated. 40 | 41 | By using Hybrid framework, you just need to define the JSON schema or other binary protocol 42 | IDL for your request and then validate the request again it. All the communication is based on 43 | JSON object or binary without data conversion on the server side. 44 | -------------------------------------------------------------------------------- /docs/config.toml: -------------------------------------------------------------------------------- 1 | baseurl = "https://networknt.github.io/light-hybrid-4j/" 2 | languageCode = "en-us" 3 | title = "Light Hybrid 4J" 4 | theme = "hugo-material-docs" 5 | metadataformat = "yaml" 6 | canonifyurls = true 7 | # Enable Google Analytics by entering your tracking id 8 | googleAnalytics = "" 9 | 10 | [params] 11 | # General information 12 | author = "Steve Hu" 13 | description = "Light Hybrid 4J Docs" 14 | copyright = "Released under the MIT license" 15 | 16 | # Repository 17 | provider = "GitHub" 18 | repo_url = "https://github.com/networknt/light-hybrid-4j" 19 | 20 | version = "1.5.4" 21 | logo = "images/logo.png" 22 | favicon = "" 23 | 24 | permalink = "#" 25 | 26 | # Custom assets 27 | custom_css = [] 28 | custom_js = [] 29 | 30 | # Syntax highlighting theme 31 | highlight_css = "" 32 | 33 | [params.palette] 34 | primary = "red" 35 | accent = "teal" 36 | 37 | [params.font] 38 | text = "Ubuntu" 39 | code = "Ubuntu Mono" 40 | 41 | 42 | [social] 43 | twitter = "" 44 | github = "stevehu" 45 | email = "stevehu@gmail.com" 46 | 47 | 48 | [[menu.main]] 49 | name = "Introduction" 50 | url = "/index.html" 51 | weight = 0 52 | 53 | [[menu.main]] 54 | name = "Getting started" 55 | url = "getting-started/" 56 | weight = 10 57 | 58 | 59 | [[menu.main]] 60 | name = "Architecture" 61 | url = "architecture/" 62 | weight = 20 63 | 64 | [[menu.main]] 65 | name = "Design" 66 | url = "design/" 67 | weight = 25 68 | 69 | [[menu.main]] 70 | name = "Middleware" 71 | url = "middleware/" 72 | weight = 30 73 | 74 | [[menu.main]] 75 | name = "Other Components" 76 | url = "other/" 77 | weight = 30 78 | 79 | [[menu.main]] 80 | name = "DevOps" 81 | url = "devops/" 82 | weight = 35 83 | 84 | [[menu.main]] 85 | name = "Tutorials" 86 | url = "tutorial/" 87 | weight = 40 88 | 89 | [[menu.main]] 90 | name = "Examples" 91 | url = "example/" 92 | weight = 45 93 | 94 | [[menu.main]] 95 | name = "Issues & Help" 96 | url = "https://github.com/networknt/light-4j/issues" 97 | weight = 60 98 | 99 | [[menu.main]] 100 | name = "Roadmap" 101 | url = "roadmap/" 102 | weight = 70 103 | 104 | [[menu.main]] 105 | name = "License" 106 | url = "https://www.apache.org/licenses/LICENSE-2.0" 107 | weight = 80 108 | 109 | 110 | [blackfriday] 111 | smartypants = true 112 | fractions = true 113 | smartDashes = true 114 | plainIDAnchors = true 115 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | TODO create logger for audit only. 20 | http://stackoverflow.com/questions/2488558/logback-to-log-different-messages-to-two-files 21 | 22 | PROFILER 23 | 24 | NEUTRAL 25 | 26 | 27 | 28 | 30 | 31 | %d{HH:mm:ss.SSS} [%thread] %-5marker %-5level %logger{36} - %msg%n 32 | 33 | 34 | 35 | 36 | target/test.log 37 | false 38 | 39 | %d{HH:mm:ss.SSS} [%thread] %-5level %class{36}:%L %M - %msg%n 40 | 41 | 42 | 43 | 44 | 45 | target/audit.log 46 | 47 | %-5level [%thread] %date{ISO8601} %F:%L - %msg%n 48 | true 49 | 50 | 51 | target/audit.log.%i.zip 52 | 1 53 | 5 54 | 55 | 56 | 200MB 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | TODO create logger for audit only. 20 | http://stackoverflow.com/questions/2488558/logback-to-log-different-messages-to-two-files 21 | 22 | PROFILER 23 | 24 | NEUTRAL 25 | 26 | 27 | 28 | 30 | 31 | %d{HH:mm:ss.SSS} [%thread] %-5marker %-5level %logger{36} - %msg%n 32 | 33 | 34 | 35 | 36 | target/test.log 37 | false 38 | 39 | %d{HH:mm:ss.SSS} [%thread] %-5level %class{36}:%L %M - %msg%n 40 | 41 | 42 | 43 | 44 | 45 | target/audit.log 46 | 47 | %-5level [%thread] %date{ISO8601} %F:%L - %msg%n 48 | true 49 | 50 | 51 | target/audit.log.%i.zip 52 | 1 53 | 5 54 | 55 | 56 | 200MB 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /rpc-security/src/main/java/com/networknt/rpc/security/HybridJwtVerifyHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.security; 2 | 3 | import com.networknt.config.Config; 4 | import com.networknt.httpstring.AttachmentConstants; 5 | import com.networknt.security.AbstractJwtVerifyHandler; 6 | import com.networknt.security.JwtVerifier; 7 | import com.networknt.security.SecurityConfig; 8 | import com.networknt.utility.Constants; 9 | import com.networknt.utility.ModuleRegistry; 10 | import io.undertow.server.HttpServerExchange; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | public class HybridJwtVerifyHandler extends AbstractJwtVerifyHandler { 18 | static final Logger logger = LoggerFactory.getLogger(HybridJwtVerifyHandler.class); 19 | 20 | public HybridJwtVerifyHandler() { 21 | // at this moment, we assume that the OpenApiHandler is fully loaded with a single spec or multiple specs. 22 | // And the basePath is the correct one from the OpenApiHandler helper or helperMap if multiple is used. 23 | config = SecurityConfig.load(); 24 | jwtVerifier = new JwtVerifier(config); 25 | } 26 | 27 | @Override 28 | public boolean isSkipAuth(HttpServerExchange exchange) { 29 | // check if the request path is in the skipPathPrefixes list 30 | String reqPath = exchange.getRequestPath(); 31 | if (config.getSkipPathPrefixes() != null && config.getSkipPathPrefixes().stream().anyMatch(reqPath::startsWith)) { 32 | if(logger.isTraceEnabled()) logger.trace("Skip auth base on skipPathPrefixes for {}", reqPath); 33 | return true; 34 | } 35 | // check if the service has skipAuth flag set to true in the schema. 36 | Map auditInfo = exchange.getAttachment(AttachmentConstants.AUDIT_INFO); 37 | Map serviceMap = (Map)auditInfo.get(Constants.HYBRID_SERVICE_MAP); 38 | Boolean skipAuth = (Boolean)serviceMap.get("skipAuth"); 39 | if(skipAuth != null) { 40 | return skipAuth; 41 | } 42 | return false; 43 | } 44 | 45 | @Override 46 | public List getSpecScopes(HttpServerExchange exchange, Map auditInfo) throws Exception { 47 | Map serviceMap = (Map)auditInfo.get(Constants.HYBRID_SERVICE_MAP); 48 | String scope = (String)serviceMap.get("scope"); 49 | if(scope != null) { 50 | return List.of(scope); 51 | } else { 52 | return null; 53 | } 54 | } 55 | 56 | @Override 57 | public boolean isEnabled() { 58 | return config.isEnableVerifyJwt(); 59 | } 60 | 61 | @Override 62 | public void register() { 63 | ModuleRegistry.registerModule(SecurityConfig.CONFIG_NAME, HybridJwtVerifyHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(SecurityConfig.CONFIG_NAME), null); 64 | } 65 | 66 | @Override 67 | public void reload() { 68 | config.reload(); 69 | jwtVerifier = new JwtVerifier(config); 70 | ModuleRegistry.registerModule(SecurityConfig.CONFIG_NAME, HybridJwtVerifyHandler.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(SecurityConfig.CONFIG_NAME), null); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/content/tutorial/implementation.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-10-12T17:05:47-04:00 3 | title: Unit Test 4 | --- 5 | 6 | ## Implementation service 7 | 8 | 9 | 1 change maven pom file for required dependency. For todo-list eventuate example project, following are required dependencies: 10 | 11 | 12 | com.networknt 13 | eventuate-common 14 | 15 | 16 | com.networknt 17 | eventuate-client 18 | 19 | 20 | com.networknt 21 | eventuate-todo-common 22 | 23 | 24 | com.networknt 25 | eventuate-todo-command 26 | 27 | 28 | 29 | 2. Implement service handler classes and test classes: 30 | 31 | To consist with Restful microservice, Hybrid service use RPC call with Json format input and output. In the handler class, we pass the input object as below: 32 | 33 | ------------------------------------------------------------------------------------------------------------- 34 | JsonNode inputPara =Config.getInstance().getMapper().valueToTree(input); 35 | 36 | TodoInfo todo = new TodoInfo(); 37 | todo.setTitle(inputPara.findPath("title").asText()); 38 | todo.setCompleted(inputPara.findPath("completed").asBoolean()); 39 | todo.setOrder(inputPara.findPath("order").asInt()); 40 | --------------------------------------------------------------------------------------------------------------- 41 | 42 | Please refer github source code for detail implementation: 43 | 44 | https://github.com/networknt/light-eventuate-example/tree/master/todo-list/command-hybridservice 45 | https://github.com/networknt/light-eventuate-example/tree/master/todo-list/query-hybridservice 46 | 47 | 48 | 49 | ## Setup service 50 | 51 | 1. Build project: 52 | cd light-eventuate-example/todo-list 53 | mvn clean install 54 | 55 | 56 | 2. Go to command server module in light-eventaute-4j project. 57 | 58 | change com.networknt.server.StartupHookProvider by adding following line: 59 | 60 | # registry all service handlers by from annotations 61 | com.networknt.rpc.router.RpcStartupHookProvider 62 | com.networknt.eventuate.cdcservice.CdcStartupHookProvider 63 | 64 | RpcStartupHookProvider is used for hybrid service to register and process RPC handlers 65 | CdcStartupHookProvider: eventuate sourcing run CDC service on command side to convert events in event store 66 | 67 | 3. Go to query server module in light-eventaute-4j project. 68 | 69 | change com.networknt.server.StartupHookProvider by adding following line: 70 | 71 | # config event handle registration 72 | com.networknt.eventuate.client.EventuateClientStartupHookProvider 73 | 74 | EventuateClientStartupHookProvider: eventatuate event handler startup provide. query side service need run defined event handler to process event. 75 | 76 | 4. Prepare resource config in docker folder 77 | 78 | Add and modify required config files into light/eventuate-4j/docker folder for testing 79 | 80 | Please refer source in github: 81 | https://github.com/networknt/light-eventuate-4j/tree/master/docker 82 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/router/JsonHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.handler.LightHttpHandler; 4 | import com.networknt.httpstring.AttachmentConstants; 5 | import com.networknt.rpc.HybridHandler; 6 | import com.networknt.server.ServerConfig; 7 | import com.networknt.utility.Constants; 8 | import io.undertow.server.DirectByteBufferDeallocator; 9 | import io.undertow.server.HttpServerExchange; 10 | import io.undertow.util.StatusCodes; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | import java.nio.ByteBuffer; 15 | import java.util.Map; 16 | 17 | /** 18 | * This is the entry handler for json api, it will parse the JSON request body and 19 | * construct service id and then calls the service handler to get the final response. 20 | * 21 | * @author Steve Hu 22 | */ 23 | public class JsonHandler implements LightHttpHandler { 24 | 25 | static final String STATUS_HANDLER_NOT_FOUND = "ERR11200"; 26 | 27 | static private final Logger LOG = LoggerFactory.getLogger(JsonHandler.class); 28 | 29 | @Override 30 | public void handleRequest(HttpServerExchange exchange) throws Exception { 31 | LOG.trace("JsonHandler is called"); 32 | 33 | // validation and security have been done already. The SchemaHandler should have already 34 | // parsed the body and set the auditInfo attachment. 35 | Map auditInfo = exchange.getAttachment(AttachmentConstants.AUDIT_INFO); 36 | final String serviceId; 37 | final ServerConfig serverConfig = ServerConfig.getInstance(); 38 | 39 | // get the serviceId from the auditInfo attachment. 40 | if (auditInfo != null) { 41 | serviceId = (String)auditInfo.get(Constants.ENDPOINT_STRING); 42 | if(serviceId == null) { 43 | setExchangeStatus(exchange, STATUS_HANDLER_NOT_FOUND); 44 | return; 45 | } 46 | 47 | // if the auditInfo is null, then get the serviceId from the serverConfig. 48 | } else if (serverConfig != null) { 49 | serviceId = serverConfig.getServiceId(); 50 | 51 | // if the serverConfig is null, then set the exchange status to STATUS_HANDLER_NOT_FOUND. 52 | } else { 53 | setExchangeStatus(exchange, STATUS_HANDLER_NOT_FOUND); 54 | return; 55 | } 56 | 57 | LOG.trace("serviceId = {}", serviceId); 58 | 59 | HybridHandler handler = RpcStartupHookProvider.serviceMap.get(serviceId); 60 | Map data = (Map)auditInfo.get(Constants.HYBRID_SERVICE_DATA); 61 | ByteBuffer result = handler.handle(exchange, data); 62 | 63 | if(result == null) { 64 | 65 | // there is nothing returned from the handler. 66 | exchange.setStatusCode(StatusCodes.OK); 67 | exchange.endExchange(); 68 | 69 | } else { 70 | 71 | // we are expecting the handler set the statusCode if there is an error. 72 | // if there is no status code, default 200 will be used. 73 | exchange.addExchangeCompleteListener((exchange1, nextListener) -> { 74 | try { 75 | DirectByteBufferDeallocator.free(result); 76 | } finally { 77 | nextListener.proceed(); 78 | } 79 | }); 80 | exchange.getResponseSender().send(result); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/router/RpcStartupHookProvider.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.config.Config; 4 | import com.networknt.resource.PathResourceProvider; 5 | import com.networknt.resource.PredicatedHandlersProvider; 6 | import com.networknt.rpc.HybridHandler; 7 | import com.networknt.server.Server; 8 | import com.networknt.server.StartupHookProvider; 9 | import com.networknt.service.SingletonServiceFactory; 10 | import com.networknt.utility.ModuleRegistry; 11 | import io.github.classgraph.ClassGraph; 12 | import io.github.classgraph.ScanResult; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | 16 | import java.util.Arrays; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * Register all the service handlers by the annotation from jars in classpath. 23 | * If you only want to scan services from a specific package, please update rpc-router.yml 24 | * Normally, you would put all the service into something com.yourcompany.xxx to speed up 25 | * the scanning. 26 | * 27 | * @author Steve Hu 28 | */ 29 | public class RpcStartupHookProvider implements StartupHookProvider { 30 | static final Logger logger = LoggerFactory.getLogger(RpcStartupHookProvider.class); 31 | static RpcRouterConfig config; 32 | 33 | public static final Map serviceMap = new HashMap<>(); 34 | public static PathResourceProvider[] pathResourceProviders; 35 | public static PredicatedHandlersProvider[] predicatedHandlersProviders; 36 | public RpcStartupHookProvider() { 37 | logger.info("RpcStartupHookProvider is constructed"); 38 | config = RpcRouterConfig.load(); 39 | ModuleRegistry.registerModule(RpcRouterConfig.CONFIG_NAME, RpcStartupHookProvider.class.getName(), Config.getNoneDecryptedInstance().getJsonMapConfigNoCache(RpcRouterConfig.CONFIG_NAME), null); 40 | } 41 | 42 | @Override 43 | public void onStartup() { 44 | logger.debug("Handler scanning package = {}", config.getHandlerPackages()); 45 | 46 | final var packages = config.getHandlerPackages().toArray(new String[0]); 47 | System.out.println("packages: " + Arrays.toString(packages)); 48 | 49 | // lookup all ServiceHandler and register them to handle request 50 | List handlers; 51 | try (ScanResult scanResult = new ClassGraph() 52 | .acceptPackages(packages) 53 | .enableAllInfo() 54 | .scan()) { 55 | 56 | handlers = scanResult 57 | .getClassesWithAnnotation(ServiceHandler.class.getName()) 58 | .getNames(); 59 | } 60 | logger.debug("RpcStartupHookProvider.onStartup handlers size = {}", handlers.size()); 61 | 62 | // for each handler, create instance and register. 63 | for(String className: handlers) { 64 | 65 | try { 66 | Class handler = Class.forName(className); 67 | ServiceHandler a = (ServiceHandler)handler.getAnnotation(ServiceHandler.class); 68 | serviceMap.put(a.id(), (HybridHandler)handler.getConstructor().newInstance()); 69 | 70 | logger.debug("RpcStartupHookProvider add id {} maps to {}", a.id(), className); 71 | 72 | if(config.isRegisterService()) 73 | Server.serviceIds.add(a.id().replace('/', '.')); 74 | 75 | } catch (Exception e) { 76 | logger.error("Exception:", e); 77 | } 78 | } 79 | 80 | pathResourceProviders = SingletonServiceFactory.getBeans(PathResourceProvider.class); 81 | predicatedHandlersProviders = SingletonServiceFactory.getBeans(PredicatedHandlersProvider.class); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/RpcRouterConfigTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.config.Config; 4 | import io.github.classgraph.ClassGraph; 5 | import io.github.classgraph.ScanResult; 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | import java.util.List; 10 | 11 | public class RpcRouterConfigTest { 12 | @Test 13 | public void testHandlerPackage() { 14 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(RpcRouterConfig.CONFIG_NAME, RpcRouterConfig.class); 15 | Assert.assertTrue(config.getHandlerPackages().size() == 2); 16 | } 17 | 18 | @Test 19 | public void testRegisterServiceTrue() { 20 | String configName = "rpc-router-true"; 21 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 22 | Assert.assertTrue(config.isRegisterService()); 23 | } 24 | 25 | @Test 26 | public void testRegisterServiceFalse() { 27 | String configName = "rpc-router-false-package"; 28 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 29 | Assert.assertFalse(config.isRegisterService()); 30 | } 31 | 32 | @Test 33 | public void testRegisterServiceEmpty() { 34 | String configName = "rpc-router-empty"; 35 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 36 | Assert.assertFalse(config.isRegisterService()); 37 | } 38 | 39 | @Test 40 | public void testHandlerPackageEmpty() { 41 | String configName = "rpc-router-empty"; 42 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 43 | List handlers; 44 | final var handler_packages = config.getHandlerPackages().toArray(new String[0]); 45 | try (ScanResult scanResult = new ClassGraph() 46 | .acceptPackages(handler_packages) 47 | .enableAllInfo() 48 | .scan()) { 49 | handlers = scanResult 50 | .getClassesWithAnnotation(ServiceHandler.class.getName()) 51 | .getNames(); 52 | } 53 | Assert.assertFalse(handlers.isEmpty()); 54 | } 55 | 56 | @Test 57 | public void testHandlerPackageSingle() { 58 | String configName = "rpc-router-false-package"; 59 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 60 | List handlers; 61 | final var handler_packages = config.getHandlerPackages().toArray(new String[0]); 62 | try (ScanResult scanResult = new ClassGraph() 63 | .acceptPackages(handler_packages) 64 | .enableAllInfo() 65 | .scan()) { 66 | handlers = scanResult 67 | .getClassesWithAnnotation(ServiceHandler.class.getName()) 68 | .getNames(); 69 | } 70 | Assert.assertFalse(handlers.isEmpty()); 71 | } 72 | 73 | @Test 74 | public void testMultiplePackageHandlers() { 75 | String configName = "rpc-router-multi-package"; 76 | RpcRouterConfig config = (RpcRouterConfig) Config.getInstance().getJsonObjectConfig(configName, RpcRouterConfig.class); 77 | List handlers; 78 | final var handler_packages = config.getHandlerPackages().toArray(new String[0]); 79 | try (ScanResult scanResult = new ClassGraph() 80 | .acceptPackages(handler_packages) 81 | .enableAllInfo() 82 | .scan()) { 83 | handlers = scanResult 84 | .getClassesWithAnnotation(ServiceHandler.class.getName()) 85 | .getNames(); 86 | } 87 | // 3 handlers are expected. 88 | // - DeleteRuleHybridHandler (com.networknt package) 89 | // - TestServiceHybridHandler (com.networknt package) 90 | // - OtherHybridHandler (com.other.handler package) 91 | Assert.assertEquals(3, handlers.size()); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/spec.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | host: lightapi.net 3 | service: rule 4 | action: 5 | - name: createRule 6 | version: 0.1.0 7 | handler: CreateRule 8 | scope: portal.w 9 | request: 10 | schema: 11 | $ref: "#/schemas/createRuleRequest" 12 | example: 13 | hostId: "host123" 14 | ruleId: "rule456" 15 | ruleName: "My New Rule" 16 | ruleVersion: "1.0" 17 | ruleType: "Authorization" 18 | ruleGroup: "Security" 19 | ruleDesc: "This is a rule description for my new rule" 20 | ruleOwner: "admin" 21 | common: "Y" 22 | conditions: 23 | - field: "user_type" 24 | operator: "equals" 25 | value: "employee" 26 | - field: "department" 27 | operator: "contains" 28 | value: "IT" 29 | - name: updateRule 30 | version: 0.1.0 31 | handler: UpdateRule 32 | scope: portal.w 33 | request: 34 | schema: 35 | $ref: "#/schemas/updateRuleRequest" 36 | example: 37 | ruleId: "rule456" 38 | ruleName: "Updated Rule Name" 39 | ruleVersion: "1.1" 40 | ruleType: "Validation" 41 | ruleGroup: "Compliance" 42 | ruleDesc: "Updated rule description" 43 | ruleOwner: "manager" 44 | common: "N" 45 | conditions: 46 | - field: "status" 47 | operator: "not_equals" 48 | value: "pending" 49 | 50 | - name: deleteRule 51 | version: 0.1.0 52 | handler: DeleteRule 53 | scope: portal.w 54 | request: 55 | schema: 56 | $ref: "#/schemas/deleteRuleRequest" 57 | example: 58 | hostId: "host123" 59 | ruleId: "rule456" 60 | 61 | schemas: 62 | createRuleRequest: 63 | title: Create Rule 64 | type: object 65 | properties: 66 | hostId: 67 | type: string 68 | description: Host Id 69 | ruleId: 70 | type: string 71 | description: Rule id 72 | ruleName: 73 | type: string 74 | description: Rule Name 75 | ruleVersion: 76 | type: string 77 | description: Rule Version 78 | ruleType: 79 | type: string 80 | description: Rule Type 81 | ruleGroup: 82 | type: string 83 | description: Rule Group 84 | ruleDesc: 85 | type: string 86 | description: Rule desc 87 | ruleOwner: 88 | type: string 89 | description: Rule Owner 90 | common: 91 | type: string 92 | enum: 93 | - Y 94 | - N 95 | description: Common 96 | conditions: 97 | type: array 98 | items: 99 | type: object 100 | description: Rule Conditions 101 | required: 102 | - hostId 103 | - ruleId 104 | - ruleName 105 | - ruleVersion 106 | - ruleType 107 | - ruleOwner 108 | - common 109 | - conditions 110 | updateRuleRequest: 111 | title: Update Rule 112 | type: object 113 | properties: 114 | ruleId: 115 | type: string 116 | description: Rule id 117 | ruleName: 118 | type: string 119 | description: Rule Name 120 | ruleVersion: 121 | type: string 122 | description: Rule Version 123 | ruleType: 124 | type: string 125 | description: Rule Type 126 | ruleGroup: 127 | type: string 128 | description: Rule Group 129 | ruleDesc: 130 | type: string 131 | description: Rule Desc 132 | ruleOwner: 133 | type: string 134 | description: Rule Owner 135 | common: 136 | type: string 137 | description: Common Flag 138 | conditions: 139 | type: array 140 | items: 141 | type: object 142 | description: Rule Conditions 143 | required: 144 | - ruleId 145 | - ruleName 146 | - ruleVersion 147 | - ruleType 148 | - ruleOwner 149 | - common 150 | - conditions 151 | deleteRuleRequest: 152 | title: Delete Rule 153 | type: object 154 | properties: 155 | hostId: 156 | type: string 157 | description: Host Id 158 | ruleId: 159 | type: string 160 | description: Rule Id 161 | required: 162 | - hostId 163 | - ruleId 164 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/spec.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | host: lightapi.net 3 | service: rule 4 | action: 5 | - name: createRule 6 | version: 0.1.0 7 | handler: CreateRule 8 | scope: portal.w 9 | request: 10 | schema: 11 | $ref: "#/schemas/createRuleRequest" 12 | example: 13 | hostId: "host123" 14 | ruleId: "rule456" 15 | ruleName: "My New Rule" 16 | ruleVersion: "1.0" 17 | ruleType: "Authorization" 18 | ruleGroup: "Security" 19 | ruleDesc: "This is a rule description for my new rule" 20 | ruleOwner: "admin" 21 | common: "Y" 22 | conditions: 23 | - field: "user_type" 24 | operator: "equals" 25 | value: "employee" 26 | - field: "department" 27 | operator: "contains" 28 | value: "IT" 29 | - name: updateRule 30 | version: 0.1.0 31 | handler: UpdateRule 32 | scope: portal.w 33 | request: 34 | schema: 35 | $ref: "#/schemas/updateRuleRequest" 36 | example: 37 | ruleId: "rule456" 38 | ruleName: "Updated Rule Name" 39 | ruleVersion: "1.1" 40 | ruleType: "Validation" 41 | ruleGroup: "Compliance" 42 | ruleDesc: "Updated rule description" 43 | ruleOwner: "manager" 44 | common: "N" 45 | conditions: 46 | - field: "status" 47 | operator: "not_equals" 48 | value: "pending" 49 | 50 | - name: deleteRule 51 | version: 0.1.0 52 | handler: DeleteRule 53 | scope: world.r 54 | request: 55 | schema: 56 | $ref: "#/schemas/deleteRuleRequest" 57 | example: 58 | hostId: "host123" 59 | ruleId: "rule456" 60 | 61 | schemas: 62 | createRuleRequest: 63 | title: Create Rule 64 | type: object 65 | properties: 66 | hostId: 67 | type: string 68 | description: Host Id 69 | ruleId: 70 | type: string 71 | description: Rule id 72 | ruleName: 73 | type: string 74 | description: Rule Name 75 | ruleVersion: 76 | type: string 77 | description: Rule Version 78 | ruleType: 79 | type: string 80 | description: Rule Type 81 | ruleGroup: 82 | type: string 83 | description: Rule Group 84 | ruleDesc: 85 | type: string 86 | description: Rule desc 87 | ruleOwner: 88 | type: string 89 | description: Rule Owner 90 | common: 91 | type: string 92 | enum: 93 | - Y 94 | - N 95 | description: Common 96 | conditions: 97 | type: array 98 | items: 99 | type: object 100 | description: Rule Conditions 101 | required: 102 | - hostId 103 | - ruleId 104 | - ruleName 105 | - ruleVersion 106 | - ruleType 107 | - ruleOwner 108 | - common 109 | - conditions 110 | updateRuleRequest: 111 | title: Update Rule 112 | type: object 113 | properties: 114 | ruleId: 115 | type: string 116 | description: Rule id 117 | ruleName: 118 | type: string 119 | description: Rule Name 120 | ruleVersion: 121 | type: string 122 | description: Rule Version 123 | ruleType: 124 | type: string 125 | description: Rule Type 126 | ruleGroup: 127 | type: string 128 | description: Rule Group 129 | ruleDesc: 130 | type: string 131 | description: Rule Desc 132 | ruleOwner: 133 | type: string 134 | description: Rule Owner 135 | common: 136 | type: string 137 | description: Common Flag 138 | conditions: 139 | type: array 140 | items: 141 | type: object 142 | description: Rule Conditions 143 | required: 144 | - ruleId 145 | - ruleName 146 | - ruleVersion 147 | - ruleType 148 | - ruleOwner 149 | - common 150 | - conditions 151 | deleteRuleRequest: 152 | title: Delete Rule 153 | type: object 154 | properties: 155 | hostId: 156 | type: string 157 | description: Host Id 158 | ruleId: 159 | type: string 160 | description: Rule Id 161 | required: 162 | - hostId 163 | - ruleId 164 | -------------------------------------------------------------------------------- /docs/content/tutorial/helloworld.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-06-10T09:33:02-04:00 3 | title: Hello World Tutorial 4 | --- 5 | 6 | This is a tutorial to show users how to generate a hybrid server and a hybrid service 7 | and put them together to serve request from curl. It leverage the generic server and 8 | generic service defined in model-config/hybrid. These server and service are used to 9 | test the new version of the light-hybrid-4j and new version of light-codegen. 10 | 11 | # Prepare Environment 12 | 13 | Bofore start, we need to prepare the environment by clone several projects from networknt 14 | and build them. Let's assume that you are using a workspace called networknt under your 15 | user directory. 16 | 17 | ``` 18 | cd ~/networknt 19 | git clone git@github.com:networknt/light-codegen.git 20 | git clone git@github.com:networknt/model-config.git 21 | git clone git@github.com:networknt/light-example-4j.git 22 | cd light-codegen 23 | mvn clean install 24 | cd .. 25 | 26 | ``` 27 | 28 | As we are going to regenerate the generic-server and generic-service in light-example-4j, 29 | let's rename these folder so that you can compare them if you want. 30 | 31 | ``` 32 | cd ~/networknt/light-example-4j/hybrid 33 | mv generic-server generic-server.bak 34 | mv generic-service generic-service.bak 35 | ``` 36 | 37 | 38 | # Generate Generic Server 39 | 40 | In light-codegen light-hybrid-4j framework generator it needs a config.json as input 41 | to generate a server project. This file can be found in model-config/hybrid/generic-server 42 | 43 | Here is the content of config.json 44 | 45 | ``` 46 | { 47 | "rootPackage": "com.networknt.gserver", 48 | "handlerPackage":"com.networknt.gserver.handler", 49 | "modelPackage":"com.networknt.gserver.model", 50 | "artifactId": "gserver", 51 | "groupId": "com.networknt", 52 | "name": "gserver", 53 | "version": "1.0.0", 54 | "overwriteHandler": true, 55 | "overwriteHandlerTest": true 56 | } 57 | 58 | ``` 59 | 60 | Here is the command line to generate server from light-codegen folder. 61 | 62 | ``` 63 | cd ~/networknt/light-codegen 64 | java -jar codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-server -o ../light-example-4j/hybrid/generic-server -c ../model-config/hybrid/generic-server/config.json 65 | ``` 66 | 67 | Build generic server 68 | 69 | ``` 70 | cd ~/networknt/light-example-4j/hybrid/generic-server 71 | mvn clean install 72 | ``` 73 | 74 | # Generate Generic Service 75 | 76 | Now let's generate a service. For hybrid service generator, it needs a config.json and also a 77 | schema.json to define the interface/contract for the service. 78 | 79 | Service config.json can be found in model-config/hybrid/generic-service and its content is 80 | 81 | ``` 82 | { 83 | "rootPackage": "com.networknt.gservice", 84 | "handlerPackage":"com.networknt.gservice.handler", 85 | "modelPackage":"com.networknt.gservice.model", 86 | "artifactId": "gservice", 87 | "groupId": "com.networknt", 88 | "name": "gservice", 89 | "version": "1.0.0", 90 | "overwriteHandler": true, 91 | "overwriteHandlerTest": true 92 | } 93 | 94 | ``` 95 | 96 | Here is the command line to generate hybrid service 97 | 98 | ``` 99 | cd ~/networknt/light-codegen 100 | java -jar codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-service -o ../light-example-4j/hybrid/generic-service -m ../model-config/hybrid/generic-service/schema.json -c ../model-config/hybrid/generic-service/config.json 101 | ``` 102 | 103 | Build generic service 104 | 105 | ``` 106 | cd ~/networknt/light-example-4j/hybrid/generic-service 107 | mvn clean install 108 | ``` 109 | 110 | # Start the server with Service 111 | 112 | Now let's start the server with service in the classpath. 113 | 114 | ``` 115 | cd ~/networknt/light-example-4j/hybrid/generic-server 116 | java -cp target/gserver-1.0.0.jar:../generic-service/target/gservice-1.0.0.jar com.networknt.server.Server 117 | ``` 118 | Now the server is up and running with 4 handlers. 119 | 120 | 121 | # Test 122 | 123 | ``` 124 | curl -X POST \ 125 | http://localhost:8080/api/json \ 126 | -H 'cache-control: no-cache' \ 127 | -H 'content-type: application/json' \ 128 | -H 'postman-token: 58bb63eb-de70-b855-a633-5b043bb52c95' \ 129 | -d '{ 130 | "host": "lightapi.net", 131 | "service": "world", 132 | "action": "hello", 133 | "version": "0.1.1", 134 | "lastName": "Hu", 135 | "firstName": "Steve" 136 | }' 137 | 138 | ``` 139 | 140 | You will have the reponse like this. 141 | 142 | ``` 143 | {"message":"Hello World!"} 144 | ``` 145 | -------------------------------------------------------------------------------- /docs/content/tutorial/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2016-10-09T08:01:56-04:00 3 | title: Microservices 4 | --- 5 | 6 | ## Introduction 7 | 8 | Hybrid service is type of service between J2ee service and microservice. It can help some organizations to take step by step to fully adopt microservices. 9 | User can split original large project to several Hybrid services to run at same JVM. From consumer side, Hybrid services can be treated as microservices to be use. 10 | 11 | Now on the tutorial, we are using light-eventuate-4j's todo-list example to generate and run hybrid service; 12 | 13 | 14 | 15 | ## Prepare workspace 16 | 17 | All specifications and code of the services are on github.com but we are going to 18 | redo it again by following the steps in the tutorial. Let's first create a 19 | workspace. I have created a directory named networknt under user directory. 20 | 21 | Checkout related projects. 22 | 23 | ``` 24 | cd ~/networknt 25 | git clone git@github.com:networknt/light-4j.git 26 | git clone git@github.com:networknt/light-hybrid-4j.git 27 | git clone git@github.com:networknt/light-codegen.git 28 | git clone git@github.com:networknt/light-eventuate-4j.git 29 | git clone git@github.com:networknt/light-eventuate-example.git 30 | git clone git@github.com:networknt/light-codegen.git 31 | 32 | 33 | 34 | Go into the projects root folder above, and build the projects with maven 35 | 36 | ``` 37 | mvn clean install 38 | 39 | ``` 40 | 41 | 42 | 43 | ## Use light-codegen to generate hybrid service project 44 | 45 | Light-codegen use the schema JSON file as base to generate hybrid service project. We created a sample json config file under networknt/model-config project 46 | 47 | you can checkout to you local for reference: 48 | 49 | -> git clone https://github.com/networknt/model-config.git 50 | 51 | 52 | under the folder model-config/hybrid, you can see we have two hybrid services for todo-list: 53 | 1 for command side hybrid service: todocommand-service 54 | 2 for query side hybrid service: todoquery-service 55 | 56 | Inside each folder, there are three json config files: 57 | 58 | serverConfig.json -- config file for generating hybrid service server. 59 | config.json -- config file for generating hybrid service project 60 | schema.json -- schema config to define the service handler, action, version, data type, and validation 61 | 62 | Now let's generate light-hybrid-4j service server first. The server can be a host to run Multiple service modules: 63 | 64 | 1. Generate command side server, run following command on your workspace: 65 | 66 | ``` 67 | java -jar light-codegen/codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-server -o light-eventuate-4j/command -c model-config/hybrid/todocommand-service/serverConfig.json 68 | ``` 69 | 70 | It will generate command module under light-eventuate-4j project. Please refer existing code in GitHub: 71 | 72 | https://github.com/networknt/light-eventuate-4j/tree/master/command 73 | 74 | 75 | 2. Generate query side server, run following command on your workspace: 76 | 77 | ``` 78 | java -jar light-codegen/codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-server -o light-eventuate-4j/query -c model-config/hybrid/todoquery-service/serverConfig.json 79 | ``` 80 | 81 | It will generate command module under light-eventuate-4j project. Please refer existing code in GitHub: 82 | 83 | https://github.com/networknt/light-eventuate-4j/tree/master/query 84 | 85 | 86 | 3. Generate command side hybrid service under light-eventuate-example/todo-list project 87 | 88 | ``` 89 | java -jar light-codegen/codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-service -o light-eventuate-example/todo-list/command-hybridservice -m model-config/hybrid/todocommand-service/schema.json -c model-config/hybrid/todocommand-service/config.json 90 | ``` 91 | 92 | Please refer existing code in GitHub: 93 | 94 | https://github.com/networknt/light-eventuate-example/tree/master/todo-list/command-hybridservice 95 | 96 | 4. Generate query side hybrid service under light-eventuate-example/todo-list project 97 | 98 | ``` 99 | java -jar light-codegen/codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-service -o light-eventuate-example/todo-list/query-hybridservice -m model-config/hybrid/todoquery-service/schema.json -c model-config/hybrid/todoquery-service/config.json 100 | ``` 101 | 102 | Please refer existing code in GitHub: 103 | 104 | https://github.com/networknt/light-eventuate-example/tree/master/todo-list/query-hybridservice 105 | 106 | 107 | # Ready 108 | 109 | Now we generate required server and service for hybrid service. Let's go to next step to develop and setup service 110 | -------------------------------------------------------------------------------- /rpc-router/src/test/resources/config/handler.yml: -------------------------------------------------------------------------------- 1 | # Handler middleware chain configuration 2 | --- 3 | enabled: ${handler.enabled:true} 4 | 5 | # Configuration for the LightHttpHandler. The handler is the base class for all middleware, server and health handlers 6 | # set the Status Object in the AUDIT_INFO, for auditing purposes 7 | # default, if not set:false 8 | auditOnError: ${handler.auditOnError:false} 9 | 10 | # set the StackTrace in the AUDIT_INFO, for auditing purposes 11 | # default, if not set:false 12 | auditStackTrace: ${handler.auditStackTrace:false} 13 | 14 | #------------------------------------------------------------------------------ 15 | # Support individual handler chains for each separate endpoint. It allows framework 16 | # handlers like health check, server info to bypass majority of the middleware handlers 17 | # and allows mixing multiple frameworks like OpenAPI and GraphQL in the same instance. 18 | # 19 | # handlers -- list of handlers to be used across chains in this microservice 20 | # including the routing handlers for ALL endpoints 21 | # -- format: fully qualified handler class name@optional:given name 22 | # chains -- allows forming of [1..N] chains, which could be wholly or 23 | # used to form handler chains for each endpoint 24 | # ex.: default chain below, reused partially across multiple endpoints 25 | # paths -- list all the paths to be used for routing within the microservice 26 | # ---- path: the URI for the endpoint (ex.: path: '/v1/pets') 27 | # ---- method: the operation in use (ex.: 'post') 28 | # ---- exec: handlers to be executed -- this element forms the list and 29 | # the order of execution for the handlers 30 | # 31 | # IMPORTANT NOTES: 32 | # - to avoid executing a handler, it has to be removed/commented out in the chain 33 | # or change the enabled:boolean to false for a middleware handler configuration. 34 | # - all handlers, routing handler included, are to be listed in the execution chain 35 | # - for consistency, give a name to each handler; it is easier to refer to a name 36 | # vs a fully qualified class name and is more elegant 37 | # - you can list in chains the fully qualified handler class names, and avoid using the 38 | # handlers element altogether 39 | #------------------------------------------------------------------------------ 40 | handlers: 41 | # Light-framework cross-cutting concerns implemented in the microservice 42 | - com.networknt.exception.ExceptionHandler@exception 43 | # - com.networknt.metrics.MetricsHandler@metrics 44 | - com.networknt.traceability.TraceabilityHandler@traceability 45 | - com.networknt.correlation.CorrelationHandler@correlation 46 | # - com.networknt.jaeger.tracing.JaegerHandler@jaeger 47 | # Cors handler to handler post/put pre-flight 48 | # - com.networknt.cors.CorsHttpHandler@cors 49 | # - com.networknt.openapi.OpenApiHandler@specification 50 | # - com.networknt.openapi.JwtVerifyHandler@security 51 | # - com.networknt.body.BodyHandler@body 52 | # - com.networknt.audit.AuditHandler@audit 53 | # - com.networknt.sanitizer.SanitizerHandler@sanitizer 54 | # - com.networknt.openapi.ValidatorHandler@validator 55 | # Header middleware to manipulate request and/or response headers before or after downstream server 56 | # - com.networknt.header.HeaderHandler@header 57 | # Direct requests to named services based on the request path 58 | # - com.networknt.router.middleware.PathPrefixServiceHandler@path 59 | # - com.networknt.router.RouterHandler@router 60 | # - com.networknt.resource.PathResourceHandler@resource 61 | - com.networknt.rpc.router.SchemaHandler@schema 62 | - com.networknt.rpc.router.JsonHandler@json 63 | # Customer business domain specific cross-cutting concerns handlers 64 | # - com.example.validator.CustomizedValidator@custvalidator 65 | # Framework endpoint handlers 66 | - com.networknt.health.HealthGetHandler@health 67 | - com.networknt.info.ServerInfoGetHandler@info 68 | # - com.networknt.metrics.prometheus.PrometheusGetHandler@getprometheus 69 | 70 | chains: 71 | default: 72 | - exception 73 | - traceability 74 | - correlation 75 | - schema 76 | - json 77 | 78 | paths: 79 | - path: '/api/json' 80 | method: 'POST' 81 | exec: 82 | - default 83 | - path: '/api/json' 84 | method: 'GET' 85 | exec: 86 | - default 87 | 88 | - path: '/adm/health/com.networknt.portal.hybrid.command-1.0.0' 89 | method: 'get' 90 | exec: 91 | - health 92 | 93 | # In most case, the /server/info endpoint shouldn't be exposed. If it is, then it must be protected by OAuth 2.0 or Basic Auth 94 | - path: '/adm/server/info' 95 | method: 'get' 96 | exec: 97 | - info 98 | -------------------------------------------------------------------------------- /docs/content/tutorial/merge-schema.md: -------------------------------------------------------------------------------- 1 | --- 2 | date: 2017-06-10T11:25:01-04:00 3 | title: Merge Multiple Schemas Tutorial 4 | --- 5 | 6 | In this tutorial, we are going to start a server with multiple services. Each service will have 7 | its own schema.json and they need to be merge during server startup so that validation and scope 8 | verification can be done on all request based on the merged schema. 9 | 10 | # Prepare Environment 11 | 12 | Bofore start, we need to prepare the environment by clone several projects from networknt 13 | and build them. Let's assume that you are using a workspace called networknt under your 14 | user directory. 15 | 16 | ``` 17 | cd ~/networknt 18 | git clone git@github.com:networknt/light-codegen.git 19 | git clone git@github.com:networknt/model-config.git 20 | git clone git@github.com:networknt/light-example-4j.git 21 | cd light-codegen 22 | mvn clean install 23 | cd .. 24 | 25 | ``` 26 | 27 | As we are going to regenerate a server and several services in light-example-4j, 28 | let's rename these folder so that you can compare them if you want. 29 | 30 | ``` 31 | cd ~/networknt/light-example-4j/hybrid 32 | mv merge-schema merge-schema.bak 33 | ``` 34 | 35 | 36 | # Generate Merge Server 37 | 38 | In light-codegen light-hybrid-4j framework generator it needs a config.json as input 39 | to generate a server project. This file can be found in model-config/hybrid/merge-schema/server 40 | 41 | Here is the content of config.json 42 | 43 | ``` 44 | { 45 | "rootPackage": "com.networknt.merger", 46 | "handlerPackage":"com.networknt.merger.handler", 47 | "modelPackage":"com.networknt.merger.model", 48 | "artifactId": "merger", 49 | "groupId": "com.networknt", 50 | "name": "merger", 51 | "version": "1.0.0" 52 | } 53 | ``` 54 | 55 | Here is the command line to generate server from light-codegen folder. 56 | 57 | ``` 58 | cd ~/networknt/light-codegen 59 | java -jar codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-server -o ../light-example-4j/hybrid/merge-schema/server -c ../model-config/hybrid/merge-schema/server/config.json 60 | ``` 61 | 62 | Build generic server 63 | 64 | ``` 65 | cd ~/networknt/light-example-4j/hybrid/generic-server 66 | mvn clean install 67 | ``` 68 | 69 | # Generate Two Services 70 | 71 | Now let's generate two services. For hybrid service generator, it needs a config.json and also a 72 | schema.json to define the interface/contract for the service. 73 | 74 | These files can be found in model-config/hybrid/merge-schema/service1 and service2 folder. 75 | 76 | 77 | Here is the list of command lines to generate hybrid services 78 | 79 | ``` 80 | cd ~/networknt/light-codegen 81 | java -jar codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-service -o ../light-example-4j/hybrid/merge-schema/service1 -m ../model-config/hybrid/merge-schema/service1/schema.json -c ../model-config/hybrid/merge-schema/service1/config.json 82 | java -jar codegen-cli/target/codegen-cli.jar -f light-hybrid-4j-service -o ../light-example-4j/hybrid/merge-schema/service2 -m ../model-config/hybrid/merge-schema/service2/schema.json -c ../model-config/hybrid/merge-schema/service2/config.json 83 | 84 | ``` 85 | 86 | Build services 87 | 88 | ``` 89 | cd ~/networknt/light-example-4j/hybrid/merge-schema/service1 90 | mvn clean install 91 | cd ~/networknt/light-example-4j/hybrid/merge-schema/service2 92 | mvn clean install 93 | 94 | ``` 95 | 96 | # Start the server with services 97 | 98 | Now let's start the server with services in the classpath. 99 | 100 | ``` 101 | cd ~/networknt/light-example-4j/hybrid/merge-schema/server 102 | java -cp target/merger-1.0.0.jar:../service1/target/merger-1.0.0.jar:../service2/target/merger-1.0.0.jar com.networknt.server.Server 103 | ``` 104 | Now the server is up and running with two services 105 | 106 | 107 | # Test 108 | 109 | Access the query service 110 | 111 | ``` 112 | curl -X POST \ 113 | http://localhost:8080/api/json \ 114 | -H 'cache-control: no-cache' \ 115 | -H 'content-type: application/json' \ 116 | -H 'postman-token: 58bb63eb-de70-b855-a633-5b043bb52c95' \ 117 | -d '{ 118 | "host": "lightapi.net", 119 | "service": "service1", 120 | "action": "query", 121 | "version": "0.1.0", 122 | "q1": "Hu", 123 | "q2": "Steve" 124 | }' 125 | 126 | ``` 127 | 128 | You will have the response like this. 129 | 130 | ``` 131 | {"message":"Hello World!"} 132 | ``` 133 | 134 | Access the command service 135 | 136 | ``` 137 | curl -X POST \ 138 | http://localhost:8080/api/json \ 139 | -H 'cache-control: no-cache' \ 140 | -H 'content-type: application/json' \ 141 | -H 'postman-token: 58bb63eb-de70-b855-a633-5b043bb52c95' \ 142 | -d '{ 143 | "host": "lightapi.net", 144 | "service": "service2", 145 | "action": "command", 146 | "version": "0.1.0", 147 | "c1": "Hu", 148 | "c2": "Steve" 149 | }' 150 | 151 | ``` 152 | 153 | You will have the response like this. 154 | 155 | ``` 156 | {"message":"Hello World!"} 157 | ``` 158 | -------------------------------------------------------------------------------- /rpc-security/src/test/resources/config/handler.yml: -------------------------------------------------------------------------------- 1 | # Handler middleware chain configuration 2 | --- 3 | enabled: ${handler.enabled:true} 4 | 5 | # Configuration for the LightHttpHandler. The handler is the base class for all middleware, server and health handlers 6 | # set the Status Object in the AUDIT_INFO, for auditing purposes 7 | # default, if not set:false 8 | auditOnError: ${handler.auditOnError:false} 9 | 10 | # set the StackTrace in the AUDIT_INFO, for auditing purposes 11 | # default, if not set:false 12 | auditStackTrace: ${handler.auditStackTrace:false} 13 | 14 | #------------------------------------------------------------------------------ 15 | # Support individual handler chains for each separate endpoint. It allows framework 16 | # handlers like health check, server info to bypass majority of the middleware handlers 17 | # and allows mixing multiple frameworks like OpenAPI and GraphQL in the same instance. 18 | # 19 | # handlers -- list of handlers to be used across chains in this microservice 20 | # including the routing handlers for ALL endpoints 21 | # -- format: fully qualified handler class name@optional:given name 22 | # chains -- allows forming of [1..N] chains, which could be wholly or 23 | # used to form handler chains for each endpoint 24 | # ex.: default chain below, reused partially across multiple endpoints 25 | # paths -- list all the paths to be used for routing within the microservice 26 | # ---- path: the URI for the endpoint (ex.: path: '/v1/pets') 27 | # ---- method: the operation in use (ex.: 'post') 28 | # ---- exec: handlers to be executed -- this element forms the list and 29 | # the order of execution for the handlers 30 | # 31 | # IMPORTANT NOTES: 32 | # - to avoid executing a handler, it has to be removed/commented out in the chain 33 | # or change the enabled:boolean to false for a middleware handler configuration. 34 | # - all handlers, routing handler included, are to be listed in the execution chain 35 | # - for consistency, give a name to each handler; it is easier to refer to a name 36 | # vs a fully qualified class name and is more elegant 37 | # - you can list in chains the fully qualified handler class names, and avoid using the 38 | # handlers element altogether 39 | #------------------------------------------------------------------------------ 40 | handlers: 41 | # Light-framework cross-cutting concerns implemented in the microservice 42 | - com.networknt.exception.ExceptionHandler@exception 43 | # - com.networknt.metrics.MetricsHandler@metrics 44 | - com.networknt.traceability.TraceabilityHandler@traceability 45 | - com.networknt.correlation.CorrelationHandler@correlation 46 | # - com.networknt.jaeger.tracing.JaegerHandler@jaeger 47 | # Cors handler to handler post/put pre-flight 48 | # - com.networknt.cors.CorsHttpHandler@cors 49 | # - com.networknt.openapi.OpenApiHandler@specification 50 | # - com.networknt.openapi.JwtVerifyHandler@security 51 | # - com.networknt.body.BodyHandler@body 52 | # - com.networknt.audit.AuditHandler@audit 53 | # - com.networknt.sanitizer.SanitizerHandler@sanitizer 54 | # - com.networknt.openapi.ValidatorHandler@validator 55 | # Header middleware to manipulate request and/or response headers before or after downstream server 56 | # - com.networknt.header.HeaderHandler@header 57 | # Direct requests to named services based on the request path 58 | # - com.networknt.router.middleware.PathPrefixServiceHandler@path 59 | # - com.networknt.router.RouterHandler@router 60 | # - com.networknt.resource.PathResourceHandler@resource 61 | - com.networknt.rpc.router.SchemaHandler@schema 62 | - com.networknt.rpc.router.JsonHandler@json 63 | - com.networknt.rpc.security.HybridJwtVerifyHandler@jwt 64 | - com.networknt.apikey.ApiKeyHandler@apikey 65 | - com.networknt.basicauth.BasicAuthHandler@basic 66 | - com.networknt.security.UnifiedSecurityHandler@security 67 | # Customer business domain specific cross-cutting concerns handlers 68 | # - com.example.validator.CustomizedValidator@custvalidator 69 | # Framework endpoint handlers 70 | - com.networknt.health.HealthGetHandler@health 71 | - com.networknt.info.ServerInfoGetHandler@info 72 | # - com.networknt.metrics.prometheus.PrometheusGetHandler@getprometheus 73 | 74 | chains: 75 | default: 76 | - exception 77 | - traceability 78 | - correlation 79 | - schema 80 | - security 81 | - json 82 | 83 | paths: 84 | - path: '/api/json' 85 | method: 'POST' 86 | exec: 87 | - default 88 | - path: '/api/json' 89 | method: 'GET' 90 | exec: 91 | - default 92 | 93 | - path: '/adm/health/com.networknt.portal.hybrid.command-1.0.0' 94 | method: 'get' 95 | exec: 96 | - health 97 | 98 | # In most case, the /server/info endpoint shouldn't be exposed. If it is, then it must be protected by OAuth 2.0 or Basic Auth 99 | - path: '/adm/server/info' 100 | method: 'get' 101 | exec: 102 | - info 103 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/HybridHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 Network New Technologies Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.networknt.rpc; 17 | 18 | import com.fasterxml.jackson.databind.JsonNode; 19 | import com.networknt.config.Config; 20 | import com.networknt.schema.JsonSchema; 21 | import com.networknt.schema.JsonSchemaFactory; 22 | import com.networknt.schema.SpecVersion; 23 | import com.networknt.schema.ValidationMessage; 24 | import com.networknt.status.Status; 25 | import com.networknt.utility.NioUtils; 26 | import io.undertow.server.HttpServerExchange; 27 | import org.slf4j.Logger; 28 | import org.slf4j.LoggerFactory; 29 | 30 | import java.nio.ByteBuffer; 31 | import java.util.Map; 32 | import java.util.Set; 33 | 34 | /** 35 | * This is the interface that every business handler should implement. It has two default methods 36 | * that can be shared by all handlers. 37 | * 38 | * @author Steve Hu 39 | */ 40 | public interface HybridHandler { 41 | Logger logger = LoggerFactory.getLogger(HybridHandler.class); 42 | 43 | String REQUEST_SUCCESS = "SUC10200"; 44 | String ERROR_NOT_DEFINED = "ERR10042"; 45 | String STATUS_VALIDATION_ERROR = "ERR11004"; 46 | 47 | ByteBuffer handle (HttpServerExchange exchange, Object object); 48 | 49 | default ByteBuffer validate(String serviceId, Map schema, Map data) { 50 | if(logger.isDebugEnabled()) { 51 | try { 52 | logger.debug("serviceId = {} data = {}", serviceId, Config.getInstance().getMapper().writeValueAsString(data)); 53 | } catch (Exception e) { 54 | logger.error("Exception:", e); 55 | } 56 | } 57 | JsonNode jsonNode = Config.getInstance().getMapper().valueToTree(schema); 58 | JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V202012); 59 | JsonSchema jsonSchema = factory.getSchema(jsonNode); 60 | Set errors = jsonSchema.validate(Config.getInstance().getMapper().valueToTree(data)); 61 | ByteBuffer bf = null; 62 | if(errors.size() > 0) { 63 | // like the light-rest-4j, we only return one validation error. 64 | ValidationMessage vm = errors.iterator().next(); 65 | Status status = new Status(STATUS_VALIDATION_ERROR, vm.getMessage()); 66 | logger.error("Validation Error:" + status.toString()); 67 | bf = NioUtils.toByteBuffer(status.toString()); 68 | } 69 | return bf; 70 | } 71 | 72 | /** 73 | * Return a Status object so that the handler can get the HTTP response code to set exchange response. 74 | * @param exchange HttpServerExchange used to set the response code 75 | * @param code Error code defined in status.yml 76 | * @param args A number of arguments in the error description 77 | * @return status Status object 78 | */ 79 | default String getStatus(HttpServerExchange exchange, String code, final Object... args) { 80 | Status status = new Status(code, args); 81 | if(status.getStatusCode() == 0) { 82 | // There is no entry in status.yml for this particular error code. 83 | status = new Status(ERROR_NOT_DEFINED, code); 84 | } 85 | StackTraceElement[] elements = Thread.currentThread().getStackTrace(); 86 | logger.error(status.toString() + " at " + elements[2].getClassName() + "." + elements[2].getMethodName() + "(" + elements[2].getFileName() + ":" + elements[2].getLineNumber() + ")"); 87 | // set status code here so that the response has the right status code. 88 | exchange.setStatusCode(status.getStatusCode()); 89 | return status.toString(); 90 | } 91 | 92 | /** 93 | * There are situations that the downstream service returns an error status response and we just 94 | * want to bubble up to the caller and eventually to the original caller. 95 | * 96 | * @param exchange HttpServerExchange 97 | * @param status error status 98 | * @return String the status string 99 | */ 100 | default String getStatus(HttpServerExchange exchange, Status status) { 101 | exchange.setStatusCode(status.getStatusCode()); 102 | StackTraceElement[] elements = Thread.currentThread().getStackTrace(); 103 | logger.error(status.toString() + " at " + elements[2].getClassName() + "." + elements[2].getMethodName() + "(" + elements[2].getFileName() + ":" + elements[2].getLineNumber() + ")"); 104 | return status.toString(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /rpc-router/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | 22 | com.networknt 23 | light-hybrid-4j 24 | 2.3.2-SNAPSHOT 25 | ../pom.xml 26 | 27 | 28 | rpc-router 29 | jar 30 | rpc-router 31 | A rpc router for hybrid server that can map http requests to handlers based on a map lookup 32 | 33 | 34 | 35 | com.networknt 36 | config 37 | 38 | 39 | com.networknt 40 | status 41 | 42 | 43 | com.networknt 44 | utility 45 | 46 | 47 | com.networknt 48 | exception 49 | 50 | 51 | com.networknt 52 | handler 53 | 54 | 55 | com.networknt 56 | security 57 | 58 | 59 | com.networknt 60 | unified-security 61 | 62 | 63 | com.networknt 64 | api-key 65 | 66 | 67 | com.networknt 68 | basic-auth 69 | 70 | 71 | com.networknt 72 | metrics 73 | 74 | 75 | com.networknt 76 | traceability 77 | 78 | 79 | com.networknt 80 | correlation 81 | 82 | 83 | com.networknt 84 | server 85 | 86 | 87 | com.networknt 88 | resource 89 | 90 | 91 | com.networknt 92 | health 93 | 94 | 95 | com.networknt 96 | info 97 | 98 | 99 | com.networknt 100 | json-schema-validator 101 | 102 | 103 | com.fasterxml.jackson.core 104 | jackson-databind 105 | 106 | 107 | org.slf4j 108 | slf4j-api 109 | 110 | 111 | io.undertow 112 | undertow-core 113 | 114 | 115 | io.github.classgraph 116 | classgraph 117 | 118 | 119 | 120 | com.networknt 121 | client 122 | test 123 | 124 | 125 | ch.qos.logback 126 | logback-classic 127 | test 128 | 129 | 130 | junit 131 | junit 132 | test 133 | 134 | 135 | org.mockito 136 | mockito-core 137 | test 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /rpc-security/pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | 21 | 22 | com.networknt 23 | light-hybrid-4j 24 | 2.3.2-SNAPSHOT 25 | ../pom.xml 26 | 27 | 28 | rpc-security 29 | jar 30 | rpc-security 31 | A rpc security for hybrid server that can verfiy the scopes for jwt 32 | 33 | 34 | 35 | com.networknt 36 | rpc-router 37 | 38 | 39 | com.networknt 40 | config 41 | 42 | 43 | com.networknt 44 | status 45 | 46 | 47 | com.networknt 48 | utility 49 | 50 | 51 | com.networknt 52 | exception 53 | 54 | 55 | com.networknt 56 | handler 57 | 58 | 59 | com.networknt 60 | security 61 | 62 | 63 | com.networknt 64 | unified-security 65 | 66 | 67 | com.networknt 68 | api-key 69 | 70 | 71 | com.networknt 72 | basic-auth 73 | 74 | 75 | com.networknt 76 | metrics 77 | 78 | 79 | com.networknt 80 | traceability 81 | 82 | 83 | com.networknt 84 | correlation 85 | 86 | 87 | com.networknt 88 | server 89 | 90 | 91 | com.networknt 92 | resource 93 | 94 | 95 | com.networknt 96 | health 97 | 98 | 99 | com.networknt 100 | info 101 | 102 | 103 | com.networknt 104 | json-schema-validator 105 | 106 | 107 | com.fasterxml.jackson.core 108 | jackson-databind 109 | 110 | 111 | org.slf4j 112 | slf4j-api 113 | 114 | 115 | io.undertow 116 | undertow-core 117 | 118 | 119 | io.github.classgraph 120 | classgraph 121 | 122 | 123 | 124 | com.networknt 125 | client 126 | test 127 | 128 | 129 | ch.qos.logback 130 | logback-classic 131 | test 132 | 133 | 134 | junit 135 | junit 136 | test 137 | 138 | 139 | org.mockito 140 | mockito-core 141 | test 142 | 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/router/RpcRouterConfig.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.fasterxml.jackson.core.JacksonException; 4 | import com.fasterxml.jackson.core.JsonParser; 5 | import com.fasterxml.jackson.core.type.TypeReference; 6 | import com.fasterxml.jackson.databind.DeserializationContext; 7 | import com.fasterxml.jackson.databind.JsonDeserializer; 8 | import com.fasterxml.jackson.databind.JsonNode; 9 | import com.fasterxml.jackson.databind.ObjectMapper; 10 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 11 | import com.fasterxml.jackson.databind.module.SimpleModule; 12 | import com.networknt.config.Config; 13 | import com.networknt.config.ConfigException; 14 | import com.networknt.config.schema.*; 15 | 16 | import java.io.IOException; 17 | import java.util.Arrays; 18 | import java.util.List; 19 | import java.util.Map; 20 | import java.util.stream.Collectors; 21 | 22 | /** 23 | * Created by steve on 19/02/17. 24 | */ 25 | @ConfigSchema(configKey = "rpc-router", configName = "rpc-router", configDescription = "rpc-router configuration", outputFormats = {OutputFormat.JSON_SCHEMA, OutputFormat.YAML}) 26 | public class RpcRouterConfig { 27 | public static final String CONFIG_NAME = "rpc-router"; 28 | public static final String HANDLER_PACKAGES = "handlerPackages"; 29 | public static final String JSON_PATH = "jsonPath"; 30 | public static final String FORM_PATH = "formPath"; 31 | public static final String REGISTER_SERVICE = "registerService"; 32 | 33 | @ArrayField( 34 | configFieldName = HANDLER_PACKAGES, 35 | externalizedKeyName = HANDLER_PACKAGES, 36 | externalized = true, 37 | description = "The hybrid handler package names that is used for scanner during server startup. The more specific of package names, the faster to start\n" + 38 | "List the package prefixes for all handlers used. Leave an empty array to indicate wildcard (all packages)", 39 | items = String.class 40 | ) 41 | private List handlerPackages; 42 | 43 | @StringField( 44 | configFieldName = JSON_PATH, 45 | externalizedKeyName = JSON_PATH, 46 | externalized = true, 47 | defaultValue = "/api/json", 48 | description = "The JSON RPC API path" 49 | ) 50 | private String jsonPath; 51 | 52 | @StringField( 53 | configFieldName = FORM_PATH, 54 | externalizedKeyName = FORM_PATH, 55 | externalized = true, 56 | defaultValue = "/api/form", 57 | description = "The form RPC API path" 58 | ) 59 | private String formPath; 60 | 61 | @BooleanField( 62 | configFieldName = REGISTER_SERVICE, 63 | externalizedKeyName = REGISTER_SERVICE, 64 | externalized = true, 65 | defaultValue = "false", 66 | description = "if we want to register all handlers as services to the Consul for client to discover" 67 | ) 68 | private boolean registerService; 69 | 70 | private Map mappedConfig; 71 | private final Config config; 72 | 73 | private RpcRouterConfig(String configName) { 74 | config = Config.getInstance(); 75 | mappedConfig = config.getJsonMapConfigNoCache(configName); 76 | setConfigData(); 77 | } 78 | private RpcRouterConfig() { 79 | this(CONFIG_NAME); 80 | } 81 | 82 | public static RpcRouterConfig load(String configName) { 83 | return new RpcRouterConfig(configName); 84 | } 85 | 86 | public static RpcRouterConfig load() { 87 | return new RpcRouterConfig(); 88 | } 89 | 90 | public void reload() { 91 | mappedConfig = config.getJsonMapConfigNoCache(CONFIG_NAME); 92 | setConfigData(); 93 | } 94 | 95 | public void reload(String configName) { 96 | mappedConfig = config.getJsonMapConfigNoCache(configName); 97 | setConfigData(); 98 | } 99 | 100 | public List getHandlerPackages() { 101 | return handlerPackages; 102 | } 103 | 104 | public String getJsonPath() { 105 | return jsonPath; 106 | } 107 | 108 | public String getFormPath() { 109 | return formPath; 110 | } 111 | 112 | public boolean isRegisterService() { 113 | return registerService; 114 | } 115 | 116 | public Map getMappedConfig() { 117 | return mappedConfig; 118 | } 119 | 120 | private void setConfigData() { 121 | if(getMappedConfig() != null) { 122 | Object object = getMappedConfig().get(HANDLER_PACKAGES); 123 | if(object != null) { 124 | if (object instanceof String) { 125 | String s = (String) object; 126 | s = s.trim(); 127 | if (s.startsWith("[")) { 128 | // this is a JSON string, and we need to parse it. 129 | try { 130 | handlerPackages = Config.getInstance().getMapper().readValue(s, new TypeReference>() { 131 | }); 132 | } catch (Exception e) { 133 | throw new ConfigException("could not parse the handlerPackages json with a list of strings."); 134 | } 135 | } else { 136 | // this is a comma separated string. 137 | handlerPackages = Arrays.asList(s.split("\\s*,\\s*")); 138 | } 139 | } else if (object instanceof List) { 140 | handlerPackages = (List) getMappedConfig().get(HANDLER_PACKAGES); 141 | } else { 142 | throw new ConfigException("handlerPackages list is missing or wrong type."); 143 | } 144 | } 145 | object = getMappedConfig().get(JSON_PATH); 146 | if(object != null) jsonPath = (String)object; 147 | object = getMappedConfig().get(FORM_PATH); 148 | if(object != null) formPath = (String)object; 149 | object = getMappedConfig().get(REGISTER_SERVICE); 150 | if(object != null) registerService = Config.loadBooleanValue(REGISTER_SERVICE, object); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /docs/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit #abort if any command fails 3 | me=$(basename "$0") 4 | 5 | help_message="\ 6 | Usage: $me [-c FILE] [] 7 | Deploy generated files to a git branch. 8 | 9 | Options: 10 | 11 | -h, --help Show this help information. 12 | -v, --verbose Increase verbosity. Useful for debugging. 13 | -e, --allow-empty Allow deployment of an empty directory. 14 | -m, --message MESSAGE Specify the message used when committing on the 15 | deploy branch. 16 | -n, --no-hash Don't append the source commit's hash to the deploy 17 | commit's message. 18 | -c, --config-file PATH Override default & environment variables' values 19 | with those in set in the file at 'PATH'. Must be the 20 | first option specified. 21 | 22 | Variables: 23 | 24 | GIT_DEPLOY_DIR Folder path containing the files to deploy. 25 | GIT_DEPLOY_BRANCH Commit deployable files to this branch. 26 | GIT_DEPLOY_REPO Push the deploy branch to this repository. 27 | 28 | These variables have default values defined in the script. The defaults can be 29 | overridden by environment variables. Any environment variables are overridden 30 | by values set in a '.env' file (if it exists), and in turn by those set in a 31 | file specified by the '--config-file' option." 32 | 33 | parse_args() { 34 | # Set args from a local environment file. 35 | if [ -e ".env" ]; then 36 | source .env 37 | fi 38 | 39 | # Set args from file specified on the command-line. 40 | if [[ $1 = "-c" || $1 = "--config-file" ]]; then 41 | source "$2" 42 | shift 2 43 | fi 44 | 45 | # Parse arg flags 46 | # If something is exposed as an environment variable, set/overwrite it 47 | # here. Otherwise, set/overwrite the internal variable instead. 48 | while : ; do 49 | if [[ $1 = "-h" || $1 = "--help" ]]; then 50 | echo "$help_message" 51 | return 0 52 | elif [[ $1 = "-v" || $1 = "--verbose" ]]; then 53 | verbose=true 54 | shift 55 | elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then 56 | allow_empty=true 57 | shift 58 | elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then 59 | commit_message=$2 60 | shift 2 61 | elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then 62 | GIT_DEPLOY_APPEND_HASH=false 63 | shift 64 | else 65 | break 66 | fi 67 | done 68 | 69 | # Set internal option vars from the environment and arg flags. All internal 70 | # vars should be declared here, with sane defaults if applicable. 71 | 72 | # Source directory & target branch. 73 | deploy_directory=${GIT_DEPLOY_DIR:-dist} 74 | deploy_branch=${GIT_DEPLOY_BRANCH:-gh-pages} 75 | 76 | #if no user identity is already set in the current git environment, use this: 77 | default_username=${GIT_DEPLOY_USERNAME:-deploy.sh} 78 | default_email=${GIT_DEPLOY_EMAIL:-} 79 | 80 | #repository to deploy to. must be readable and writable. 81 | repo=${GIT_DEPLOY_REPO:-origin} 82 | 83 | #append commit hash to the end of message by default 84 | append_hash=${GIT_DEPLOY_APPEND_HASH:-true} 85 | } 86 | 87 | main() { 88 | parse_args "$@" 89 | 90 | enable_expanded_output 91 | 92 | if ! git diff --exit-code --quiet --cached; then 93 | echo Aborting due to uncommitted changes in the index >&2 94 | return 1 95 | fi 96 | 97 | commit_title=`git log -n 1 --format="%s" HEAD` 98 | commit_hash=` git log -n 1 --format="%H" HEAD` 99 | 100 | #default commit message uses last title if a custom one is not supplied 101 | if [[ -z $commit_message ]]; then 102 | commit_message="publish: $commit_title" 103 | fi 104 | 105 | #append hash to commit message unless no hash flag was found 106 | if [ $append_hash = true ]; then 107 | commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash" 108 | fi 109 | 110 | previous_branch=`git rev-parse --abbrev-ref HEAD` 111 | 112 | if [ ! -d "$deploy_directory" ]; then 113 | echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2 114 | return 1 115 | fi 116 | 117 | # must use short form of flag in ls for compatibility with OS X and BSD 118 | if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then 119 | echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2 120 | return 1 121 | fi 122 | 123 | if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then 124 | # deploy_branch exists in $repo; make sure we have the latest version 125 | 126 | disable_expanded_output 127 | git fetch --force $repo $deploy_branch:$deploy_branch 128 | enable_expanded_output 129 | fi 130 | 131 | # check if deploy_branch exists locally 132 | if git show-ref --verify --quiet "refs/heads/$deploy_branch" 133 | then incremental_deploy 134 | else initial_deploy 135 | fi 136 | 137 | restore_head 138 | } 139 | 140 | initial_deploy() { 141 | git --work-tree "$deploy_directory" checkout --orphan $deploy_branch 142 | git --work-tree "$deploy_directory" add --all 143 | commit+push 144 | } 145 | 146 | incremental_deploy() { 147 | #make deploy_branch the current branch 148 | git symbolic-ref HEAD refs/heads/$deploy_branch 149 | #put the previously committed contents of deploy_branch into the index 150 | git --work-tree "$deploy_directory" reset --mixed --quiet 151 | git --work-tree "$deploy_directory" add --all 152 | 153 | set +o errexit 154 | diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$? 155 | set -o errexit 156 | case $diff in 157 | 0) echo No changes to files in $deploy_directory. Skipping commit.;; 158 | 1) commit+push;; 159 | *) 160 | echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2 161 | return $diff 162 | ;; 163 | esac 164 | } 165 | 166 | commit+push() { 167 | set_user_id 168 | git --work-tree "$deploy_directory" commit -m "$commit_message" 169 | 170 | disable_expanded_output 171 | #--quiet is important here to avoid outputting the repo URL, which may contain a secret token 172 | git push --quiet $repo $deploy_branch 173 | enable_expanded_output 174 | } 175 | 176 | #echo expanded commands as they are executed (for debugging) 177 | enable_expanded_output() { 178 | if [ $verbose ]; then 179 | set -o xtrace 180 | set +o verbose 181 | fi 182 | } 183 | 184 | #this is used to avoid outputting the repo URL, which may contain a secret token 185 | disable_expanded_output() { 186 | if [ $verbose ]; then 187 | set +o xtrace 188 | set -o verbose 189 | fi 190 | } 191 | 192 | set_user_id() { 193 | if [[ -z `git config user.name` ]]; then 194 | git config user.name "$default_username" 195 | fi 196 | if [[ -z `git config user.email` ]]; then 197 | git config user.email "$default_email" 198 | fi 199 | } 200 | 201 | restore_head() { 202 | if [[ $previous_branch = "HEAD" ]]; then 203 | #we weren't on any branch before, so just set HEAD back to the commit it was on 204 | git update-ref --no-deref HEAD $commit_hash $deploy_branch 205 | else 206 | git symbolic-ref HEAD refs/heads/$previous_branch 207 | fi 208 | 209 | git reset --mixed 210 | } 211 | 212 | filter() { 213 | sed -e "s|$repo|\$repo|g" 214 | } 215 | 216 | sanitize() { 217 | "$@" 2> >(filter 1>&2) | filter 218 | } 219 | 220 | [[ $1 = --source-only ]] || main "$@" 221 | -------------------------------------------------------------------------------- /rpc-security/src/test/java/com/networknt/rpc/security/HybridUnifiedSecurityTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.security; 2 | 3 | import com.networknt.client.Http2Client; 4 | import com.networknt.exception.ClientException; 5 | import io.undertow.Handlers; 6 | import io.undertow.Undertow; 7 | import io.undertow.UndertowOptions; 8 | import io.undertow.client.ClientConnection; 9 | import io.undertow.client.ClientRequest; 10 | import io.undertow.client.ClientResponse; 11 | import io.undertow.server.HttpHandler; 12 | import io.undertow.server.RoutingHandler; 13 | import io.undertow.util.Headers; 14 | import io.undertow.util.HttpString; 15 | import io.undertow.util.Methods; 16 | import org.junit.*; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.xnio.IoUtils; 20 | import org.xnio.OptionMap; 21 | 22 | import java.net.URI; 23 | import java.net.URLEncoder; 24 | import java.nio.charset.StandardCharsets; 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.atomic.AtomicReference; 27 | 28 | public class HybridUnifiedSecurityTest { 29 | @ClassRule 30 | public static TestServer server = TestServer.getInstance(); 31 | static final Logger logger = LoggerFactory.getLogger(HybridUnifiedSecurityTest.class); 32 | static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2(); 33 | static final boolean enableHttps = server.getServerConfig().isEnableHttps(); 34 | static final int httpPort = server.getServerConfig().getHttpPort(); 35 | static final int httpsPort = server.getServerConfig().getHttpsPort(); 36 | static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort; 37 | 38 | static Undertow server2 = null; 39 | @BeforeClass 40 | public static void setUp() { 41 | if (server2 == null) { 42 | logger.info("starting server2"); 43 | HttpHandler handler = getJwksHandler(); 44 | server2 = Undertow.builder() 45 | .addHttpListener(7082, "localhost") 46 | .setHandler(handler) 47 | .build(); 48 | server2.start(); 49 | } 50 | 51 | } 52 | 53 | @AfterClass 54 | public static void tearDown() throws Exception { 55 | if (server2 != null) { 56 | try { 57 | Thread.sleep(100); 58 | } catch (InterruptedException ignored) { 59 | 60 | } 61 | server2.stop(); 62 | logger.info("The server2 is stopped."); 63 | } 64 | 65 | } 66 | 67 | static RoutingHandler getJwksHandler() { 68 | return Handlers.routing() 69 | .add(Methods.GET, "/oauth2/N2CMw0HGQXeLvC1wBfln2A/keys", exchange -> { 70 | exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); 71 | exchange.getResponseSender().send("{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"Tj_l_tIBTginOtQbL0Pv5w\",\"n\":\"0YRbWAb1FGDpPUUcrIpJC6BwlswlKMS-z2wMAobdo0BNxNa7hG_gIHVPkXu14Jfo1JhUhS4wES3DdY3a6olqPcRN1TCCUVHd-1TLd1BBS-yq9tdJ6HCewhe5fXonaRRKwutvoH7i_eR4m3fQ1GoVzVAA3IngpTr4ptnM3Ef3fj-5wZYmitzrRUyQtfARTl3qGaXP_g8pHFAP0zrNVvOnV-jcNMKm8YZNcgcs1SuLSFtUDXpf7Nr2_xOhiNM-biES6Dza1sMLrlxULFuctudO9lykB7yFh3LHMxtIZyIUHuy0RbjuOGC5PmDowLttZpPI_j4ynJHAaAWr8Ddz764WdQ\",\"e\":\"AQAB\"}]}"); 72 | }); 73 | 74 | } 75 | 76 | private static String auth = "Bearer eyJraWQiOiJUal9sX3RJQlRnaW5PdFFiTDBQdjV3IiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJ1cm46Y29tOm5ldHdvcmtudDpvYXV0aDI6djEiLCJhdWQiOiJ1cm46Y29tLm5ldHdvcmtudCIsImV4cCI6MjAxOTg3MzkyOSwianRpIjoiLWZaNzY5cWxFa05RRnFGQWNkLWlGQSIsImlhdCI6MTcwNDUxMzkyOSwibmJmIjoxNzA0NTEzODA5LCJ2ZXJzaW9uIjoiJzEuMCciLCJ1c2VyX2lkIjoic3RldmUiLCJ1c2VyX3R5cGUiOiJFTVBMT1lFRSIsImNsaWVudF9pZCI6ImY3ZDQyMzQ4LWM2NDctNGVmYi1hNTJkLTRjNTc4NzQyMWU3MiIsInJvbGVzIjoidXNlciIsInNjb3BlIjpbIndvcmxkLnIiLCJ3b3JsZC53Iiwic2VydmVyLmluZm8uciJdfQ.za1C5bh4QxI6McXeogmXtVYKS611VVPNn14PCcV5Q8NadVDIcgL7TCjT_KKbU35BiV0-xlu1BdyzMEZOr3E1xlhK-WB-eAHRF__IXydJXN-pWSo6wp-Jq4-rzZlW_e4kprlT_B0GHngzlJuru5Y-00Mh8bnhUuy1QTxF3JFHnz-62bJZmyeRx9iveNGvUTF5AJGPHhRoHMMCFJjSakiuy8El-wrTFN-Zi3e09n3xxPHj6qH_mDG_WyHMJ5r3bew9asMxU8QV_A0c7jkcwxJRg6UcVLSecmd-GxrCmjw3hiIJKInyqBBIa0uZkHmm4T1c9XiG1LbFHNhHwi0H_sykoQ"; 77 | 78 | @Test 79 | public void testJsonRpcValidationError() throws Exception { 80 | Http2Client client = Http2Client.getInstance(); 81 | String message = "{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\"}}"; 82 | 83 | final CountDownLatch latch = new CountDownLatch(1); 84 | final ClientConnection connection; 85 | try { 86 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get(); 87 | } catch (Exception e) { 88 | throw new ClientException(e); 89 | } 90 | final AtomicReference reference = new AtomicReference<>(); 91 | try { 92 | ClientRequest request = new ClientRequest().setPath("/api/json").setMethod(Methods.POST); 93 | request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json"); 94 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 95 | request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked"); 96 | connection.sendRequest(request, client.createClientCallback(reference, latch, message)); 97 | 98 | latch.await(); 99 | } catch (Exception e) { 100 | logger.error("Exception: ", e); 101 | throw new ClientException(e); 102 | } finally { 103 | IoUtils.safeClose(connection); 104 | } 105 | int statusCode = reference.get().getResponseCode(); 106 | System.out.println("statusCode = " + statusCode); 107 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 108 | System.out.println("body = " + body); 109 | Assert.assertTrue(body.contains("ERR11004")); 110 | } 111 | 112 | // Ignore it as we cannot get the jwks and x509 certificate is not supported anymore. 113 | @Test 114 | public void testJsonRpcPostNoError() throws Exception { 115 | Http2Client client = Http2Client.getInstance(); 116 | 117 | String message = "{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\",\"ruleId\":\"ruleId\"}}"; 118 | final CountDownLatch latch = new CountDownLatch(1); 119 | final ClientConnection connection; 120 | try { 121 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get(); 122 | } catch (Exception e) { 123 | throw new ClientException(e); 124 | } 125 | final AtomicReference reference = new AtomicReference<>(); 126 | try { 127 | ClientRequest request = new ClientRequest().setPath("/api/json").setMethod(Methods.POST); 128 | request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json"); 129 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 130 | request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked"); 131 | connection.sendRequest(request, client.createClientCallback(reference, latch, message)); 132 | 133 | latch.await(); 134 | } catch (Exception e) { 135 | logger.error("Exception: ", e); 136 | throw new ClientException(e); 137 | } finally { 138 | IoUtils.safeClose(connection); 139 | } 140 | int statusCode = reference.get().getResponseCode(); 141 | System.out.println("statusCode = " + statusCode); 142 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 143 | System.out.println("body = " + body); 144 | Assert.assertEquals("OK", body); 145 | } 146 | 147 | 148 | // Ignore it as we cannot get the jwks and x509 certificate is not supported anymore. 149 | @Test 150 | public void testJsonRpcGetNoError() throws Exception { 151 | Http2Client client = Http2Client.getInstance(); 152 | String message = "/api/json?cmd=" + URLEncoder.encode("{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\",\"ruleId\":\"ruleId\"}}", StandardCharsets.UTF_8); 153 | System.out.println("message = " + message); 154 | 155 | final CountDownLatch latch = new CountDownLatch(1); 156 | final ClientConnection connection; 157 | try { 158 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get(); 159 | } catch (Exception e) { 160 | throw new ClientException(e); 161 | } 162 | final AtomicReference reference = new AtomicReference<>(); 163 | try { 164 | ClientRequest request = new ClientRequest().setMethod(Methods.GET).setPath(message); 165 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 166 | connection.sendRequest(request, client.createClientCallback(reference, latch)); 167 | latch.await(); 168 | int statusCode = reference.get().getResponseCode(); 169 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 170 | System.out.println("body = " + body); 171 | Assert.assertEquals(200, statusCode); 172 | Assert.assertEquals("OK", body); 173 | } catch (Exception e) { 174 | logger.error("Exception: ", e); 175 | throw new ClientException(e); 176 | } finally { 177 | IoUtils.safeClose(connection); 178 | } 179 | } 180 | 181 | @Test 182 | public void testJsonRpcGetNoParamValue() throws Exception { 183 | Http2Client client = Http2Client.getInstance(); 184 | String message = "/api/json?cmd="; 185 | System.out.println("message = " + message); 186 | 187 | final CountDownLatch latch = new CountDownLatch(1); 188 | final ClientConnection connection; 189 | try { 190 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get(); 191 | } catch (Exception e) { 192 | throw new ClientException(e); 193 | } 194 | final AtomicReference reference = new AtomicReference<>(); 195 | try { 196 | ClientRequest request = new ClientRequest().setMethod(Methods.GET).setPath(message); 197 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 198 | connection.sendRequest(request, client.createClientCallback(reference, latch)); 199 | latch.await(); 200 | int statusCode = reference.get().getResponseCode(); 201 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 202 | System.out.println("statusCode = " + statusCode + " body = " + body); 203 | Assert.assertEquals(400, statusCode); 204 | Assert.assertTrue(body.contains("ERR11202")); 205 | } catch (Exception e) { 206 | logger.error("Exception: ", e); 207 | throw new ClientException(e); 208 | } finally { 209 | IoUtils.safeClose(connection); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /rpc-router/src/test/java/com/networknt/rpc/router/RpcRouterTest.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.networknt.client.Http2Client; 4 | import com.networknt.exception.ClientException; 5 | import io.undertow.Handlers; 6 | import io.undertow.Undertow; 7 | import io.undertow.UndertowOptions; 8 | import io.undertow.client.ClientConnection; 9 | import io.undertow.client.ClientRequest; 10 | import io.undertow.client.ClientResponse; 11 | import io.undertow.server.HttpHandler; 12 | import io.undertow.server.RoutingHandler; 13 | import io.undertow.util.Headers; 14 | import io.undertow.util.HttpString; 15 | import io.undertow.util.Methods; 16 | import org.junit.*; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.xnio.IoUtils; 20 | import org.xnio.OptionMap; 21 | 22 | import java.net.URI; 23 | import java.net.URLEncoder; 24 | import java.nio.charset.StandardCharsets; 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.atomic.AtomicReference; 27 | 28 | /** 29 | * Created by steve on 12/04/17. 30 | */ 31 | public class RpcRouterTest { 32 | @ClassRule 33 | public static TestServer server = TestServer.getInstance(); 34 | 35 | static final Logger logger = LoggerFactory.getLogger(RpcRouterTest.class); 36 | static final boolean enableHttp2 = server.getServerConfig().isEnableHttp2(); 37 | static final boolean enableHttps = server.getServerConfig().isEnableHttps(); 38 | static final int httpPort = server.getServerConfig().getHttpPort(); 39 | static final int httpsPort = server.getServerConfig().getHttpsPort(); 40 | static final String url = enableHttp2 || enableHttps ? "https://localhost:" + httpsPort : "http://localhost:" + httpPort; 41 | 42 | static Undertow server2 = null; 43 | @BeforeClass 44 | public static void setUp() { 45 | if (server2 == null) { 46 | logger.info("starting server2"); 47 | HttpHandler handler = getJwksHandler(); 48 | server2 = Undertow.builder() 49 | .addHttpListener(7082, "localhost") 50 | .setHandler(handler) 51 | .build(); 52 | server2.start(); 53 | } 54 | 55 | } 56 | 57 | @AfterClass 58 | public static void tearDown() throws Exception { 59 | if (server2 != null) { 60 | try { 61 | Thread.sleep(100); 62 | } catch (InterruptedException ignored) { 63 | 64 | } 65 | server2.stop(); 66 | logger.info("The server2 is stopped."); 67 | } 68 | 69 | } 70 | 71 | static RoutingHandler getJwksHandler() { 72 | return Handlers.routing() 73 | .add(Methods.GET, "/oauth2/N2CMw0HGQXeLvC1wBfln2A/keys", exchange -> { 74 | exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); 75 | exchange.getResponseSender().send("{\"keys\":[{\"kty\":\"RSA\",\"kid\":\"Tj_l_tIBTginOtQbL0Pv5w\",\"n\":\"0YRbWAb1FGDpPUUcrIpJC6BwlswlKMS-z2wMAobdo0BNxNa7hG_gIHVPkXu14Jfo1JhUhS4wES3DdY3a6olqPcRN1TCCUVHd-1TLd1BBS-yq9tdJ6HCewhe5fXonaRRKwutvoH7i_eR4m3fQ1GoVzVAA3IngpTr4ptnM3Ef3fj-5wZYmitzrRUyQtfARTl3qGaXP_g8pHFAP0zrNVvOnV-jcNMKm8YZNcgcs1SuLSFtUDXpf7Nr2_xOhiNM-biES6Dza1sMLrlxULFuctudO9lykB7yFh3LHMxtIZyIUHuy0RbjuOGC5PmDowLttZpPI_j4ynJHAaAWr8Ddz764WdQ\",\"e\":\"AQAB\"}]}"); 76 | }); 77 | 78 | } 79 | 80 | private static String auth = "Bearer eyJraWQiOiJUal9sX3RJQlRnaW5PdFFiTDBQdjV3IiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJ1cm46Y29tOm5ldHdvcmtudDpvYXV0aDI6djEiLCJhdWQiOiJ1cm46Y29tLm5ldHdvcmtudCIsImV4cCI6MjAxOTg3MzkyOSwianRpIjoiLWZaNzY5cWxFa05RRnFGQWNkLWlGQSIsImlhdCI6MTcwNDUxMzkyOSwibmJmIjoxNzA0NTEzODA5LCJ2ZXJzaW9uIjoiJzEuMCciLCJ1c2VyX2lkIjoic3RldmUiLCJ1c2VyX3R5cGUiOiJFTVBMT1lFRSIsImNsaWVudF9pZCI6ImY3ZDQyMzQ4LWM2NDctNGVmYi1hNTJkLTRjNTc4NzQyMWU3MiIsInJvbGVzIjoidXNlciIsInNjb3BlIjpbIndvcmxkLnIiLCJ3b3JsZC53Iiwic2VydmVyLmluZm8uciJdfQ.za1C5bh4QxI6McXeogmXtVYKS611VVPNn14PCcV5Q8NadVDIcgL7TCjT_KKbU35BiV0-xlu1BdyzMEZOr3E1xlhK-WB-eAHRF__IXydJXN-pWSo6wp-Jq4-rzZlW_e4kprlT_B0GHngzlJuru5Y-00Mh8bnhUuy1QTxF3JFHnz-62bJZmyeRx9iveNGvUTF5AJGPHhRoHMMCFJjSakiuy8El-wrTFN-Zi3e09n3xxPHj6qH_mDG_WyHMJ5r3bew9asMxU8QV_A0c7jkcwxJRg6UcVLSecmd-GxrCmjw3hiIJKInyqBBIa0uZkHmm4T1c9XiG1LbFHNhHwi0H_sykoQ"; 81 | 82 | @Test 83 | public void testJsonRpcValidationError() throws Exception { 84 | Http2Client client = Http2Client.getInstance(); 85 | String message = "{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\"}}"; 86 | 87 | final CountDownLatch latch = new CountDownLatch(1); 88 | final ClientConnection connection; 89 | try { 90 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get(); 91 | } catch (Exception e) { 92 | throw new ClientException(e); 93 | } 94 | final AtomicReference reference = new AtomicReference<>(); 95 | try { 96 | ClientRequest request = new ClientRequest().setPath("/api/json").setMethod(Methods.POST); 97 | request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json"); 98 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 99 | request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked"); 100 | connection.sendRequest(request, client.createClientCallback(reference, latch, message)); 101 | 102 | latch.await(); 103 | } catch (Exception e) { 104 | logger.error("Exception: ", e); 105 | throw new ClientException(e); 106 | } finally { 107 | IoUtils.safeClose(connection); 108 | } 109 | int statusCode = reference.get().getResponseCode(); 110 | System.out.println("statusCode = " + statusCode); 111 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 112 | System.out.println("body = " + body); 113 | Assert.assertTrue(body.contains("ERR11004")); 114 | } 115 | 116 | /** 117 | * Test empty post request body and expect 400 error from the server. 118 | * 119 | * @throws Exception 120 | */ 121 | @Test 122 | public void testJsonRpcEmptyBody() throws Exception { 123 | Http2Client client = Http2Client.getInstance(); 124 | 125 | String message = ""; 126 | final CountDownLatch latch = new CountDownLatch(1); 127 | final ClientConnection connection; 128 | try { 129 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get(); 130 | } catch (Exception e) { 131 | throw new ClientException(e); 132 | } 133 | final AtomicReference reference = new AtomicReference<>(); 134 | try { 135 | ClientRequest request = new ClientRequest().setPath("/api/json").setMethod(Methods.POST); 136 | request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json"); 137 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 138 | request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked"); 139 | connection.sendRequest(request, client.createClientCallback(reference, latch, message)); 140 | 141 | latch.await(); 142 | } catch (Exception e) { 143 | logger.error("Exception: ", e); 144 | throw new ClientException(e); 145 | } finally { 146 | IoUtils.safeClose(connection); 147 | } 148 | int statusCode = reference.get().getResponseCode(); 149 | System.out.println("statusCode = " + statusCode); 150 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 151 | System.out.println("body = " + body); 152 | Assert.assertTrue(body.contains("ERR11201")); 153 | } 154 | 155 | 156 | // Ignore it as we cannot get the jwks and x509 certificate is not supported anymore. 157 | @Test 158 | public void testJsonRpcPostNoError() throws Exception { 159 | Http2Client client = Http2Client.getInstance(); 160 | 161 | String message = "{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\",\"ruleId\":\"ruleId\"}}"; 162 | final CountDownLatch latch = new CountDownLatch(1); 163 | final ClientConnection connection; 164 | try { 165 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, enableHttp2 ? OptionMap.create(UndertowOptions.ENABLE_HTTP2, true): OptionMap.EMPTY).get(); 166 | } catch (Exception e) { 167 | throw new ClientException(e); 168 | } 169 | final AtomicReference reference = new AtomicReference<>(); 170 | try { 171 | ClientRequest request = new ClientRequest().setPath("/api/json").setMethod(Methods.POST); 172 | request.getRequestHeaders().put(Headers.CONTENT_TYPE, "application/json"); 173 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 174 | request.getRequestHeaders().put(Headers.TRANSFER_ENCODING, "chunked"); 175 | connection.sendRequest(request, client.createClientCallback(reference, latch, message)); 176 | 177 | latch.await(); 178 | } catch (Exception e) { 179 | logger.error("Exception: ", e); 180 | throw new ClientException(e); 181 | } finally { 182 | IoUtils.safeClose(connection); 183 | } 184 | int statusCode = reference.get().getResponseCode(); 185 | System.out.println("statusCode = " + statusCode); 186 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 187 | System.out.println("body = " + body); 188 | Assert.assertEquals("OK", body); 189 | } 190 | 191 | 192 | // Ignore it as we cannot get the jwks and x509 certificate is not supported anymore. 193 | @Test 194 | public void testJsonRpcGetNoError() throws Exception { 195 | Http2Client client = Http2Client.getInstance(); 196 | String message = "/api/json?cmd=" + URLEncoder.encode("{\"host\":\"lightapi.net\",\"service\":\"rule\",\"action\":\"deleteRule\",\"version\":\"0.1.0\",\"data\":{\"hostId\":\"1234567\",\"ruleId\":\"ruleId\"}}", StandardCharsets.UTF_8); 197 | System.out.println("message = " + message); 198 | 199 | final CountDownLatch latch = new CountDownLatch(1); 200 | final ClientConnection connection; 201 | try { 202 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get(); 203 | } catch (Exception e) { 204 | throw new ClientException(e); 205 | } 206 | final AtomicReference reference = new AtomicReference<>(); 207 | try { 208 | ClientRequest request = new ClientRequest().setMethod(Methods.GET).setPath(message); 209 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 210 | connection.sendRequest(request, client.createClientCallback(reference, latch)); 211 | latch.await(); 212 | int statusCode = reference.get().getResponseCode(); 213 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 214 | System.out.println("body = " + body); 215 | Assert.assertEquals(200, statusCode); 216 | Assert.assertEquals("OK", body); 217 | } catch (Exception e) { 218 | logger.error("Exception: ", e); 219 | throw new ClientException(e); 220 | } finally { 221 | IoUtils.safeClose(connection); 222 | } 223 | } 224 | 225 | @Test 226 | public void testJsonRpcGetNoParamValue() throws Exception { 227 | Http2Client client = Http2Client.getInstance(); 228 | String message = "/api/json?cmd="; 229 | System.out.println("message = " + message); 230 | 231 | final CountDownLatch latch = new CountDownLatch(1); 232 | final ClientConnection connection; 233 | try { 234 | connection = client.connect(new URI(url), Http2Client.WORKER, Http2Client.SSL, Http2Client.BUFFER_POOL, OptionMap.create(UndertowOptions.ENABLE_HTTP2, true)).get(); 235 | } catch (Exception e) { 236 | throw new ClientException(e); 237 | } 238 | final AtomicReference reference = new AtomicReference<>(); 239 | try { 240 | ClientRequest request = new ClientRequest().setMethod(Methods.GET).setPath(message); 241 | request.getRequestHeaders().put(Headers.AUTHORIZATION, auth); 242 | connection.sendRequest(request, client.createClientCallback(reference, latch)); 243 | latch.await(); 244 | int statusCode = reference.get().getResponseCode(); 245 | String body = reference.get().getAttachment(Http2Client.RESPONSE_BODY); 246 | System.out.println("statusCode = " + statusCode + " body = " + body); 247 | Assert.assertEquals(400, statusCode); 248 | Assert.assertTrue(body.contains("ERR11202")); 249 | } catch (Exception e) { 250 | logger.error("Exception: ", e); 251 | throw new ClientException(e); 252 | } finally { 253 | IoUtils.safeClose(connection); 254 | } 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /rpc-router/src/main/java/com/networknt/rpc/router/SchemaHandler.java: -------------------------------------------------------------------------------- 1 | package com.networknt.rpc.router; 2 | 3 | import com.fasterxml.jackson.databind.JsonNode; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; 6 | import com.networknt.config.Config; 7 | import com.networknt.config.JsonMapper; 8 | import com.networknt.handler.Handler; 9 | import com.networknt.handler.MiddlewareHandler; 10 | import com.networknt.httpstring.AttachmentConstants; 11 | import com.networknt.rpc.HybridHandler; 12 | import com.networknt.status.Status; 13 | import com.networknt.utility.Constants; 14 | import com.networknt.utility.ModuleRegistry; 15 | import com.networknt.utility.Util; 16 | import io.undertow.Handlers; 17 | import io.undertow.server.HttpHandler; 18 | import io.undertow.server.HttpServerExchange; 19 | import io.undertow.util.Headers; 20 | import io.undertow.util.HttpString; 21 | import io.undertow.util.Methods; 22 | import io.undertow.util.StatusCodes; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.io.IOException; 27 | import java.io.InputStream; 28 | import java.net.URL; 29 | import java.net.URLDecoder; 30 | import java.nio.ByteBuffer; 31 | import java.nio.charset.StandardCharsets; 32 | import java.util.*; 33 | 34 | public class SchemaHandler implements MiddlewareHandler { 35 | static final Logger logger = LoggerFactory.getLogger(SchemaHandler.class); 36 | static final String REQUEST = "request"; 37 | static final String SCHEMA = "schema"; 38 | static final String DATA = "data"; 39 | static final String CMD = "cmd"; 40 | static final String STATUS_HANDLER_NOT_FOUND = "ERR11200"; 41 | static final String STATUS_REQUEST_BODY_EMPTY = "ERR11201"; 42 | static final String STATUS_METHOD_NOT_ALLOWED = "ERR10008"; 43 | static final String STATUS_REQUEST_CMD_EMPTY = "ERR11202"; 44 | 45 | private static final String SPEC_YAML = "spec.yaml"; 46 | private volatile HttpHandler next; 47 | public static Map services = new HashMap<>(); 48 | 49 | public SchemaHandler() { 50 | if(logger.isTraceEnabled()) logger.trace("SchemaHandler constructed"); 51 | // load all spec.yaml from resources folder and merge them into one map. 52 | try { 53 | final Enumeration schemaResources = SchemaHandler.class.getClassLoader().getResources(SPEC_YAML); 54 | while(schemaResources.hasMoreElements()) { 55 | URL url = schemaResources.nextElement(); 56 | if(logger.isDebugEnabled()) logger.debug("schema file = {}", url); 57 | try (InputStream is = url.openStream()) { 58 | services.putAll(parseYaml(is)); 59 | } 60 | } 61 | if(logger.isDebugEnabled()) logger.debug("services = {}", Config.getInstance().getMapper().writeValueAsString(services)); 62 | } catch (IOException e) { 63 | logger.error("Error loading spec.yaml files from service jars", e); 64 | // throw exception to stop the service as this is a serious error. 65 | throw new RuntimeException("Error loading spec.yaml files from service jars"); 66 | } 67 | } 68 | 69 | public static Map> parseYaml(InputStream yamlStream) throws IOException { 70 | ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); 71 | Map rootNode = mapper.readValue(yamlStream, Map.class); 72 | Map> resultMap = new LinkedHashMap<>(); 73 | 74 | String host = (String) rootNode.get("host"); 75 | String service = (String) rootNode.get("service"); 76 | 77 | // Extract schemas 78 | Map> schemas = new HashMap<>(); 79 | Map schemasNode = (Map) rootNode.get("schemas"); 80 | if (schemasNode != null) { 81 | for(Map.Entry entry: schemasNode.entrySet()){ 82 | schemas.put(entry.getKey(), (Map) entry.getValue()); 83 | } 84 | } 85 | 86 | 87 | // Process actions 88 | List> actionsNode = (List>) rootNode.get("action"); 89 | 90 | if (actionsNode != null) { 91 | for (Map actionNode : actionsNode) { 92 | String actionName = (String) actionNode.get("name"); 93 | String actionVersion = (String) actionNode.get("version"); 94 | String key = host + "/" + service + "/" + actionName + "/" + actionVersion; 95 | 96 | Boolean skipAuth = (Boolean) actionNode.get("skipAuth") != null ? (Boolean) actionNode.get("skipAuth") : false; 97 | String scope = (String) actionNode.get("scope") ; 98 | 99 | Map actionMap = new LinkedHashMap<>(); 100 | actionMap.put("skipAuth", skipAuth); 101 | if(scope != null) { 102 | actionMap.put("scope", scope); 103 | } 104 | 105 | // resolve request schema 106 | Map requestNode = (Map) actionNode.get("request"); 107 | if(requestNode != null) { 108 | Map requestMap = new LinkedHashMap<>(); 109 | Map schemaRef = (Map) requestNode.get("schema"); 110 | if (schemaRef != null && schemaRef.containsKey("$ref")) { 111 | String ref = (String) schemaRef.get("$ref"); 112 | String schemaName = ref.substring(ref.lastIndexOf('/') + 1); 113 | Map resolvedSchema = schemas.get(schemaName); 114 | if(resolvedSchema != null) { 115 | requestMap.put("schema", resolvedSchema); 116 | } 117 | } 118 | actionMap.put("request", requestMap); 119 | } 120 | 121 | // resolve response schema 122 | Map responseNode = (Map) actionNode.get("response"); 123 | if(responseNode != null) { 124 | Map responseMap = new LinkedHashMap<>(); 125 | Map schemaRef = (Map) responseNode.get("schema"); 126 | if (schemaRef != null && schemaRef.containsKey("$ref")) { 127 | String ref = (String) schemaRef.get("$ref"); 128 | String schemaName = ref.substring(ref.lastIndexOf('/') + 1); 129 | Map resolvedSchema = schemas.get(schemaName); 130 | if(resolvedSchema != null) { 131 | responseMap.put("schema", resolvedSchema); 132 | } 133 | } 134 | actionMap.put("response", responseMap); 135 | } 136 | 137 | resultMap.put(key, actionMap); 138 | } 139 | } 140 | return resultMap; 141 | } 142 | 143 | @Override 144 | public HttpHandler getNext() { 145 | return this.next; 146 | } 147 | 148 | @Override 149 | public MiddlewareHandler setNext(HttpHandler next) { 150 | Handlers.handlerNotNull(next); 151 | this.next = next; 152 | return this; 153 | } 154 | 155 | @Override 156 | public boolean isEnabled() { 157 | return true; 158 | } 159 | 160 | @Override 161 | public void register() { 162 | ModuleRegistry.registerModule(null, SchemaHandler.class.getName(), null, null); 163 | } 164 | 165 | @Override 166 | public void handleRequest(HttpServerExchange exchange) throws Exception { 167 | if (logger.isTraceEnabled()) 168 | logger.trace("SchemaHandler.handleRequest starts."); 169 | if(Methods.POST.equals(exchange.getRequestMethod())) { 170 | exchange.getRequestReceiver().receiveFullString((exchange1, message) -> { 171 | if(message == null || message.trim().isEmpty()) { 172 | this.handleEmptyPostRequest(exchange1); 173 | return; 174 | } 175 | if(logger.isDebugEnabled()) logger.debug("Post method with message = {}", message); 176 | processRequest(exchange1, message); 177 | }); 178 | } else if(Methods.GET.equals(exchange.getRequestMethod())) { 179 | Map> params = exchange.getQueryParameters(); 180 | String cmd = ((Deque)params.get(CMD)).getFirst(); 181 | if(cmd == null || cmd.trim().isEmpty()) { 182 | this.handleMissingGetCommand(exchange); 183 | return; 184 | } 185 | String message = URLDecoder.decode(cmd, StandardCharsets.UTF_8); 186 | if(logger.isDebugEnabled()) logger.debug("Get method with message = {}", message); 187 | processRequest(exchange, message); 188 | } else { 189 | this.handleUnsupportedMethod(exchange); 190 | } 191 | } 192 | 193 | private void processRequest(HttpServerExchange exchange, String message) { 194 | Map map = JsonMapper.string2Map(message); 195 | String serviceId = Util.getServiceId(map); 196 | logger.debug("serviceId = {}", serviceId); 197 | HybridHandler handler = RpcStartupHookProvider.serviceMap.get(serviceId); 198 | if(handler == null) { 199 | this.handleMissingHandler(exchange, serviceId); 200 | return; 201 | } 202 | Map data = (Map)map.get(DATA); 203 | Map serviceMap = (Map)services.get(serviceId); 204 | Map requestMap = (Map)serviceMap.get(REQUEST); 205 | ByteBuffer error = handler.validate(serviceId, (Map)requestMap.get(SCHEMA), data); 206 | if(error != null) { 207 | exchange.setStatusCode(StatusCodes.BAD_REQUEST); 208 | exchange.getResponseSender().send(error); 209 | return; 210 | } 211 | 212 | if(logger.isDebugEnabled()) { 213 | logger.debug("serviceId = {} serviceMap = {}", serviceId, JsonMapper.toJson(serviceMap)); 214 | } 215 | // put the serviceId and data as well as the schema into the exchange for other handlers to use. 216 | Map auditInfo = exchange.getAttachment(AttachmentConstants.AUDIT_INFO) == null 217 | ? new HashMap<>() 218 | : exchange.getAttachment(AttachmentConstants.AUDIT_INFO); 219 | auditInfo.put(Constants.ENDPOINT_STRING, serviceId); 220 | auditInfo.put(Constants.HYBRID_SERVICE_ID, serviceId); 221 | auditInfo.put(Constants.HYBRID_SERVICE_MAP, serviceMap); 222 | auditInfo.put(Constants.HYBRID_SERVICE_DATA, data); 223 | exchange.putAttachment(AttachmentConstants.AUDIT_INFO, auditInfo); 224 | 225 | // if exchange is not ended, then call the next handler in the chain. 226 | if(logger.isTraceEnabled()) logger.trace("SchemaHandler.handleRequest ends."); 227 | try { 228 | Handler.next(exchange, next); 229 | } catch (Exception e) { 230 | logger.error("Exception:", e); 231 | exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR); 232 | exchange.endExchange(); 233 | } 234 | } 235 | 236 | void completeExchange(ByteBuffer result, HttpServerExchange exchange) { 237 | if(result == null) { 238 | // there is nothing returned from the handler. 239 | exchange.setStatusCode(StatusCodes.OK); 240 | exchange.endExchange(); 241 | } else { 242 | // we are expecting the handler set the statusCode if there is an error. 243 | // if there is no status code, default 200 will be used. 244 | exchange.getResponseSender().send(result); 245 | } 246 | } 247 | 248 | private void handleMissingHandler(HttpServerExchange exchange, String serviceId) { 249 | logger.error("Handler is not found for serviceId {}", serviceId); 250 | Status status = new Status(STATUS_HANDLER_NOT_FOUND, serviceId); 251 | exchange.setStatusCode(status.getStatusCode()); 252 | exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "application/json"); 253 | exchange.getResponseSender().send(status.toString()); 254 | } 255 | 256 | private void handleEmptyPostRequest(HttpServerExchange exchange) { 257 | // payload of request is missing 258 | logger.error("Post request without body"); 259 | exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); 260 | Status status = new Status(STATUS_REQUEST_BODY_EMPTY); 261 | exchange.setStatusCode(status.getStatusCode()); 262 | exchange.getResponseSender().send(status.toString()); 263 | } 264 | 265 | private void handleMissingGetCommand(HttpServerExchange exchange) { 266 | // payload of request is missing 267 | logger.error("Get param cmd is empty for light-hybrid-4j"); 268 | exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); 269 | Status status = new Status(STATUS_REQUEST_CMD_EMPTY); 270 | exchange.setStatusCode(status.getStatusCode()); 271 | exchange.getResponseSender().send(status.toString()); 272 | } 273 | 274 | private void handleUnsupportedMethod(HttpServerExchange exchange) { 275 | // options is handled in middleware handler so if reach here, invalid. 276 | exchange.getResponseHeaders().add(new HttpString("Content-Type"), "application/json"); 277 | String method = exchange.getRequestMethod().toString(); 278 | String path = exchange.getRequestPath(); 279 | Status status = new Status(STATUS_METHOD_NOT_ALLOWED, method, path); 280 | exchange.setStatusCode(status.getStatusCode()); 281 | exchange.getResponseSender().send(status.toString()); 282 | } 283 | 284 | } 285 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 19 | 4.0.0 20 | com.networknt 21 | light-hybrid-4j 22 | 2.3.2-SNAPSHOT 23 | pom 24 | Parent POM 25 | Light 4J Framework that supports both monolithic and microservices with rpc 26 | https://github.com/networknt/light-hybrid-4j 27 | 28 | 29 | 30 | stevehu 31 | Steve Hu 32 | stevehu@gmail.com 33 | 34 | 35 | 36 | 37 | github 38 | https://github.com/networknt/light-hybrid-4j/issues 39 | 40 | 41 | 42 | 43 | Apache License Version 2.0 44 | http://repository.jboss.org/licenses/apache-2.0.txt 45 | repo 46 | 47 | 48 | 49 | 50 | scm:git://github.com:networknt/light-hybrid-4j.git 51 | scm:git://github.com:networknt/light-hybrid-4j.git 52 | https://github.com:networknt/light-hybrid-4j.git 53 | 54 | 55 | 56 | 57 | central 58 | https://central.sonatype.com/service/local/staging/deploy/maven2/ 59 | 60 | 61 | central 62 | https://central.sonatype.com/repository/maven-snapshots/ 63 | 64 | 65 | 66 | 67 | 68 | snapshot 69 | https://central.sonatype.com/repository/maven-snapshots/ 70 | 71 | false 72 | 73 | 74 | true 75 | 76 | 77 | 78 | central 79 | https://repo1.maven.org/maven2 80 | 81 | true 82 | 83 | 84 | 85 | 86 | 87 | central 88 | https://repo1.maven.org/maven2 89 | 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 21 97 | UTF-8 98 | 2.3.2-SNAPSHOT 99 | 2.18.3 100 | 2.0.17 101 | 0.9.6 102 | 1.5.18 103 | 4.13.2 104 | 2.10.0 105 | 2.3.20.Final 106 | 3.8.0 107 | 1.5.1 108 | 4.8.149 109 | 2.4 110 | 1.0.0 111 | 3.4.1 112 | 1.7.0 113 | 3.2.7 114 | 115 | 116 | 117 | rpc-router 118 | rpc-security 119 | 120 | 121 | 122 | 123 | 124 | 125 | com.networknt 126 | rpc-router 127 | ${project.version} 128 | 129 | 130 | com.networknt 131 | rpc-security 132 | ${project.version} 133 | 134 | 135 | 136 | com.networknt 137 | config 138 | ${version.light-4j} 139 | 140 | 141 | com.networknt 142 | utility 143 | ${version.light-4j} 144 | 145 | 146 | com.networknt 147 | http-string 148 | ${version.light-4j} 149 | 150 | 151 | com.networknt 152 | security 153 | ${version.light-4j} 154 | 155 | 156 | com.networknt 157 | unified-security 158 | ${version.light-4j} 159 | 160 | 161 | com.networknt 162 | api-key 163 | ${version.light-4j} 164 | 165 | 166 | com.networknt 167 | basic-auth 168 | ${version.light-4j} 169 | 170 | 171 | com.networknt 172 | client 173 | ${version.light-4j} 174 | 175 | 176 | com.networknt 177 | audit 178 | ${version.light-4j} 179 | 180 | 181 | com.networknt 182 | dump 183 | ${version.light-4j} 184 | 185 | 186 | com.networknt 187 | info 188 | ${version.light-4j} 189 | 190 | 191 | com.networknt 192 | health 193 | ${version.light-4j} 194 | 195 | 196 | com.networknt 197 | status 198 | ${version.light-4j} 199 | 200 | 201 | com.networknt 202 | exception 203 | ${version.light-4j} 204 | 205 | 206 | com.networknt 207 | body 208 | ${version.light-4j} 209 | 210 | 211 | com.networknt 212 | mask 213 | ${version.light-4j} 214 | 215 | 216 | com.networknt 217 | handler 218 | ${version.light-4j} 219 | 220 | 221 | com.networknt 222 | metrics 223 | ${version.light-4j} 224 | 225 | 226 | com.networknt 227 | sanitizer 228 | ${version.light-4j} 229 | 230 | 231 | com.networknt 232 | traceability 233 | ${version.light-4j} 234 | 235 | 236 | com.networknt 237 | correlation 238 | ${version.light-4j} 239 | 240 | 241 | com.networknt 242 | service 243 | ${version.light-4j} 244 | 245 | 246 | com.networknt 247 | switcher 248 | ${version.light-4j} 249 | 250 | 251 | com.networknt 252 | registry 253 | ${version.light-4j} 254 | 255 | 256 | com.networknt 257 | balance 258 | ${version.light-4j} 259 | 260 | 261 | com.networknt 262 | consul 263 | ${version.light-4j} 264 | 265 | 266 | com.networknt 267 | zookeeper 268 | ${version.light-4j} 269 | 270 | 271 | com.networknt 272 | cors 273 | ${version.light-4j} 274 | 275 | 276 | com.networknt 277 | server 278 | ${version.light-4j} 279 | 280 | 281 | com.networknt 282 | resource 283 | ${version.light-4j} 284 | 285 | 286 | com.networknt 287 | json-schema-validator 288 | ${version.json-schema-validator} 289 | 290 | 291 | 292 | 293 | com.fasterxml.jackson.core 294 | jackson-databind 295 | ${version.jackson} 296 | 297 | 298 | org.slf4j 299 | slf4j-api 300 | ${version.slf4j} 301 | 302 | 303 | org.bitbucket.b_c 304 | jose4j 305 | ${version.jose4j} 306 | 307 | 308 | io.undertow 309 | undertow-core 310 | ${version.undertow} 311 | 312 | 313 | io.github.classgraph 314 | classgraph 315 | ${version.classgraph} 316 | 317 | 318 | 319 | 320 | ch.qos.logback 321 | logback-classic 322 | ${version.logback} 323 | test 324 | 325 | 326 | junit 327 | junit 328 | ${version.junit} 329 | test 330 | 331 | 332 | org.assertj 333 | assertj-core 334 | ${version.assertj} 335 | test 336 | 337 | 338 | org.mockito 339 | mockito-core 340 | ${version.mockito} 341 | test 342 | 343 | 344 | 345 | 346 | 347 | ${basedir}/src/main/java 348 | ${basedir}/src/test/java 349 | 350 | 351 | false 352 | ${basedir}/src/main/resources 353 | 354 | **/* 355 | 356 | 357 | 358 | 359 | 360 | false 361 | ${basedir}/src/test/resources 362 | 363 | **/* 364 | 365 | 366 | 367 | 368 | 369 | 370 | com.networknt 371 | javadoc-packagelist-maven-plugin 372 | ${version.javadoc-packagelist-plugin} 373 | 374 | 375 | package 376 | 377 | gen-package-list 378 | 379 | 380 | 381 | 382 | 383 | net.quies.colfer 384 | colfer-maven-plugin 385 | 1.10 386 | 387 | com/networknt/colfer 388 | 389 | 390 | 391 | org.apache.maven.plugins 392 | maven-source-plugin 393 | 3.0.1 394 | 395 | 396 | attach-sources 397 | 398 | jar 399 | 400 | 401 | 402 | 403 | 404 | org.apache.maven.plugins 405 | maven-javadoc-plugin 406 | ${version.maven-javadoc} 407 | 408 | 409 | attach-javadocs 410 | 411 | jar 412 | 413 | 414 | 415 | 416 | 417 | org.apache.maven.plugins 418 | maven-surefire-plugin 419 | 2.14 420 | 421 | 422 | org.apache.maven.surefire 423 | surefire-junit47 424 | 2.14 425 | 426 | 427 | 428 | 429 | 430 | org.jacoco 431 | jacoco-maven-plugin 432 | 0.7.4.201502262128 433 | 434 | 435 | pre-unit-test 436 | 437 | prepare-agent 438 | 439 | 440 | surefireArgLine 441 | 442 | 443 | 444 | post-unit-test 445 | test 446 | 447 | report 448 | 449 | 450 | 451 | 452 | 453 | 454 | org.apache.maven.plugins 455 | maven-compiler-plugin 456 | 3.8.0 457 | 458 | ${java.version} 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | org.eclipse.m2e 467 | lifecycle-mapping 468 | 1.0.0 469 | 470 | 471 | 472 | 473 | 474 | 475 | org.apache.maven.plugins 476 | 477 | 478 | maven-compiler-plugin 479 | 480 | 481 | [3.3,) 482 | 483 | 484 | compile 485 | testCompile 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | org.jacoco 495 | 496 | jacoco-maven-plugin 497 | 498 | 499 | [0.7.4.201502262128,) 500 | 501 | 502 | prepare-agent 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | org.apache.maven.plugins 521 | maven-surefire-report-plugin 522 | 2.20.1 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | schema-generation 531 | 532 | 533 | release-sign-artifacts 534 | 535 | 536 | performRelease 537 | true 538 | 539 | 540 | 541 | 542 | 543 | org.apache.maven.plugins 544 | maven-gpg-plugin 545 | ${version.maven-gpg} 546 | 547 | 548 | sign-artifacts 549 | verify 550 | 551 | sign 552 | 553 | 554 | 555 | 556 | 557 | org.sonatype.central 558 | central-publishing-maven-plugin 559 | 0.7.0 560 | true 561 | 562 | central 563 | true 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [2.3.1](https://github.com/networknt/light-hybrid-4j/tree/2.3.1) (2025-09-19) 4 | 5 | 6 | **Merged pull requests:** 7 | 8 | 9 | - [pre-commit.ci] pre-commit autoupdate [\#162](https://github.com/networknt/light-hybrid-4j/pull/162) ([pre-commit-ci](https://github.com/apps/pre-commit-ci)) 10 | 11 | 12 | ## [2.3.0](https://github.com/networknt/light-hybrid-4j/tree/2.3.0) (2025-06-25) 13 | 14 | 15 | **Merged pull requests:** 16 | 17 | 18 | 19 | 20 | ## [2.2.2](https://github.com/networknt/light-hybrid-4j/tree/2.2.2) (2025-06-05) 21 | 22 | 23 | **Merged pull requests:** 24 | 25 | 26 | - Multiple Handler Packages Configuration Option [\#159](https://github.com/networknt/light-hybrid-4j/pull/159) ([KalevGonvick](https://github.com/KalevGonvick)) 27 | 28 | 29 | ## [2.2.1](https://github.com/networknt/light-hybrid-4j/tree/2.2.1) (2025-03-22) 30 | 31 | 32 | **Merged pull requests:** 33 | 34 | 35 | 36 | 37 | ## [2.2.0](https://github.com/networknt/light-hybrid-4j/tree/2.2.0) (2025-02-12) 38 | 39 | 40 | **Merged pull requests:** 41 | 42 | 43 | - using ExchangeCompleteListener with DirectByteBuffer cleanup in Undertow [\#156](https://github.com/networknt/light-hybrid-4j/pull/156) ([atmoshaman](https://github.com/atmoshaman)) 44 | - Updated JsonHandler to account for missing audit info. [\#153](https://github.com/networknt/light-hybrid-4j/pull/153) ([KalevGonvick](https://github.com/KalevGonvick)) 45 | - fixes \#125 implement isSkipAuth method in the jwt handler [\#151](https://github.com/networknt/light-hybrid-4j/pull/151) ([stevehu](https://github.com/stevehu)) 46 | - clean direct buffer [\#150](https://github.com/networknt/light-hybrid-4j/pull/150) ([atmoshaman](https://github.com/atmoshaman)) 47 | 48 | 49 | ## [2.1.37](https://github.com/networknt/light-hybrid-4j/tree/2.1.37) (2024-09-20) 50 | 51 | 52 | **Merged pull requests:** 53 | 54 | 55 | ## [2.1.36](https://github.com/networknt/light-hybrid-4j/tree/2.1.36) (2024-08-27) 56 | 57 | 58 | **Merged pull requests:** 59 | 60 | 61 | ## [2.1.35](https://github.com/networknt/light-hybrid-4j/tree/2.1.35) (2024-08-17) 62 | 63 | 64 | **Merged pull requests:** 65 | 66 | 67 | - fixes \#146 use security.yml for config and remove dup JwtVerifier ins… [\#147](https://github.com/networknt/light-hybrid-4j/pull/147) ([stevehu](https://github.com/stevehu)) 68 | - fixes \#144 add schema handler to use handler chain [\#145](https://github.com/networknt/light-hybrid-4j/pull/145) ([stevehu](https://github.com/stevehu)) 69 | - fixes \#142 upgrade json-schema-validator to 1.5.0 [\#143](https://github.com/networknt/light-hybrid-4j/pull/143) ([stevehu](https://github.com/stevehu)) 70 | ## [2.1.34](https://github.com/networknt/light-hybrid-4j/tree/2.1.34) (2024-06-22) 71 | 72 | 73 | **Merged pull requests:** 74 | 75 | 76 | - [pre-commit.ci] pre-commit autoupdate [\#141](https://github.com/networknt/light-hybrid-4j/pull/141) ([pre-commit-ci](https://github.com/apps/pre-commit-ci)) 77 | 78 | 79 | ## [2.1.33](https://github.com/networknt/light-hybrid-4j/tree/2.1.33) (2024-03-31) 80 | 81 | 82 | **Merged pull requests:** 83 | 84 | 85 | 86 | 87 | ## [2.1.32](https://github.com/networknt/light-hybrid-4j/tree/2.1.32) (2024-02-27) 88 | 89 | 90 | **Merged pull requests:** 91 | 92 | 93 | - [pre-commit.ci] pre-commit autoupdate [\#140](https://github.com/networknt/light-hybrid-4j/pull/140) ([pre-commit-ci](https://github.com/apps/pre-commit-ci)) 94 | - fixes \#138 add pre-commit hook and apply it [\#139](https://github.com/networknt/light-hybrid-4j/pull/139) ([stevehu](https://github.com/stevehu)) 95 | - fixes \#136 Use NoneDecryptedInstance to load the config for registerM… [\#137](https://github.com/networknt/light-hybrid-4j/pull/137) ([stevehu](https://github.com/stevehu)) 96 | 97 | 98 | ## [2.1.31](https://github.com/networknt/light-hybrid-4j/tree/2.1.31) (2024-01-16) 99 | 100 | 101 | **Merged pull requests:** 102 | 103 | 104 | - fixes \#134 Fix test cases by adding a server2 with jwks endpoint [\#135](https://github.com/networknt/light-hybrid-4j/pull/135) ([stevehu](https://github.com/stevehu)) 105 | - fixes \#132 disable some test cases that is relying on jwks endpoint [\#133](https://github.com/networknt/light-hybrid-4j/pull/133) ([stevehu](https://github.com/stevehu)) 106 | - fixes \#130 replace system.out.println with logger [\#131](https://github.com/networknt/light-hybrid-4j/pull/131) ([stevehu](https://github.com/stevehu)) 107 | 108 | 109 | ## [2.1.30](https://github.com/networknt/light-hybrid-4j/tree/2.1.30) (2023-11-21) 110 | 111 | 112 | **Merged pull requests:** 113 | 114 | 115 | 116 | 117 | ## [2.1.29](https://github.com/networknt/light-hybrid-4j/tree/2.1.29) (2023-11-19) 118 | 119 | 120 | **Merged pull requests:** 121 | 122 | 123 | - fixes \#127 refactor module registry to add config name [\#128](https://github.com/networknt/light-hybrid-4j/pull/128) ([stevehu](https://github.com/stevehu)) 124 | - fixes \#125 sync the hybrid-security.yml with the security.yml in ligh… [\#126](https://github.com/networknt/light-hybrid-4j/pull/126) ([stevehu](https://github.com/stevehu)) 125 | 126 | 127 | ## [2.1.28](https://github.com/networknt/light-hybrid-4j/tree/2.1.28) (2023-10-24) 128 | 129 | 130 | **Merged pull requests:** 131 | 132 | 133 | 134 | 135 | ## [2.1.27](https://github.com/networknt/light-hybrid-4j/tree/2.1.27) (2023-10-04) 136 | 137 | 138 | **Merged pull requests:** 139 | 140 | 141 | 142 | 143 | ## [2.1.26](https://github.com/networknt/light-hybrid-4j/tree/2.1.26) (2023-08-17) 144 | 145 | 146 | **Merged pull requests:** 147 | 148 | 149 | ## [2.1.25](https://github.com/networknt/light-hybrid-4j/tree/2.1.25) (2023-08-08) 150 | 151 | 152 | **Merged pull requests:** 153 | 154 | 155 | 156 | 157 | ## [2.1.24](https://github.com/networknt/light-hybrid-4j/tree/2.1.24) (2023-08-07) 158 | 159 | 160 | **Merged pull requests:** 161 | 162 | 163 | 164 | 165 | ## [2.1.23](https://github.com/networknt/light-hybrid-4j/tree/2.1.23) (2023-07-11) 166 | 167 | 168 | **Merged pull requests:** 169 | 170 | 171 | 172 | 173 | ## [2.1.22](https://github.com/networknt/light-hybrid-4j/tree/2.1.22) (2023-06-22) 174 | 175 | 176 | **Merged pull requests:** 177 | 178 | 179 | 180 | 181 | ## [2.1.21](https://github.com/networknt/light-hybrid-4j/tree/2.1.21) (2023-06-22) 182 | 183 | 184 | **Merged pull requests:** 185 | 186 | 187 | 188 | 189 | ## [2.1.20](https://github.com/networknt/light-hybrid-4j/tree/2.1.20) (2023-06-17) 190 | 191 | 192 | **Merged pull requests:** 193 | 194 | 195 | ## [2.1.19](https://github.com/networknt/light-hybrid-4j/tree/2.1.19) (2023-06-05) 196 | 197 | 198 | **Merged pull requests:** 199 | 200 | 201 | 202 | 203 | ## [2.1.18](https://github.com/networknt/light-hybrid-4j/tree/2.1.18) (2023-05-06) 204 | 205 | 206 | **Merged pull requests:** 207 | 208 | 209 | 210 | 211 | ## [2.1.17](https://github.com/networknt/light-hybrid-4j/tree/2.1.17) (2023-05-05) 212 | 213 | 214 | **Merged pull requests:** 215 | 216 | 217 | ## [2.1.16](https://github.com/networknt/light-hybrid-4j/tree/2.1.16) (2023-04-28) 218 | 219 | 220 | **Merged pull requests:** 221 | 222 | 223 | 224 | 225 | ## [2.1.15](https://github.com/networknt/light-hybrid-4j/tree/2.1.15) (2023-04-19) 226 | 227 | 228 | **Merged pull requests:** 229 | 230 | 231 | ## [2.1.14](https://github.com/networknt/light-hybrid-4j/tree/2.1.14) (2023-04-19) 232 | 233 | 234 | **Merged pull requests:** 235 | 236 | 237 | 238 | 239 | ## [2.1.13](https://github.com/networknt/light-hybrid-4j/tree/2.1.13) (2023-04-19) 240 | 241 | 242 | **Merged pull requests:** 243 | 244 | 245 | ## [2.1.12](https://github.com/networknt/light-hybrid-4j/tree/2.1.12) (2023-04-14) 246 | 247 | 248 | **Merged pull requests:** 249 | 250 | 251 | 252 | 253 | ## [2.1.11](https://github.com/networknt/light-hybrid-4j/tree/2.1.11) (2023-04-10) 254 | 255 | 256 | **Merged pull requests:** 257 | 258 | 259 | ## [2.1.10](https://github.com/networknt/light-hybrid-4j/tree/2.1.10) (2023-04-06) 260 | 261 | 262 | **Merged pull requests:** 263 | 264 | 265 | ## [2.1.9](https://github.com/networknt/light-hybrid-4j/tree/2.1.9) (2023-03-30) 266 | 267 | 268 | **Merged pull requests:** 269 | 270 | 271 | 272 | 273 | ## [2.1.8](https://github.com/networknt/light-hybrid-4j/tree/2.1.8) (2023-03-06) 274 | 275 | 276 | **Merged pull requests:** 277 | 278 | 279 | - fixes \#123 update the JwtVerifyHandler to get the token from the auth… [\#124](https://github.com/networknt/light-hybrid-4j/pull/124) ([stevehu](https://github.com/stevehu)) 280 | ## [2.1.7](https://github.com/networknt/light-hybrid-4j/tree/2.1.7) (2023-02-14) 281 | 282 | 283 | **Merged pull requests:** 284 | 285 | 286 | 287 | 288 | ## [2.1.6](https://github.com/networknt/light-hybrid-4j/tree/2.1.6) (2023-02-06) 289 | 290 | 291 | **Merged pull requests:** 292 | 293 | 294 | ## [2.1.5](https://github.com/networknt/light-hybrid-4j/tree/2.1.5) (2023-01-04) 295 | 296 | 297 | **Merged pull requests:** 298 | 299 | 300 | - fixes \#121 update jwt verification signature [\#122](https://github.com/networknt/light-hybrid-4j/pull/122) ([stevehu](https://github.com/stevehu)) 301 | - fixes \#119 add skipVerifyScopeWithoutSpec flag to hybrid-security.yml [\#120](https://github.com/networknt/light-hybrid-4j/pull/120) ([stevehu](https://github.com/stevehu)) 302 | - fixes \#117 update openapi-security.yml to add jwtCacheFullSize [\#118](https://github.com/networknt/light-hybrid-4j/pull/118) ([stevehu](https://github.com/stevehu)) 303 | ## [2.1.4](https://github.com/networknt/light-hybrid-4j/tree/2.1.4) (2022-11-30) 304 | 305 | 306 | **Merged pull requests:** 307 | 308 | 309 | - fixes \#115 upgrade classgraph to version 4.8.149 [\#116](https://github.com/networknt/light-hybrid-4j/pull/116) ([stevehu](https://github.com/stevehu)) 310 | ## [2.1.3](https://github.com/networknt/light-hybrid-4j/tree/2.1.3) (2022-11-10) 311 | 312 | 313 | **Merged pull requests:** 314 | 315 | 316 | ## [2.1.2](https://github.com/networknt/light-hybrid-4j/tree/2.1.2) (2022-10-22) 317 | 318 | 319 | **Merged pull requests:** 320 | 321 | 322 | - fixes \#110 pass in request path to verifyJwt to support mulitple OAut… [\#111](https://github.com/networknt/light-hybrid-4j/pull/111) ([stevehu](https://github.com/stevehu)) 323 | - fixes \#108 update hybrid-security.yml to use JsonWebKeySet for keyRes… [\#109](https://github.com/networknt/light-hybrid-4j/pull/109) ([stevehu](https://github.com/stevehu)) 324 | - fixes \#106 add providerId to the security.yml for oauth key service [\#107](https://github.com/networknt/light-hybrid-4j/pull/107) ([stevehu](https://github.com/stevehu)) 325 | - fixes \#104 externalize rpc-router.yml and revised the test cases [\#105](https://github.com/networknt/light-hybrid-4j/pull/105) ([stevehu](https://github.com/stevehu)) 326 | - fixes \#102 update JWT verification to use SecurityConfig [\#103](https://github.com/networknt/light-hybrid-4j/pull/103) ([stevehu](https://github.com/stevehu)) 327 | ## [2.1.1](https://github.com/networknt/light-hybrid-4j/tree/2.1.1) (2022-04-26) 328 | 329 | 330 | **Merged pull requests:** 331 | 332 | 333 | - fixes \#99 add more trace in AbstractRpcHandler to detect the reason f… [\#100](https://github.com/networknt/light-hybrid-4j/pull/100) ([stevehu](https://github.com/stevehu)) 334 | ## [2.1.0](https://github.com/networknt/light-hybrid-4j/tree/2.1.0) (2022-02-27) 335 | 336 | 337 | **Merged pull requests:** 338 | 339 | 340 | - fixes \#97 Add method and path to the STATUS_METHOD_NOT_ALLOWED [\#98](https://github.com/networknt/light-hybrid-4j/pull/98) ([stevehu](https://github.com/stevehu)) 341 | - fixes \#95 add keyResolver X509Certificate to the hybrid-security.yml [\#96](https://github.com/networknt/light-hybrid-4j/pull/96) ([stevehu](https://github.com/stevehu)) 342 | 343 | 344 | ## [2.0.32](https://github.com/networknt/light-hybrid-4j/tree/2.0.32) (2021-10-19) 345 | 346 | 347 | **Merged pull requests:** 348 | 349 | 350 | 351 | 352 | ## [2.0.31](https://github.com/networknt/light-hybrid-4j/tree/2.0.31) (2021-09-22) 353 | 354 | 355 | **Merged pull requests:** 356 | 357 | 358 | ## [2.0.30](https://github.com/networknt/light-hybrid-4j/tree/2.0.30) (2021-08-23) 359 | 360 | 361 | **Merged pull requests:** 362 | 363 | 364 | ## [2.0.29](https://github.com/networknt/light-hybrid-4j/tree/2.0.29) (2021-07-25) 365 | 366 | 367 | **Merged pull requests:** 368 | 369 | 370 | 371 | 372 | ## [2.0.28](https://github.com/networknt/light-hybrid-4j/tree/2.0.28) (2021-06-27) 373 | 374 | 375 | **Merged pull requests:** 376 | 377 | 378 | ## [2.0.27](https://github.com/networknt/light-hybrid-4j/tree/2.0.27) (2021-05-25) 379 | 380 | 381 | **Merged pull requests:** 382 | 383 | 384 | ## [2.0.26](https://github.com/networknt/light-hybrid-4j/tree/2.0.26) (2021-04-27) 385 | 386 | 387 | **Merged pull requests:** 388 | 389 | 390 | ## [2.0.25](https://github.com/networknt/light-hybrid-4j/tree/2.0.25) (2021-03-28) 391 | 392 | 393 | **Merged pull requests:** 394 | 395 | 396 | 397 | 398 | ## [2.0.24](https://github.com/networknt/light-hybrid-4j/tree/2.0.24) (2021-02-24) 399 | 400 | 401 | **Merged pull requests:** 402 | 403 | 404 | - Bump jackson-databind from 2.10.4 to 2.10.5.1 [\#94](https://github.com/networknt/light-hybrid-4j/pull/94) ([dependabot](https://github.com/apps/dependabot)) 405 | ## [2.0.23](https://github.com/networknt/light-hybrid-4j/tree/2.0.23) (2021-01-29) 406 | 407 | 408 | **Merged pull requests:** 409 | 410 | 411 | ## [2.0.22](https://github.com/networknt/light-hybrid-4j/tree/2.0.22) (2020-12-22) 412 | 413 | 414 | **Merged pull requests:** 415 | 416 | 417 | 418 | 419 | ## [2.0.21](https://github.com/networknt/light-hybrid-4j/tree/2.0.21) (2020-11-25) 420 | 421 | 422 | **Merged pull requests:** 423 | 424 | 425 | - fixes \#92 support cid uid for client_id and user_id for some jwt tokens [\#93](https://github.com/networknt/light-hybrid-4j/pull/93) ([stevehu](https://github.com/stevehu)) 426 | - fixes \#90 remove secret.yml from test config and update client and se… [\#91](https://github.com/networknt/light-hybrid-4j/pull/91) ([stevehu](https://github.com/stevehu)) 427 | 428 | 429 | ## [2.0.20](https://github.com/networknt/light-hybrid-4j/tree/2.0.20) (2020-11-05) 430 | 431 | 432 | **Merged pull requests:** 433 | 434 | 435 | - fixes \#88 Adding support for scp claim in addition to scope claim for… [\#89](https://github.com/networknt/light-hybrid-4j/pull/89) ([stevehu](https://github.com/stevehu)) 436 | ## [2.0.19](https://github.com/networknt/light-hybrid-4j/tree/2.0.19) (2020-11-01) 437 | 438 | 439 | **Merged pull requests:** 440 | 441 | 442 | - fixes \#86 get callerId from the header and put it into the auditInfo … [\#87](https://github.com/networknt/light-hybrid-4j/pull/87) ([stevehu](https://github.com/stevehu)) 443 | - Bump junit from 4.12 to 4.13.1 [\#85](https://github.com/networknt/light-hybrid-4j/pull/85) ([dependabot](https://github.com/apps/dependabot)) 444 | ## [2.0.18](https://github.com/networknt/light-hybrid-4j/tree/2.0.18) (2020-10-01) 445 | 446 | 447 | **Merged pull requests:** 448 | 449 | 450 | 451 | 452 | ## [2.0.17](https://github.com/networknt/light-hybrid-4j/tree/2.0.17) (2020-08-28) 453 | 454 | 455 | **Merged pull requests:** 456 | 457 | 458 | ## [2.0.16](https://github.com/networknt/light-hybrid-4j/tree/2.0.16) (2020-08-01) 459 | 460 | 461 | **Merged pull requests:** 462 | 463 | 464 | ## [2.0.15](https://github.com/networknt/light-hybrid-4j/tree/2.0.15) (2020-07-01) 465 | 466 | 467 | **Merged pull requests:** 468 | 469 | 470 | ## [2.0.14](https://github.com/networknt/light-hybrid-4j/tree/2.0.14) (2020-05-29) 471 | 472 | 473 | **Merged pull requests:** 474 | 475 | 476 | ## [2.0.13](https://github.com/networknt/light-hybrid-4j/tree/2.0.13) (2020-05-01) 477 | 478 | 479 | **Merged pull requests:** 480 | 481 | 482 | ## [2.0.12](https://github.com/networknt/light-hybrid-4j/tree/2.0.12) (2020-03-31) 483 | 484 | 485 | **Merged pull requests:** 486 | 487 | 488 | ## [2.0.11](https://github.com/networknt/light-hybrid-4j/tree/2.0.11) (2020-02-29) 489 | 490 | 491 | **Merged pull requests:** 492 | 493 | 494 | ## [2.0.10](https://github.com/networknt/light-hybrid-4j/tree/2.0.10) (2020-01-31) 495 | 496 | 497 | **Merged pull requests:** 498 | 499 | 500 | 501 | 502 | ## [2.0.9](https://github.com/networknt/light-hybrid-4j/tree/2.0.9) (2019-12-30) 503 | 504 | 505 | **Merged pull requests:** 506 | 507 | 508 | ## [2.0.8](https://github.com/networknt/light-hybrid-4j/tree/2.0.8) (2019-11-27) 509 | 510 | 511 | **Merged pull requests:** 512 | 513 | 514 | ## [2.0.7](https://github.com/networknt/light-hybrid-4j/tree/2.0.7) (2019-10-26) 515 | 516 | 517 | **Merged pull requests:** 518 | 519 | 520 | ## [2.0.6](https://github.com/networknt/light-hybrid-4j/tree/2.0.6) (2019-09-13) 521 | 522 | 523 | **Merged pull requests:** 524 | 525 | 526 | ## [2.0.5](https://github.com/networknt/light-hybrid-4j/tree/2.0.5) (2019-08-30) 527 | 528 | 529 | **Merged pull requests:** 530 | 531 | 532 | 533 | 534 | ## [2.0.4](https://github.com/networknt/light-hybrid-4j/tree/2.0.4) (2019-08-16) 535 | 536 | 537 | **Merged pull requests:** 538 | 539 | 540 | ## [2.0.3](https://github.com/networknt/light-hybrid-4j/tree/2.0.3) (2019-07-31) 541 | 542 | 543 | **Merged pull requests:** 544 | 545 | 546 | 547 | 548 | ## [2.0.2](https://github.com/networknt/light-hybrid-4j/tree/2.0.2) (2019-07-10) 549 | 550 | 551 | **Merged pull requests:** 552 | 553 | 554 | ## [2.0.1](https://github.com/networknt/light-hybrid-4j/tree/2.0.1) (2019-06-13) 555 | 556 | 557 | **Merged pull requests:** 558 | 559 | 560 | ## [1.6.4](https://github.com/networknt/light-hybrid-4j/tree/1.6.4) (2019-06-10) 561 | 562 | 563 | **Merged pull requests:** 564 | 565 | 566 | ## [1.6.2](https://github.com/networknt/light-hybrid-4j/tree/1.6.2) (2019-05-17) 567 | 568 | 569 | **Merged pull requests:** 570 | 571 | 572 | ## [1.6.1](https://github.com/networknt/light-hybrid-4j/tree/1.6.1) (2019-05-03) 573 | 574 | 575 | **Merged pull requests:** 576 | 577 | 578 | ## [1.6.0](https://github.com/networknt/light-hybrid-4j/tree/1.6.0) (2019-04-05) 579 | 580 | 581 | **Merged pull requests:** 582 | 583 | 584 | ## [1.5.31](https://github.com/networknt/light-hybrid-4j/tree/1.5.31) (2019-03-02) 585 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.30...1.5.31) 586 | 587 | ## [1.5.30](https://github.com/networknt/light-hybrid-4j/tree/1.5.30) (2019-02-21) 588 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.29...1.5.30) 589 | 590 | ## [1.5.29](https://github.com/networknt/light-hybrid-4j/tree/1.5.29) (2019-02-16) 591 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.28...1.5.29) 592 | 593 | ## [1.5.28](https://github.com/networknt/light-hybrid-4j/tree/1.5.28) (2019-01-13) 594 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.27...1.5.28) 595 | 596 | ## [1.5.27](https://github.com/networknt/light-hybrid-4j/tree/1.5.27) (2019-01-12) 597 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.25...1.5.27) 598 | 599 | ## [1.5.25](https://github.com/networknt/light-hybrid-4j/tree/1.5.25) (2018-12-24) 600 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.24...1.5.25) 601 | 602 | **Closed issues:** 603 | 604 | - log validation error in the handler [\#46](https://github.com/networknt/light-hybrid-4j/issues/46) 605 | 606 | ## [1.5.24](https://github.com/networknt/light-hybrid-4j/tree/1.5.24) (2018-12-15) 607 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.23...1.5.24) 608 | 609 | **Closed issues:** 610 | 611 | - add a new default method to getStatus from a Status object [\#45](https://github.com/networknt/light-hybrid-4j/issues/45) 612 | 613 | ## [1.5.23](https://github.com/networknt/light-hybrid-4j/tree/1.5.23) (2018-12-01) 614 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.22...1.5.23) 615 | 616 | ## [1.5.22](https://github.com/networknt/light-hybrid-4j/tree/1.5.22) (2018-11-10) 617 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.21...1.5.22) 618 | 619 | ## [1.5.21](https://github.com/networknt/light-hybrid-4j/tree/1.5.21) (2018-10-05) 620 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.20...1.5.21) 621 | 622 | ## [1.5.20](https://github.com/networknt/light-hybrid-4j/tree/1.5.20) (2018-10-05) 623 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.19...1.5.20) 624 | 625 | **Closed issues:** 626 | 627 | - remove security.yml from module jar file [\#44](https://github.com/networknt/light-hybrid-4j/issues/44) 628 | 629 | ## [1.5.19](https://github.com/networknt/light-hybrid-4j/tree/1.5.19) (2018-09-22) 630 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.18...1.5.19) 631 | 632 | **Closed issues:** 633 | 634 | - clear output api path during server startup [\#42](https://github.com/networknt/light-hybrid-4j/issues/42) 635 | - update the way error is handled in handler [\#41](https://github.com/networknt/light-hybrid-4j/issues/41) 636 | - JsonHandler needs to check if hybrid-security.yml exists before falling back to security.yml [\#40](https://github.com/networknt/light-hybrid-4j/issues/40) 637 | - switch to http-string module for HttpString headers constants [\#39](https://github.com/networknt/light-hybrid-4j/issues/39) 638 | - add default method getStatus to Handler interface [\#37](https://github.com/networknt/light-hybrid-4j/issues/37) 639 | 640 | ## [1.5.18](https://github.com/networknt/light-hybrid-4j/tree/1.5.18) (2018-08-16) 641 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.17...1.5.18) 642 | 643 | **Closed issues:** 644 | 645 | - remove dependencies on XLogger [\#36](https://github.com/networknt/light-hybrid-4j/issues/36) 646 | - flatten the config files into the same directory for k8s [\#35](https://github.com/networknt/light-hybrid-4j/issues/35) 647 | - support specific hybrid security configuration [\#33](https://github.com/networknt/light-hybrid-4j/issues/33) 648 | - upgrade to undertow 2.0.11.Final [\#32](https://github.com/networknt/light-hybrid-4j/issues/32) 649 | 650 | **Merged pull requests:** 651 | 652 | - fixes \#33 support specific hybrid security configuration [\#34](https://github.com/networknt/light-hybrid-4j/pull/34) ([stevehu](https://github.com/stevehu)) 653 | 654 | ## [1.5.17](https://github.com/networknt/light-hybrid-4j/tree/1.5.17) (2018-07-15) 655 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.16...1.5.17) 656 | 657 | ## [1.5.16](https://github.com/networknt/light-hybrid-4j/tree/1.5.16) (2018-07-05) 658 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.15...1.5.16) 659 | 660 | ## [1.5.15](https://github.com/networknt/light-hybrid-4j/tree/1.5.15) (2018-06-18) 661 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.14...1.5.15) 662 | 663 | **Closed issues:** 664 | 665 | - switch to default setExchangeStatus method for errors [\#31](https://github.com/networknt/light-hybrid-4j/issues/31) 666 | 667 | ## [1.5.14](https://github.com/networknt/light-hybrid-4j/tree/1.5.14) (2018-05-19) 668 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.13...1.5.14) 669 | 670 | **Closed issues:** 671 | 672 | - update VerifyJwt signature and security.yml [\#30](https://github.com/networknt/light-hybrid-4j/issues/30) 673 | - Log error message on the server if validation error occurs [\#29](https://github.com/networknt/light-hybrid-4j/issues/29) 674 | 675 | **Merged pull requests:** 676 | 677 | - Resources handler POC [\#27](https://github.com/networknt/light-hybrid-4j/pull/27) ([NicholasAzar](https://github.com/NicholasAzar)) 678 | 679 | ## [1.5.13](https://github.com/networknt/light-hybrid-4j/tree/1.5.13) (2018-04-20) 680 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.12...1.5.13) 681 | 682 | **Closed issues:** 683 | 684 | - hybrid service path configurable [\#28](https://github.com/networknt/light-hybrid-4j/issues/28) 685 | 686 | ## [1.5.12](https://github.com/networknt/light-hybrid-4j/tree/1.5.12) (2018-04-07) 687 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.11...1.5.12) 688 | 689 | ## [1.5.11](https://github.com/networknt/light-hybrid-4j/tree/1.5.11) (2018-03-31) 690 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.10...1.5.11) 691 | 692 | **Closed issues:** 693 | 694 | - need to pass the exchange to the hybrid handlers for headers and errors [\#25](https://github.com/networknt/light-hybrid-4j/issues/25) 695 | - add more debug info into the schema merger and validation [\#23](https://github.com/networknt/light-hybrid-4j/issues/23) 696 | - The json handler should support get method as well. [\#14](https://github.com/networknt/light-hybrid-4j/issues/14) 697 | 698 | **Merged pull requests:** 699 | 700 | - Feature/refactor exchange responses [\#26](https://github.com/networknt/light-hybrid-4j/pull/26) ([NicholasAzar](https://github.com/NicholasAzar)) 701 | - feat\(zip\): Adding handler to return a zip object to the client. [\#24](https://github.com/networknt/light-hybrid-4j/pull/24) ([NicholasAzar](https://github.com/NicholasAzar)) 702 | 703 | ## [1.5.10](https://github.com/networknt/light-hybrid-4j/tree/1.5.10) (2018-03-02) 704 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.9...1.5.10) 705 | 706 | **Closed issues:** 707 | 708 | - add subject\_claims and access\_claims into auditInfo attachment [\#22](https://github.com/networknt/light-hybrid-4j/issues/22) 709 | 710 | ## [1.5.9](https://github.com/networknt/light-hybrid-4j/tree/1.5.9) (2018-02-21) 711 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.8...1.5.9) 712 | 713 | **Closed issues:** 714 | 715 | - update travis CI to only build from master branch [\#21](https://github.com/networknt/light-hybrid-4j/issues/21) 716 | - update JsonSchemaFactory instance creation [\#20](https://github.com/networknt/light-hybrid-4j/issues/20) 717 | 718 | ## [1.5.8](https://github.com/networknt/light-hybrid-4j/tree/1.5.8) (2018-02-03) 719 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.7...1.5.8) 720 | 721 | ## [1.5.7](https://github.com/networknt/light-hybrid-4j/tree/1.5.7) (2018-01-09) 722 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.6...1.5.7) 723 | 724 | ## [1.5.6](https://github.com/networknt/light-hybrid-4j/tree/1.5.6) (2017-12-28) 725 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.4...1.5.6) 726 | 727 | **Closed issues:** 728 | 729 | - Update default security.yml in rpc-security [\#19](https://github.com/networknt/light-hybrid-4j/issues/19) 730 | - Maven build warnings [\#18](https://github.com/networknt/light-hybrid-4j/issues/18) 731 | 732 | ## [1.5.4](https://github.com/networknt/light-hybrid-4j/tree/1.5.4) (2017-11-21) 733 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.3...1.5.4) 734 | 735 | ## [1.5.3](https://github.com/networknt/light-hybrid-4j/tree/1.5.3) (2017-11-20) 736 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.2...1.5.3) 737 | 738 | ## [1.5.2](https://github.com/networknt/light-hybrid-4j/tree/1.5.2) (2017-11-20) 739 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.1...1.5.2) 740 | 741 | ## [1.5.1](https://github.com/networknt/light-hybrid-4j/tree/1.5.1) (2017-11-09) 742 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.5.0...1.5.1) 743 | 744 | ## [1.5.0](https://github.com/networknt/light-hybrid-4j/tree/1.5.0) (2017-10-21) 745 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.6...1.5.0) 746 | 747 | **Closed issues:** 748 | 749 | - Upgrade dependencies and add maven-version [\#17](https://github.com/networknt/light-hybrid-4j/issues/17) 750 | - Move payload into a sub attribute data in the request body [\#16](https://github.com/networknt/light-hybrid-4j/issues/16) 751 | 752 | ## [1.4.6](https://github.com/networknt/light-hybrid-4j/tree/1.4.6) (2017-09-24) 753 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.4...1.4.6) 754 | 755 | ## [1.4.4](https://github.com/networknt/light-hybrid-4j/tree/1.4.4) (2017-09-21) 756 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.3...1.4.4) 757 | 758 | ## [1.4.3](https://github.com/networknt/light-hybrid-4j/tree/1.4.3) (2017-09-10) 759 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.2...1.4.3) 760 | 761 | ## [1.4.2](https://github.com/networknt/light-hybrid-4j/tree/1.4.2) (2017-08-31) 762 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.1...1.4.2) 763 | 764 | ## [1.4.1](https://github.com/networknt/light-hybrid-4j/tree/1.4.1) (2017-08-30) 765 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.4.0...1.4.1) 766 | 767 | **Closed issues:** 768 | 769 | - Upgrade to newer version of Undertow and Jackson [\#15](https://github.com/networknt/light-hybrid-4j/issues/15) 770 | 771 | ## [1.4.0](https://github.com/networknt/light-hybrid-4j/tree/1.4.0) (2017-08-22) 772 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.5...1.4.0) 773 | 774 | **Closed issues:** 775 | 776 | - Replace Client with Http2Client and remove dependency of apache httpclient [\#13](https://github.com/networknt/light-hybrid-4j/issues/13) 777 | - Upgrade to Undertow 1.4.18.Final and remove JsonPath from pom.xml [\#12](https://github.com/networknt/light-hybrid-4j/issues/12) 778 | 779 | ## [1.3.5](https://github.com/networknt/light-hybrid-4j/tree/1.3.5) (2017-08-01) 780 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.4...1.3.5) 781 | 782 | **Merged pull requests:** 783 | 784 | - Multipart handler support [\#11](https://github.com/networknt/light-hybrid-4j/pull/11) ([NicholasAzar](https://github.com/NicholasAzar)) 785 | 786 | ## [1.3.4](https://github.com/networknt/light-hybrid-4j/tree/1.3.4) (2017-07-08) 787 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.3...1.3.4) 788 | 789 | ## [1.3.3](https://github.com/networknt/light-hybrid-4j/tree/1.3.3) (2017-06-14) 790 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.2...1.3.3) 791 | 792 | ## [1.3.2](https://github.com/networknt/light-hybrid-4j/tree/1.3.2) (2017-06-14) 793 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.1...1.3.2) 794 | 795 | **Closed issues:** 796 | 797 | - Update security handler to populate AuditInfo exchange attachment for Audit and Metrics [\#10](https://github.com/networknt/light-hybrid-4j/issues/10) 798 | - rpc-security should populate auditInfo like rest in order to enable metrics [\#9](https://github.com/networknt/light-hybrid-4j/issues/9) 799 | - when multiple jars deployed on one hybrid server, the schemas in these jars need to be merged in order to do validation and jwt scope verification [\#8](https://github.com/networknt/light-hybrid-4j/issues/8) 800 | 801 | ## [1.3.1](https://github.com/networknt/light-hybrid-4j/tree/1.3.1) (2017-06-03) 802 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.3.0...1.3.1) 803 | 804 | **Closed issues:** 805 | 806 | - Add JwtVerifyHandler for signature and expiration verification [\#7](https://github.com/networknt/light-hybrid-4j/issues/7) 807 | - Null pointer exception when navigating to localhost:8080/api/json in browser [\#6](https://github.com/networknt/light-hybrid-4j/issues/6) 808 | 809 | ## [1.3.0](https://github.com/networknt/light-hybrid-4j/tree/1.3.0) (2017-05-06) 810 | [Full Changelog](https://github.com/networknt/light-hybrid-4j/compare/1.2.8...1.3.0) 811 | 812 | **Closed issues:** 813 | 814 | - change project name to light-hybrid-4j from light-java-hybrid as java is a trademark of Oracle [\#5](https://github.com/networknt/light-hybrid-4j/issues/5) 815 | 816 | ## [1.2.8](https://github.com/networknt/light-hybrid-4j/tree/1.2.8) (2017-05-02) 817 | **Closed issues:** 818 | 819 | - Bypass JWT verification if enableVerifyJwt is false in security.yml [\#4](https://github.com/networknt/light-hybrid-4j/issues/4) 820 | - Add jwt token verification for security [\#3](https://github.com/networknt/light-hybrid-4j/issues/3) 821 | - Add json schema validator for json rpc in hybrid [\#2](https://github.com/networknt/light-hybrid-4j/issues/2) 822 | - Add Colfer support for RPC along with JSON [\#1](https://github.com/networknt/light-hybrid-4j/issues/1) 823 | 824 | 825 | 826 | \* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)* 827 | --------------------------------------------------------------------------------