/
GlobalCommandRegistry.java
130 lines (111 loc) · 5.47 KB
/
GlobalCommandRegistry.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
* Copyright (c) 2019 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.signals.commands.base;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.concurrent.Immutable;
import org.atteo.classindex.ClassIndex;
import org.eclipse.ditto.json.JsonMissingFieldException;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.base.json.JsonParsableCommand;
import org.eclipse.ditto.signals.base.AbstractJsonParsableRegistry;
import org.eclipse.ditto.signals.base.JsonParsable;
import org.eclipse.ditto.signals.base.JsonTypeNotParsableException;
/**
* Contains all strategies to deserialize subclasses of {@link Command} from a combination of
* {@link JsonObject} and {@link DittoHeaders}.
*/
@Immutable
public final class GlobalCommandRegistry extends AbstractJsonParsableRegistry<Command>
implements CommandRegistry<Command> {
private static final GlobalCommandRegistry INSTANCE = new GlobalCommandRegistry(new JsonParsableCommandRegistry());
private final Map<String, String> nameToTypePrefixMap;
private GlobalCommandRegistry(final JsonParsableCommandRegistry jsonParsableCommandRegistry) {
super(jsonParsableCommandRegistry.getParseRegistries());
nameToTypePrefixMap =
Collections.unmodifiableMap(new HashMap<>(jsonParsableCommandRegistry.getNameToTypePrefixMap()));
}
/**
* Gets an instance of {@link GlobalCommandRegistry}.
*
* @return the instance of {@link GlobalCommandRegistry}.
*/
public static GlobalCommandRegistry getInstance() {
return INSTANCE;
}
/**
* Contains all strategies to deserialize {@link Command} annotated with {@link JsonParsableCommand}
* from a combination of {@link JsonObject} and {@link DittoHeaders}.
*/
private static final class JsonParsableCommandRegistry {
private static final Class<?> JSON_OBJECT_PARAMETER = JsonObject.class;
private static final Class<?> DITTO_HEADERS_PARAMETER = DittoHeaders.class;
private final Map<String, JsonParsable<Command>> parseRegistries = new HashMap<>();
private final Map<String, String> nameToTypePrefixMap = new HashMap<>();
private JsonParsableCommandRegistry() {
final Iterable<Class<?>> jsonParsableCommands = ClassIndex.getAnnotated(JsonParsableCommand.class);
jsonParsableCommands.forEach(parsableCommand -> {
final JsonParsableCommand fromJsonAnnotation = parsableCommand.getAnnotation(JsonParsableCommand.class);
try {
final String methodName = fromJsonAnnotation.method();
final String typePrefix = fromJsonAnnotation.typePrefix();
final String name = fromJsonAnnotation.name();
final Method method = parsableCommand
.getMethod(methodName, JSON_OBJECT_PARAMETER, DITTO_HEADERS_PARAMETER);
appendMethodToParseStrategies(typePrefix, name, method);
} catch (final NoSuchMethodException e) {
final String message = String.format("Could not create deserializing strategy for '%s'.",
parsableCommand.getName());
throw new Error(message, e);
}
});
}
private void appendMethodToParseStrategies(final String typePrefix, final String name, final Method method) {
final String type = typePrefix + name;
nameToTypePrefixMap.put(name, typePrefix);
parseRegistries.put(type, ((jsonObject, dittoHeaders) -> {
try {
return (Command) method.invoke(null, jsonObject, dittoHeaders);
} catch (final IllegalAccessException | InvocationTargetException e) {
throw JsonTypeNotParsableException.newBuilder(type, getClass().getSimpleName())
.dittoHeaders(dittoHeaders).build();
}
}));
}
private Map<String, JsonParsable<Command>> getParseRegistries() {
return new HashMap<>(parseRegistries);
}
private Map<String, String> getNameToTypePrefixMap() {
return new HashMap<>(nameToTypePrefixMap);
}
}
@Override
protected String resolveType(final JsonObject jsonObject) {
/*
* If type was not present (was included in V2) take "event" instead and transform to V2 format.
* Fail if "event" also is not present.
*/
return jsonObject.getValue(Command.JsonFields.TYPE)
.orElseGet(() -> extractTypeV1(jsonObject)
.orElseThrow(() -> new JsonMissingFieldException(Command.JsonFields.TYPE)));
}
@SuppressWarnings({"squid:CallToDeprecatedMethod"})
private Optional<String> extractTypeV1(final JsonObject jsonObject) {
return jsonObject.getValue(Command.JsonFields.ID).map(event -> nameToTypePrefixMap.get(event) + event);
}
}