Can Velocity be configure to fail (i.e. throw an Exception) when a $var is undefined.
Such a "fail-fast" strategy would help in our testing cycles.
In Velocity 1.6 you can add the following property to your velocity.properties
runtime.references.strict = true
Edit: Full list of configuration is available here: http://velocity.apache.org/engine/devel/configuration.html
You can register an event handler which tells Velocity to throw an exception on an undefined reference
You could switch to FreeMarker. It throws exceptions on missing fields, and, invalid types.
Not only that the exceptions thron are precise and readable. ' Missing field FOO at line 234 in BAR.ftl ' etc. etc.
I would absolutly recommend Freemarker over any other templating system.
Related
i have a question. I would like to call a static java method Integer.toHexString() in a freemaker template.
In my code i implemented following lines:
....
cfg.setSharedVariable("Integer",BeansWrapper.getDefaultInstance().
getStaticModels().
get("java.lang.Integer");
....
);
My template looks like this
<#list Items.iterator() as var>
<#assign hex = Integer.toHexString(var) />
${hex}
</#list>
But if i execute the code i get following error:
freemarker.core._TemplateModelException: An error has occurred when reading existing sub-variable "Integer"; see cause exception! The type of the containing value was: extended_hash+string (org.json.JSONObject wrapped into f.e.b.StringModel)
----
FTL stack trace ("~" means nesting-related):
- Failed at: #assign hex = Integer.toHexString(var... [in template "decToHex.ftl"...]
What am i doing wrong?
Thanks.
Based on the error message, your data-model (aka. template context) is a org.json.JSONObject. FreeMarker doesn't know that API, but it discovers that JSONObject has a get(String) method, and tries to use that. Unfortunately, that get method doesn't behave as a Map-style get(key). FreeMarker first calls JSONObject.get("Integer") to see if the variable is in the data-model. If it isn't, it expects a null to be returned, and then will try to get it from higher scopes (like from the shared variables). But JSONObject.get(String) throws JSONException instead of returning null, which is what you see in the error log (if you look at the whole stack trace, JSONException should be there as the cause exception).
To solve this, you need teach FreeMarker how to deal with JSONObject-s:
Create a class, let's call it JSONObjectAdapter, which implements TemplateHashModelEx2 (or, the much simpler TemplateHashModel can be enough for basic use-cases). Inside that, when implementing TemplateHashModel.get(String), you must call JSONObject.has(key) to check if the key exists, and if not, return null, otherwise continue with calling JSONObject.get(key).
Create a class, let's call it DefaultObjectWrapperWithJSONSupport, that extends DefaultObjectWrapper. Your class should wrap JSONObject-s with JSONObjectAdapter.
Where you already configure FreeMarker (NOT before each template processing), specify the objectWrapper to be a DefaultObjectWrapperWithJSONSupport.
There's a few non-obvious things with doing above properly, so I strongly recommend starting out from this example:
https://freemarker.apache.org/docs/pgui_datamodel_objectWrapper.html#pgui_datamodel_customObjectWrappingExample
Above linked example does the above 3 steps, but to support the Tupple class, instead of JSONObject. That's a TemplateSequenceModel, instead of TemplateHashModel, but otherwise what has to be done is very similar.
I want Thymeleaf to throw an exception if a variable used in a template is not found in the Context. It seems by default Thymeleaf will inject empty text in an HTML tag if the bound variable is not found in the Context. This seems dangerous as it essentially silently hides errors.
<div data-th-text="${amount}">Blah</div>
Code:
Context context = new Context();
// never set "amount" variable
Output:
<div></div>
I assumed there would be a "strict" mode where it would throw an exception with the variable name and additional context if the variable is not found in the Context. I've been searching stackoverflow and the API docs but cannot find anything like this. I thought of overriding the Context getVariable so that it does a containsVariable check, but I'm not sure if that has performance implications. Also, I cannot capture any metadata about where in the template it failed. Am I missing something obvious?
I'm using Thymeleaf as a standalone engine -- not as part of a web/spring app.
You can do this by taking advantage of th:assert. If this assertion is not satisfied then thymeleaf will throw an exception during document processing and print a relevant error to know why it failed.
So your code will be
<div th:assert="${amount} != null" data-th-text="${amount}">Blah</div>
If context variable amount is never set it would be null so then the assertion will fail which will produce the exception.
Quick question regarding Java Micrometer with #Counted and #ExceptionHandler please.
I have a very straightforward #ExceptionHandler:
#ExceptionHandler
#Counted(value = "MY_EXCEPTION", description = "SOME_DESCRIPTION")
public Mono<ResponseEntity<String>> myCoolExceptionHandler(final RuntimeException runtimeException) {
System.err.println("an exception!" + runtimeException);
return Mono.just("bad");
}
I think this combination is quite interesting, as it gives visibility on exception happening. We can build dashboard, alerts, etc, quite cool.
Unfortunately, when I looked at the metric generated, it was something like:
# HELP MY_EXCEPTION_total SOME_DESCRIPTION
# TYPE MY_EXCEPTION_total counter
MY_EXCEPTION_total{class="package.MyController",exception="none",method="myCoolExceptionHandler",result="success",} 3.0
I am quite puzzled on exception="none" and result="success"
May I ask how those values got into the metric in the first place?
Also, how to change them into something more meaningful, such as the exception class for instance?
Thank you!
The annotated method itself does not throw an exception and always completes normally. Therefore, the intercepting code installed by the annotation will never record an exception (exception=none) and the outcome will always be good (result=success). Only exceptions thrown from within the annotated method will be recorded as an error outcome.
You can always manually record metrics by injecting the MetricRegistry and then registering a metric with the appropriate name and tags.
In Spring 4, using the #Value annotation, what is the right way to specify a system property as a default if a specified property does not exists?
Whereas this works for the no-default case:
#Value("${myapp.temp}")
private String tempDirectory;
This doesn't work when I need a default:
#Value("#{myapp.temp ?: systemProperties.java.io.tmpdir}")
private String tempDirectory;
Nor does this:
#Value("#{myapp.temp ?: systemProperties(java.io.tmpdir)}")
private String tempDirectory;
Both of these give me an exception at the time Spring is trying to create the bean:
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'configurationService': Invocation of init method failed;
nested exception is java.lang.NullPointerException
Can this be done?
I tried the following and it worked for me:
#Value("${myapp.temp:#{systemProperties['java.io.tmpdir']}}")
private String tempDirectory;
The missing parts for you I believe was not using ?: and needing the #{}. According to this answer:
${...} is the property placeholder syntax. It can only be used to dereference properties.
#{...} is SpEL syntax, which is far more capable and complex. It can also handle property placeholders, and a lot more besides.
So basically what is happening is we are telling Spring to first interpret myapp.temp as property placeholder syntax by using the ${} syntax. We then use : instead of ?: (which is called the Elvis operator) since the elvis operator only applies to Spring Expression Language expressions, not property placeholder syntax. The third part of our statement is #{systemProperties['java.io.tmpdir']} which is telling Spring to interpret the next expression as a Spring Expression and allows us to get system properties.
Try systemProperties['java.io.tmpdir'].
It's a map, so if the key has a dot in the name, you should use [..]
For me it only works with different property-names (being property.name.a a key with a value in my application.properties and property.name.b a Environment-Variable) like:
#Value("${property.name.a:${property.name.b}}")
The same names didn´t work for me like expected (loading the default, when the first property isn´t present), e.g.:
#Value("${property.name.a:${property.name.a}}")
I have two different annotation which uses AspectJ Compile time weaving, say
1) for retrying -- if a particular exception is thrown the method will retry itself
2) translating exception -- i.e if particular exception is thrown will translate that source exception to target exception as specified
how can i define the order in which these two annotation will work. Both annotation are implemented using aspectj and use #around advice.
How can i achieve this type of functionality
`
Case 1
#retry(IllegalArgumentException.class)
#translate(source = IllegalArgumentException , target = IllegalStateException)
void method() {
//if it throws IllegalArgument exception
//method should retry and translate it to IllegalState after it
}
case2
#translate(source = IllegalArgumentException , target = IllegalStateException)
#retry(IllegalStateException.class)
void method() {
//methodthrows IllegalArgument exception which gets translated to IllegalState
//method should retry for IllegalStateException after it
}
`
can there be a way by which we can ensure the order of operation of annotation.
Right now when i run the code retry annotation runs first and then exception translation annotation works.
Use declare precedence in order to define your desired order of aspect precedence. Further information about this and default precedence can be found in the AspectJ Programming Guide, section Language Semantics.
If the advice order should be dynamic, your best bet is to have one pointcut catch both annotations and decide about the order depending on the annotations' values which can be determined via reflection.