Skip to content

Commit 708c3aa

Browse files
author
Luis Sanchez
committed
[FAB-3220] sync compositekey api w/ go
ChaincodeStub: - getCompositeKey() - splitCompositKey() CompositeKey: - new class - new unit tests Change-Id: I697b7bb02b61a4b8acb5a086580e0ae34405a8c1 Signed-off-by: Luis Sanchez <[email protected]>
1 parent 2318b8d commit 708c3aa

File tree

6 files changed

+260
-13
lines changed

6 files changed

+260
-13
lines changed

core/chaincode/shim/java/build.gradle

+2
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ dependencies {
145145
compile 'io.grpc:grpc-all:0.13.2'
146146
compile 'commons-cli:commons-cli:1.3.1'
147147
compile 'io.netty:netty-tcnative-boringssl-static:1.1.33.Fork21:' + tcnative_classifier
148+
testCompile 'junit:junit:4.12'
149+
testCompile 'org.hamcrest:hamcrest-library:1.3'
148150
}
149151

150152
publishing {

core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStub.java

+15-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent;
2525
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
26+
import org.hyperledger.fabric.shim.ledger.CompositeKey;
2627

2728
public interface ChaincodeStub {
2829

@@ -43,7 +44,7 @@ public interface ChaincodeStub {
4344
* @return a list of arguments cast to UTF-8 strings
4445
*/
4546
List<String> getStringArgs();
46-
47+
4748
/**
4849
* A convenience method that returns the first argument of the chaincode
4950
* invocation for use as a function name.
@@ -53,7 +54,7 @@ public interface ChaincodeStub {
5354
* @return the function name
5455
*/
5556
String getFunction();
56-
57+
5758
/**
5859
* A convenience method that returns all except the first argument of the
5960
* chaincode invocation for use as the parameters to the function returned
@@ -117,14 +118,22 @@ public interface ChaincodeStub {
117118
*
118119
* @param objectType
119120
* @param attributes
120-
* @return
121+
* @return a composite key
121122
*/
122-
String createCompositeKey(String objectType, String[] attributes);
123+
CompositeKey createCompositeKey(String objectType, String... attributes);
124+
125+
/**
126+
* Parses a composite key from a string.
127+
*
128+
* @param compositeKey a composite key string
129+
* @return a composite key
130+
*/
131+
CompositeKey splitCompositeKey(String compositeKey);
123132

124133
/**
125134
* Defines the CHAINCODE type event that will be posted to interested
126135
* clients when the chaincode's result is committed to the ledger.
127-
*
136+
*
128137
* @param name
129138
* Name of event. Cannot be null or empty string.
130139
* @param payload
@@ -220,4 +229,4 @@ default void putStringState(String key, String value) {
220229
*/
221230
ChaincodeEvent getEvent();
222231

223-
}
232+
}

core/chaincode/shim/java/src/main/java/org/hyperledger/fabric/shim/ChaincodeStubImpl.java

+12-7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.commons.logging.LogFactory;
2727
import org.hyperledger.fabric.protos.peer.ChaincodeEventPackage.ChaincodeEvent;
2828
import org.hyperledger.fabric.protos.peer.ProposalResponsePackage.Response;
29+
import org.hyperledger.fabric.shim.ledger.CompositeKey;
2930

3031
import com.google.protobuf.ByteString;
3132

@@ -133,17 +134,21 @@ public void putState(String key, byte[] value) {
133134
public void delState(String key) {
134135
handler.handleDeleteState(key, txid);
135136
}
137+
136138
/* (non-Javadoc)
137139
* @see org.hyperledger.fabric.shim.ChaincodeStub#createCompositeKey(java.lang.String, java.lang.String[])
138140
*/
139141
@Override
140-
public String createCompositeKey(String objectType, String[] attributes) {
141-
String compositeKey = new String();
142-
compositeKey = compositeKey + objectType;
143-
for (String attribute : attributes) {
144-
compositeKey = compositeKey + attribute.length() + attribute;
145-
}
146-
return compositeKey;
142+
public CompositeKey createCompositeKey(String objectType, String... attributes) {
143+
return new CompositeKey(objectType, attributes);
144+
}
145+
146+
/* (non-Javadoc)
147+
* @see org.hyperledger.fabric.shim.ChaincodeStub#splitCompositeKey(java.lang.String)
148+
*/
149+
@Override
150+
public CompositeKey splitCompositeKey(String compositeKey) {
151+
return CompositeKey.parseCompositeKey(compositeKey);
147152
}
148153

149154
/* (non-Javadoc)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
Copyright IBM 2017 All Rights Reserved.
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 org.hyperledger.fabric.shim.ledger;
17+
18+
import static java.util.stream.Collectors.joining;
19+
20+
import java.util.Arrays;
21+
import java.util.Collections;
22+
import java.util.List;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
25+
26+
public class CompositeKey {
27+
28+
private static final String DELIMITER = new String(Character.toChars(Character.MIN_CODE_POINT));
29+
private static final String INVALID_SEGMENT_CHAR = new String(Character.toChars(Character.MAX_CODE_POINT));
30+
private static final String INVALID_SEGMENT_PATTERN = String.format("(?:%s|%s)", INVALID_SEGMENT_CHAR, DELIMITER);
31+
32+
final String objectType;
33+
final List<String> attributes;
34+
final String compositeKey;
35+
36+
public CompositeKey(String objectType, String... attributes) {
37+
this(objectType, attributes == null ? Collections.emptyList() : Arrays.asList(attributes));
38+
}
39+
40+
public CompositeKey(String objectType, List<String> attributes) {
41+
if (objectType == null)
42+
throw new NullPointerException("objectType cannot be null");
43+
this.objectType = objectType;
44+
this.attributes = attributes;
45+
this.compositeKey = generateCompositeKeyString(objectType, attributes);
46+
}
47+
48+
public String getObjectType() {
49+
return objectType;
50+
}
51+
52+
public List<String> getAttributes() {
53+
return attributes;
54+
}
55+
56+
@Override
57+
public String toString() {
58+
return compositeKey;
59+
}
60+
61+
public static CompositeKey parseCompositeKey(String compositeKey) {
62+
if(compositeKey == null) return null;
63+
final String[] segments = compositeKey.split(DELIMITER, 0);
64+
return new CompositeKey(segments[0], Arrays.stream(segments).skip(1).toArray(String[]::new));
65+
}
66+
67+
private String generateCompositeKeyString(String objectType, List<String> attributes) {
68+
69+
// object type must be a valid composite key segment
70+
validateCompositeKeySegment(objectType);
71+
72+
// the attributes must be valid composite key segments
73+
attributes.stream().forEach(x -> validateCompositeKeySegment(x));
74+
75+
// return objectType + DELIMITER + (attribute + DELIMITER)*
76+
return attributes.stream().collect(joining(DELIMITER, objectType + DELIMITER, DELIMITER));
77+
78+
}
79+
80+
private void validateCompositeKeySegment(String segment) {
81+
final Matcher matcher = Pattern.compile(INVALID_SEGMENT_PATTERN).matcher(segment);
82+
if (matcher.find()) {
83+
throw CompositeKeyFormatException.forInputString(segment, matcher.group(), matcher.start());
84+
}
85+
}
86+
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
Copyright IBM 2017 All Rights Reserved.
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 org.hyperledger.fabric.shim.ledger;
17+
18+
class CompositeKeyFormatException extends IllegalArgumentException {
19+
private static final long serialVersionUID = 1L;
20+
21+
private CompositeKeyFormatException() {
22+
super();
23+
}
24+
25+
private CompositeKeyFormatException(String message, Throwable cause) {
26+
super(message, cause);
27+
}
28+
29+
private CompositeKeyFormatException(String s) {
30+
super(s);
31+
}
32+
33+
private CompositeKeyFormatException(Throwable cause) {
34+
super(cause);
35+
}
36+
37+
static CompositeKeyFormatException forInputString(String s, String group, int index) {
38+
return new CompositeKeyFormatException(String.format("For input string '%s', found 'U+%06X' at index %d.", s, group.codePointAt(0), index));
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
Copyright IBM 2017 All Rights Reserved.
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 org.hyperledger.fabric.shim.ledger;
17+
18+
import static org.hamcrest.Matchers.contains;
19+
import static org.hamcrest.Matchers.equalTo;
20+
import static org.hamcrest.Matchers.hasSize;
21+
import static org.hamcrest.Matchers.is;
22+
import static org.junit.Assert.assertThat;
23+
24+
import java.util.Arrays;
25+
26+
import org.junit.Test;
27+
28+
public class CompositeKeyTest {
29+
30+
@Test
31+
public void testCompositeKeyStringStringArray() {
32+
final CompositeKey key = new CompositeKey("abc", new String[] {"def", "ghi", "jkl", "mno"});
33+
assertThat(key.getObjectType(), is(equalTo("abc")));
34+
assertThat(key.getAttributes(), hasSize(4));
35+
assertThat(key.toString(), is(equalTo("abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000")));
36+
}
37+
38+
@Test
39+
public void testCompositeKeyStringListOfString() {
40+
final CompositeKey key = new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
41+
assertThat(key.getObjectType(), is(equalTo("abc")));
42+
assertThat(key.getAttributes(), hasSize(4));
43+
assertThat(key.toString(), is(equalTo("abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000")));
44+
}
45+
46+
@Test(expected=CompositeKeyFormatException.class)
47+
public void testCompositeKeyWithInvalidObjectTypeDelimiter() {
48+
new CompositeKey("ab\u0000c", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
49+
}
50+
51+
@Test(expected=CompositeKeyFormatException.class)
52+
public void testCompositeKeyWithInvalidAttributeDelimiter() {
53+
new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "j\u0000kl", "mno"}));
54+
}
55+
56+
@Test(expected=CompositeKeyFormatException.class)
57+
public void testCompositeKeyWithInvalidObjectTypeMaxCodePoint() {
58+
new CompositeKey("ab\udbff\udfffc", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
59+
}
60+
@Test(expected=CompositeKeyFormatException.class)
61+
public void testCompositeKeyWithInvalidAttributeMaxCodePoint() {
62+
new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "jk\udbff\udfffl", "mno"}));
63+
}
64+
65+
@Test
66+
public void testGetObjectType() {
67+
final CompositeKey key = new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
68+
assertThat(key.getObjectType(), is(equalTo("abc")));
69+
}
70+
71+
@Test
72+
public void testGetAttributes() {
73+
final CompositeKey key = new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
74+
assertThat(key.getObjectType(), is(equalTo("abc")));
75+
assertThat(key.getAttributes(), hasSize(4));
76+
assertThat(key.getAttributes(), contains("def", "ghi", "jkl", "mno"));
77+
}
78+
79+
@Test
80+
public void testToString() {
81+
final CompositeKey key = new CompositeKey("abc", Arrays.asList(new String[] {"def", "ghi", "jkl", "mno"}));
82+
assertThat(key.toString(), is(equalTo("abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000")));
83+
}
84+
85+
@Test
86+
public void testParseCompositeKey() {
87+
final CompositeKey key = CompositeKey.parseCompositeKey("abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000");
88+
assertThat(key.getObjectType(), is(equalTo("abc")));
89+
assertThat(key.getAttributes(), hasSize(4));
90+
assertThat(key.getAttributes(), contains("def", "ghi", "jkl", "mno"));
91+
assertThat(key.toString(), is(equalTo("abc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000")));
92+
}
93+
94+
@Test(expected=CompositeKeyFormatException.class)
95+
public void testParseCompositeKeyInvalidObjectType() {
96+
CompositeKey.parseCompositeKey("ab\udbff\udfffc\u0000def\u0000ghi\u0000jkl\u0000mno\u0000");
97+
}
98+
99+
@Test(expected=CompositeKeyFormatException.class)
100+
public void testParseCompositeKeyInvalidAttribute() {
101+
CompositeKey.parseCompositeKey("abc\u0000def\u0000ghi\u0000jk\udbff\udfffl\u0000mno\u0000");
102+
}
103+
104+
}

0 commit comments

Comments
 (0)