Skip to content

Commit b52d26a

Browse files
mbwhitejt-nti
authored andcommitted
[FAB-15634] Annotations for Serializer
Provides the annotation to say what serializer is in use. Defaults to the current JSON format. Users can implement their own if they wish. This brings Java up to the same functional level as Node. Change-Id: I1d8095cafddf14cd64fc36c67420f1c5246d50cb Signed-off-by: Matthew B. White <[email protected]>
1 parent 207bd94 commit b52d26a

File tree

17 files changed

+305
-36
lines changed

17 files changed

+305
-36
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@
1010
import java.util.logging.Logger;
1111

1212
import org.hyperledger.fabric.Logging;
13+
import org.hyperledger.fabric.contract.annotation.Serializer;
1314
import org.hyperledger.fabric.contract.execution.ExecutionFactory;
1415
import org.hyperledger.fabric.contract.execution.ExecutionService;
1516
import org.hyperledger.fabric.contract.execution.InvocationRequest;
17+
import org.hyperledger.fabric.contract.execution.SerializerInterface;
1618
import org.hyperledger.fabric.contract.metadata.MetadataBuilder;
1719
import org.hyperledger.fabric.contract.routing.ContractDefinition;
1820
import org.hyperledger.fabric.contract.routing.RoutingRegistry;
1921
import org.hyperledger.fabric.contract.routing.TxFunction;
2022
import org.hyperledger.fabric.contract.routing.TypeRegistry;
2123
import org.hyperledger.fabric.contract.routing.impl.RoutingRegistryImpl;
24+
import org.hyperledger.fabric.contract.routing.impl.SerializerRegistryImpl;
2225
import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl;
2326
import org.hyperledger.fabric.metrics.Metrics;
2427
import org.hyperledger.fabric.shim.ChaincodeBase;
@@ -34,7 +37,11 @@ public class ContractRouter extends ChaincodeBase {
3437

3538
private RoutingRegistry registry;
3639
private TypeRegistry typeRegistry;
37-
private ExecutionService executor;
40+
41+
// Store instances of SerializerInterfaces - identified by the contract annotation
42+
// (default is JSON)
43+
public SerializerRegistryImpl serializers;
44+
3845

3946
/**
4047
* Take the arguments from the cli, and initiate processing of cli options and
@@ -55,8 +62,19 @@ public ContractRouter(String[] args) {
5562
super.validateOptions();
5663
logger.fine("ContractRouter<init>");
5764
registry = new RoutingRegistryImpl();
58-
typeRegistry = new TypeRegistryImpl();
59-
executor = ExecutionFactory.getInstance().createExecutionService(typeRegistry);
65+
typeRegistry = TypeRegistry.getRegistry();
66+
67+
serializers = new SerializerRegistryImpl();
68+
69+
try {
70+
serializers.findAndSetContents();
71+
} catch (InstantiationException | IllegalAccessException e) {
72+
ContractRuntimeException cre = new ContractRuntimeException("Unable to locate Serializers",e);
73+
logger.severe(()-> Logging.formatError(cre));
74+
throw new RuntimeException(cre);
75+
}
76+
77+
6078

6179
}
6280

@@ -91,6 +109,11 @@ private Response processRequest(ChaincodeStub stub) {
91109
InvocationRequest request = ExecutionFactory.getInstance().createRequest(stub);
92110
TxFunction txFn = getRouting(request);
93111

112+
// based on the routing information the serializer can be found
113+
// TRANSACTION target as this on the 'inbound' to invoke a tx
114+
SerializerInterface si = serializers.getSerializer(txFn.getRouting().getSerializerName(),Serializer.TARGET.TRANSACTION);
115+
ExecutionService executor = ExecutionFactory.getInstance().createExecutionService(si);
116+
94117
logger.info(() -> "Got routing:" + txFn.getRouting());
95118
return executor.executeRequest(txFn, request, stub);
96119
} else {
@@ -137,6 +160,8 @@ public static void main(String[] args) {
137160
ContractRouter cfc = new ContractRouter(args);
138161
cfc.findAllContracts();
139162

163+
logger.fine(cfc.getRoutingRegistry().toString());
164+
140165
// Create the Metadata ahead of time rather than have to produce every
141166
// time
142167
MetadataBuilder.initialize(cfc.getRoutingRegistry(), cfc.getTypeRegistry());

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Contract.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,15 @@
3838
* @return Name of the contract to be used instead of the Classname
3939
*/
4040
String name() default "";
41+
42+
/**
43+
* Fully Qualified Classname of the TRANSACTION serializer
44+
* that should be used with this contract.
45+
*
46+
* This is the serializer that is used to parse incoming transaction request
47+
* parameters and convert the return type
48+
*/
49+
String transactionSerializer() default "org.hyperledger.fabric.contract.execution.JSONTransactionSerializer";
50+
51+
4152
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
package org.hyperledger.fabric.contract.annotation;
7+
8+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
9+
10+
import java.lang.annotation.ElementType;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.Target;
13+
14+
/**
15+
* Class level annotation that defines the serializer that should be used to
16+
* convert objects to and from the wire format.
17+
*
18+
* This should annotate a class that implements the Serializer interface
19+
*/
20+
@Retention(RUNTIME)
21+
@Target({ElementType.TYPE,ElementType.TYPE_USE})
22+
public @interface Serializer {
23+
public enum TARGET {
24+
TRANSACTION, ALL
25+
}
26+
27+
TARGET target() default Serializer.TARGET.ALL;
28+
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/annotation/Transaction.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
@Retention(RUNTIME)
2424
@Target(METHOD)
2525
public @interface Transaction {
26+
27+
/**
28+
* SUBMIT or EVALUATE semantics
29+
*/
30+
public enum TYPE {
31+
SUBMIT,EVALUATE
32+
}
33+
2634
/**
2735
* TRUE indicates that this function is intended to be called with the 'submit'
2836
* semantics
@@ -31,9 +39,17 @@
3139
* semantics
3240
*
3341
* @return boolean, default is true
42+
* @deprecated Please use intent
3443
*/
44+
@Deprecated
3545
boolean submit() default true;
3646

47+
/**
48+
* SUBMIT - indicates that this function is intended to be called with the 'submit' semantics
49+
* EVALUATE - indicates that this is intended to be called with the 'evaluate' semantics
50+
*/
51+
TYPE intent() default Transaction.TYPE.SUBMIT;
52+
3753
/**
3854
* The name of the callable transaction if it should be different to the method
3955
* name.

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/ExecutionFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import org.hyperledger.fabric.contract.execution.impl.ContractExecutionService;
1010
import org.hyperledger.fabric.contract.execution.impl.ContractInvocationRequest;
11-
import org.hyperledger.fabric.contract.routing.TypeRegistry;
1211
import org.hyperledger.fabric.shim.ChaincodeStub;
1312

1413
public class ExecutionFactory {
@@ -26,9 +25,10 @@ public InvocationRequest createRequest(ChaincodeStub context) {
2625
return new ContractInvocationRequest(context);
2726
}
2827

29-
public ExecutionService createExecutionService(TypeRegistry typeRegistry) {
28+
29+
public ExecutionService createExecutionService(SerializerInterface serializers) {
3030
if (es == null) {
31-
es = new ContractExecutionService(typeRegistry);
31+
es = new ContractExecutionService(serializers);
3232
}
3333
return es;
3434
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/JSONTransactionSerializer.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,20 @@
2525
import org.json.JSONException;
2626
import org.json.JSONObject;
2727

28+
import org.hyperledger.fabric.contract.annotation.Serializer;
29+
2830
/**
2931
* Used as a the default serialisation for transmission from SDK to Contract
3032
*/
31-
public class JSONTransactionSerializer {
33+
@Serializer()
34+
public class JSONTransactionSerializer implements SerializerInterface {
3235
private static Logger logger = Logger.getLogger(JSONTransactionSerializer.class.getName());
33-
private TypeRegistry typeRegistry;
36+
private TypeRegistry typeRegistry = TypeRegistry.getRegistry();
3437

3538
/**
36-
* Create a new serialiser and maintain a reference to the TypeRegistry
37-
*
38-
* @param typeRegistry
39+
* Create a new serialiser
3940
*/
40-
public JSONTransactionSerializer(TypeRegistry typeRegistry) {
41-
this.typeRegistry = typeRegistry;
41+
public JSONTransactionSerializer() {
4242
}
4343

4444
/**
@@ -48,6 +48,7 @@ public JSONTransactionSerializer(TypeRegistry typeRegistry) {
4848
* @param ts
4949
* @return Byte buffer
5050
*/
51+
@Override
5152
public byte[] toBuffer(Object value, TypeSchema ts) {
5253
logger.debug(() -> "Schema to convert is " + ts);
5354
byte[] buffer = null;
@@ -87,6 +88,7 @@ public byte[] toBuffer(Object value, TypeSchema ts) {
8788
* @throws InstantiationException
8889
* @throws IllegalAccessException
8990
*/
91+
@Override
9092
public Object fromBuffer(byte[] buffer, TypeSchema ts) {
9193
try {
9294
String stringData = new String(buffer, StandardCharsets.UTF_8);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package org.hyperledger.fabric.contract.execution;
8+
9+
import org.hyperledger.fabric.contract.metadata.TypeSchema;
10+
import org.hyperledger.fabric.contract.routing.TypeRegistry;
11+
12+
/**
13+
* This interface allows contract developers to change the serialization mechanism.
14+
* There are two scenaios where instances of DataTypes are serialized.
15+
*
16+
* When the objects are (logically) transferred from the Client application to the Contract resulting
17+
* in a transaction function being invoked. Typicaly this is JSON, hence a default JSON parser is provided.
18+
*
19+
* The JSONTransactionSerializer can be extended if needed
20+
*/
21+
public interface SerializerInterface {
22+
23+
/**
24+
* Convert the value supplied to a byte array, according to the TypeSchema
25+
*
26+
* @param value
27+
* @param ts
28+
* @return
29+
*/
30+
byte[] toBuffer(Object value, TypeSchema ts);
31+
32+
/**
33+
* Take the byte buffer and return the object as required
34+
*
35+
* @param buffer Byte buffer from the wire
36+
* @param ts TypeSchema representing the type
37+
*
38+
* @return Object created; relies on Java auto-boxing for primitives
39+
*
40+
* @throws InstantiationException
41+
* @throws IllegalAccessException
42+
*/
43+
Object fromBuffer(byte[] buffer, TypeSchema ts);
44+
45+
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/impl/ContractExecutionService.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,10 @@
1818
import org.hyperledger.fabric.contract.ContractRuntimeException;
1919
import org.hyperledger.fabric.contract.execution.ExecutionService;
2020
import org.hyperledger.fabric.contract.execution.InvocationRequest;
21-
import org.hyperledger.fabric.contract.execution.JSONTransactionSerializer;
21+
import org.hyperledger.fabric.contract.execution.SerializerInterface;
2222
import org.hyperledger.fabric.contract.metadata.TypeSchema;
2323
import org.hyperledger.fabric.contract.routing.ParameterDefinition;
2424
import org.hyperledger.fabric.contract.routing.TxFunction;
25-
import org.hyperledger.fabric.contract.routing.TypeRegistry;
2625
import org.hyperledger.fabric.shim.Chaincode;
2726
import org.hyperledger.fabric.shim.ChaincodeException;
2827
import org.hyperledger.fabric.shim.ChaincodeStub;
@@ -32,12 +31,11 @@ public class ContractExecutionService implements ExecutionService {
3231

3332
private static Logger logger = Logger.getLogger(ContractExecutionService.class.getName());
3433

35-
private JSONTransactionSerializer serializer;
34+
private SerializerInterface serializer;
3635
Map<String, Object> proxies = new HashMap<>();
3736

38-
public ContractExecutionService(TypeRegistry typeRegistry) {
39-
// FUTURE: Permit this to swapped out as per node.js
40-
this.serializer = new JSONTransactionSerializer(typeRegistry);
37+
public ContractExecutionService(SerializerInterface serializer) {
38+
this.serializer = serializer;
4139
}
4240

4341
@Override

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/TxFunction.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ interface Routing {
2222

2323
ContractInterface getContractInstance() throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException;
2424

25+
String getSerializerName();
2526
}
2627

2728
boolean isUnknownTx();
@@ -45,4 +46,6 @@ interface Routing {
4546
void setParameterDefinitions(List<ParameterDefinition> list);
4647

4748
List<ParameterDefinition> getParamsList();
49+
50+
4851
}

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/routing/TypeRegistry.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77

88
import java.util.Collection;
99

10+
import org.hyperledger.fabric.contract.routing.impl.TypeRegistryImpl;
11+
1012
public interface TypeRegistry {
1113

14+
static TypeRegistry getRegistry(){
15+
return TypeRegistryImpl.getInstance();
16+
}
17+
1218
void addDataType(DataTypeDefinition dtd);
1319

1420
void addDataType(Class<?> cl);

0 commit comments

Comments
 (0)