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.
Related
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.
My Xpages app has a cacheBean for application wide settings. I have a managed Bean for a PC document, which has field status of type integer.
In the cacheBean I have a method getPCStatus(Integer status) that when given the number will return the string text of the status.
On my Xpage I have a text field which I want to bind to the result of
cacheBean.getPCStatus(PCBean.status)
so it will return "In Inventory" for a 1 and something else for a 2 etc.
However, the code is throwing an error.
Here is the code:
readonly="true">
<xp:this.value><![CDATA[#{CacheBean.getPCStatus(PCModelBean.status)}]]></xp:this.value>
</xp:inputText>
The error is
Error in EL syntax, property 'value': CacheBean.getPCStatus(PCModelBean.status)
I know I read something about this long ago but cannot remember how to handle this, but cannot find it.
I was wondering if the method getPCStatus should be in the PCBean or in the cacheBean?
The version of EL used n XPages doesn't have support for calling methods with parameters. If getPCStatus() were a zero-argument method, you could call it with #{CacheBean.pCStatus}, presumably, but as it is it's the parameter that's in your way.
There are a few common workarounds: if CacheBean itself implements Map or DataObject, then EL will call the get or getValue method, respectively, with whatever you put after the "." - you could use that to sort of fake method calls.
Alternatively, you could keep CacheBean a POJO (not implementing one of those interfaces) but have the return value from getPCStatus itself be a Map or DataObject, which would take whatever value you pass in (in this case, PCModelBean.status) and do the lookup, with a binding like #{CacheBean.pCStatus[PCModelBean.status]}. DataObjects aren't too bad to write: https://frostillic.us/blog/posts/FE0AE00B7CEC4F8885257D46006CAB68
Or, as an complete alternative to all of this, if you don't need your binding to be read+write, you could use SSJS to call the method.
First let me say that using Struts2 + Freemarker is a real blast.
Yet there's something is driving me crazy, because I cannot understand why it happens. I ask here as maybe someone else has an idea to share about it.
I've got an action, with a property.
Say
private String myText;
Then I've got a setter and a getter:
public void setMyText(String myText)
{
this.myText = myText;
}
public String getMyText()
{
if (myText == null)
myText = "(empty)";
return this.myText;
}
The result (in struts.xml) is a freemarker result.
So in my Freemarker template there's a line like the following:
<p>The text is: ${myText}</p>
Now consider I'm calling the action without any text parameter: say the url is
http:localhost:8080/myapp/myaction
As the getter provides a default value, when the action is processed and the result passed to my template, the property is set to the default; so I get (html on the browser side)
<p>The text is: (empty)</p>
If I call my action with the parameter set, instead (I mean with something like:
http:localhost:8080/myapp/myaction?myText=hallo
) things go wrong. Freemarker fires the following exception:
Exception occurred during processing request: For "${...}" content:
Expected a string or something automatically convertible to string
(number, date or boolean), but this has evaluated to a
sequence+extended_hash (String[] wrapped into f.e.b.ArrayModel)
It seems that "myText" is found twice...
What am I doing wrong? Or, at least, is there anyone that can explain to me why it happens?
P.S.: it's really found twice; the following is a way to workaround the problem:
<#if myText?is_sequence>${myText[0]}<#else>${myText}</#if>
Yet it seems to me not viable to wrap every variable in that way.
P.P.S.: a further hint: in the freemarker template there's a call to another action some lines before. Something like:
<#s.action var="innerAction" name="getTable" namespace="/foo" />
If I comment the line above, everything works fine.
The myText could be a variable from the freemarker context, but if you want to use action property
<p>The text is: ${action.myText}</p>
Note, that action prefix is not required to access action properties. A property resolution method is applied when resolving freemarker variables:
Property Resoloution:
Your action properties are automatically resolved - just like in a
velocity view.
for example ${name} will result in stack.findValue("name"), which
generally results in action.getName() being executed.
A search process is used to resolve the variable, searching the
following scopes in order, until a value is found :
freemarker variables
value stack
request attributes
session attributes
servlet context attributes
And later you can read what objects are accessible from the context.
Objects in the Context:
The following variables exist in the FreeMarker views
req - the current HttpServletRequest
res - the current HttpServletResponse
stack - the current OgnlValueStack
ognl - the OgnlTool instance
This class contains useful methods to execute OGNL expressions against arbitary objects, and a method to generate a select list using
the <s:select> pattern. (i.e. taking the name of the list property, a
listKey and listValue)
struts - an instance of StrutsBeanWrapper
action - the current Struts action
exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view
The error might be caused by searches from the value stack and returning something that you didn't expect depending on the structure of the stack at the moment of execution.
Adding a prefix to the variable to point out the exact location of the property should fix the redundancy in the code when searching in the value stack.
I need to implement a controller that has a command object that is backing a filtering form for a search across multiple entries.
The problem is that the i was asked to do that without using POST request, instead using GET request only, and there before loosing the functionality of the default data binding that springs makes happily for us.
So i tried to implement a method, inside my controller, that looks like this:
#Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
if (isSearchRequest(request)) {
MyCommandObject myCommandObject = (MyCommandObject) getCommand(request);
System.out.println(managePositionsForm);
}
return super.handleRequestInternal(request, response);
}
But the getCommand returns me a brand new CommandObject with no values, despite that the values are present in the request object (i could retrieve then using the getParameter method of HttpServletRequest). But there isn't any binding.
So the question :
1) Is there any way to archive this?
2) Also is very important, that all the values in the form, are lost and, eventually (if this problem is solved) i will need to "persist" the filters for the users in order to avoid re entering after the first search.
Auto Response : setSessionForm(true); looks like can do the work! (According to javadoc)
Thanks to all!
Greetings
Victor.
Okey, i found a way to archive what a was looking for.
I will explain for the sake of those have the same problem before, and hoping to find a experienced user to validate this method... some quiet common is there a multiple ways to do a same thing and as human beings is very difficult to know without proper acknowledge the right path.. so this i a found looking inside the AbstractFormController (that is excellently documented with javadoc).
So what i did was the following, on my controller constructor i add these lines at the end :
setSessionForm(true);
setBindOnNewForm(true);
That all the magic!
But is not enought with setSessionForm(true). According to javadoc the setBindOnNewForm(boolean) method does the following :
/**
* Set if request parameters should be bound to the form object
* in case of a non-submitting request, i.e. a new form.
*/
So my guess are that these two flags are necessary to be marked as true, because :
The setSessionForm makes posible to store as a session attribute the form object, so "is stored in the session to keep the form object instance between requests, instead of creating a new one on each request" (according to javadoc of the setSessionForm method).
The setBindOnNewForm allows the population of the form object with the initial request (despites what type of request method we have). According the javadoc found the AbstractFormController "Only if bindOnNewForm is set to true, then ServletRequestDataBinder gets applied to populate the new form object with initial request parameters..."
But still i noticed, following the controller flow with a debugger, that the population is happening inside the method "getErrorsForNewForm(HttpServletRequest request)".. that is where a concrete object of type ServletRequestDataBinder is used IF the setBindOnNewForm is true, and later (as the javadoc stated) the onBindOnNewForm method is invoked, allowing the programmer to overwrite it with custom behavior, the default behavior is just empty (again this was double checked against the code of AbstractFormController).
I have an strong felling to validate my thoughts here, so if anyone can help me, that would be alright, besides the problem is solved!
Thanks to all in advance!
Greetings.
I have followed Michal's guide from his answer to this question [1] on implementing custom annotation for injecting arbitrary things into resource methods. This (gist) [2] contains a minimalistic testbed adapted from the jersey-quickstart-grizzly2 archetype. I will refer to it in the following text.
I encountered a few problems:
My resolver is parametrized type (or at least it would make things much more cleaner, in my opinion) and thus I can not bind it in my binder the way Michal suggests.
// Instead of the suggested:
// bind(MyInjectionResolver.class).to(new TypeLiteral<InjectionResolver<MyAnnotation>>() {}).in(Singleton.class);
// I do:
bind(myInjectionResolverObject).to(new TypeLiteral<InjectionResolver<MyAnnotation>>() {});
When I use MyAnnotation in the resource method, I get the following warning on server start-up (but the server still starts):
WARNING: The following warnings have been detected: WARNING: A HTTP GET method, public java.lang.String com.example.MyResource.get02(java.lang.Integer,java.lang.String), should not consume any entity.
When I request the resource (curl -X GET http://localhost:8080/myresource/01/aa), I get 02: value = aa; providedValue = null instead of the expected 02: value = aa; providedValue = 123.
The MyResolver.resolve method is never executed (but MyBinder.configure is executed).
(This does not concern the testbed application, but I encounter the problem in a slightly more complicated code: the resource method is never executed and the server responds with 500 No content to map to Object due to end of input)
Any ideas as to what I might be doing wrong?