Skip to content

Commit d95151d

Browse files
Add initial ServiceRuntime for Dynamic Services [ECR-3404] (#1065)
Adds initial implementation of ServiceRuntime operations for dynamic services. The code in dependent on core modules has not been migrated as the ServiceRuntime interface is not finalized yet, neither are the user-facing interfaces (like Service, Transaction, etc.).
1 parent 17dc381 commit d95151d

File tree

55 files changed

+1700
-1592
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1700
-1592
lines changed

exonum-java-binding/core/checkstyle-suppressions.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,7 @@
2424

2525
<!-- Suppress indentation as there is a bug with lambdas -->
2626
<suppress files="UserServiceAdapter.java" checks="Indentation"/>
27+
28+
<!-- Allow constant names in inner classes (they can't have static final members) -->
29+
<suppress files="ServiceRuntimeIntegrationTest\.java" checks="(AbbreviationAsWordInName)|(MemberName)"/>
2730
</suppressions>

exonum-java-binding/core/findbugs-exclude.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
<Bug pattern="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE"/>
1313
</Match>
1414

15+
<!-- Use \n in the message that might be recorded in the blockchain instead of %n, which
16+
is resolved to a platform-dependent value. -->
17+
<Match>
18+
<Class name="~.*ServiceWrapper"/>
19+
<Method name="executeTransaction"/>
20+
<Bug pattern="VA_FORMAT_STRING_USES_NEWLINE"/>
21+
</Match>
22+
1523
<!-- Exclude the auto-generated files.
1624
Remove the rules below once https://github.com/spotbugs/spotbugs/issues/694 is resolved. -->
1725

@@ -27,7 +35,11 @@
2735
<Bug pattern="RV_RETURN_VALUE_IGNORED"/>
2836
</Match>
2937

38+
<!-- Auto-generated Protobuf-messages -->
3039
<Match>
3140
<Source name="~.*CoreProtos\.java"/>
3241
</Match>
42+
<Match>
43+
<Source name="~.*ServiceRuntimeProtos\.java"/>
44+
</Match>
3345
</FindBugsFilter>

exonum-java-binding/core/rust/src/utils/jni_cache.rs

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ static mut OBJECT_GET_CLASS: Option<JMethodID> = None;
3737
static mut CLASS_GET_NAME: Option<JMethodID> = None;
3838
static mut THROWABLE_GET_MESSAGE: Option<JMethodID> = None;
3939

40+
// todo: Remove transaction and service adapter items when native JavaServiceRuntime is implemented
4041
static mut TRANSACTION_ADAPTER_EXECUTE: Option<JMethodID> = None;
4142
static mut TRANSACTION_ADAPTER_INFO: Option<JMethodID> = None;
4243

@@ -76,30 +77,6 @@ unsafe fn cache_methods(env: &JNIEnv) {
7677
"getMessage",
7778
"()Ljava/lang/String;",
7879
);
79-
TRANSACTION_ADAPTER_EXECUTE = get_method_id(
80-
&env,
81-
"com/exonum/binding/core/service/adapters/UserTransactionAdapter",
82-
"execute",
83-
"(J[B[B)V",
84-
);
85-
TRANSACTION_ADAPTER_INFO = get_method_id(
86-
&env,
87-
"com/exonum/binding/core/service/adapters/UserTransactionAdapter",
88-
"info",
89-
"()Ljava/lang/String;",
90-
);
91-
SERVICE_ADAPTER_STATE_HASHES = get_method_id(
92-
&env,
93-
"com/exonum/binding/core/service/adapters/UserServiceAdapter",
94-
"getStateHashes",
95-
"(J)[[B",
96-
);
97-
SERVICE_ADAPTER_CONVERT_TRANSACTION = get_method_id(
98-
&env,
99-
"com/exonum/binding/core/service/adapters/UserServiceAdapter",
100-
"convertTransaction",
101-
"(S[B)Lcom/exonum/binding/core/service/adapters/UserTransactionAdapter;",
102-
);
10380
JAVA_LANG_ERROR = env
10481
.new_global_ref(env.find_class("java/lang/Error").unwrap().into())
10582
.ok();
@@ -118,10 +95,6 @@ unsafe fn cache_methods(env: &JNIEnv) {
11895
OBJECT_GET_CLASS.is_some()
11996
&& JAVA_LANG_ERROR.is_some()
12097
&& THROWABLE_GET_MESSAGE.is_some()
121-
&& TRANSACTION_ADAPTER_EXECUTE.is_some()
122-
&& TRANSACTION_ADAPTER_INFO.is_some()
123-
&& SERVICE_ADAPTER_STATE_HASHES.is_some()
124-
&& SERVICE_ADAPTER_CONVERT_TRANSACTION.is_some()
12598
&& JAVA_LANG_ERROR.is_some()
12699
&& JAVA_LANG_RUNTIME_EXCEPTION.is_some()
127100
&& TRANSACTION_EXECUTION_EXCEPTION.is_some(),

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

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@
1818

1919
import static com.google.inject.name.Names.named;
2020

21-
import com.exonum.binding.core.service.adapters.ViewFactory;
22-
import com.exonum.binding.core.service.adapters.ViewProxyFactory;
2321
import com.exonum.binding.core.transport.Server;
2422
import com.google.common.collect.ImmutableMap;
2523
import com.google.inject.AbstractModule;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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 com.exonum.binding.core.service.ServiceModule;
20+
import com.google.inject.Inject;
21+
import com.google.inject.Injector;
22+
import com.google.inject.Module;
23+
import java.util.function.Supplier;
24+
25+
/**
26+
* The default service factory. Although the naming choice might imply non-Guice factories
27+
* are possible, it is not true — see the {@link ServicesFactory} javadoc.
28+
*/
29+
final class GuiceServicesFactory implements ServicesFactory {
30+
31+
private final Injector frameworkInjector;
32+
33+
/**
34+
* Creates a new factory of services with the given framework injector.
35+
*
36+
* @param frameworkInjector the framework injector providing system-wide bindings to services
37+
*/
38+
@Inject
39+
GuiceServicesFactory(Injector frameworkInjector) {
40+
this.frameworkInjector = frameworkInjector;
41+
}
42+
43+
@Override
44+
public ServiceWrapper createService(LoadedServiceDefinition definition,
45+
ServiceInstanceSpec instanceSpec) {
46+
// Take the user-supplied module configuring service bindings
47+
Supplier<ServiceModule> serviceModuleSupplier = definition.getModuleSupplier();
48+
Module serviceModule = serviceModuleSupplier.get();
49+
// Create a framework-supplied module with per-service bindings
50+
Module serviceFrameworkModule = new ServiceFrameworkModule(instanceSpec);
51+
// Create a new service
52+
// todo: [ECR-3433] Reconsider the relationships between the framework injector and the child.
53+
// Currently the child injector sees everything from the parent, but it does not
54+
// seem to need that, the service needs only a well-defined subset of dependencies.
55+
Injector serviceInjector = frameworkInjector.createChildInjector(serviceModule,
56+
serviceFrameworkModule);
57+
return serviceInjector.getInstance(ServiceWrapper.class);
58+
}
59+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@
2929
abstract class LoadedServiceDefinition {
3030

3131
/**
32-
* Returns the unique identifier of the service.
32+
* Returns the unique identifier of the service artifact.
3333
*/
34-
public abstract ServiceId getId();
34+
public abstract ServiceArtifactId getId();
3535

3636
/**
3737
* Returns a supplier of {@linkplain ServiceModule service modules} configuring their bindings.
@@ -40,8 +40,8 @@ abstract class LoadedServiceDefinition {
4040
*/
4141
public abstract Supplier<ServiceModule> getModuleSupplier();
4242

43-
static LoadedServiceDefinition newInstance(ServiceId serviceId,
43+
static LoadedServiceDefinition newInstance(ServiceArtifactId artifactId,
4444
Supplier<ServiceModule> serviceModuleSupplier) {
45-
return new AutoValue_LoadedServiceDefinition(serviceId, serviceModuleSupplier);
45+
return new AutoValue_LoadedServiceDefinition(artifactId, serviceModuleSupplier);
4646
}
4747
}

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

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,14 @@
4343
*/
4444
final class Pf4jServiceLoader implements ServiceLoader {
4545

46-
private static final Comparator<ServiceId> SERVICE_ID_COMPARATOR =
47-
Comparator.comparing(ServiceId::getGroupId)
48-
.thenComparing(ServiceId::getArtifactId)
49-
.thenComparing(ServiceId::getVersion);
46+
private static final Comparator<ServiceArtifactId> SERVICE_ID_COMPARATOR =
47+
Comparator.comparing(ServiceArtifactId::getGroupId)
48+
.thenComparing(ServiceArtifactId::getArtifactId)
49+
.thenComparing(ServiceArtifactId::getVersion);
5050

5151
private final PluginManager pluginManager;
5252
private final ClassLoadingScopeChecker classLoadingChecker;
53-
private final SortedMap<ServiceId, LoadedServiceDefinition> loadedServices;
53+
private final SortedMap<ServiceArtifactId, LoadedServiceDefinition> loadedServices;
5454

5555
@Inject
5656
Pf4jServiceLoader(PluginManager pluginManager, ClassLoadingScopeChecker classLoadingChecker) {
@@ -116,19 +116,20 @@ private void startPlugin(String pluginId) throws ServiceLoadingException {
116116

117117
/** Loads the service definition from the already loaded plugin with the given id. */
118118
private LoadedServiceDefinition loadDefinition(String pluginId) throws ServiceLoadingException {
119-
ServiceId serviceId = extractServiceId(pluginId);
119+
ServiceArtifactId artifactId = extractServiceId(pluginId);
120120
Supplier<ServiceModule> serviceModuleSupplier = findServiceModuleSupplier(pluginId);
121121
LoadedServiceDefinition serviceDefinition =
122-
LoadedServiceDefinition.newInstance(serviceId, serviceModuleSupplier);
122+
LoadedServiceDefinition.newInstance(artifactId, serviceModuleSupplier);
123123

124-
assert !loadedServices.containsKey(serviceId);
125-
loadedServices.put(serviceId, serviceDefinition);
124+
assert !loadedServices.containsKey(artifactId);
125+
loadedServices.put(artifactId, serviceDefinition);
126126
return serviceDefinition;
127127
}
128128

129-
private static ServiceId extractServiceId(String pluginId) throws ServiceLoadingException {
129+
private static ServiceArtifactId extractServiceId(String pluginId)
130+
throws ServiceLoadingException {
130131
try {
131-
return ServiceId.parseFrom(pluginId);
132+
return ServiceArtifactId.parseFrom(pluginId);
132133
} catch (IllegalArgumentException e) {
133134
throw new ServiceLoadingException(
134135
String.format("Invalid plugin id (%s) is specified in service artifact metadata, "
@@ -178,22 +179,22 @@ private void unloadPlugin(String pluginId) {
178179
}
179180

180181
@Override
181-
public Optional<LoadedServiceDefinition> findService(ServiceId serviceId) {
182-
return Optional.ofNullable(loadedServices.get(serviceId));
182+
public Optional<LoadedServiceDefinition> findService(ServiceArtifactId artifactId) {
183+
return Optional.ofNullable(loadedServices.get(artifactId));
183184
}
184185

185186
@Override
186-
public void unloadService(ServiceId serviceId) {
187-
checkArgument(loadedServices.containsKey(serviceId), "No such serviceId: %s", serviceId);
187+
public void unloadService(ServiceArtifactId artifactId) {
188+
checkArgument(loadedServices.containsKey(artifactId), "No such artifactId: %s", artifactId);
188189

189-
String pluginId = serviceId.toString();
190+
String pluginId = artifactId.toString();
190191
try {
191192
boolean stopped = pluginManager.unloadPlugin(pluginId);
192193
// The docs don't say why it may fail to stop the plugin.
193194
// Follow: https://github.com/pf4j/pf4j/issues/291
194195
checkState(stopped, "Unknown error whilst unloading the plugin (%s)", pluginId);
195196
} finally {
196-
loadedServices.remove(serviceId);
197+
loadedServices.remove(artifactId);
197198
}
198199
}
199200

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

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* <p>The extensions of this class must be immutable and hence thread-safe.
3030
*/
3131
@AutoValue
32-
public abstract class ServiceId {
32+
public abstract class ServiceArtifactId {
3333

3434
private static final String DELIMITER = ":";
3535
private static final Pattern FORBIDDEN_CHARS_PATTERN = Pattern.compile("[\\s:]");
@@ -53,15 +53,16 @@ public abstract class ServiceId {
5353
/**
5454
* Parses a service id in format "groupId:artifactId:version" as {@link #toString()} produces.
5555
*
56-
* @param serviceId a string in format "groupId:artifactId:version". Whitespace characters,
57-
* including preceding and trailing, are not allowed
58-
* @return a ServiceId with the given coordinates
56+
* @param serviceArtifactId a string in format "groupId:artifactId:version". Whitespace
57+
* characters, including preceding and trailing, are not allowed
58+
* @return a ServiceArtifactId with the given coordinates
5959
* @throws IllegalArgumentException if the format is not correct
6060
*/
61-
public static ServiceId parseFrom(String serviceId) {
62-
String[] coordinates = serviceId.split(DELIMITER, KEEP_EMPTY);
61+
public static ServiceArtifactId parseFrom(String serviceArtifactId) {
62+
String[] coordinates = serviceArtifactId.split(DELIMITER, KEEP_EMPTY);
6363
checkArgument(coordinates.length == 3,
64-
"Invalid serviceId (%s), must have 'groupId:artifactId:version' format", serviceId);
64+
"Invalid serviceArtifactId (%s), must have 'groupId:artifactId:version' format",
65+
serviceArtifactId);
6566
String groupId = coordinates[0];
6667
String artifactId = coordinates[1];
6768
String version = coordinates[2];
@@ -74,8 +75,8 @@ public static ServiceId parseFrom(String serviceId) {
7475
* @throws IllegalArgumentException if any coordinate contains forbidden characters: whitespace,
7576
* colon
7677
*/
77-
public static ServiceId of(String groupId, String artifactId, String version) {
78-
return new AutoValue_ServiceId(checkNoForbiddenChars(groupId),
78+
public static ServiceArtifactId of(String groupId, String artifactId, String version) {
79+
return new AutoValue_ServiceArtifactId(checkNoForbiddenChars(groupId),
7980
checkNoForbiddenChars(artifactId),
8081
checkNoForbiddenChars(version));
8182
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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 com.google.inject.AbstractModule;
20+
21+
/**
22+
* A framework module providing per-service bindings. These bindings are supplied
23+
* by the framework.
24+
*/
25+
class ServiceFrameworkModule extends AbstractModule {
26+
27+
private final ServiceInstanceSpec instanceSpec;
28+
29+
ServiceFrameworkModule(ServiceInstanceSpec instanceSpec) {
30+
this.instanceSpec = instanceSpec;
31+
}
32+
33+
@Override
34+
protected void configure() {
35+
// todo: consider named bindings for the name and id — they will require publicly
36+
// accessible key names.
37+
bind(ServiceInstanceSpec.class).toInstance(instanceSpec);
38+
}
39+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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 com.exonum.binding.common.message.TransactionMessage;
20+
import com.google.auto.value.AutoValue;
21+
22+
/**
23+
* A specification of a service instance.
24+
*/
25+
@AutoValue
26+
public abstract class ServiceInstanceSpec {
27+
28+
/**
29+
* Returns the name of the service instance. It serves as the primary identifier of this service
30+
* in most operations. It is assigned by the network administrators.
31+
*/
32+
public abstract String getName();
33+
34+
/**
35+
* Returns the numeric id of the service instance. Exonum assigns it to the service
36+
* on instantiation. It is mainly used to route the transaction messages belonging
37+
* to this instance.
38+
*
39+
* @see TransactionMessage#getServiceId()
40+
*/
41+
public abstract int getId();
42+
43+
/**
44+
* Returns the service artifact id.
45+
*/
46+
public abstract ServiceArtifactId getArtifactId();
47+
48+
public static ServiceInstanceSpec newInstance(String name, int id, ServiceArtifactId artifactId) {
49+
return new AutoValue_ServiceInstanceSpec(name, id, artifactId);
50+
}
51+
}

0 commit comments

Comments
 (0)