Skip to content

Commit 15f1d8c

Browse files
authored
Merge pull request #695 from scalecube/feature/issue-694-pre-destroy-in-injector
Issue #694 BeforeDestroy realization
2 parents 8ec4b9b + 064920c commit 15f1d8c

File tree

17 files changed

+294
-143
lines changed

17 files changed

+294
-143
lines changed

services-api/src/main/java/io/scalecube/services/Reflect.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import io.scalecube.services.auth.Auth;
1616
import io.scalecube.services.auth.Principal;
1717
import io.scalecube.services.methods.MethodInfo;
18-
import java.lang.reflect.Field;
1918
import java.lang.reflect.Method;
2019
import java.lang.reflect.Parameter;
2120
import java.lang.reflect.ParameterizedType;
@@ -133,6 +132,7 @@ public static boolean isRequestTypeServiceMessage(Method method) {
133132
* @param object to inspect
134133
* @return the parameterized Type of a given object or Object class if unknown.
135134
*/
135+
@SuppressWarnings("unused")
136136
public static Type parameterizedType(Object object) {
137137
if (object != null) {
138138
Type type = object.getClass().getGenericSuperclass();
@@ -269,7 +269,7 @@ public static String methodName(Method method) {
269269
*
270270
* @param serviceInterface service interface to get qualifier for
271271
* @param method service's method to get qualifier for
272-
* @return
272+
* @return qualifier string
273273
*/
274274
public static String qualifier(Class<?> serviceInterface, Method method) {
275275
return Qualifier.asString(Reflect.serviceName(serviceInterface), Reflect.methodName(method));
@@ -387,12 +387,6 @@ private static boolean isRequestChannel(Method method) {
387387
|| Publisher.class.isAssignableFrom(reqTypes[0]));
388388
}
389389

390-
public static void setField(Field field, Object object, Object value)
391-
throws IllegalAccessException {
392-
field.setAccessible(true);
393-
field.set(object, value);
394-
}
395-
396390
public static boolean isService(Class<?> type) {
397391
return type.isAnnotationPresent(Service.class);
398392
}

services-api/src/main/java/io/scalecube/services/ServiceInfo.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
import java.util.Collections;
77
import java.util.HashMap;
88
import java.util.Map;
9+
import java.util.StringJoiner;
910

11+
@SuppressWarnings("rawtypes")
1012
public class ServiceInfo {
1113

1214
private final Object serviceInstance;
@@ -23,6 +25,10 @@ private ServiceInfo(Builder builder) {
2325
this.authenticator = builder.authenticator;
2426
}
2527

28+
public static Builder from(ServiceInfo serviceInfo) {
29+
return new Builder(serviceInfo);
30+
}
31+
2632
public static Builder fromServiceInstance(Object serviceInstance) {
2733
return new Builder(serviceInstance);
2834
}
@@ -47,14 +53,35 @@ public Authenticator authenticator() {
4753
return authenticator;
4854
}
4955

56+
@Override
57+
public String toString() {
58+
return new StringJoiner(", ", ServiceInfo.class.getSimpleName() + "[", "]")
59+
.add("serviceInstance=" + serviceInstance)
60+
.add("tags=" + tags)
61+
.add("errorMapper=" + errorMapper)
62+
.add("dataDecoder=" + dataDecoder)
63+
.add("authenticator=" + authenticator)
64+
.toString();
65+
}
66+
67+
@SuppressWarnings("rawtypes")
5068
public static class Builder {
51-
private final Object serviceInstance;
52-
private final Map<String, String> tags = new HashMap<>();
69+
70+
private Object serviceInstance;
71+
private Map<String, String> tags = new HashMap<>();
5372
private ServiceProviderErrorMapper errorMapper;
5473
private ServiceMessageDataDecoder dataDecoder;
5574
private Authenticator authenticator;
5675

57-
public Builder(Object serviceInstance) {
76+
private Builder(ServiceInfo serviceInfo) {
77+
this.serviceInstance = serviceInfo.serviceInstance;
78+
this.tags.putAll(new HashMap<>(serviceInfo.tags));
79+
this.errorMapper = serviceInfo.errorMapper;
80+
this.dataDecoder = serviceInfo.dataDecoder;
81+
this.authenticator = serviceInfo.authenticator;
82+
}
83+
84+
private Builder(Object serviceInstance) {
5885
this.serviceInstance = serviceInstance;
5986
}
6087

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package io.scalecube.services.annotations;
2+
3+
import static java.lang.annotation.ElementType.METHOD;
4+
import static java.lang.annotation.RetentionPolicy.RUNTIME;
5+
6+
import java.lang.annotation.Documented;
7+
import java.lang.annotation.Retention;
8+
import java.lang.annotation.Target;
9+
10+
/**
11+
* This annotation is used to mark the method which will be executed before shutdown of service
12+
* <br>
13+
* Scalecube services doesn't support {@link javax.annotation.PreDestroy} since Java API *
14+
* Specification for it has strict limitation for annotated method.
15+
*/
16+
@Documented
17+
@Retention(RUNTIME)
18+
@Target(METHOD)
19+
public @interface BeforeDestroy {}

services-api/src/main/java/io/scalecube/services/methods/MethodInfo.java

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import io.scalecube.services.CommunicationMode;
44
import io.scalecube.services.api.Qualifier;
55
import java.lang.reflect.Type;
6+
import java.util.StringJoiner;
67

78
public final class MethodInfo {
89

@@ -97,33 +98,19 @@ public boolean isAuth() {
9798
return auth;
9899
}
99100

100-
/**
101-
* Shortened version of {@code toString} method.
102-
*
103-
* @return method info as string
104-
*/
105-
public String asString() {
106-
final StringBuilder sb = new StringBuilder("MethodInfo{");
107-
sb.append("qualifier='").append(qualifier).append('\'');
108-
sb.append(", auth=").append(auth);
109-
sb.append('}');
110-
return sb.toString();
111-
}
112-
113101
@Override
114102
public String toString() {
115-
final StringBuilder sb = new StringBuilder("MethodInfo{");
116-
sb.append("serviceName='").append(serviceName).append('\'');
117-
sb.append(", methodName='").append(methodName).append('\'');
118-
sb.append(", qualifier='").append(qualifier).append('\'');
119-
sb.append(", parameterizedReturnType=").append(parameterizedReturnType);
120-
sb.append(", isReturnTypeServiceMessage=").append(isReturnTypeServiceMessage);
121-
sb.append(", communicationMode=").append(communicationMode);
122-
sb.append(", parameterCount=").append(parameterCount);
123-
sb.append(", requestType=").append(requestType);
124-
sb.append(", isRequestTypeServiceMessage=").append(isRequestTypeServiceMessage);
125-
sb.append(", auth=").append(auth);
126-
sb.append('}');
127-
return sb.toString();
103+
return new StringJoiner(", ", MethodInfo.class.getSimpleName() + "[", "]")
104+
.add("serviceName='" + serviceName + "'")
105+
.add("methodName='" + methodName + "'")
106+
.add("qualifier='" + qualifier + "'")
107+
.add("parameterizedReturnType=" + parameterizedReturnType)
108+
.add("isReturnTypeServiceMessage=" + isReturnTypeServiceMessage)
109+
.add("communicationMode=" + communicationMode)
110+
.add("parameterCount=" + parameterCount)
111+
.add("requestType=" + requestType)
112+
.add("isRequestTypeServiceMessage=" + isRequestTypeServiceMessage)
113+
.add("auth=" + auth)
114+
.toString();
128115
}
129116
}

services-api/src/main/java/io/scalecube/services/methods/ServiceMethodInvoker.java

Lines changed: 15 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,9 @@
99
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
1010
import java.lang.reflect.InvocationTargetException;
1111
import java.lang.reflect.Method;
12-
import java.lang.reflect.Parameter;
1312
import java.util.Optional;
1413
import java.util.StringJoiner;
1514
import java.util.function.Consumer;
16-
import java.util.stream.Collectors;
17-
import java.util.stream.Stream;
1815
import org.reactivestreams.Publisher;
1916
import reactor.core.publisher.Flux;
2017
import reactor.core.publisher.Mono;
@@ -43,7 +40,7 @@ public final class ServiceMethodInvoker {
4340
* @param errorMapper error mapper
4441
* @param dataDecoder data decoder
4542
*/
46-
@SuppressWarnings("unchecked")
43+
@SuppressWarnings({"unchecked", "rawtypes"})
4744
public ServiceMethodInvoker(
4845
Method method,
4946
Object service,
@@ -202,34 +199,23 @@ private void applyRequestReleaser(ServiceMessage request, Consumer<Object> reque
202199
}
203200
}
204201

205-
/**
206-
* Shortened version of {@code toString} method.
207-
*
208-
* @return service method invoker as string
209-
*/
210-
public String asString() {
211-
return new StringJoiner(", ", ServiceMethodInvoker.class.getSimpleName() + "[", "]")
212-
.add("methodInfo=" + methodInfo.asString())
213-
.add(
214-
"serviceMethod='"
215-
+ service.getClass().getCanonicalName()
216-
+ "."
217-
+ method.getName()
218-
+ "("
219-
+ methodInfo.parameterCount()
220-
+ ")"
221-
+ "'")
222-
.toString();
202+
public Object service() {
203+
return service;
204+
}
205+
206+
public MethodInfo methodInfo() {
207+
return methodInfo;
223208
}
224209

225210
@Override
226211
public String toString() {
227-
String classAndMethod = service.getClass().getCanonicalName() + "." + method.getName();
228-
String args =
229-
Stream.of(method.getParameters())
230-
.map(Parameter::getType)
231-
.map(Class::getSimpleName)
232-
.collect(Collectors.joining(", ", "(", ")"));
233-
return classAndMethod + args;
212+
return new StringJoiner(", ", ServiceMethodInvoker.class.getSimpleName() + "[", "]")
213+
.add("method=" + method)
214+
.add("service=" + service)
215+
.add("methodInfo=" + methodInfo)
216+
.add("errorMapper=" + errorMapper)
217+
.add("dataDecoder=" + dataDecoder)
218+
.add("authenticator=" + authenticator)
219+
.toString();
234220
}
235221
}
Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
package io.scalecube.services.methods;
22

3-
import io.scalecube.services.auth.Authenticator;
4-
import io.scalecube.services.exceptions.ServiceProviderErrorMapper;
5-
import io.scalecube.services.transport.api.ServiceMessageDataDecoder;
3+
import io.scalecube.services.ServiceInfo;
64
import java.util.List;
75

86
public interface ServiceMethodRegistry {
97

10-
void registerService(
11-
Object serviceInstance,
12-
ServiceProviderErrorMapper errorMapper,
13-
ServiceMessageDataDecoder dataDecoder,
14-
Authenticator authenticator);
8+
void registerService(ServiceInfo serviceInfo);
159

1610
boolean containsInvoker(String qualifier);
1711

1812
ServiceMethodInvoker getInvoker(String qualifier);
1913

2014
List<ServiceMethodInvoker> listInvokers();
15+
16+
List<ServiceInfo> listServices();
2117
}

services-discovery/src/main/java/io/scalecube/services/discovery/ScalecubeServiceDiscovery.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ public void serialize(Message message, OutputStream stream) throws Exception {
363363
}
364364
}
365365

366+
@SuppressWarnings("unused")
366367
public interface MonitorMBean {
367368

368369
String getClusterConfig();

services/src/main/java/io/scalecube/services/Injector.java

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
package io.scalecube.services;
22

33
import io.scalecube.services.annotations.AfterConstruct;
4+
import io.scalecube.services.annotations.BeforeDestroy;
5+
import io.scalecube.services.annotations.Inject;
46
import io.scalecube.services.routing.Router;
7+
import java.lang.annotation.Annotation;
58
import java.lang.reflect.Field;
69
import java.lang.reflect.Method;
710
import java.util.Arrays;
811
import java.util.Collection;
9-
import org.slf4j.Logger;
10-
import org.slf4j.LoggerFactory;
12+
import reactor.core.Exceptions;
1113

1214
/** Service Injector scan and injects beans to a given Microservices instance. */
1315
final class Injector {
1416

15-
private static final Logger LOGGER = LoggerFactory.getLogger(Injector.class);
16-
1717
private Injector() {
1818
// Do not instantiate
1919
}
@@ -36,44 +36,48 @@ public static Microservices inject(Microservices microservices, Collection<Objec
3636
}
3737

3838
private static void injectField(Microservices microservices, Field field, Object service) {
39-
try {
40-
if (field.isAnnotationPresent(io.scalecube.services.annotations.Inject.class)
41-
&& field.getType().equals(Microservices.class)) {
42-
Reflect.setField(field, service, microservices);
43-
} else if (field.isAnnotationPresent(io.scalecube.services.annotations.Inject.class)
44-
&& Reflect.isService(field.getType())) {
45-
io.scalecube.services.annotations.Inject injection =
46-
field.getAnnotation(io.scalecube.services.annotations.Inject.class);
47-
Class<? extends Router> routerClass = injection.router();
48-
49-
final ServiceCall call = microservices.call();
50-
51-
if (!routerClass.isInterface()) {
52-
call.router(routerClass);
53-
}
54-
55-
final Object targetProxy = call.api(field.getType());
56-
57-
Reflect.setField(field, service, targetProxy);
39+
if (field.isAnnotationPresent(Inject.class) && field.getType().equals(Microservices.class)) {
40+
setField(field, service, microservices);
41+
} else if (field.isAnnotationPresent(Inject.class) && Reflect.isService(field.getType())) {
42+
Inject injection = field.getAnnotation(Inject.class);
43+
Class<? extends Router> routerClass = injection.router();
44+
final ServiceCall call = microservices.call();
45+
if (!routerClass.isInterface()) {
46+
call.router(routerClass);
5847
}
59-
} catch (Exception ex) {
60-
LOGGER.error(
61-
"failed to set service proxy of type: {} reason:{}",
62-
service.getClass().getName(),
63-
ex.getMessage());
48+
final Object targetProxy = call.api(field.getType());
49+
setField(field, service, targetProxy);
50+
}
51+
}
52+
53+
private static void setField(Field field, Object object, Object value) {
54+
try {
55+
field.setAccessible(true);
56+
field.set(object, value);
57+
} catch (Exception e) {
58+
throw Exceptions.propagate(e);
6459
}
6560
}
6661

6762
private static void processAfterConstruct(Microservices microservices, Object targetInstance) {
63+
processMethodWithAnnotation(microservices, targetInstance, AfterConstruct.class);
64+
}
65+
66+
public static void processBeforeDestroy(Microservices microservices, Object targetInstance) {
67+
processMethodWithAnnotation(microservices, targetInstance, BeforeDestroy.class);
68+
}
69+
70+
private static <A extends Annotation> void processMethodWithAnnotation(
71+
Microservices microservices, Object targetInstance, Class<A> annotation) {
6872
Method[] declaredMethods = targetInstance.getClass().getDeclaredMethods();
6973
Arrays.stream(declaredMethods)
70-
.filter(method -> method.isAnnotationPresent(AfterConstruct.class))
74+
.filter(method -> method.isAnnotationPresent(annotation))
7175
.forEach(
72-
afterConstructMethod -> {
76+
targetMethod -> {
7377
try {
74-
afterConstructMethod.setAccessible(true);
78+
targetMethod.setAccessible(true);
7579
Object[] parameters =
76-
Arrays.stream(afterConstructMethod.getParameters())
80+
Arrays.stream(targetMethod.getParameters())
7781
.map(
7882
mapper -> {
7983
if (mapper.getType().equals(Microservices.class)) {
@@ -85,9 +89,9 @@ private static void processAfterConstruct(Microservices microservices, Object ta
8589
}
8690
})
8791
.toArray();
88-
afterConstructMethod.invoke(targetInstance, parameters);
92+
targetMethod.invoke(targetInstance, parameters);
8993
} catch (Exception ex) {
90-
throw new RuntimeException(ex);
94+
throw Exceptions.propagate(ex);
9195
}
9296
});
9397
}

0 commit comments

Comments
 (0)