i'm pretty new to erlang and i'm trying to get a basic try / catch statement to work. I"m using webmachine to process some requests and all i really want to do is parse some JSON data and return it. In the event that the JSON data is invalid, I just want to return an error msg. Here is the code I have so far.
(the JSON data is invalid)
to_text(ReqData, Context) ->
Body = "{\"firstName\": \"John\"\"lastName\": \"Smith\"}",
try decode(Body) of
_ -> {"Success! Json decoded!",ReqData,Context}
catch
_ -> {"Error! Json is invalid",ReqData,Context}
end.
decode(Body) ->
{struct, MJ} = mochijson:decode(Body).
The code compiles, but when i run it, and send a request for the text, i get the following error back.
error,{error,{case_clause,{{const,"lastName"},
": \"Smith\"}",
{decoder,utf8,null,1,31,comma}}},
[{mochijson,decode_object,3},
{mochijson,json_decode,2},
{webmachine_demo_resource,test,1},
{webmachine_demo_resource,to_text,2},
{webmachine_demo_resource,to_html,2},
{webmachine_resource,resource_call,3},
{webmachine_resource,do,3},
{webmachine_decision_core,resource_call,1}]}}
What exactly am i doing wrong? documentation says the "catch" statement handles all errors, or do i have to do something to catch a specific error that is thrown by mochijson:decode.
Please any leads or advice would be helpful. Thanks.
The catch-clause _ -> ... only catches exceptions of the 'throw' class. To catch other kinds of exceptions, you need to write a pattern on the form Class:Term -> ... (i.e., the default Class is throw). In your case:
catch
_:_ -> {"Error! Json is invalid", ReqData, Context}
end
When you do this, you should always ask yourself why you're catching every possible exception. If it's because you're calling third-party code that you don't know how it might behave, it's usually OK. If you're calling your own code, remember that you're basically throwing away all information about the failure, possibly making debugging a lot more difficult. If you can narrow it down to catching only particular expected cases and let any other exceptions fall through (so you see where the real failure occurred), then do so.
Related
when looking for Information when and why to use Exceptions there are many people (also on this platform) making the point of not using exceptions when validating user-input because invalid input is not an exceptional thing to happen.
I now have the case where I have to parse a complex string of user input and map it to an Object-Tree basically, similar to a Parser.
Example in pseudo code:
input:
----
hello[5]
+
foo["ok"]
----
results in something like that:
class Hello {
int id = 5
}
class Add {}
class foo {
string name = 'ok'
}
Now in order to "validate" that input I have to parse it, having code that parses the input for validation and code to create the objects separately feels redundant.
Currently I'm using Exceptions while parsing single tokens to collect all Errors.
// one token is basically a single
try {
foreach (token in tokens) {
factory = getFactory(token) // throws ParseException
addObject(factory.create(token)) // throws ParseException
}
} catch (ParseException e) {
// e.g. "Foo Token expects value to be string"
addError(e)
}
is this bad use of exceptions?
An alternative would be to inject a validation class in every factory or mess around with return types (feels a bit dirty)
If exceptions work for your use case, go for it.
The usual problem with exceptions is that they don't let you fix things up and continue, which makes it hard to implement parser error recovery. You can't really fix up a bad input, and you probably shouldn't even in cases where you could, but error recovery lets you report more than one error from the same input, which is often considered convenient.
All of that depends on your needs and parsing strategy, so there's not a lot of information to go on here.
When I tried to use ResilienceDecorator.executeCallable() to enable circuit breaker, I have to throw out ResilienceRuntimeException in my callable to make the circuit break work. Sample code as below. Without it, circuit breaker is always closed. is this the right way to do it?
response = ResilienceDecorator.executeCallable(() -> {
HttpResponse response1 = tryHttpClient.get().execute(request);
if (response1.getStatusLine().getStatusCode() == 404){
throw new ResilienceRuntimeException("404 error is raised when calling SB api");
}
return response1;
},
ResilienceConfiguration.of(SubscriptionBillingAdapter.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(ResilienceConfiguration.TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(ResilienceConfiguration.CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(600000L)).failureRateThreshold(1).closedBufferSize(1).halfOpenBufferSize(1)),
e -> {LOG.warn("resiliience fallback call: " + e); return response1;});
I am asking since I don't see any document of it. Also when I check how destination configuration in SCP is retrieved, I saw the following code in com.sap.cloud.sdk.cloudplatform.connectivity.DestinationService . It doesn't throw out ResilienceRuntimeException, when using ResilienceDecorator.executeCallable(). so my question is do I need to throw out ResilienceRuntimeException or not to make circuit breaker work? if I don't need, anything wrong in my code?
return (String)ResilienceDecorator.executeCallable(() -> {
XsuaaCredentials xsuaaCredentials = (new ServiceCredentialsRetriever()).getClientCredentials("destination");
AccessToken accessToken;
if (propagateUser) {
accessToken = xsuaaService.retrieveAccessTokenViaUserTokenExchange(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
} else {
accessToken = xsuaaService.retrieveAccessTokenViaClientCredentialsGrant(xsuaaCredentials.getXsuaaUri(), xsuaaCredentials.getCredentials(), useProviderTenant);
}
return this.fetchDestinationsJson(servicePath, accessToken);
}, ResilienceConfiguration.of(DestinationService.class).isolationMode(ResilienceIsolationMode.TENANT_OPTIONAL).timeLimiterConfiguration(TimeLimiterConfiguration.of().timeoutDuration(Duration.ofSeconds(6L))).circuitBreakerConfiguration(CircuitBreakerConfiguration.of().waitDuration(Duration.ofSeconds(6L))));
Steven
I'm not the most experienced in this specific part, but looking at your code it seems fine to me. When a server returns 404 Not found it's not an indication of a service failure or error, but that resource is simply not found. If in your case 404 means that an error took place and the request has to be retried with a resilient approach, you have to throw that exception to inform Resilience4J that smth went wrong.
While we're working on improving our documentation, I recommend you take a look at the existing tutorial explaining resilience within the SAP Cloud SDK context. There we also throw ResilienceRuntimeException for clarity:
public List<BusinessPartner> execute() {
return ResilienceDecorator.executeSupplier(this::run, myResilienceConfig, e -> {
logger.warn("Fallback called because of exception.", e);
return Collections.emptyList();
});
}
private List<BusinessPartner> run() {
try {
return businessPartnerService
.getAllBusinessPartner()
.select(BusinessPartner.BUSINESS_PARTNER,
BusinessPartner.LAST_NAME,
BusinessPartner.FIRST_NAME,
BusinessPartner.IS_MALE,
BusinessPartner.IS_FEMALE,
BusinessPartner.CREATION_DATE,
BusinessPartner.TO_BUSINESS_PARTNER_ADDRESS
.select(BusinessPartnerAddress.CITY_NAME,
BusinessPartnerAddress.COUNTRY,
BusinessPartnerAddress.TO_EMAIL_ADDRESS
.select(AddressEmailAddress.EMAIL_ADDRESS)
)
)
.filter(BusinessPartner.BUSINESS_PARTNER_CATEGORY.eq(CATEGORY_PERSON))
.orderBy(BusinessPartner.LAST_NAME, Order.ASC)
.top(200)
.execute(destination);
} catch (ODataException e) {
throw new ResilienceRuntimeException(e);
}
}
Regarding the code snippet from the DestinationService, I believe that fetchDestinationsJson() method throws an implicit exception thus letting Resilience4J know that smth went wrong. While in your case HttpClient won't throw anything when receiving 404 as it's a correct response code as any other.
I also think that checking CircuitsBreaker examples from Resilience4J library might be helpful.
I hope it helps:)
No you do not have to throw a ResilienceRuntimeException. In fact the SDK only uses that to wrap checked and unchecked exceptions into an unchecked exception which wraps all kinds of failures that occur within a resilient call.
Please expand your question with more details, I'll then expand this answer:
Which version of the SDK are you using?
How (and how often) do you invoke the decorated callable? Please expand the code block.
Please specify the exact behaviour you observe. What exception is thrown when you invoke the decorated callable? If the circuit breaker opens this should be a CallNotPermittedException wrapped inside a ResilienceRuntimeException
Reduce the callable to simply throw an exception in order to simplify the code
Reduce the resilience configuration to only use a circuit breaker (leverage ResilienceConfiguration.empty()). If that works add stuff back in again until it doesn't.
For reference also please find the documentation of resilience4j which the SDK uses under the hood to perform resilient operations.
Is it possible to avoid that if one mono in mono.zip throws exception all other monos are stopping immediately? I want them to end normally and perhaps to handle the erroneous one by something like „.doOnError“ or „.continueOnError. Is that a way to go?
Regards
Bernado
Yes, it's possible. You can use Mono.zipDelayError. As you can understand from the method's name, it delays errors from the Monos. If several Monos error, their exceptions are combined.
If you have to get the combined result anyway, zipDelayError is not the solution. Use the zip operator and handle the error case with a fallback operator like onErrorResume or retry on the zipped Mono or any upstream one.
I stated that my question is answered but it is not yet. The following example states my case: some mono will fail, but i want the result as the error too. i expected the follwoing code as to run to completion but it fails:
Mono<String> error = Mono.error(new RuntimeException());
error = error.onErrorResume(throwable -> Mono.just("hell0"));
Mono<String> test = Mono.just("test");
Mono<String> test1 = Mono.just("test1");
Mono<String> test2 = Mono.just("test2");
List<Mono<String>> monolist = new ArrayList<>();
monolist.add(test);
monolist.add(test1);
monolist.add(test2);
monolist.add(error);
Mono<Long> zipDelayError = Mono.zipDelayError(monolist, arrayObj -> Arrays.stream(arrayObj).count());
System.out.println(zipDelayError.block());
I want to receive data (simply as a string) from the client-web side to update the database, but it's a bit lock for me now, so first write the data to a file in the drive with using System.IO.File.WriteAllText(#"my-file.txt")
I got that error
error FS9001: Method name not found in JavaScript compilation: (receive : System.Object -> unit)
Can you tell me where I did wrong and fix it?
Remoting.fs
Client.fs
Main.fs
WebSharper's client side can call methods that are themselves in [<JavaScript>] scope or marked [<Remote>] for remote calls. The error message is not mentioning the second option, but that is what you need here (same as the sample function DoSomething has it too).
You will also need to make the remote function to not send over an obj but a string. Remote function arguments are deserialized based on type information and cannot be obj. For example in client code, use Server.receive rvInput.Value. (rvInput is a reactive variable for which .Value contains current value)
Note that if you want to return a value to the server, the remote function must be an async. Here, just for logging, returning unit works too, but then you have no way on the server to determine if the logging was successful. By returning an async<unit>, you can catch errors in the client code if you want to guard against connection or server errors. Again, the sample code in the template gives some guidance.
(Cross posting from http://forums.websharper.com/topic/84579)
Here is what your server-side function should look like:
[<Remote>]
let Receive (input: string) =
async {
System.IO.File.WriteAllText(#"D:/myDatabase.txt", "Server received data: " + input)
}
and to call it from Client.fs, you need:
...
button [
on.click (fun _ _ ->
async {
do! Server.Receive rvInput.Value
} |> Async.Start
)
] [text "Receive"]
...
A friend's query was failing. Fortunately, he was catching it in his fail callback (you DO have a fail callback for every server call, right?). Here's kind of what he had:
var getPersons = function(personsObservable) {
return EntityQuery.from('Person')
.using(manager).execute()
.then(querySucceeded).fail(queryFailed);
}
function queryFailed(error) {
var msg = 'Error retreiving data. ' + error.message;
logError(msg, error);
throw error;
}
The error.message simply showed the JSON data ... which looked a bit like this:
"[{"$id":"1","$type":"Person, ProjectName","Id":12,"FirstName":"Bob","LastName":"Smith","Email":"bs#contoso.com","Blog":"http://bs.contoso.com","Twitter": ..."
WAT?
He examined the error.XHR which provides the full AJAX XHR object used for this query. He could see that the HTTP Status Code was a 200 ... meaning that everything was cool from the server. The fact that he had real data pretty much said the same thing.
So why is Breeze failing? How does he diagnose the problem?
Breeze might be failing. But there's a good chance that the problem lies elsewhere. Usually if Breeze fails, there is a meaningful error message. This error message is not meaningful. But it does provide clues.
Check your success callback first
The fail callback can be invoked (1) if the operation fails or (2) if the success callback fails. If the operation fails, you've got a Breeze-related problem. If the success callback fails, you probably have an application code problem.
To determine which, put a breakpoint on the first line of the success callback (in his case, the first line of querySucceeded). If you hit the breakpoint, you know Breeze has done its bit and has handed off to you. Step through your callback to find the mistakes which are most likely yours and, therefore, easy to fix.
Check your custom EntityType constructors and initializers
In his case it did not get to the success callback. So something went wrong as Breeze tried to make cached entities out of the JSON data from the server. What could that be?
There are many potential causes. Could be a Breeze bug. Always best, though, to eliminate pilot error first. Did you write a custom constructor or initializer for this EntityType?
He did. He had an initializer that added a fullName calculated property to his Person. It looked sort of like this:
metadataStore.registerEntityTypeCtor('Person', null, personInitializer);
function personInitializer(person) {
person.fullName = ko.computed(function () {
return entity.firstName() + ' ' + person.lastName();
});
}
He didn't see a problem. But following diagnostic procedure, he put a breakpoint on the initializer.
Sure enough ... he had a typo ...
// "entity" does not exist. Null object error
return entity.firstName() + ' ' + person.lastName();
As soon as he changed entity to person, all was well.
I can't explain at the moment why the null object reference manifested as a Q promise fail error with the JSON Person data in the message. Strange stuff happens in JavaScript. But the clues were there:
server delivered the data
failed before getting to the success callback
data are about Person
have a Person initializer (or constructor)
Read the clues and you'll know where to look.
Hope this tip saves you from gray hair and a bald head.