How can my Freemarker ObjectWrapper access a template setting - java

Use case: system administrator stores a Freemarker template in a database which is used (by Spring Boot REST API) to present information stored by system users (respondents) in a locale-aware way to a different user type (reviewer).
A respondent's response might be stored in this sort of object (or in lists of this sort of object, in the event a question posed to the respondent is expected to have multiple answers):
// snip
import com.fasterxml.jackson.databind.node.ObjectNode;
// more imports snipped
public class LanguageStringMap {
private Map<Language, String> languageStringMap;
public LanguageStringMap(ObjectNode languageMapNode) {
// snip of code instantiating a LanguageStringMap from JSON
}
public void put(Language language, String value) {
if (value.length() == 0)
throw new IllegalArgumentException(String.format(
"value for language '%s' of zero length", language.getCode()));
languageStringMap.put(language, value);
}
public String get(Language language) { return languageStringMap.get(language); }
}
What I think I want to do is write an ObjectWrapper that maps instances of LanguageStringMap to a string (obtained by calling the get() method with a language derived from the Locale requested by the reviewer's browser and set in the template's settings). This presents a cleaner user experience to the system administrator than making the uploaded template contain a bunch of template method calls would.
To do this, my object wrapper needs to access a template setting. I have perused the pertinent Freemarker documentation, but I am still unclear on how to do this or if it is even possible.
I think it would be a mistake to try to implement this with resource bundles uploaded to the database alongside the templates, but that is a consideration.

Typically you simply put the locale specific string into the data-model before the template is processed, along with all the other variables. In that case no ObjectWrapper customization is needed. But if you have to use an ObjectWrapper-based solution, then you can get the locale inside an ObjectWrapper method (like in the override of DefaultObjectWrapper.handleUnknownType) with Environment.getCurrentEnvironment().getLocale().

Related

Grails XML marshalling: change default "<list>" root element name

By default Grails renders List in XML with a <list> element tag at its root. Likewise it renders Map with <map>. I would like to control the name of the root element.
If I'm returning an ArrayList of User, then I'd like to see:
<users>
<user>...</user>
<user>...</user>
</users>
How can I achieve the above? Here are the Requirements:
Easy to apply this serialization for 50+ domain classes
Abstracted from developers so no explicit coding is required during rendering domain objects (i.e., when render() or respond() is invoked, an ArrayList is still passed in, no explicit casting/converting like as MyNewType)
Able to handle the edge case of an empty list (should return <users/>)
Nice-to-haves:
If this formula can be applied to Map as well, great :)
I have been semi-successful in achieving the goals above, except I don't know how to account for the empty list case. I implemented my own ObjectMarshaller which renders all objects of type List. So long as the list contains one element, I can check the element's type and determine what the plural tag name should be (User => users). But if the list is empty, and since Java generics are by erasure (unless that's different in Groovy?) then I have no way to properly name an empty list other than defaulting to something like <list/>, which is not acceptable.
Some resources that I've been through:
http://www.cacoethes.co.uk/blog/groovyandgrails/dry-json-and-xml-with-grails
http://grails.1312388.n4.nabble.com/Custom-XML-Marshaller-change-the-root-element-name-td4649949.html
http://jwicz.wordpress.com/2011/07/11/grails-custom-xml-marshaller/
http://mrhaki.blogspot.com/2013/11/grails-goodness-register-custom.html
http://manbuildswebsite.com/2010/02/15/rendering-json-in-grails-part-3-customise-your-json-with-object-marshallers/
A way to achieve this is to write a subclass for the CollectionMarshaller class and register it in our Grails application. We can for example register a custom implementation in BootStrap.groovy with the following code:
import org.codehaus.groovy.grails.web.converters.marshaller.xml.CollectionMarshaller
import grails.converters.XML
class BootStrap {
def init = { servletContext ->
// Register custom collection marshaller for List with User instances.
// The root element name is set to users.
XML.registerObjectMarshaller(new CollectionMarshaller() {
#Override
public boolean supports(Object object) {
object instanceof List<User>
}
#Override
String getElementName(final Object o) {
'users'
}
})
}
}
To make this work for more domain classes we might get a reference to all domain classes in BootStrap.groovy and loop through them to configure custom CollectionMarshaller instances.
For maps you can extend MapMarshaller
Also described in http://mrhaki.blogspot.com/2014/02/grails-goodness-customize-root-element.html

Importing a file with different layouts to a database in Java

I need to import a text file, with values separated by pipes ( | ), into a database using Java and Hibernate. The text file is generated elsewhere, and has the following layout:
example-file.txt
|0150|A|B|C|
|0150|X|Y|Z|
|0190|1|2|
|0200|9|8|7|H|F|E|
Each line corresponds to a record.
The first value (i.e 0150, 0190, 0200) is the type of info it holds (to which table it should be stored).
The rest are the values to be stored in that table.
So far, I've been able to read the lines, find to which Object the record corresponds to - using a Factory pattern - separating the values into a String[] array and calling a method createInstance(String[] fields) to create the object and store it into the database - using a Template pattern:
ImportServiceInterface
public interface ImportServiceInterface {
public void createInstance(String[] fields);
}
AbstractImportService
public abstract class AbstractImportService implements ImportServiceInterface {
public static ImportServiceInterface getImportService(String line) {
// returns the correct subclass
}
public void import() {
createInstance(splitFields());
}
public String[] splitFields(String line) {
// splits the line
}
}
So I have 3 separate services, each implementing their own version of createInstance(String[] fields):
ImportExampleTypeService
public ImportExampleTypeService implements AbstractImportService {
public void createInstance(String[] fields) {
ExampleModel myExample = new myExampleModel(); // mapped with Hibernate
// find which object members corresponds to the fields
// call the DAO to store the object
}
}
My problem is that the user will be able to specify his own layout: to which fields the values correspond to, size and position.
I thought about creating a table to store the layouts, then matching the names of the attributes using Reflection.
But I must be missing something, perhaps there's an easier way to do this?
SuperCSV supports custom delimiters and population of java objects via reflection, so I think it would do most of your work for you in this case.
Furthermore, it supports the concept of a header row as the first line in the file which then defines which fields those columns are mapped to in the java object, or you can just customize the column mappings manually.
Thank you #increment1 and #Templar for your answers!
The requirements have changed. The system has to be able to import both the above format (which will not be user-defined) and a user-defined, CSV-like, flat file, with a single type of record per file. It makes my life easier. I have been looking at different flat-file parsing libraries, and I'm posting it here in case anyone stumbles upon the same problem:
jflat: simple to use, extensible and customizable framework. Probably the best choice for most.
BeanIO: a flat-file marshaller/unmarshaller that uses xml files to figure out how to parse the file. Supports many formats, more than one type of record per file etc.
FFP: Flat File Parsing. Also supports absolute and relative definitions, using POJOs instead of xml files. I would have chosen this one, but it seems to be dead?
Flatworm: very similar to BeanIO. It appears it has inspired BeanIO, and there is not much activity on Flatworm either...
I have chosen BeanIO, because its flexibility suits my project better. So here's what I am going to do:
1) Keep my design, implementing my createInstance() method as needed;
2) Use a different implementation using BeanIO for the user-defined files;
3) Use a Facade to call the parser I need:
FacadeInterface
public interface ImportFacadeInterface {
public void importFile();
}
ImportDefaultLayoutFacadeImpl
public class ImportDefaultLayoutFacadeImpl implements ImportFacadeInterface {
public void importFile() {
// use the ImportServiceInterface
}
}
ImportUserDefinedLayoutFacadeImpl
public class ImportUserDefinedLayoutFacadeImpl implements ImportFacadeInterface {
public void importFile() {
// use BeanIO
}
}
My approch to store the possible record structures would be a Map with |0150| as Key and |A|B|C| as Value. This could be an approch to parse a line.
String line = ...;
String structure = map.get(line.substring(1, 4));
// Now you have the line structure and can parse it into your own format.

how to refer to the property of bean used in BeanEditForm (tapestry5) in Java?

I've got in my .tml file something like this:
<t:beaneditform t:id="adForm" object="editableAd"
reorder="actiontype,shops,movies,streams,widgets" ....
My question is how to access (refer) actionType, which is an Enum (and in fact SELECT) in .java file? I just need to handle event when user changes the value of this select (dropdown), obviously before submitting the form itself.
If something like this would work for me...
#OnEvent(component = "adForm.actionType", value=EventConstants.VALUE_CHANGED)
public void actionTypeValueChanged(String value) {
log.info("value is: " + value);
}
To be updated with the changed value in a Select html component on the client side, have a tapestry select component in your template file with a t:zone attribute (i.e. in your case it could point to any dummy zone, this is only needed to be set correctly if you need to update a zone when a value is changed)
Also set the t:value attribute to your enum variable in your page\component java file, usually this variable will be annotated with tapestry's #Property.
Example:
<t:select t:id="myEnumVariable" t:zone="dummyZone" t:value="myEnumVariable"/>
myEnumVariable is used to refer to your class's variable AND to act as an ID (i.e. the actual string myEnumVariable is used as an id), this is not necessary, but it's more readable and maintainable that way)
public class MyClass{
#Property
private MyEnum myEnumVariable;
#OnEvent(component = "myEnumVariable", value=EventConstants.VALUE_CHANGED)
public void actionTypeValueChanged(**MyEnum** newValue) {
this.myEnumVariable = newValue; // <<<<<<
log.info("value is: " + myEnumVariable );
}
}
If you don't mind using the ChenilleKit framework for tapestry you could try using the
framework's OnEvent mixin.
You 'll find the example on the link I share but basically you add two attributes the select tag:
<t:select t:id="myselect" ... t:mixins="ck/OnEvent" t:event="change" />
then you add the event handler on your java class:
#OnEvent(component="myselect", value='change')
public void onChangeDoSomething(String value) {
hope that helps, by the way I think Muhammad's answer is equally correct (and doesn't requires the use of an extra framework).

Can I access property keys with Selenium on a wicket generated html page?

I am trying to automate frontend tests with Selenium for a wicket based web application.
Therefore I have:
- Different languages
- language property files (submit.signup.form=Submit) and wicket messages () using them
- HTML pages which are generated by wicket (input type:button and value:Submit)
If I go ahead and automate a test case with that, it will work properly.
The problems start when somebody decides to change the property file to f.i. submit.signup.form=Send.
If that happens I will have to adjust all Selenium tests to check for the correct label again to make a test successful (this is not really applicalbe for that example but for error messages it will be a problem)
Now the question:
Is there a way to make wicket to put the property key onto/into the generated html files?
Desired benefit:
I can use Java and make Selenium take the property ke and check the property file for the text. That way a change of a label in the property file would not effect the Selenium tests at all and would make it much more easy to handle.
I am grateful for any answer. :)
Best regards
By default, Wicket starts in development mode. In development mode you should see the wicket tags, you should take a look in to IDebugSettings
, however you will not see the properties gathered from the java code, but you can add the key as attribute, for example
new Label(getString("propertieKey")).add(new AttributeAppender("key","propertieKey"))
It's quite easy to do actually.
Put in your application init method:
getResourceSettings().getStringResourceLoaders().add(0, new NoResourceLoader());
Implement NoResourceLoader:
public class NoResourceLoader implements IStringResourceLoader {
#Override
public String loadStringResource(Class<?> clazz, String key, Locale locale, String style, String variation) {
if ("noProperties".equals(style)) {
return key;
}
return null;
}
#Override
public String loadStringResource(Component component, String key, Locale locale, String style, String variation) {
if ("noProperties".equals(style)) {
return key;
}
return null;
}
}
This resource loader just returns the key if the style is set to noProperties. As it returns null, the localizer will try the next resourceloader for any other invocation.
In order to set style to "noProperties" I'd suggest adding a parameter check to your pages' constructor that would set the style on the session object when you call your application with the parameter.
public BasePage(PageParameters pp) {
String style = pp.get("st").toOptionalString();
if (style != null) {
getSession().setStyle("noProperties");
}
It would be enough to call your first url with this parameter set, then you should walk through the whole session with property keys instead of values in the html. I'd also disable this check when the app is running in production.

Play 2.0: Push a URL parameter to the view

I'm just learning how the Play 2.0 framework. So I have a quite basic question: I just want to take a URL parameter and display it in the view. How do you do that?
I created the URL pattern:
GET /test/:id controllers.Application.testOutput(id: Long)
And an apporoptiate methode in Application:
public static Result testOutput(long id) {
return ok(
views.html.test.render(id)
);
}
How do I call the id variable form the view? I know how to call methodes defined in the model in the view, but I don't know how to display the id variable in the view. Is it the right way to pass the id variable to the render methode?
I'd like to understand the underlying concept, so an detailed explanation to the answer would be great!
Our test URL will be http://localhost:9000/greeter?message=hello and this will output a text/plain response with the content of the parameter message (ie hello). First, let's define the route
GET /greeter controllers.Greeter.say(message: String)
Then, create a Greeter controller (I use Java)
package controllers;
import play.*;
import play.mvc.*;
// This lets you call the template without the views.html prefix
// import views.html.*;
import views.txt.*;
public class Greeter extends Controller {
public static Result say(String message) {
return ok(greeter.render(message));
}
}
You can see that ok() calls a scala function defined in the file app/views/greeter.scala.txt Here is the content of that file (the first line defines the message parameter of type String used inside the function
#(message: String)
I'm the content. Note that you can place
anything you want here. Scala expressions
begin with the '##' character. For example
next line contains the content of message:
#message
In this case I used .txt for file extensions because I wanted plain text response. If you want to produce HTML output, simply make a .scala.html file
The client request is handled by the Play router, which in turn forwards it to some action (which is a method inside a Controller)
GET /greeter Greeter.say
From the Play doc
The last part of a route definition is the Java call. This part is
defined by the fully-qualified name of an action method. The action
method must be a public static void method of a Controller class. A
Controller class must be defined in the controllers package and must
be a subclass of play.mvc.Controller.
You can add a Java package before the Controller class name if it
isn’t defined directly under the controllers package. The controllers
package itself is implicit, so you don’t need to specify it
Inside the action, you can get the parameters via the param object or directly by the method signature:
public static void say(String what) {
...
}
and you can pass objects to the template via render(what) like you do in your sample.
Finally your template can access those object with the ${what} syntax.
EDIT This is the Play 1.x way of doing things. I didn't notice your tag, but still hope this helps. Play2 uses the new Scala template engine, here is the guide. It seems you must declare your parameters at the beginning of the template, then you can access them via the #what syntax. That's because the template is now a compiled Scala function, so it can do type checking at compile time and things like this. You pass parameters to this function with render(what) (like you do) . I personally don't use Play2.0 myself: it's a big improvement from the technical point of view, at the cost of being less intuitive and more verbose. I don't think that simpler projects benefit from these improvements
In Play 2 templates are just plain functions, so you can call them as you would call any function.

Categories

Resources