Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract DI to separate module #711

Closed
wants to merge 74 commits into from
Closed
Show file tree
Hide file tree
Changes from 59 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
dad2bd9
ISSUE-708 Extract interface from Microservices
Feb 18, 2020
3d4011c
ISSUE-708 New entrypoint for DI frameworks
Feb 18, 2020
26c3442
Merge branch 'develop' into feature/issue-708-di&security-extract-to-…
Feb 18, 2020
0c89c39
ISSUE-708 fix code style
Feb 18, 2020
07a5650
ISSUE-708 fix code style
Feb 18, 2020
6140c6d
ISSUE-708 replace service instance to service instance supplier
Feb 19, 2020
c48041b
ISSUE-708 Renaming: Microservices -> ScaleCube, IMicroservices -> Mic…
Feb 22, 2020
7d969cb
ISSUE 708, WIP. Add spring extension experiment for example
Feb 28, 2020
4cbd2ac
ISSUE-708 First worked prototype
Mar 1, 2020
60215ec
ISSUE-708 Rollback lazy initialization in ServiceInfo and ServiceMeth…
Mar 1, 2020
c4ae568
ISSUE-708 Refactoring
Mar 1, 2020
5c759de
ISSUE-708 Refactoring
Mar 2, 2020
d34362b
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Mar 10, 2020
afee8d0
ISSUE 708, WIP. Refactoring
Mar 10, 2020
5fc6cac
ISSUE 708, WIP. Fix tests
Mar 11, 2020
695b7e8
ISSUE 708, WIP. Refactoring
Mar 11, 2020
c73d5e4
ISSUE 708, Set spring version
Mar 11, 2020
5cde334
ISSUE 708, replace ServiceInfo to ServiceDefinition for ServiceEndpoi…
Mar 12, 2020
5deffbc
ISSUE 708, replace Fix async operation order
Mar 12, 2020
0f14e42
ISSUE 708, fix check style
Mar 12, 2020
4ad59b0
ISSUE 708, return back of ServiceDiscovery
Mar 12, 2020
9c001be
ISSUE 708, replace flatMap(x -> x.flapMap()) to flatMap().flatMap()
Mar 12, 2020
e8147b0
ISSUE 708, delete reactor debug hooks
Mar 12, 2020
65d02a0
ISSUE 708, revert redundant changes
Mar 13, 2020
3faf17a
ISSUE 708, fix api
Mar 13, 2020
6914527
ISSUE 708, refactoring
Mar 13, 2020
7eae1a7
ISSUE 708, fix check style
Mar 13, 2020
5b98d7c
ISSUE 708, fix check style
Mar 13, 2020
22b88fc
Fixed style
artem-v Mar 14, 2020
8e48b22
ISSUE-708 fix code style
Mar 16, 2020
5dcfa30
ISSUE-708 fix code style
Mar 16, 2020
5f6c4b2
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
May 4, 2020
8145b05
ISSUE-708 merge with develop
May 4, 2020
598250e
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
May 14, 2020
a22dcbe
ISSUE-708 merge with develop and add MicroservicesContext
May 14, 2020
cc4711d
code style checks
May 14, 2020
50c3cc7
Fix tests and examples
Jun 7, 2020
8550e06
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Jun 7, 2020
13b2403
Fix code review checks
Jun 7, 2020
1895d8f
add spring & guice review
Jun 8, 2020
117fb11
Add marker interface for Service Proxy
Jun 9, 2020
ade6485
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Jun 9, 2020
e6d17bd
Rename static constructor
Jun 9, 2020
096c14a
Rename static constructor
Jun 9, 2020
39d7e30
Merge remote-tracking branch 'origin/feature/issue-708-di-security-ex…
Jun 9, 2020
c1a2842
Rename static constructor
Jun 9, 2020
f81d2c9
add TIMEOUT
Jun 9, 2020
e56245a
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Jun 9, 2020
5a9fdcb
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
eutkin Jun 9, 2020
cec7371
remove RemoteService
Jun 9, 2020
01b8530
Merge remote-tracking branch 'origin/feature/issue-708-di-security-ex…
Jun 9, 2020
f3eee6c
separate MicroservicesContext to MicroservicesContext without Service…
Jun 10, 2020
ccff077
fix code style
Jun 10, 2020
51796af
fix code style
Jun 10, 2020
d07e90a
fix code style and add lightweight spring factory
Jun 10, 2020
09a920e
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
eutkin Jun 12, 2020
f51d0b6
minor fix
Jun 13, 2020
2de26b8
merge MicroservicesContext and ExtendedMicroservicesContext and remov…
Jun 13, 2020
03aa5e1
fix checkstyles
Jun 13, 2020
0d81eee
Context is private now
Jun 13, 2020
b25f026
change MicroservicesContext signatures
Jun 13, 2020
1ecb7a7
fix example
Jun 13, 2020
b91d749
fix example
Jun 13, 2020
592c398
remove MicroservicesContext#discoveyAddress because it not yet initia…
Jun 13, 2020
4ee55fb
remove MicroservicesContext#discoveyAddress because it not yet initia…
Jun 13, 2020
9d060da
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Jun 16, 2020
02eee19
Merge with develop and extract public api of Microservices to Microse…
Jun 16, 2020
e677a73
update docs
Jun 16, 2020
c20ede7
update example
Jun 16, 2020
d7a3771
remove redundant step
Jun 16, 2020
3b20f33
remove context from Microservices state
Jun 16, 2020
3d62277
merge with develop
Jun 19, 2020
ed75878
Merge branch 'develop' into feature/issue-708-di-security-extract-to-…
Jun 19, 2020
7a0bd3a
merge with develop
Jun 19, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,18 @@ The example provisions 2 cluster nodes and making a remote interaction.
//1. ScaleCube Node node with no members
Microservices seed = Microservices.builder().startAwait();

//2. Construct a ScaleCube node which joins the cluster hosting the Greeting Service
//2. Create ServiceFactory
ServiceFactory serviceFactory = ScalecubeServiceFactory.fromInstances(new GreetingServiceImpl());

//3. Construct a ScaleCube node which joins the cluster hosting the Greeting Service
Microservices microservices =
Microservices.builder()
.discovery(
self ->
new ScalecubeServiceDiscovery(self)
.options(opts -> opts.seedMembers(toAddress(seed.discovery().address()))))
.transport(ServiceTransports::rsocketServiceTransport)
.services(new GreetingServiceImpl())
.serviceFactory(serviceFactory)
.startAwait();

//3. Create service proxy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package io.scalecube.services;

import io.scalecube.net.Address;
import io.scalecube.services.discovery.api.ServiceDiscovery;

/**
* Context of Scalecube node. Used in {@link ServiceFactory}.
*
* @see ServiceFactory
*/
public interface MicroservicesContext {

/**
* Id of Scalecube node.
*
* @return id
*/
String id();
eutkin marked this conversation as resolved.
Show resolved Hide resolved

/**
* Used for remote service call.
eutkin marked this conversation as resolved.
Show resolved Hide resolved
*
* @return service call
* @see ServiceCall
*/
ServiceCall serviceCall();

/**
* Network address of Scalecube node.
eutkin marked this conversation as resolved.
Show resolved Hide resolved
*
* @return address of node
* @see Address
*/
Address serviceAddress();

/**
* Service discovery for services localed in other nodes.
*
* @return service discovery
* @see ServiceDiscovery
*/
ServiceDiscovery serviceDiscovery();
}
eutkin marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 18 additions & 6 deletions services-api/src/main/java/io/scalecube/services/Reflect.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -243,14 +243,26 @@ public static Map<String, Method> serviceMethods(Class<?> serviceInterface) {
/**
* Util function to get service interfaces collections from service instance.
*
* @param serviceObject with extends service interface with @Service annotation.
* @param serviceObject object with extends service interface with @Service annotation.
* @return service interface class.
*/
public static Collection<Class<?>> serviceInterfaces(Object serviceObject) {
Class<?>[] interfaces = serviceObject.getClass().getInterfaces();
public static Stream<Class<?>> serviceInterfaces(Object serviceObject) {
return serviceInterfaces(serviceObject.getClass());
}

/**
* Util function to get service interfaces collections from service instance.
*
* @param serviceType with extends service interface with @Service annotation.
* @return service interface class.
*/
public static Stream<Class<?>> serviceInterfaces(Class<?> serviceType) {
if (serviceType.isInterface() && serviceType.isAnnotationPresent(Service.class)) {
return Stream.of(serviceType);
}
Class<?>[] interfaces = serviceType.getInterfaces();
return Arrays.stream(interfaces)
.filter(interfaceClass -> interfaceClass.isAnnotationPresent(Service.class))
.collect(Collectors.toList());
.filter(interfaceClass -> interfaceClass.isAnnotationPresent(Service.class));
}

public static String methodName(Method method) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ public <T> T api(Class<T> serviceInterface) {
return (T)
Proxy.newProxyInstance(
getClass().getClassLoader(),
new Class[] {serviceInterface},
new Class[]{serviceInterface},
eutkin marked this conversation as resolved.
Show resolved Hide resolved
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] params) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.scalecube.services;

import java.util.Collections;
import java.util.Map;

/** Definition of service - type and tags. */
public class ServiceDefinition {
artem-v marked this conversation as resolved.
Show resolved Hide resolved

private final Class<?> serviceType;
private final Map<String, String> tags;

public ServiceDefinition(Class<?> serviceType, Map<String, String> tags) {
this.serviceType = serviceType;
this.tags = tags;
}

public ServiceDefinition(Class<?> serviceType) {
this(serviceType, Collections.emptyMap());
}

public Class<?> type() {
return serviceType;
}

public Map<String, String> tags() {
return tags;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package io.scalecube.services;

import java.util.Collection;
import reactor.core.publisher.Mono;

/** Manages the life cycle of all services registered with Scalecube Services. */
public interface ServiceFactory {

/**
* Provide service definitions.
*
* @return collection of service definitions - service type and tags.
* @see ServiceDefinition
*/
Collection<ServiceDefinition> getServiceDefinitions();

/**
* Initialize instances of services.
*
* @param microservices microservices context
* @return Completed Mono if initialization was successful for all services.
*/
Mono<? extends Collection<ServiceInfo>> initializeServices(
MicroservicesContext microservices);

/**
* Finalization of service instances.
*
* @param microservices microservices context
* @return completed Mono if finalization was successful for all services.
*/
default Mono<Void> shutdownServices(MicroservicesContext microservices) {
return Mono.defer(Mono::empty);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public String toString() {

public static class Builder {

private Object serviceInstance;
private final Object serviceInstance;
private Map<String, String> tags = new HashMap<>();
private ServiceProviderErrorMapper errorMapper;
private ServiceMessageDataDecoder dataDecoder;
Expand Down Expand Up @@ -161,6 +161,12 @@ public <A, T> Builder principalMapper(
return this;
}

/**
* Set up {@link ServiceProviderErrorMapper} if it hasn't been set up before.
*
* @param errorMapper error mapper.
* @return current builder's state.
*/
Builder errorMapperIfAbsent(ServiceProviderErrorMapper errorMapper) {
if (this.errorMapper == null) {
this.errorMapper = errorMapper;
Expand All @@ -175,6 +181,12 @@ Builder dataDecoderIfAbsent(ServiceMessageDataDecoder dataDecoder) {
return this;
}

/**
* Set up {@link Authenticator} if it hasn't been set up before.
*
* @param authenticator authenticator.
* @return current builder's state.
*/
Builder authenticatorIfAbsent(Authenticator<Object> authenticator) {
if (this.authenticator == null) {
this.authenticator = authenticator;
Expand All @@ -189,6 +201,11 @@ Builder principalMapperIfAbsent(PrincipalMapper<Object, Object> principalMapper)
return this;
}

/**
* Build service info.
*
* @return service info.
*/
public ServiceInfo build() {
return new ServiceInfo(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@

import java.util.Collection;

/**
* Provide service instances.
*
* @deprecated use {@link ServiceFactory}
*/
@FunctionalInterface
@Deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All is cool with this interface. It shouldn't be deprecated. I still want to use good-old approach with .services(Object... ) / .services(ServiceInfo... ) or services(ServiceProvider). Keep deprecated mentioned top level functions on Microservices (or how u call it here Scalecube), but decalre them on the ScaleCubeServicesProvider (with the same fluent API style).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes you have to throw away the old one and move on :) And seriously, this API is deprecated for the following reason: we delegate tasks to store and manage service life cycle to ServiceFactory (ex ServicesProvider) implementations. Scalecube (Microservices) should not perform such tasks, imho. Especially if we allocate a separate component that solves these tasks, I see no sense in leaving the old API, it's better to transfer these methods to the new one. As an option, you can add registerDefinition(ServiceInfo serviceInfo) and <T> registerDefinition(Supplier<T> instanceSupplier, Class<T> type, Tag... tags) methods to ServiceFactory (ex ServiceProducer).

In the end.

  • I find it wrong to leave the API .services(ServiceInfo... services) in Scalecube (Microservices);
  • Take this API into the interface and implement it.

P.S. During development, I added the registration of additional services through register(ServiceInfo service) but decided to remove it because it violated the immunity of the ServiceFactory (ServicesProvider).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its ok to deprecate .services(ServiceInfo... services) but we cant just do that without discussing the public api.
services is used in all the examples and documentations and we need to direct the users as part of this pull request to a proper use of the api with examples / readme / website and basically everywhere.

plust we are changing this method we also need to discuss the public api from end user point of view. we need to review it to make sure its clean and easy to use.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ronenhamias So api is not removed, but @deprecated :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we delegate tasks to store and manage service life cycle to ServiceFactory (ex ServicesProvider) implementations

Correct. We don't want .services(Object... ) / .services(ServiceInfo... ) or services(ServiceProvider) on Microservices anymore. That's why:

Keep @deprecated functions .services(Object... ) / .services(ServiceInfo... ) or services(ServiceProvider) on

Next.

As an option, you can add registerDefinition(ServiceIn ...

We don't need options. We need clear and well understood approach. Shall we discuss it here? No. Not in the scope of this PR. That's why:

Declare them on the ScaleCubeServicesProvider (with the same fluent API style).

Upon this one.

I see no sense in leaving the old API, it's better to transfer these methods to the new one.

Good. But not in this PR. In this PR we will keep them but just move to another place.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eutkin @eutkin
Coming back to this question.

Again, why those methods were deprecated:

services(Object... services)
services(ServiceInfo... services)
services(ServiceProvider serviceProvider)

Can't u keep them and use ScalecubeServiceFactory which will be defined internally?

services(Object... services)
services(ServiceInfo... services)
services(ServiceProvider serviceProvider)
services(ServiceFactory serviceFactory)

I.e first three works with internal ScalecubeServiceFactory (client will not know even) + last one allows to specifiy explicitly serviceFactory. This way u would not need to deprecate you would just expose internal toolset called ServiceFactory.

public interface ServiceProvider {

Collection<ServiceInfo> provide(ServiceCall call);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.scalecube.services;

import io.scalecube.services.annotations.Service;
import io.scalecube.services.annotations.ServiceMethod;
import java.util.Arrays;
import java.util.Collections;
Expand All @@ -18,15 +17,15 @@ private ServiceScanner() {
/**
* Scans {@code ServiceInfo} and builds list of {@code ServiceRegistration}-s.
*
* @param serviceInfo service info instance
* @param serviceDefinition service info instance
* @return list of {@code ServiceRegistration}-s
*/
public static List<ServiceRegistration> scanServiceInfo(ServiceInfo serviceInfo) {
return Arrays.stream(serviceInfo.serviceInstance().getClass().getInterfaces())
.filter(serviceInterface -> serviceInterface.isAnnotationPresent(Service.class))
public static List<ServiceRegistration> scanServiceDefinition(
ServiceDefinition serviceDefinition) {
return Reflect.serviceInterfaces(serviceDefinition.type())
.map(
serviceInterface -> {
Map<String, String> serviceInfoTags = serviceInfo.tags();
Map<String, String> serviceInfoTags = serviceDefinition.tags();
Map<String, String> apiTags = Reflect.serviceTags(serviceInterface);
Map<String, String> buffer = new HashMap<>(apiTags);
// service tags override tags from @Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
import java.lang.annotation.Target;

/**
* This annotation is used to mark the method which will be executed before shutdown of service
* <br>
* This annotation is used to mark the method which will be executed before shutdown of service <br>
* Scalecube services doesn't support {@link javax.annotation.PreDestroy} since Java API *
* Specification for it has strict limitation for annotated method.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import io.scalecube.benchmarks.BenchmarkState;
import io.scalecube.net.Address;
import io.scalecube.services.Microservices;
import io.scalecube.services.ScalecubeServiceFactory;
import io.scalecube.services.ServiceCall;
import io.scalecube.services.ServiceFactory;
import io.scalecube.services.discovery.ScalecubeServiceDiscovery;
import io.scalecube.services.transport.rsocket.RSocketServiceTransport;
import java.time.Duration;
Expand Down Expand Up @@ -38,14 +40,16 @@ public void beforeAll() {

final Address seedAddress = seed.discovery().address();

ServiceFactory serviceFactory = ScalecubeServiceFactory.fromInstances(services);

node =
Microservices.builder()
.discovery(
endpoint ->
new ScalecubeServiceDiscovery(endpoint)
.membership(cfg -> cfg.seedMembers(seedAddress)))
.transport(RSocketServiceTransport::new)
.services(services)
.serviceFactory(serviceFactory)
.startAwait();

LOGGER.info(
Expand Down
1 change: 1 addition & 0 deletions services-examples-parent/services-examples-runner/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<artifactId>scalecube-services-examples-runner</artifactId>

<properties>
<!--suppress UnresolvedMavenProperty -->
<extratags>${env.TRAVIS_COMMIT}</extratags>
<mainClass>io.scalecube.services.examples.ExamplesRunner</mainClass>
</properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import io.scalecube.net.Address;
import io.scalecube.runners.Runners;
import io.scalecube.services.Microservices;
import io.scalecube.services.ScalecubeServiceFactory;
import io.scalecube.services.ServiceEndpoint;
import io.scalecube.services.ServiceFactory;
import io.scalecube.services.discovery.ScalecubeServiceDiscovery;
import io.scalecube.services.discovery.api.ServiceDiscovery;
import io.scalecube.services.transport.rsocket.RSocketServiceTransport;
Expand Down Expand Up @@ -52,6 +54,9 @@ public static void main(String[] args) {
.orElse(Runtime.getRuntime().availableProcessors());
LOGGER.info("Number of worker threads: " + numOfThreads);

ServiceFactory serviceFactory =
ScalecubeServiceFactory.fromInstances(
new BenchmarkServiceImpl(), new GreetingServiceImpl());
Microservices microservices =
Microservices.builder()
.discovery(endpoint -> serviceDiscovery(endpoint, config))
Expand All @@ -72,7 +77,7 @@ public static void main(String[] args) {
.port(config.servicePort())
.runOn(loopResources)
.noSSL()))
.services(new BenchmarkServiceImpl(), new GreetingServiceImpl())
.serviceFactory(serviceFactory)
.startAwait();

Runners.onShutdown(() -> microservices.shutdown().subscribe());
Expand Down
15 changes: 15 additions & 0 deletions services-examples-parent/services-examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,21 @@
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import io.scalecube.net.Address;
import io.scalecube.services.Microservices;
import io.scalecube.services.ScalecubeServiceFactory;
import io.scalecube.services.ServiceFactory;
import io.scalecube.services.discovery.ScalecubeServiceDiscovery;
import io.scalecube.services.examples.helloworld.service.GreetingServiceImpl;
import io.scalecube.services.examples.helloworld.service.api.GreetingsService;
Expand Down Expand Up @@ -29,6 +31,10 @@ public static void main(String[] args) {

final Address seedAddress = seed.discovery().address();

// Create service factory for GreetingService
ServiceFactory serviceFactory =
ScalecubeServiceFactory.fromInstances(new GreetingServiceImpl());

// Construct a ScaleCube node which joins the cluster hosting the Greeting Service
Microservices ms =
Microservices.builder()
Expand All @@ -37,7 +43,7 @@ public static void main(String[] args) {
new ScalecubeServiceDiscovery(endpoint)
.membership(cfg -> cfg.seedMembers(seedAddress)))
.transport(RSocketServiceTransport::new)
.services(new GreetingServiceImpl())
.serviceFactory(serviceFactory)
.startAwait();

seed.call()
Expand Down
Loading