Skip to content

Commit bd59b34

Browse files
author
Simon Stone
committed
[FAB-16217] Do not load JSON Schema schema from network
Currently, the JSON Schema schema (http://json-schema.org/draft-04/schema) is loaded from the network every time the Java chaincode starts. This has been seen to cause delays/hangs in the CI system, and we should avoid it if possible. This CR adds the JSON Schema schema to the chaincode resources, and uses that copy (instead of the network copy) to validate any contract schemas. Signed-off-by: Simon Stone <[email protected]> Change-Id: Ic559fb5f1a07f86bcd4d2eb2003c791e71abc701 (cherry picked from commit 4371d07)
1 parent 91445c8 commit bd59b34

File tree

3 files changed

+192
-5
lines changed

3 files changed

+192
-5
lines changed

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/MetadataBuilder.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import java.io.IOException;
99
import java.io.InputStream;
1010
import java.io.Serializable;
11+
import java.net.URI;
1112
import java.util.ArrayList;
1213
import java.util.Collection;
1314
import java.util.Collections;
@@ -19,7 +20,9 @@
1920

2021
import org.everit.json.schema.Schema;
2122
import org.everit.json.schema.ValidationException;
23+
import org.everit.json.schema.loader.SchemaClient;
2224
import org.everit.json.schema.loader.SchemaLoader;
25+
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
2326
import org.hyperledger.fabric.Logger;
2427
import org.hyperledger.fabric.contract.annotation.Contract;
2528
import org.hyperledger.fabric.contract.annotation.Info;
@@ -61,17 +64,27 @@ V putIfNotNull(K key, V value) {
6164
static Map<String, Object> overallInfoMap = new HashMap<String, Object>();
6265
static Map<String, Object> componentMap = new HashMap<String, Object>();
6366

67+
// The schema client used to load any other referenced schemas
68+
static SchemaClient schemaClient = new DefaultSchemaClient();
69+
6470
/**
6571
* Validation method
6672
*
6773
* @throws ValidationException if the metadata is not valid
6874
*/
6975
public static void validate() {
7076
logger.info("Running schema test validation");
71-
try (InputStream inputStream = MetadataBuilder.class.getClassLoader()
72-
.getResourceAsStream("contract-schema.json")) {
73-
JSONObject rawSchema = new JSONObject(new JSONTokener(inputStream));
74-
Schema schema = SchemaLoader.load(rawSchema);
77+
ClassLoader cl = MetadataBuilder.class.getClassLoader();
78+
try (InputStream contractSchemaInputStream = cl.getResourceAsStream("contract-schema.json");
79+
InputStream jsonSchemaInputStream = cl.getResourceAsStream("json-schema-draft-04-schema.json")) {
80+
JSONObject rawContractSchema = new JSONObject(new JSONTokener(contractSchemaInputStream));
81+
JSONObject rawJsonSchema = new JSONObject(new JSONTokener(jsonSchemaInputStream));
82+
SchemaLoader schemaLoader = SchemaLoader.builder()
83+
.schemaClient(schemaClient)
84+
.schemaJson(rawContractSchema)
85+
.registerSchemaByURI(URI.create("http://json-schema.org/draft-04/schema"), rawJsonSchema)
86+
.build();
87+
Schema schema = schemaLoader.load().build();
7588
schema.validate(metadata());
7689

7790
} catch (IOException e) {
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
{
2+
"id": "http://json-schema.org/draft-04/schema#",
3+
"$schema": "http://json-schema.org/draft-04/schema#",
4+
"description": "Core schema meta-schema",
5+
"definitions": {
6+
"schemaArray": {
7+
"type": "array",
8+
"minItems": 1,
9+
"items": { "$ref": "#" }
10+
},
11+
"positiveInteger": {
12+
"type": "integer",
13+
"minimum": 0
14+
},
15+
"positiveIntegerDefault0": {
16+
"allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
17+
},
18+
"simpleTypes": {
19+
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
20+
},
21+
"stringArray": {
22+
"type": "array",
23+
"items": { "type": "string" },
24+
"minItems": 1,
25+
"uniqueItems": true
26+
}
27+
},
28+
"type": "object",
29+
"properties": {
30+
"id": {
31+
"type": "string"
32+
},
33+
"$schema": {
34+
"type": "string"
35+
},
36+
"title": {
37+
"type": "string"
38+
},
39+
"description": {
40+
"type": "string"
41+
},
42+
"default": {},
43+
"multipleOf": {
44+
"type": "number",
45+
"minimum": 0,
46+
"exclusiveMinimum": true
47+
},
48+
"maximum": {
49+
"type": "number"
50+
},
51+
"exclusiveMaximum": {
52+
"type": "boolean",
53+
"default": false
54+
},
55+
"minimum": {
56+
"type": "number"
57+
},
58+
"exclusiveMinimum": {
59+
"type": "boolean",
60+
"default": false
61+
},
62+
"maxLength": { "$ref": "#/definitions/positiveInteger" },
63+
"minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
64+
"pattern": {
65+
"type": "string",
66+
"format": "regex"
67+
},
68+
"additionalItems": {
69+
"anyOf": [
70+
{ "type": "boolean" },
71+
{ "$ref": "#" }
72+
],
73+
"default": {}
74+
},
75+
"items": {
76+
"anyOf": [
77+
{ "$ref": "#" },
78+
{ "$ref": "#/definitions/schemaArray" }
79+
],
80+
"default": {}
81+
},
82+
"maxItems": { "$ref": "#/definitions/positiveInteger" },
83+
"minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
84+
"uniqueItems": {
85+
"type": "boolean",
86+
"default": false
87+
},
88+
"maxProperties": { "$ref": "#/definitions/positiveInteger" },
89+
"minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
90+
"required": { "$ref": "#/definitions/stringArray" },
91+
"additionalProperties": {
92+
"anyOf": [
93+
{ "type": "boolean" },
94+
{ "$ref": "#" }
95+
],
96+
"default": {}
97+
},
98+
"definitions": {
99+
"type": "object",
100+
"additionalProperties": { "$ref": "#" },
101+
"default": {}
102+
},
103+
"properties": {
104+
"type": "object",
105+
"additionalProperties": { "$ref": "#" },
106+
"default": {}
107+
},
108+
"patternProperties": {
109+
"type": "object",
110+
"additionalProperties": { "$ref": "#" },
111+
"default": {}
112+
},
113+
"dependencies": {
114+
"type": "object",
115+
"additionalProperties": {
116+
"anyOf": [
117+
{ "$ref": "#" },
118+
{ "$ref": "#/definitions/stringArray" }
119+
]
120+
}
121+
},
122+
"enum": {
123+
"type": "array",
124+
"minItems": 1,
125+
"uniqueItems": true
126+
},
127+
"type": {
128+
"anyOf": [
129+
{ "$ref": "#/definitions/simpleTypes" },
130+
{
131+
"type": "array",
132+
"items": { "$ref": "#/definitions/simpleTypes" },
133+
"minItems": 1,
134+
"uniqueItems": true
135+
}
136+
]
137+
},
138+
"format": { "type": "string" },
139+
"allOf": { "$ref": "#/definitions/schemaArray" },
140+
"anyOf": { "$ref": "#/definitions/schemaArray" },
141+
"oneOf": { "$ref": "#/definitions/schemaArray" },
142+
"not": { "$ref": "#" }
143+
},
144+
"dependencies": {
145+
"exclusiveMaximum": [ "maximum" ],
146+
"exclusiveMinimum": [ "minimum" ]
147+
},
148+
"default": {}
149+
}

fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/metadata/MetadataBuilderTest.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@
55
*/
66
package org.hyperledger.fabric.contract.metadata;
77

8+
import java.io.InputStream;
89
import java.io.Serializable;
910
import java.util.HashMap;
1011

12+
import org.everit.json.schema.loader.SchemaClient;
13+
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
1114
import org.hyperledger.fabric.contract.ChaincodeStubNaiveImpl;
1215
import org.hyperledger.fabric.contract.Context;
16+
import org.hyperledger.fabric.contract.routing.ContractDefinition;
17+
import org.hyperledger.fabric.contract.routing.impl.ContractDefinitionImpl;
1318
import org.hyperledger.fabric.contract.systemcontract.SystemContract;
1419
import org.hyperledger.fabric.shim.ChaincodeStub;
20+
import org.junit.After;
1521
import org.junit.Before;
1622
import org.junit.Rule;
1723
import org.junit.Test;
1824
import org.junit.rules.ExpectedException;
1925

26+
import contract.SampleContract;
27+
2028
public class MetadataBuilderTest {
2129
@Rule
2230
public ExpectedException thrown = ExpectedException.none();
@@ -34,10 +42,12 @@ public class MetadataBuilderTest {
3442
+ " \"contact\": {\"email\": \"[email protected]\"}\n" + " }\n" + " }\n" + "";
3543

3644
@Before
37-
public void beforeEach() {
45+
@After
46+
public void beforeAndAfterEach() {
3847
MetadataBuilder.componentMap = new HashMap<String, Object>();
3948
MetadataBuilder.contractMap = new HashMap<String, HashMap<String, Serializable>>();
4049
MetadataBuilder.overallInfoMap = new HashMap<String, Object>();
50+
MetadataBuilder.schemaClient = new DefaultSchemaClient();
4151
}
4252

4353
@Test
@@ -49,4 +59,19 @@ public void systemContract() {
4959
String metadataCompressed = system.getMetadata(new Context(stub));
5060
}
5161

62+
@Test
63+
public void defaultSchemasNotLoadedFromNetwork() {
64+
ContractDefinition contractDefinition = new ContractDefinitionImpl(SampleContract.class);
65+
MetadataBuilder.addContract(contractDefinition);
66+
MetadataBuilder.schemaClient = new SchemaClient(){
67+
68+
@Override
69+
public InputStream get(String uri) {
70+
throw new RuntimeException("Refusing to load schema: " + uri);
71+
}
72+
73+
};
74+
MetadataBuilder.validate();
75+
}
76+
5277
}

0 commit comments

Comments
 (0)