Wicket why page expires when opening link in new tab? - ajax

I'm building a wicket bootsrap web application with the following specs (from pom.xml):
wicket version: 6.15.0
wicket-bootstrap-core.version: 0.9.3-SNAPSHOT
I have a base page which is the father of my other pages and adds to mark up a horizontal navigation bar on top, with key component:
BootstrapBookmarkablePageLink extends BookmarkablePageLink
This is part of my BasePage.java
public abstract class BasePage extends GenericWebPage<Void> {
private static final long serialVersionUID = 1L;
String username;
public WicketApplication getApp() {
return WicketApplication.class.cast(getApplication());
}
public BasePage(final PageParameters parameters) {
super(parameters);
// Read session data
cachedUsername = (String)
BasicAuthenticationSession.get().getAttribute("username");
// create navbar
add(newNavbar("navbar"));
}
/**
* #return application properties
*/
public Properties getProperties() {
return WicketApplication.get().getProperties();
}
/**
* creates a new {#link Navbar} instance
*
* #param markupId
* The components markup id.
* #return a new {#link Navbar} instance
*/
protected Navbar newNavbar(String markupId) {
Navbar navbar = new Navbar(markupId) {
private static final long serialVersionUID = 1L;
#Override
protected TransparentWebMarkupContainer newCollapseContainer(String
componentId) {
TransparentWebMarkupContainer container =
super.newCollapseContainer(componentId);
container.add(new CssClassNameAppender("bs-navbar-collapse"));
return container;
}
};
navbar.setPosition(Navbar.Position.TOP);
// navbar.setInverted(true);
NavbarButton<Void> myTab = new NavbarButton<Void>(MyPage.class, new
PageParameters().add("name", "")
.add("status", "All").add("date", ""), Model.of("My page"));
NavbarButton<Void> myOtherTab = new NavbarButton<Void>
(MyOtherPage.class, new PageParameters().add("status", "initial")
.add("date", ""), Model.of("My other page"));
navbar.addComponents(NavbarComponents.transform(
Navbar.ComponentPosition.LEFT,
myTab, myOtherTab));
return navbar;
}
}
Then, MyPage renders a filter form, an html table, ajaxbuttons and some links, Some of my components are ajax components:
public class MyPage extends BasePage {
private static final long serialVersionUID = 5772520351966806522L;
#SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(MyPage.class);
private static final Integer DAYS = 270;
private DashboardFilteringPageForm filteringForm;
private CityInitialForm ncForm;
private String CityName;
private String startDate;
private CitysTablePanel citysTable;
private WebMarkupContainer numberOfNodes;
public MyPage(PageParameters parameters) throws ParseException {
super(parameters);
// get Citys list from repo
final List<City> repoCitys = (List<City>) methodToGetCities();
// select number of nodes
numberOfNodes = new WebMarkupContainer("numberOfNodes") {
private static final long serialVersionUID = 5772520351966806522L;
};
numberOfNodes.setOutputMarkupId(true);
ncForm = new CityInitialForm("ncForm");
// validation
add(new FeedbackPanel("feedbackPanel")).setOutputMarkupId(true);
ncForm.getNumberField().setRequired(true);
ncForm.add(new AjaxButton("ncButton") {
private static final long serialVersionUID = -6846211690328190809L;
#Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
// redirect to other page
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
numberOfNodes.add(ncForm);
// filters
CityName = parameters.get("name").toString() == null ? "" :
parameters.get("name").toString();
startDate = parameters.get("date").toString();
filteringForm = new DashboardFilteringPageForm("filteringForm") {
private static final long serialVersionUID = -1702151172272765464L;
};
// initialize form inputs
filteringForm.setCityName(CityName);
try {
filteringForm.setStartDate(new SimpleDateFormat("EE MMM dd HH:mm:ss
z yyyy", Locale.ENGLISH)
.parse(getStartDate().equals("") ?
CortexWebUtil.subtractDays(new Date(), DAYS).toString() : getStartDate()));
} catch (Exception e) {
setResponsePage(SignInPage.class, new PageParameters());
}
filteringForm.add(new AjaxButton("button") {
private static final long serialVersionUID = -6846211690328190809L;
#Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> paForm) {
// retrieve Citys
filterCitysAjax(target, "All");
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
filteringForm.getCityNameTextField().add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = 1468056167693038096L;
#Override
protected void onUpdate(AjaxRequestTarget target) {
try {
filterCitysAjax(target, "All");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
// new City link
AjaxLink<Void> newCityLink = newCityLink("newCity", repoCitys);
// Citys table
citysTable = new CitysTablePanel("CitysTable", repoCitys);
citysTable.setOutputMarkupId(true);
// add components
add(filteringForm, newCityLink, numberOfNodes, citysTable);
}
private void filterCitysAjax(AjaxRequestTarget target, String status) {
methodToFilterResults();
// re-render table component
CitysTablePanel cityTableNew = new CitysTablePanel("CitysTable", citys);
cityTableNew.setOutputMarkupId(true);
cityTableNew.setVisibilityAllowed(true);
cityTableNew.setVisible(true);
citysTable.replaceWith(cityTableNew);
target.add(cityTableNew);
citysTable = cityTableNew;
target.appendJavaScript(CortexWebUtil.TABLE_ODD_EVEN_ROWS);
}
private AjaxLink<Void> newCityLink(String string, final List<City> Citys) {
final AjaxLink<Void> newCityLink = new AjaxLink<Void>(string) {
private static final long serialVersionUID = -5420108740617806989L;
#Override
public void onClick(final AjaxRequestTarget target) {
numberOfNodes.add(new AttributeModifier("style",
"display:block"));
target.add(numberOfNodes);
}
};
// new City image
Image newCityImage = new Image("newCityIcon", new
ContextRelativeResource("/img/new_City_icon.png"));
add(newCityLink);
newCityLink.add(newCityImage);
return newCityLink;
}
}
So MyPage works but when I open MyOtherPage Link in an a new tab and trigger an ajax component in MyPage (e.g the AjaxButton) then I get the page expirtaion error.
Why is that happening?
Do I need to use stateless pages? ( stateless link )
Why would it be so ard in wicket to open links in new tabs and use ajax components? I must be missing sometthing..

Here are few possible reasons:
MyPage fails to serialize
Wicket stores stateful pages in page storage (in the disk, by default). Later when you click a stateful link Wicket tries to load the page. First it looks in the http session where the page is kept in its live form (i.e. not serialized). If it is not found there then Wicket looks in the disk.
Wicket keeps only the page(s) used in the last user request in the Http Session (to keep memory footprint small). By clicking on MyOtherPage link you put an instance of MyOtherPage in the Http session and the old instance (of MyPage) is only in the disk. But: if MyPage fails to serialize to byte[] then it cannot be stored in the disk and thus later requests will fail with PageExpiredException.
Todo: Check your logs for NotSerializableException with nice debug message of the reason.
MyOtherPage is too big
By default Wicket writes up to 10M per user session in the disk. If MyPage is let's say 2M and MyOtherPage is 9M (both sizes are quite big, but I don't know what happens in your app...) then saving MyOtherPage will remove MyPage from the disk. Later attempts to load MyPage will fail with PageExpiredException.
Todo: Review your usage of Wicket Models.

Related

Dynamically replace ListView table cell with textField wicket

I have a listView which has a single column which needs to be editable only in certain cases. If the user needs to change the column, I want them to click a edit button for the row, then replace the label in the cell with a textField. When I call replace I can see the TextField is now in the place of the label, although it is never rendered. I have a AjaxLink to handle the event. I am using a container to repaint the listView. Here is my listview:
parent = new WebMarkupContainer("emp-table-parent");
parent.add(new AjaxLink<Object>(FIRST_NAME_HEADER_LINK) {
/**
*
*/
private static final long serialVersionUID = -1937727929649333407L;
#Override
public void onClick(AjaxRequestTarget target) {
changeGlyphUpdateList(target, parent.get(FIRST_NAME_HEADER_LINK).get(FIRST_NAME_HEADER_ICON),
parent.get(LAST_NAME_HEADER_LINK).get(LAST_NAME_HEADER_ICON),
parent.get(EMAIL_HEADER_LINK).get(EMAIL_HEADER_ICON), parent.get(ELIGIBILITY_CLASS_HEADER_LINK).get(ELIGIBILITY_CLASS_HEADER_ICON),
parent.get(EMPLOYER_HEADER_LINK).get(EMPLOYER_HEADER_ICON));
}
}.add(new Label("first-name-header-label", Model.of("First Name")), new WebComponent(FIRST_NAME_HEADER_ICON)),
new AjaxLink<Object>(LAST_NAME_HEADER_LINK) {
/**
*
*/
private static final long serialVersionUID = -3438649095509412910L;
#Override
public void onClick(AjaxRequestTarget target) {
changeGlyphUpdateList(target, parent.get(LAST_NAME_HEADER_LINK).get(LAST_NAME_HEADER_ICON),
parent.get(FIRST_NAME_HEADER_LINK).get(FIRST_NAME_HEADER_ICON),
parent.get(EMAIL_HEADER_LINK).get(EMAIL_HEADER_ICON),
parent.get(ELIGIBILITY_CLASS_HEADER_LINK).get(ELIGIBILITY_CLASS_HEADER_ICON),
parent.get(EMPLOYER_HEADER_LINK).get(EMPLOYER_HEADER_ICON));
}
}.add(new Label("last-name-header-label", Model.of("Last Name")), new WebComponent(LAST_NAME_HEADER_ICON)),
new AjaxLink<Object>(EMAIL_HEADER_LINK) {
/**
*
*/
private static final long serialVersionUID = 2890934302751793454L;
#Override
public void onClick(AjaxRequestTarget target) {
changeGlyphUpdateList(target, parent.get(EMAIL_HEADER_LINK).get(EMAIL_HEADER_ICON),
parent.get(LAST_NAME_HEADER_LINK).get(LAST_NAME_HEADER_ICON),
parent.get(FIRST_NAME_HEADER_LINK).get(FIRST_NAME_HEADER_ICON),
parent.get(ELIGIBILITY_CLASS_HEADER_LINK).get(ELIGIBILITY_CLASS_HEADER_ICON),
parent.get(EMPLOYER_HEADER_LINK).get(EMPLOYER_HEADER_ICON));
}
}.add(new Label("email-header-label", Model.of("Email")), new WebComponent(EMAIL_HEADER_ICON)),
new AjaxLink<Object>(ELIGIBILITY_CLASS_HEADER_LINK) {
/**
*
*/
private static final long serialVersionUID = -4022209586109961448L;
#Override
public void onClick(AjaxRequestTarget target) {
changeGlyphUpdateList(target, parent.get(ELIGIBILITY_CLASS_HEADER_LINK).get(ELIGIBILITY_CLASS_HEADER_ICON),
parent.get(EMAIL_HEADER_LINK).get(EMAIL_HEADER_ICON),
parent.get(LAST_NAME_HEADER_LINK).get(LAST_NAME_HEADER_ICON),
parent.get(FIRST_NAME_HEADER_LINK).get(FIRST_NAME_HEADER_ICON),
parent.get(EMPLOYER_HEADER_LINK).get(EMPLOYER_HEADER_ICON));
}
}.add(new Label("eligibility-class-header-label", Model.of("Elig. Class")), new WebComponent(ELIGIBILITY_CLASS_HEADER_ICON)),
new AjaxLink<Object>(EMPLOYER_HEADER_LINK) {
/**
*
*/
private static final long serialVersionUID = -738777257301408437L;
#Override
public void onClick(AjaxRequestTarget target) {
changeGlyphUpdateList(target, parent.get(EMPLOYER_HEADER_LINK).get(EMPLOYER_HEADER_ICON),
parent.get(ELIGIBILITY_CLASS_HEADER_LINK).get(ELIGIBILITY_CLASS_HEADER_ICON),
parent.get(EMAIL_HEADER_LINK).get(EMAIL_HEADER_ICON),
parent.get(LAST_NAME_HEADER_LINK).get(LAST_NAME_HEADER_ICON),
parent.get(FIRST_NAME_HEADER_LINK).get(FIRST_NAME_HEADER_ICON));
}
}.add(new Label("employer-header-label", Model.of("Employer")), new WebComponent(EMPLOYER_HEADER_ICON)),
new PageableListView<EmployeeSummaryPkt>("data", employeeSummaryModel.getObject(), 25) {
/**
*
*/
private static final long serialVersionUID = -1697070076764699904L;
#Override
protected void populateItem(final ListItem<EmployeeSummaryPkt> item) {
item.setDefaultModel(new CompoundPropertyModel<EmployeeSummaryPkt>(item.getModelObject()));
item.add(new Label("firstName"),
new Label("lastName"),
new Label("employeeEmail"),
new Link<Object>("eligibility-class-data-link") {
/**
*
*/
private static final long serialVersionUID = -3842291392813313171L;
#Override
public void onClick() {
//LINK TO ELIGIBILITY CLASS OR MAYBE THE SECTION WITHIN THE EMP?
}
}.add(new Label("employeeEligibilityClassSummaryPkt.name")),
new Link<Object>("employer-data-link") {
/**
*
*/
private static final long serialVersionUID = 6809571267919974106L;
#Override
public void onClick() {
getIndex().getHomePanel().setNewContent(new EmployerDetailPanel("panel-content", item.getModelObject().getEmployerSummaryPkt().getId()));
}
}.add(new Label("employerSummaryPkt.name")),
new Label("employeeDateOfBirth"),
new Label("employee-code", Model.of(item.getModelObject().getEmployeeName())).setOutputMarkupId(true),
new AjaxLink<Object>("edit-employee-link") {
/**
*
*/
private static final long serialVersionUID = 6061544430700059358L;
#Override
public void onClick(AjaxRequestTarget target) {
logr.log(Level.FINER, "onClick for edit employee");
logr.log(Level.FINER, "employee code pre: " + item.get("employee-code").getClass().getSimpleName());
item.get("employee-code").replaceWith(new TextField<String>("employee-code", new Model<String>(item.getModelObject().getEmployeeName())).setOutputMarkupId(true));
logr.log(Level.FINER, "employee code post: " + item.get("employee-code").getClass().getSimpleName());
target.addChildren(parent, TextField.class);
target.add(parent);
}
});
}
As you can see, the label with the id "employee-code" is the label I wish to replace. Inside the AjaxLink onClick, you can see where I am getting the label and replacing it. Nothing is changing. Any direction or help is greatly appreciated.
A ListView recreates each if its items on each render, thus the altered listItem is thrown away immediately.
ListView#setReuseItems(true) should help.
An alternative to svenmeier's answer could also be to add only the specific item that has been changed to the AjaxRequestTarget. That is, replace
target.addChildren(parent, TextField.class);
target.add(parent);
With
target.add(item.get("employee-code"))
Which would cause only the piece of markup that concerns the change to be re-rendered, as opposed to the entire table. If your table is big and contains a lot of elements, whose models involve complicated retrieval mechanisms rendering the entire table would be a much more laborious process, and hence re-rendering only the item would be a better solution.
Having said that, in your particular case svenmeier's solution is better, as otherwise if you implement my solution and later on re-render the entire table, the changes would be lost.

Login failed on Wicket 7 with Spring Security 4.1

After upgrading from Wicket 6.23.0 to 7.4.0 the Login on my Wicket Page does not work anymore.
I get an PageExpiredException:
14:50:31,772 WARN [RequestCycleExtra] (default task-20) ********************************
14:50:31,773 WARN [RequestCycleExtra] (default task-20) Handling the following exception: org.apache.wicket.protocol.http.PageExpiredException: Bookmarkable page with id 'null' has expired.
14:50:31,774 WARN [RequestCycleExtra] (default task-20) ********************************
The LoginPage looks like this:
#MountPath("login")
public class LoginPage extends WebPage {
private static final long serialVersionUID = 1L;
public LoginPage() {
}
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptReferenceHeaderItem.forReference(JQueryJavaScriptResourceReference.get()));
response.render(JavaScriptReferenceHeaderItem.forReference(JQueryUiJavaScriptResourceReference.get()));
response.render(CssContentHeaderItem.forReference(JQueryUiCssResourceReference.get()));
}
#Override
protected void onInitialize() {
super.onInitialize();
IModel<AuthenticationData> model = new CompoundPropertyModel<>(new AuthenticationData());
add(new FeedbackPanel("feedbackPanel"));
#SuppressWarnings("serial")
StatelessForm<AuthenticationData> form = new StatelessForm<AuthenticationData>("form", model) {
/** {#inheritDoc} */
#Override
protected void onSubmit() {
WebSession session = WebSession.get();
if (session.signIn(getModelObject().getUserName(), getModelObject().getPassword())) {
setResponsePage(getApplication().getHomePage());
} else {
error("Login failed due to invalid credentials");
}
}
};
add(form);
TextField<String> userNameTextField = new TextField<>("userName");
form.add(userNameTextField);
PasswordTextField passwordTextField = new PasswordTextField("password");
form.add(passwordTextField);
SubmitLink submitLink = new SubmitLink("submitLink");
form.add(submitLink);
form.setDefaultButton(submitLink);
}
}
The onSubmit Method of the StatelessForm is never reached.
The setup is simular to here: https://ci.apache.org/projects/wicket/guide/7.x/guide/security.html#security_1
The problem was the StatelessForm on the LoginPage:
#MountPath("login")
public class LoginPage extends WebPage {
private static final long serialVersionUID = 1L;
public LoginPage() {
}
#Override
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
response.render(JavaScriptReferenceHeaderItem.forReference(JQueryJavaScriptResourceReference.get()));
response.render(JavaScriptReferenceHeaderItem.forReference(JQueryUiJavaScriptResourceReference.get()));
response.render(CssContentHeaderItem.forReference(JQueryUiCssResourceReference.get()));
}
#Override
protected void onInitialize() {
super.onInitialize();
IModel<AuthenticationData> model = new CompoundPropertyModel<>(new AuthenticationData());
add(new FeedbackPanel("feedbackPanel"));
#SuppressWarnings("serial")
Form<AuthenticationData> form = new Form<AuthenticationData>("form", model) {
/** {#inheritDoc} */
#Override
protected void onSubmit() {
WebSession session = WebSession.get();
if (session.signIn(getModelObject().getUserName(), getModelObject().getPassword())) {
setResponsePage(getApplication().getHomePage());
} else {
error("Login failed due to invalid credentials");
}
}
};
add(form);
TextField<String> userNameTextField = new TextField<>("userName");
form.add(userNameTextField);
PasswordTextField passwordTextField = new PasswordTextField("password");
form.add(passwordTextField);
SubmitLink submitLink = new SubmitLink("submitLink");
form.add(submitLink);
form.setDefaultButton(submitLink);
}
}

GWT application crashes in latest Firefox versions 21 and above

We have a GWT application which crashes in Firefox versions 21 and above, including in the latest version 23.0.1. In earlier versions of Firefox and IE 9, it works fine. This is in deployed mode and not because of the GWT plugin. The situation it crashes is when there are huge number of RPC calls, may be around 300 to 400.
As the application in which it happens is fairly complex, I tried to simulate this issue with a simple prototype. I observed that my prototype crashes when the number of RPC calls reach 100000. But this scenario is very unlikely in my application where RPC calls are around 300-400 as observed using Firebug.
I am trying to find out what else I am missing in my prototype so that it also crashes with 300-400 RPC calls.
GWT version - 2.4
GXT version - 2.2.5
package com.ganesh.check.firefox.client;
public class FirefoxCrash implements EntryPoint {
private static final String SERVER_ERROR = "An error occurred while "
+ "attempting to contact the server. Please check your network "
+ "connection and try again.";
private final GreetingServiceAsync greetingService = GWT
.create(GreetingService.class);
public native static void consoleLog(String text)/*-{
$wnd.console.log(text);
}-*/;
public void onModuleLoad() {
final Button sendButton = new Button("Send");
final TextBox nameField = new TextBox();
nameField.setText("GWT User");
final Label errorLabel = new Label();
final Label countLabel = new Label();
// We can add style names to widgets
sendButton.addStyleName("sendButton");
// Add the nameField and sendButton to the RootPanel
// Use RootPanel.get() to get the entire body element
RootPanel.get("nameFieldContainer").add(nameField);
RootPanel.get("sendButtonContainer").add(sendButton);
RootPanel.get("errorLabelContainer").add(errorLabel);
RootPanel.get("count").add(countLabel);
// Focus the cursor on the name field when the app loads
nameField.setFocus(true);
nameField.selectAll();
// Create the popup dialog box
final DialogBox dialogBox = new DialogBox();
dialogBox.setText("Remote Procedure Call");
dialogBox.setAnimationEnabled(true);
final Button closeButton = new Button("Close");
// We can set the id of a widget by accessing its Element
closeButton.getElement().setId("closeButton");
final Label textToServerLabel = new Label();
final HTML serverResponseLabel = new HTML();
VerticalPanel dialogVPanel = new VerticalPanel();
dialogVPanel.addStyleName("dialogVPanel");
dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
dialogVPanel.add(textToServerLabel);
dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
dialogVPanel.add(serverResponseLabel);
dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
dialogVPanel.add(closeButton);
dialogBox.setWidget(dialogVPanel);
// Add a handler to close the DialogBox
closeButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
dialogBox.hide();
sendButton.setEnabled(true);
sendButton.setFocus(true);
}
});
class MyHandler implements ClickHandler, KeyUpHandler {
private int resultCount = 0;
/**
* Fired when the user clicks on the sendButton.
*/
public void onClick(ClickEvent event) {
sendNameToServer();
}
public void onKeyUp(KeyUpEvent event) {
if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
sendNameToServer();
}
}
private void sendNameToServer() {
// First, we validate the input.
errorLabel.setText("");
String textToServer = nameField.getText();
// Then, we send the input to the server.
textToServerLabel.setText(textToServer);
serverResponseLabel.setText("");
final int loopCount = Integer.parseInt(textToServer);
resultCount=0;
for (int i = 0; i < loopCount; i++) {
greetingService.getResult(textToServer,
new AsyncCallback<ResultBean>() {
public void onFailure(Throwable caught) {
consoleLog(caught.getMessage());
}
public void onSuccess(ResultBean result) {
//countLabel.setText(++resultCount + "");
resultCount++;
if(resultCount==loopCount){
countLabel.setText(resultCount + "");
}
consoleLog("Result returned for "+resultCount);
}
});
}
}
}
// Add a handler to send the name to the server
MyHandler handler = new MyHandler();
sendButton.addClickHandler(handler);
nameField.addKeyUpHandler(handler);
}
}
public class GreetingServiceImpl extends RemoteServiceServlet implements
GreetingService {
public ResultBean getResult(String name) {
ResultBean result = new ResultBean();
Random random = new Random();
int suffix = random.nextInt();
result.setName("Name "+suffix);
result.setAddress("Address "+suffix);
result.setZipCode(suffix);
result.setDoorNumber("Door "+suffix);
return result;
}
public class ResultBean implements Serializable {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getZipCode() {
return zipCode;
}
public void setZipCode(int zipCode) {
this.zipCode = zipCode;
}
public String getDoorNumber() {
return doorNumber;
}
public void setDoorNumber(String doorNumber) {
this.doorNumber = doorNumber;
}
private String name;
private String address;
private int zipCode;
private String doorNumber;
}

wicket 6.0.0-beta2 Updating content of DataTable when submitting a form with AjaxButton

I want to change the content of a DataTable depending on the content of a form (think of it as a searchbar functionality). I used to do that in wicket 1.5.x but I can not seem to make it work in wicket 6.0.0-beta2. It does not seem to enter in the onSubmit method of the AjaxButton. Everything else works just fine, every components render correctly and the dataTable is filled with the correct data when the page load, but when I click the button, nothing happens.
Any help would be greatly appreciated. Here is what my code look like :
The dataTable :
public SubscriberPage(PageParameters parameters) {
super(parameters);
add(new SearchForm("searchForm"));
List<IColumn<Subscriber, String>> columns = new ArrayList<IColumn<Subscriber, String>>();
columns.add(new PropertyColumn<Subscriber, String>(new Model<String>("Telephone Number"),
"tn",
"tn"));
[...]
columns.add(new PropertyColumn<Subscriber, String>(new Model<String>("Initialized MB"),
"initializedMB"));
table = new AjaxFallbackDefaultDataTable<Subscriber, String>("table",
columns,
subscriberDataProvider,
40);
table.setOutputMarkupId(true);
add(table);
}
and here is the form with the AjaxButton:
private class SearchForm extends Form<String> {
private static final long serialVersionUID = 1L;
private String tnModel;
private Label tnLabel = new Label("tnLabel", "Telephone Number :");
private TextField<String> tn;
public SearchForm(String id) {
super(id);
tn = new TextField<String>("tnTextField", new PropertyModel<String>(this, "tnModel"));
tn.setOutputMarkupId(true);
add(tnLabel);
add(tn);
AjaxButton lSearchButton = new AjaxButton("searchButton") {
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
SubscriberFilter filter = new SubscriberFilter();
target.add(table);
if (!(tn.getValue() == null) && !tn.getValue().isEmpty()) {
filter.setTn(tn.getValue());
}
// giving the new filter to the dataProvider
subscriberDataProvider.setFilterState(filter);
}
#Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
// TODO Implement onError(..)
throw new UnsupportedOperationException("Not yet implemented.");
}
};
lSearchButton.setOutputMarkupId(true);
this.setDefaultButton(lSearchButton);
add(lSearchButton);
}
}
The components that you want to refresh need to be added in a container. When you submit, the container needs to be added to target. This way your components will be refreshed. Something like:
WebMarkupContainer outputContainer = new WebMarkupContainer("searchResult");
outputContainer.setOutputMarkupId(true);
outputContainer.add(table);
add(outputContainer);
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
//change table ..... stuff ..... ...
//refresh container
target.add(outputContainer);
}
<div wicket:id="searchResult"></div>

Wicket serving images from File System

I am pretty new to Wicket and i have some difficulties with using resource references. I am using wicket 1.5.4 and have following problem: I store images on the file system. I have class ImageElement which holds part of the file path relative to configured rootFilePath (i.e dir1/dir2/img1.png). On the page I add Image as follows:
new Image("id",ImagesResourceReference.get(), pageParameters)
where page parameters includes image path parameter (path="/dir1/dir2/img1.png"). My questions are:
Is it the simplest way of serving images from the file system?
Is it ok to use ResourceReference with static method? or I should construct each time new ResourceReference? I saw that in previous version it was possible to use new ResourceReference(globalId), but it seems not to be the case anymore. If so what is the global resource reference for? So far as I understand resource reference is supposed to be factory for resources so it would be rather strange to create new factory for each resource request.
The last question is, how can i pass the path to the image in a better way so that i do not have to concatenate indexed parameters to build the path once respond method is invoked on ImageResource.
What would be the best scenario to get it working in efficient and simple way, i saw the example in 'Wicket in action', but this is meant for dynamic image generation from db and am not sure if it suites for my case
My implementation of ResourceReference which I mounted in Application under "/images" path, looks as follows:
public class ImagesResourceReference extends ResourceReference {
private static String rootFileDirectory;
private static ImagesResourceReference instance;
private ImagesResourceReference() {
super(ImagesResourceReference.class, "imagesResourcesReference");
}
public static ImagesResourceReference get() {
if(instance == null) {
if(StringUtils.isNotBlank(rootFileDirectory)) {
instance = new ImagesResourceReference();
} else {
throw new IllegalStateException("Parameter configuring root directory " +
"where images are saved is not set");
}
}
return instance;
}
public static void setRootFileDirectory(String rootFileDirectory) {
ImagesResourceReference.rootFileDirectory = rootFileDirectory;
}
private static final long serialVersionUID = 1L;
#Override
public IResource getResource() {
return new ImageResource(rootFileDirectory);
}
private static class ImageResource implements IResource {
private static final long serialVersionUID = 1L;
private final String rootFileDirectory;
public ImageResource(String rootFileDirectory) {
this.rootFileDirectory = rootFileDirectory;
}
#Override
public void respond(Attributes attributes) {
PageParameters parameters = attributes.getParameters();
List<String> indexedParams = getAllIndexedParameters(parameters);
if(!indexedParams.isEmpty() && isValidImagePath(indexedParams)) {
String pathToRequestedImage = getImagePath(indexedParams);
FileResourceStream fileResourceStream = new FileResourceStream(new File(pathToRequestedImage));
ResourceStreamResource resource = new ResourceStreamResource(fileResourceStream);
resource.respond(attributes);
}
}
private boolean isValidImagePath(List<String> indexedParams) {
String fileName = indexedParams.get(indexedParams.size() -1);
return !FilenameUtils.getExtension(fileName).isEmpty();
}
private List<String> getAllIndexedParameters(PageParameters parameters) {
int indexedparamCount = parameters.getIndexedCount();
List<String> indexedParameters = new ArrayList<String>();
for(int i=0; i<indexedparamCount ;i++) {
indexedParameters.add(parameters.get(i).toString());
}
return indexedParameters;
}
private String getImagePath(List<String> indexedParams) {
return rootFileDirectory + File.separator + StringUtils.join(indexedParams, File.separator);
}
}
Any help and advices appreciated! Thanks in advance.
You could use it as a shared resource:
public class WicketApplication extends WebApplication {
#Override
public Class<HomePage> getHomePage() {
return HomePage.class;
}
#Override
public void init() {
super.init();
getSharedResources().add("downloads", new FolderContentResource(new File("C:\\Users\\ronald.tetsuo\\Downloads")));
mountResource("downloads", new SharedResourceReference("downloads"));
}
static class FolderContentResource implements IResource {
private final File rootFolder;
public FolderContentResource(File rootFolder) {
this.rootFolder = rootFolder;
}
public void respond(Attributes attributes) {
PageParameters parameters = attributes.getParameters();
String fileName = parameters.get(0).toString();
File file = new File(rootFolder, fileName);
FileResourceStream fileResourceStream = new FileResourceStream(file);
ResourceStreamResource resource = new ResourceStreamResource(fileResourceStream);
resource.respond(attributes);
}
}
}
You can still use ResourceReferences with global IDs. You just have to use a SharedResourceReference. This is probably better, too.
add(new Image("image", new SharedResourceReference("mySharedResourceRef", parameters));
I would try to avoid building paths from URL parameters. This can easily end up in security leaks.

Resources