Set a Page for Component Testing with Apache Wicket - java

I am testing some components in my wicket 7 application.
My Component is nothing special but it inherits from
public PageAwarePanel extends Panel {
#Override
protected void onInitialize() {
super.onInitialize();
//refuse if used on page without PageConfig
if (getPageConfigurationModel() == null){
throw new RuntimeException("this component is only allowed inside pages having PageConfigurationModel");
}
}
protected IModel<PageConfiguration> getPageConfigurationModel(){
if (getPage() instanceof TemplatePage){
return ((TemplatePage)getPage()).getPageConfigurationModel();
}
return null;
}
}
With this I can access some configurations from a certain panel.
Now when I try in a test:
PositionsPanel p = new PositionsPanel("123", asmNumber, Model.of());
tester.startPage(MyPage.class);
tester.startComponentInPage(p);
where MyPage is a TemplatePage.
I get the defined RuntimeException. My Question is:
How can I test this component with defining on which page it should be rendered?
Thanks for all the help in advanced.

tester.startPage(MyPage.class);
tester.startComponentInPage(p);
those two are not related. It is like navigating two different pages in the browser.
The best way is to create a page for the tests that fulfills the requirements, add the panel to this page and do tester.startPage(TestPage.class).
Another way is to override org.apache.wicket.util.tester.BaseWicketTester#createPage() and return an instance of MyPage. This way you can still use startComponentInPage() but otherwise it is basically the same as the first approach.

Related

Wicket - AjaxEventBehavior not rendered properly

Look at the following snippet.
add(new AjaxEventBehavior("onclick") {
private boolean toggle = false;
#Override
protected void onEvent(AjaxRequestTarget target)
{
log.debug("onEvent: " + toggle);
if (toggle)
{
toggle = false;
target.prependJavaScript("toogle(true)");
}
else
{
toggle = true;
target.prependJavaScript("toogle(false)");
}
}
});
But after the page rendering [no errors, warnings], I could see no event associated to the DOM [verified by means of firebug]. Even the debug log was never printed.
Is there any option in wicket to verify the behavior is added or not?
You should iterate trougth Behaviors added to this component to verify your one is added:
for (Behavior behavior : component.getBehaviors()) {
if(AjaxEventBehavior.class.equals(behavior.getClass())) {
// it works
}
}
The behavior won't contribute its JavaScript if the component it is attached on is either invisible or disabled.
BTW both of your prependJavaScript() calls use the same content toggle(true).
You should (almost) never have HTML ids in your markup:
For Wicket these ids take precedence over generated unique markup ids. If the id is present multiple times on the page (e.g. if you use a component multiple times), all Ajax handlers will attach to the first markup tag with that id.

Multi Threading in web application

I have class which is used to display the current location in the bread crumb.
This class is getting initialised per user from the front end.
my question is do I need to synchronise the methods in this class if yes which are methods needs to be synchronised .There is some confusion. so please advise.
public class CrumbNav {
private Stack<Crumb> stack;
private Crumb crumb;
public CrumbNav() {
stack = new Stack<Crumb>();
crumb = new Crumb("Home");
stack.push(crumb);
}
public void addcrumb(String current) {
Crumb newCrumb = new Crumb(current);
if (stack.peek().equals(newCrumb)) {
return;
}
stack.push(newCrumb);
}
public void removeCurrent() {
stack.pop();
}
public void eraseTrail() {
stack.clear();
stack.push(crumb);
}
public void removeCrumbsClicked(Crumb selected) {
while (true) {
if (stack.peek().equals(selected) || stack.isEmpty())
break;
stack.pop();
}
}
public Crumb getLastBreadCrumb() {
return stack.lastElement();
}
public Crumb getPreviousCrumb() {
if (stack.size() > 1) {
return stack.get(stack.size() - 2);
}
return null;
}
public List<Crumb> getCrumbTrail() {
return stack.subList(0, stack.size());
}
}
It depends on what web framework you're using as to if the object is created per-thread, or as a singleton for the entire session. Spring will, by default, create a single instance and may require synchronization. Other web frameworks may not.
Note that in the "happy path" world, there's only one active thread rendering a page for any given user, but there's nothing stopping the user from rapid-fire clicking on different link initiating many connections and therefore threads.
(I mentioned this in comments, I think it's better to make it an answer.)
There are so many web framework in the industry, who knows how they gonna use your class? The best way is to test whether multiple thread gonna access the same instance concurrently.
Maintain a concurrent set in your class. Every time the method been called, add currentThread.thread_id into the set. Then output the set at last, seeing that whether it's been called from multiple threads. If so, then rewrite your class to a thread-safe version.
Tracking navigation is normally on a per user basis. This class contains storage of the navigation state - which is user specific state, so you need one instance per user session. The methods don't need to be synchronised unless there is the possibility of the same user re-entrantly entering the web server (as mentioned by PaulProgrammer).
Some frameworks will allow this. It can be prevented in Spring MVC by setting the synchronizeOnSession controller property, and there are likely similar techniques for other frameworks.

MVC architecture in Swing application

I have a question concerning MVC Swing java application.
Lets say, Entity e is simple class without any logic – only attributes and getters, setters, equals, hashCode, toString (or maybe compareTo). It will represent Model in MVC.
Than we have MainWindow (as the View in MVC).
Is it okay to use e.getSomething();, e.setSomething(someValue); or even sort/iterate some collection of Elements in MainWindow? And therefore do some GUI rendering and actions in component listeners anonymous classes (I guess listener implementation cannot be in Controller, because it's "view dependent" - HTML doesn't have listeners)?
I did something like this in MainWindow:
...
final Element el = Controller.getInstance().getSomeElement();
JButton save = new JButton();
JTextField field = new JTextField(el.getSomething());
save.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
el.setSomething(field.getText());
Controller.getInstance().persist(); //let controller know some Element has changed and needs to be saved
}
});
...
How to change this piece of code for it to comply with MVC? Thanks.
There's not a hard and fast rule; often the view and controller are combined in Swing apps.
However, in strict MVC, the view should not depend on the controller. The view simply listens to the model and draws itself, then exposes its components and events to the controller. The controller reacts to those events and alters the model as appropriate, which changes the view.
So, in your example, I would the following methods to MainWindow:
public void addSaveListener(ActionListener l) {
save.addActionListener(l);
}
public void removeSaveListener(ActionListener l) {
save.removeActionListener(l);
}
Furthermore, I would pass the instance of Element into the MainWindow constructor, so that it does not have to get it from the Controller. The Controller would be creating the MainWindow, passing its own reference in.
Then, in the controller:
myMainWindow.addSaveListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
el.setSomething(field.getText());
persist(); // Element has changed and needs to be saved
}
});
In larger apps, I would consider an event bus architecture instead of what I wrote above, but that is probably a different question.

Calling getString() in constructor in Wicket gives error

I'm having some problems with localization in wicket.
This is the code:
private String displayString;
private TextField<String> myTextField;
public myPage(DomainObject domainObject){
if(domainObject != null)
displayString = domainObject.getDisplayString();
myTextField = new TextField<String>("myTextField", new PropertyModel<String>(this, "displayString"));
if(Strings.isEmpty(displayString))
displayString = getString("mandatory"); //<- error message here
}
The problem is that calling getString in the constructor results in an error message("...This can sometimes lead to an invalid or no localized resource returned...").
I want to use a PropertyModel for the TextField since I don't want to translate the string I get from domainObject.getDisplayString(). I don't want the changes made in the TextField to affect the value in domainObject directly.
It's possible to get rid of the error message by doing this instead of getString:
if(Strings.isEmpty(displayString))
displayString = new ResourceModel("mandatory").getObject(); //<- no error message
To my understanding, this is the same thing as calling getString (you just hack away the warnings, but the problem still exist).
A solution i thought of is this:
#Override
protected void onAfterRender() {
super.onAfterRender();
if(Strings.isEmpty(displayString))
displayString = getString("mandatory"); //<- no error message
}
Does anyone see a problem with this solution? Maybe I'm not thinking "wickety" enough?
Calling getString() requires the component to be inside a component hierarchy, where it can access it's parent to have the chance to fall back to properties defined there or further up in the tree. This isn't possible inside the component's constructor (as you add it to it's parent at a later point). Wicket 1.5 introduces the onInitialize function for these operations. With Wicket versions prior to this, there is an easy way to emulate this behaviour:
In your base component and page define a non-final empty method as
protected void onInitialize() {}
and add this to the onBeforeRender method:
protected void onBeforeRender() {
...
if (!hasBeenRendered()) {
onInitialize();
}
...
}
Then you can use an overridden onInitialize() method in any of your components to deal with stuff that has to wait until the component hierarchy is established.
What about a reusable behavior:
public class MandatoryBehavior extends AbstractBehavior {
public void onComponentTag(Component component, ComponentTag tag) {
if (((AbstractTextComponent)component).isRequired() && Strings.isEmpty(tag.get("value"))) {
tag.put("value", component.getString("mandatory"));
}
}
}
You'd have to check submitted values in a validator though.
HTML5 placeholders are even nicer.

Set focus on a component with Apache Wicket?

How do you set focus on a component with Apache Wicket? Searching leads to very little information, mostly on setting the default field. I do not want to set a default field, rather I am looking to set focus when, for example, a specific radio button is selected.
I suggest using the native org.apache.wicket.ajax.AjaxRequestTarget#focusComponent(). For example:
/**
* Sets the focus in the browser to the given component. The markup id must be set. If
* the component is null the focus will not be set to any component.
*
* #param component
* The component to get the focus or null.
*/
org.apache.wicket.ajax.AjaxRequestTarget#focusComponent(Component component)
Once you create your behavior to set the focus, you should be able to add it to the component on any event, just make sure that component is part of the AjaxRequestTarget. I don't see why this wouldn't work...
myRadioButton.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
myOtherComponent.add(new DefaultFocusBehavior());
target.addComponent(myForm);
}
});
Here's a link that shows how to create the default focus behavior if you do not have one already:
http://javathoughts.capesugarbird.com/2009/01/wicket-and-default-focus-behavior.html
If you only want to setFocus through javascript and don't want to reload a form or a component, you can use the following code:
import org.apache.wicket.Component;
public class JavascriptUtils {
private JavascriptUtils() {
}
public static String getFocusScript(Component component) {
return "document.getElementById('" + component.getMarkupId() + "').focus();";
}
}
And then in any Ajax Method you can use:
target.appendJavascript(JavascriptUtils.getFocusScript(componentToFocus));
For a pop-up like modalWindow my workaround solution was to use the attribute "autofocus" on the first input tag.
An easy solution is to add it to the html directly.
<input ..... autofocus>
Another solution is to add it to the modalWindow itself:
#Override
public void show(AjaxRequestTarget target) {
super.show(target);
setUpFocus();
}
protected void setUpFocus() {
DeepChildFirstVisitor visitor = new DeepChildFirstVisitor() {
#Override
public void component(Component component, IVisit<Void> iVisit) {
if (isAutofocusable(component)) {
component.add(new AttributeAppender("autofocus", ""));
iVisit.stop();
}
}
#Override
public boolean preCheck(Component component) {
return false;
}
};
this.visitChildren(FormComponent.class, visitor);
}
protected boolean isAutofocusable(Component component) {
if (component instanceof TextArea ||
component instanceof DropDownChoice ||
// component instanceof RadioChoice ||
component instanceof AjaxCheckBox ||
component instanceof AjaxButton ||
component instanceof TextField) {
return true;
}
return false;
}
RadioChoice is commented out because this solution is not working on that. For RadioChoice i would recommend to implement a FocusedRadioChoice:
public class FocusedRadioChoice<T> extends RadioChoice<T> {
//constructors...
#Override
protected IValueMap getAdditionalAttributes(int index, T choice) {
super.getAdditionalAttributes(0, choice);
AttributeMap am = new AttributeMap();
am.put("autofocus", "");
return am;
}
}
Is there a way to achieve the same without JavaScript?
(I am implementing a form with a feedback-Panel that only comes up when Javascript is turned off, so it would not make sense to depend on JavaScript there...,-)
I could only find answers which use JS .focs()... maybe Wicket 1.5 will provide a method Component.setFocus()...
If you happen to be using an Ajax button, you can simply call target.focusComponent(myComponent); in the button's onSubmit method.
#martin-g 's solution was the only solution that got it working for my scenario - a modal/pop up.
Note:
I think autofocus embedded explicitly in HTML only works on page load, not modal load so any efforts to skillfully set the autofocus attribute in the HTML of a modal just fail miserably - always.
Here I lay out the steps for setting the focus on an input field called 'myInput' using the full power of Wicket (no JS!):
In onInitialize:
// Make sure the field has an ID in markup
myInput.setOutoutMarkupId(true);
Provide an overridden show method where you call the focusComponent method:
public void show(AjaxRequestTarget target)
{
// Make sure you call the super method first!
super.show(target);
target.focusComponent(myInput);
}
This does require that your component is an attribute of your modal content class so that you can access it in the show method. To avoid creating a class attribute for your input component you could blend this solution with the solution from BlondCode by replacing that solution's
component.add(new AttributeAppender("autofocus", ""));
with
target.focusComponent(component);
This also works!

Categories