Skip to content

Commit 7b55c6e

Browse files
bullet-toothdmitry-timofeev
authored andcommitted
Update artifact id representation [ECR-4041] (#1349)
Added version to artifact id, to match the ArtifactId used in core.
1 parent efa01ec commit 7b55c6e

File tree

37 files changed

+375
-237
lines changed

37 files changed

+375
-237
lines changed

exonum-java-binding/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
4949
to require throwing `ExecutionException` instead of
5050
`IllegalArgumentException`.
5151
- Transaction index in block type changed from `long` to `int`. (#1348)
52+
- Extracted artifact version to the separate field from the artifact name.
53+
Artifact name format is `groupId/artifactId` now.
54+
PluginId format is `runtimeId:artifactName:artifactVersion` now. (#1349)
5255

5356
### Removed
5457
- Classes supporting no longer used tree-like list proof representation.

exonum-java-binding/core/rust/exonum-java/qa-service.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ plugins:
1818
artifacts:
1919
qa-service:
2020
runtime: java
21-
name: "com.exonum.binding:exonum-java-binding-qa-service:0.10.0-SNAPSHOT"
21+
name: "com.exonum.binding/exonum-java-binding-qa-service"
22+
version: "0.10.0-SNAPSHOT"
2223
spec:
2324
artifact_filename: "exonum-java-binding-qa-service-0.10.0-SNAPSHOT-artifact.jar"
2425
time:
2526
runtime: rust
26-
name: "exonum-time:0.13.0-rc.2"
27+
name: "exonum-time"
28+
version: "0.13.0-rc.2"
2729

2830
instances:
2931
qa-service:

exonum-java-binding/core/src/main/java/com/exonum/binding/core/runtime/JavaArtifactNames.java

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2019 The Exonum Team
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+
17+
package com.exonum.binding.core.runtime;
18+
19+
import java.util.regex.Matcher;
20+
import java.util.regex.Pattern;
21+
22+
final class JavaArtifactUtils {
23+
private static final Pattern FORBIDDEN_CHARS_PATTERN = Pattern.compile("[\\s]");
24+
25+
/**
26+
* Validates the string representation of the artifact id for forbidden characters.
27+
* @throws IllegalArgumentException if any forbidden characters found
28+
*/
29+
static void checkNoForbiddenChars(String artifactId) {
30+
Matcher matcher = FORBIDDEN_CHARS_PATTERN.matcher(artifactId);
31+
if (matcher.find()) {
32+
throw new IllegalArgumentException(String.format("'%s' must not have any forbidden "
33+
+ "characters, but there is '%s' at index %d",
34+
artifactId, matcher.group(), matcher.start()));
35+
}
36+
}
37+
38+
private JavaArtifactUtils() {
39+
}
40+
}

exonum-java-binding/core/src/main/java/com/exonum/binding/core/runtime/Pf4jServiceLoader.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package com.exonum.binding.core.runtime;
1818

19+
import static com.exonum.binding.core.runtime.RuntimeId.JAVA;
1920
import static com.google.common.base.Preconditions.checkArgument;
2021
import static com.google.common.base.Preconditions.checkNotNull;
2122
import static com.google.common.base.Preconditions.checkState;
@@ -48,7 +49,8 @@ final class Pf4jServiceLoader implements ServiceLoader {
4849

4950
private static final Comparator<ServiceArtifactId> SERVICE_ID_COMPARATOR =
5051
// No need to compare id — it is always Java
51-
Comparator.comparing(ServiceArtifactId::getName);
52+
Comparator.comparing(ServiceArtifactId::getName)
53+
.thenComparing(ServiceArtifactId::getVersion);
5254

5355
private final PluginManager pluginManager;
5456
private final ClassLoadingScopeChecker classLoadingChecker;
@@ -131,8 +133,11 @@ private LoadedServiceDefinition loadDefinition(String pluginId) throws ServiceLo
131133
private static ServiceArtifactId extractServiceId(String pluginId)
132134
throws ServiceLoadingException {
133135
try {
134-
JavaArtifactNames.checkArtifactName(pluginId);
135-
return ServiceArtifactId.newJavaId(pluginId);
136+
ServiceArtifactId serviceArtifactId = ServiceArtifactId.parseFrom(pluginId);
137+
checkArgument(serviceArtifactId.getRuntimeId() == JAVA.getId(),
138+
"Required Java (%s) runtime id, but actually was %s",
139+
JAVA.getId(), serviceArtifactId.getRuntimeId());
140+
return serviceArtifactId;
136141
} catch (IllegalArgumentException e) {
137142
String message = String.format(
138143
"Invalid plugin id (%s) is specified in service artifact metadata", pluginId);
@@ -189,8 +194,7 @@ public Optional<LoadedServiceDefinition> findService(ServiceArtifactId artifactI
189194
@Override
190195
public void unloadService(ServiceArtifactId artifactId) {
191196
checkArgument(loadedServices.containsKey(artifactId), "No such artifactId: %s", artifactId);
192-
193-
String pluginId = artifactId.getName();
197+
String pluginId = artifactId.toString();
194198
try {
195199
boolean stopped = pluginManager.unloadPlugin(pluginId);
196200
// The docs don't say why it may fail to stop the plugin.
@@ -210,7 +214,7 @@ public void unloadAll() {
210214
// Unload the plugins
211215
List<Exception> errors = new ArrayList<>();
212216
for (ServiceArtifactId artifactId : loadedServices.keySet()) {
213-
String pluginId = artifactId.getName();
217+
String pluginId = artifactId.toString();
214218
try {
215219
unloadPlugin(pluginId);
216220
} catch (Exception e) {

exonum-java-binding/core/src/main/java/com/exonum/binding/core/runtime/ServiceArtifactId.java

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,53 +21,65 @@
2121
import static java.lang.Integer.parseInt;
2222
import static org.apache.logging.log4j.util.Strings.isNotBlank;
2323

24+
import com.exonum.core.messages.Runtime.ArtifactId;
2425
import com.google.auto.value.AutoValue;
2526

2627
/**
2728
* A service artifact identifier. It consists of the runtime id in which the service shall be
28-
* deployed and the service artifact name. The name of Java artifacts usually contains the three
29-
* coordinates identifying any Java artifact: groupId, artifactId and version.
29+
* deployed, the service artifact name and its version.
3030
*
3131
* <p>The extensions of this class must be immutable and hence thread-safe.
3232
*/
3333
@AutoValue
3434
public abstract class ServiceArtifactId {
3535

3636
private static final String DELIMITER = ":";
37-
private static final int NUM_FIELDS = 2;
37+
private static final int KEEP_EMPTY = -1;
3838

3939
/**
4040
* Returns the runtime id in which the service shall be deployed.
4141
*/
4242
public abstract int getRuntimeId();
4343

4444
/**
45-
* Returns the full artifact name of this service (e.g., "com.acme:land-registry:1.2.0").
45+
* Returns the artifact name of this service.
46+
* The name of Java artifacts usually (but not necessary) contains the two coordinates:
47+
* groupId and artifactId separated by "/" (e.g., "com.acme/land-registry").
4648
*/
4749
public abstract String getName();
4850

4951
/**
50-
* Parses a service id in format "runtimeId:serviceName" as {@link #toString()} produces.
52+
* Returns the artifact version of this service (e.g., "1.2.0").
53+
*/
54+
public abstract String getVersion();
55+
56+
/**
57+
* Parses a service id in format "runtimeId:serviceName:version" as {@link #toString()} produces.
5158
*
52-
* @param serviceArtifactId a string in format "runtimeId:serviceName". Whitespace
59+
* @param serviceArtifactId a string in format "runtimeId:serviceName:version". Whitespace
5360
* characters, including preceding and trailing, are not allowed
5461
* @return a ServiceArtifactId with the given coordinates
5562
* @throws IllegalArgumentException if the format is not correct
5663
*/
5764
public static ServiceArtifactId parseFrom(String serviceArtifactId) {
58-
String[] coordinates = serviceArtifactId.split(DELIMITER, NUM_FIELDS);
65+
JavaArtifactUtils.checkNoForbiddenChars(serviceArtifactId);
66+
String[] coordinates = serviceArtifactId.split(DELIMITER, KEEP_EMPTY);
67+
checkArgument(coordinates.length == 3,
68+
"Invalid artifact id (%s), must have 'runtimeId:artifactName:version' format",
69+
serviceArtifactId);
5970
int runtimeId = parseInt(coordinates[0]);
6071
String name = coordinates[1];
61-
return valueOf(runtimeId, name);
72+
String version = coordinates[2];
73+
return valueOf(runtimeId, name, version);
6274
}
6375

6476
/**
6577
* Creates a new service artifact id of a Java artifact.
6678
*
6779
* @param name the name of the service; must not be blank
6880
*/
69-
public static ServiceArtifactId newJavaId(String name) {
70-
return valueOf(JAVA.getId(), name);
81+
public static ServiceArtifactId newJavaId(String name, String version) {
82+
return valueOf(JAVA.getId(), name, version);
7183
}
7284

7385
/**
@@ -76,16 +88,24 @@ public static ServiceArtifactId newJavaId(String name) {
7688
* @param runtimeId the runtime id in which the service shall be deployed
7789
* @param name the name of the service; must not be blank
7890
*/
79-
public static ServiceArtifactId valueOf(int runtimeId, String name) {
91+
public static ServiceArtifactId valueOf(int runtimeId, String name, String version) {
8092
checkArgument(isNotBlank(name), "name is blank: '%s'", name);
81-
return new AutoValue_ServiceArtifactId(runtimeId, name);
93+
checkArgument(isNotBlank(version), "version is blank: '%s'", version);
94+
return new AutoValue_ServiceArtifactId(runtimeId, name, version);
95+
}
96+
97+
/**
98+
* Creates a new service artifact from the given artifact id message.
99+
*/
100+
public static ServiceArtifactId fromProto(ArtifactId artifactId) {
101+
return valueOf(artifactId.getRuntimeId(), artifactId.getName(), artifactId.getVersion());
82102
}
83103

84104
/**
85-
* Returns an artifact id in the following format: "runtimeId:serviceName".
105+
* Returns an artifact id in the following format: "runtimeId:serviceName:version".
86106
*/
87107
@Override
88108
public final String toString() {
89-
return getRuntimeId() + DELIMITER + getName();
109+
return getRuntimeId() + DELIMITER + getName() + DELIMITER + getVersion();
90110
}
91111
}

exonum-java-binding/core/src/main/java/com/exonum/binding/core/runtime/ServiceRuntimeAdapter.java

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,36 +75,51 @@ void initialize(long nodeNativeHandle) {
7575
/**
7676
* Deploys the Java service artifact.
7777
*
78-
* @param name the Java service artifact name in format "groupId:artifactId:version"
78+
* @param artifactId bytes representation of the Java service artifact id as a serialized message
7979
* @param deploySpec the deploy specification as a serialized
8080
* {@link com.exonum.binding.core.runtime.DeployArguments}
8181
* protobuf message
8282
* @throws IllegalArgumentException if the deploy specification or id are not valid
8383
* @throws ServiceLoadingException if the runtime failed to load the service or it is not correct
8484
* @see ServiceRuntime#deployArtifact(ServiceArtifactId, String)
8585
*/
86-
void deployArtifact(String name, byte[] deploySpec) throws ServiceLoadingException {
87-
DeployArguments deployArguments = parseDeployArgs(name, deploySpec);
86+
void deployArtifact(byte[] artifactId, byte[] deploySpec) throws ServiceLoadingException {
87+
ArtifactId artifact = parseArtifact(artifactId);
88+
ServiceArtifactId javaArtifactId = ServiceArtifactId.fromProto(artifact);
89+
90+
DeployArguments deployArguments = parseDeployArgs(javaArtifactId, deploySpec);
8891
String artifactFilename = deployArguments.getArtifactFilename();
8992

90-
serviceRuntime.deployArtifact(ServiceArtifactId.newJavaId(name), artifactFilename);
93+
serviceRuntime.deployArtifact(javaArtifactId, artifactFilename);
9194
}
9295

9396
/**
9497
* Returns true if the artifact with the given name is deployed in this runtime;
9598
* false — otherwise.
96-
* @param name the service artifact name in format "groupId:artifactId:version"
99+
* @param artifactId bytes representation of the service artifact
97100
*/
98-
boolean isArtifactDeployed(String name) {
99-
ServiceArtifactId artifactId = ServiceArtifactId.newJavaId(name);
100-
return serviceRuntime.isArtifactDeployed(artifactId);
101+
boolean isArtifactDeployed(byte[] artifactId) {
102+
ArtifactId artifact = parseArtifact(artifactId);
103+
ServiceArtifactId serviceArtifact = ServiceArtifactId.newJavaId(
104+
artifact.getName(), artifact.getVersion());
105+
return serviceRuntime.isArtifactDeployed(serviceArtifact);
101106
}
102107

103-
private static DeployArguments parseDeployArgs(String name, byte[] deploySpec) {
108+
private static DeployArguments parseDeployArgs(ServiceArtifactId artifact, byte[] deploySpec) {
104109
try {
105110
return DeployArguments.parseFrom(deploySpec);
106111
} catch (InvalidProtocolBufferException e) {
107-
String message = "Invalid deploy specification for " + name;
112+
String message = "Invalid deploy specification for artifact " + artifact;
113+
logger.error(message, e);
114+
throw new IllegalArgumentException(message, e);
115+
}
116+
}
117+
118+
private static ArtifactId parseArtifact(byte[] artifactId) {
119+
try {
120+
return ArtifactId.parseFrom(artifactId);
121+
} catch (InvalidProtocolBufferException e) {
122+
String message = "Invalid artifact";
108123
logger.error(message, e);
109124
throw new IllegalArgumentException(message, e);
110125
}
@@ -156,8 +171,7 @@ private static ServiceInstanceSpec parseInstanceSpec(byte[] instanceSpec) {
156171
try {
157172
InstanceSpec spec = InstanceSpec.parseFrom(instanceSpec);
158173
ArtifactId artifact = spec.getArtifact();
159-
ServiceArtifactId artifactId = ServiceArtifactId.valueOf(artifact.getRuntimeId(),
160-
artifact.getName());
174+
ServiceArtifactId artifactId = ServiceArtifactId.fromProto(artifact);
161175
return ServiceInstanceSpec.newInstance(spec.getName(), spec.getId(), artifactId);
162176
} catch (InvalidProtocolBufferException e) {
163177
logger.error(e);

exonum-java-binding/core/src/test/java/com/exonum/binding/core/runtime/GuiceServicesFactoryTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void setUp() {
4545

4646
@Test
4747
void createService() {
48-
ServiceArtifactId artifactId = ServiceArtifactId.newJavaId("com.acme:foo-service:1.0.0");
48+
ServiceArtifactId artifactId = ServiceArtifactId.newJavaId("com.acme/foo-service", "1.0.0");
4949
LoadedServiceDefinition serviceDefinition = LoadedServiceDefinition
5050
.newInstance(artifactId, TestServiceModule::new);
5151
ServiceInstanceSpec instanceSpec = ServiceInstanceSpec.newInstance(TEST_NAME,
@@ -62,7 +62,8 @@ void createService() {
6262

6363
@Test
6464
void createServiceFailsIfNoServiceBindingsInModule() {
65-
ServiceArtifactId artifactId = ServiceArtifactId.newJavaId("com.acme:incomplete-service:1.0.0");
65+
ServiceArtifactId artifactId = ServiceArtifactId
66+
.newJavaId("com.acme/incomplete-service", "1.0.0");
6667
LoadedServiceDefinition serviceDefinition = LoadedServiceDefinition
6768
.newInstance(artifactId, IncompleteServiceModule::new);
6869
ServiceInstanceSpec instanceSpec = ServiceInstanceSpec.newInstance(TEST_NAME,

0 commit comments

Comments
 (0)