Skip to content

Commit

Permalink
Introduce GlobalErrorRegistry that registers all DittoRuntimeExceptions
Browse files Browse the repository at this point in the history
annotated with @JsonParsableException

Signed-off-by: Klem Yannic (INST/ECS1) <Yannic.Klem@bosch-si.com>
  • Loading branch information
Yannic92 committed Jan 25, 2019
1 parent 81438f9 commit a0e00ca
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 0 deletions.
8 changes: 8 additions & 0 deletions bom/pom.xml
Expand Up @@ -79,6 +79,8 @@
<js.long.version>3.2.0</js.long.version>
<caffeine.version>2.6.2</caffeine.version>

<classindex.version>3.5</classindex.version>

<!-- ### Testing dependencies versions -->
<junit.version>4.12</junit.version>
<assertj.version>3.8.0</assertj.version>
Expand Down Expand Up @@ -772,6 +774,12 @@
<version>${caffeine.version}</version>
</dependency>

<dependency>
<groupId>org.atteo.classindex</groupId>
<artifactId>classindex</artifactId>
<version>${classindex.version}</version>
</dependency>

<!-- ### Provided - own artifacts ### -->
<dependency>
<groupId>org.eclipse.ditto</groupId>
Expand Down
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2017-2018 Bosch Software Innovations GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/index.php
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.model.base.json;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.atteo.classindex.IndexAnnotated;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@IndexAnnotated
public @interface JsonParsableException {

String errorCode();

/**
* The name of the method accepting a {@link org.eclipse.ditto.json.JsonObject} as first argument and
* {@link org.eclipse.ditto.model.base.headers.DittoHeaders} as seconds argument.
* The Method must return an instance of the exception annotated with this annotation.
*
* @return the name of this method.
*/
String method() default "fromJson";
}
4 changes: 4 additions & 0 deletions model/pom.xml
Expand Up @@ -46,6 +46,10 @@
<groupId>org.eclipse.ditto</groupId>
<artifactId>ditto-json</artifactId>
</dependency>
<dependency>
<groupId>org.atteo.classindex</groupId>
<artifactId>classindex</artifactId>
</dependency>

<!-- ### Provided ### -->
<dependency>
Expand Down
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2017-2018 Bosch Software Innovations GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/index.php
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.eclipse.ditto.signals.base;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

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.exceptions.DittoRuntimeException;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.base.json.JsonParsableException;

/**
* Collects
*/
@Immutable
public final class GlobalErrorRegistry extends AbstractErrorRegistry<DittoRuntimeException> {

private static final GlobalErrorRegistry instance = new GlobalErrorRegistry();

private GlobalErrorRegistry() {
super(getParseRegistries());
}

private static Map<String, JsonParsable<DittoRuntimeException>> getParseRegistries() {
return JsonParsableExceptionRegistry.getParseRegistries();
}

@Override
protected String resolveType(final JsonObject jsonObject) {
return jsonObject.getValue(DittoRuntimeException.JsonFields.ERROR_CODE)
.orElseThrow(() -> JsonMissingFieldException.newBuilder()
.fieldName(DittoRuntimeException.JsonFields.ERROR_CODE.getPointer().toString())
.build());
}

public static GlobalErrorRegistry getInstance() {
return instance;
}

private static class JsonParsableExceptionRegistry {

private static final Class<?> JSON_OBJECT_PARAMETER = JsonObject.class;
private static final Class<?> DITTO_HEADERS_PARAMETER = DittoHeaders.class;

private static final JsonParsableExceptionRegistry instance = new JsonParsableExceptionRegistry();

private final Map<String, JsonParsable<DittoRuntimeException>> parseRegistries = new HashMap<>();


private JsonParsableExceptionRegistry() {

final List<Class<? extends DittoRuntimeException>> jsonParsableExceptions = getJsonParsableExceptions();
jsonParsableExceptions.forEach(parsableException -> {
final JsonParsableException fromJsonAnnotation =
parsableException.getAnnotation(JsonParsableException.class);
try {
final String methodName = fromJsonAnnotation.method();
final String errorCode = fromJsonAnnotation.errorCode();
final Method method = parsableException
.getMethod(methodName, JSON_OBJECT_PARAMETER, DITTO_HEADERS_PARAMETER);

appendMethodToParseStrategies(errorCode, method);

} catch (NoSuchMethodException e) {
// TODO: Log warning or Throw an appropriate exception?
}
});
}

private void appendMethodToParseStrategies(final String errorCode, final Method method) {
parseRegistries.put(errorCode,
(jsonObject, dittoHeaders) -> {
try {
return (DittoRuntimeException) method.invoke(null, jsonObject, dittoHeaders);
} catch (IllegalAccessException | InvocationTargetException e) {
throw JsonTypeNotParsableException.newBuilder(errorCode, getClass().getSimpleName())
.dittoHeaders(dittoHeaders)
.build();
}
});
}

private List<Class<? extends DittoRuntimeException>> getJsonParsableExceptions() {
final Iterable<Class<?>> jsonParsableClasses = ClassIndex.getAnnotated(JsonParsableException.class);
final ArrayList<Class<? extends DittoRuntimeException>> jsonParsableExceptions = new ArrayList<>();
for (Class<?> classFromJson : jsonParsableClasses) {
if (DittoRuntimeException.class.isAssignableFrom(classFromJson)) {
jsonParsableExceptions.add(classFromJson.asSubclass(DittoRuntimeException.class));
}
}
return jsonParsableExceptions;
}

static Map<String, JsonParsable<DittoRuntimeException>> getParseRegistries() {
return instance.parseRegistries;
}
}
}
Expand Up @@ -21,11 +21,13 @@
import org.eclipse.ditto.model.base.exceptions.DittoRuntimeException;
import org.eclipse.ditto.model.base.exceptions.DittoRuntimeExceptionBuilder;
import org.eclipse.ditto.model.base.headers.DittoHeaders;
import org.eclipse.ditto.model.base.json.JsonParsableException;

/**
* Thrown if a JSON string or object could not be parsed as it was unexpected/unknown.
*/
@Immutable
@JsonParsableException(errorCode = JsonTypeNotParsableException.ERROR_CODE)
public final class JsonTypeNotParsableException extends DittoRuntimeException {

/**
Expand Down
@@ -0,0 +1,26 @@
package org.eclipse.ditto.signals.base;


import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Test;

/*
* Copyright (c) 2017-2018 Bosch Software Innovations GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/index.php
*
* SPDX-License-Identifier: EPL-2.0
*/
public class GlobalErrorRegistryTest {

@Test
public void globalErrorRegistryKnowsJsonTypeNotParsableException() {
final GlobalErrorRegistry instance = GlobalErrorRegistry.getInstance();
assertThat(instance.getTypes()).contains(JsonTypeNotParsableException.ERROR_CODE);
}

}

0 comments on commit a0e00ca

Please sign in to comment.