patternjavaMinor
Invoking API calls with parameters specified using JSON
Viewed 0 times
callswithapiinvokingusingjsonparametersspecified
Problem
I have an instance of a Java class that I need to call some functions on, using the API exposed by another party. For each call, I need to both check for thrown exceptions, and check the value of the returned boolean. If the boolean is false, I need to read the errorMsg object to see what went wrong. In either error case, I need to throw an exception with a relevant method. Each method takes a different number of String arguments
I think there's a cleaner way to do this using reflection, but I can't figure out how to handle the different numbers of arguments. And there may be a totally different solution I've overlooked. Open to all suggestions. Any thoughts?
Here are two examples so you can see what I'm dealing with:
```
ClassProvidedWithExposedAPI s;
private void setHttp(JsonObject jo) throws Exception {
boolean success = true;
String errorString = null;
ArrayList errorMsg = new ArrayList();
try {
JsonObject http = jo.getAsJsonObject("http");
if (http != null && !http.isJsonNull()) {
success = s.setHttp(getJsonElementAsString(http, Http.ENABLED_ELEMENT),
getJsonElementAsString(http, Http.HTTP_PORT_ELEMENT),
getJsonElementAsString(http, Http.HTTPS_PORT_ELEMENT),
getJsonElementAsString(http, Http.TIMEOUT_ELEMENT),
getJsonElementAsString(http, Http.HTTP_REDIRECT_ELEMENT), errorMsg);
}
} catch (Exception e) {
errorString = e.toString();
} finally {
if (!success) {
if (errorString == null) {
errorString = errorMsg.toString();
}
throw new Exception("Exception in setHttp: " + errorString);
}
}
}
private void setSsh(JsonObject jo) throws Exception {
boolean success = true;
String errorString = null;
ArrayList errorMsg = new ArrayList();
try {
JsonObject ssh = jo.getAsJsonObject("ssh");
if (ssh != null && !ssh.isJsonNul
I think there's a cleaner way to do this using reflection, but I can't figure out how to handle the different numbers of arguments. And there may be a totally different solution I've overlooked. Open to all suggestions. Any thoughts?
Here are two examples so you can see what I'm dealing with:
```
ClassProvidedWithExposedAPI s;
private void setHttp(JsonObject jo) throws Exception {
boolean success = true;
String errorString = null;
ArrayList errorMsg = new ArrayList();
try {
JsonObject http = jo.getAsJsonObject("http");
if (http != null && !http.isJsonNull()) {
success = s.setHttp(getJsonElementAsString(http, Http.ENABLED_ELEMENT),
getJsonElementAsString(http, Http.HTTP_PORT_ELEMENT),
getJsonElementAsString(http, Http.HTTPS_PORT_ELEMENT),
getJsonElementAsString(http, Http.TIMEOUT_ELEMENT),
getJsonElementAsString(http, Http.HTTP_REDIRECT_ELEMENT), errorMsg);
}
} catch (Exception e) {
errorString = e.toString();
} finally {
if (!success) {
if (errorString == null) {
errorString = errorMsg.toString();
}
throw new Exception("Exception in setHttp: " + errorString);
}
}
}
private void setSsh(JsonObject jo) throws Exception {
boolean success = true;
String errorString = null;
ArrayList errorMsg = new ArrayList();
try {
JsonObject ssh = jo.getAsJsonObject("ssh");
if (ssh != null && !ssh.isJsonNul
Solution
Sounds like a simple use case for polymorphism:
Explanation:
A lambda, like an anonymous inner class, captures final variables in enclosing blocks. That means that a lambda, like an anonymous inner class, can use these variables and sees the values of the variables that were assigned when the lambda / anonymous inner class was created. A lambda also captures variables that are effectively final, meaning their values are never changed whether or not they are declared as final.
When
However, since you posted to the Code Review Stack Exchange and not Programmers Stack Exchange, I'll critique your code:
-
-
The logic regarding
-
Make a helper method for getJsonElementAsString that takes a varargs array of attribute keys and returns a list of attributes. This method would make your code much terser.
interface JsonOperation {
void run();
}
private void setHttp(JsonObject jo) {
runAndHandleErrors(() -> {
// ...
});
}
private void setSsl(JsonObject jo) {
runAndHandleErrors(() -> {
// ...
});
}
private void runAndHandleErrors(JsonOperation op) {
try {
op.run();
} catch (Exception e) {
// ...
} finally {
// ...
}
}Explanation:
JsonOperation is an interface with one method so it is considered a functional interface, meaning a lambda can be considered an implementation of it. runAndHandleErrors takes a JsonOperation so whenever we call runAndHandleErrors with a lambda, the compiler infers the lambda to implement JsonOperation.A lambda, like an anonymous inner class, captures final variables in enclosing blocks. That means that a lambda, like an anonymous inner class, can use these variables and sees the values of the variables that were assigned when the lambda / anonymous inner class was created. A lambda also captures variables that are effectively final, meaning their values are never changed whether or not they are declared as final.
When
setHttp(x) is called, the following happens:xis assigned tojo
- an implementation of
JsonOperationcapturingjois created (but not run)
runAndHandleErrorsis called and theJsonOperationfrom step 2 is assigned toop
runAndHandleErrorscallsop.run(), causing theJsonOperationto run
- the
JsonOperationruns, doing whatever it wants withjo- it sees the same value ofjoas was assigned in step 2
However, since you posted to the Code Review Stack Exchange and not Programmers Stack Exchange, I'll critique your code:
-
catch Exception and throws Exception are code smells - to the greatest degree possible, catch and declare specific Exception types.-
The logic regarding
success and errorString is convoluted. You could instead separately throw an appropriate subclass of Exception in the try and catch blocks and get rid of the success and errorString variables. Your code reuse isn't actually helping because you need conditional logic in the finally block to account for both cases. Also, the thrown exception in the catch block should properly wrap the original exception by passing the latter directly into the former's constructor.-
Make a helper method for getJsonElementAsString that takes a varargs array of attribute keys and returns a list of attributes. This method would make your code much terser.
Code Snippets
interface JsonOperation {
void run();
}
private void setHttp(JsonObject jo) {
runAndHandleErrors(() -> {
// ...
});
}
private void setSsl(JsonObject jo) {
runAndHandleErrors(() -> {
// ...
});
}
private void runAndHandleErrors(JsonOperation op) {
try {
op.run();
} catch (Exception e) {
// ...
} finally {
// ...
}
}Context
StackExchange Code Review Q#124968, answer score: 2
Revisions (0)
No revisions yet.