Is it possible to refresh stale elements? - htmlelements

I have a search page that contains a table that gets populated with search results after the user presses the search button. The page object wraps the rows of the search results table in a custom HtmlElement class.
I'm getting a stale element exception when accessing the results table - as you'd expect because it was just refreshed by an ajax request.
I've worked around it by returning a new instance of the page object after performing the search but I'd rather just recreate the search results field. Is there any way to do this?
Example:
#FindBy(css = "[data-viewid='Table1'] .dojoxGridRow")
List<ActivityPerformanceRow> results;
// ...
public void search() {
search.click()
waitForAjaxToComplete();
// If it was a standard WebElement list I'd do something like this:
results = driver.findElements(By.cssSelector(
"[data-viewid='Table1'] .dojoxGridRow"));
}

After a bit of playing around I came up with this solution - it works well but doesn't deal with HtmlElement name property. Given I don't use it that I'm aware of I'm ignoring it for now...
public <T extends HtmlElement> List<T> findElements(Class<T> elementClass, By by) {
List<T> elements = new LinkedList<T>();
for (WebElement element : driver.findElements(by)) {
elements.add(HtmlElementLoader.createHtmlElement(elementClass, element, null));
}
return elements;
}

Related

How to query for empty collection in hibernate search

We are using hibernate-search with ES back end. Parent class has multiple collection of different children objects. We can filter parents based on the value of the child as the collections are annotated with #IndexedEmbedded.
We want to be able to filter parents based on if the collection of children is empty or not. We have tried using #IndexedEmbedded(indexNullAs = "null"), and then filtering on queryBuilder.phrase().onField("parent.children").sentence("null").createQuery(), which has made no difference. Using the ES console we can show the parent, but when the collection is empty it isn't listed at all, leading us to believe it hasn't been indexed due to it being empty.
Another option would be to filter on parent.collection.field using a wildcard, however this would not be recommended by the hibernate search docs due to the performance.
If you upgrade to Hibernate Search 6, you will be able to use the exists predicate:
List<MyEntity> hits = Search.session(entityManager)
.search(MyEntity.class)
.where(f -> f.matchAll().except(f.exists().field(“parent.children”))
.fetchHits(20);
That would solve your problem, and then you can start worrying about performance in your specific case.
Still in Hibernate Search 6, if your tests show that performance of the first solution really is problematic, I would suggest using a custom bridge on children that indexes whether the collection is empty or not. Something like this:
#SuppressWarnings("rawtypes")
public class MyCollectionEmptyBridge implements ValueBridge<Collection, Boolean> {
#Override
public Boolean toIndexedValue(Collection value, ValueBridgeToIndexedValueContext context) {
return value == null || value.isEmpty();
}
}
public class MyParentEntity {
// ...
#GenericField(
name = "childrenAreEmpty",
valueBridge = #ValueBridgeRef(type = MyCollectionEmptyBridge.class),
// Apply the bridge directly to the collection and not to its elements
// See https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#_disabling_container_extraction
extraction = #ContainerExtraction(extract = ContainerExtract.NO)
)
private List<Child> children = new ArrayList<>();
}
List<MyEntity> hits = Search.session(entityManager)
.search(MyEntity.class)
.where(f -> f.match().field(“parent.childrenAreEmpty”).matching(true))
.fetchHits();
That second solution can be implemented with Hibernate Search 5 as well, though Hibernate Search 5's custom bridges are somewhat harder to work with.

How to manage errors when creating an ICEFaces' OutputReource

I am trying to generate an export file out of some items, which should conform to some "criteria" in order to be able to be exported. The thing is: the user should select some items (by using checkboxes) and then click on the ICEFaces' OutputResource in order to export (hopefully all) the selected items.
The parts involved in this process are the following:
The OutputResource in the XHTML:
<ice:outputResource rendered="#{not myBackingBean.emptySelection}" resource="#{myBackingBean.excelResource}" label="export to Excel" shared="false" target="_self" />
The backing bean holding the resource:
#ManagedBean(name = "myBackingBean")
#ViewScoped
#WindowDisposed
public class MyBackingBean implements Serializable
{
...
private ExcelResource resource;
...
}
And, finally, the actual resource:
...
import com.icesoft.faces.context.Resource;
...
public class ExcelResource implements Resource
{
...
#Override
public InputStream open() throws IOException
{
//do some selection here. If there is no valid ticket to export then
//this method will return null, otherwise it will return an InputStream
//and everything will work properly
if (everythingOk)
{
return new ByteArrayInputStream(...);
}
//hopefully, it won't get to this point
return null;
}
As you can see, I'm implementing the com.icesoft.faces.context.Resource interface and overriding the open() method to create the Excel export "on the fly".
Now, once againg, what I want to do is to filter some of the originally selected items and, in case no item is left, navigate to some error page. If this was an h:commandButton or an ice:commandLink then I would use the action property to do it, but I cannot do this here because this is an ice:outputResource. Is there some workaround for this?. Please notice it is not enough to use the rendered property to do this because the user can select something (which will immediately render the ice:outputResource) but the selection should be filtered before exporting it.
Last but not least: I'm using Websphere 8 and ICEFaces 3 to do this.
Thanks in advance!
I've managed to do what I was looking for, this time using some buttons with real actions to redirect to pages.
What I did was the following:
MyBackingBean has now a method to determine whether or not the selection is empty;
It also has a method to determine if the selection is valid (this is: not empty and without any invalid item in it);
There is an <ice:commandLink> rendered when the selection is invalid. This commandLink has the actual redirection to the error page.
The <ice:outputResource> will be rendered when the <ice:commandLink> is not rendered (i.e.: when the selection is completely valid).
And now, the code:
First, the commandLink:
<ice:commandLink rendered="#{not myBackingBean.validSelection}"
disabled="#{myBackingBean.emptySelection}"
label="download excel report}"
action="redirect_to_error_page" />
Remember:
MyBackingBean.isEmptySelection() returns true if there is no item selected.
MyBackingBean.isValidSelection() returns true if there is at least one item selected and also each and every selected item is a valid item (this is: an item that is valid for export)
Now, the outputResource:
<ice:outputResource
rendered="#{myBackingBean.validSelection}"
resource="#{myBackingBean.excelResource}"
label="download excel report}" shared="false" target="_self" />
Last but not least, you may have figured out the fact that the <ice:outputResource> will now handle only valid selection (the actual redirection to the error page is being done by the <ice:commandLink>). This means there has to be a way to filter the items before passing them to the resource for the actual export. Well, in my case I decided to create a filter(...) method in the backing bean.
#ManagedBean(name = "myBackingBean")
#ViewScoped
#WindowDisposed
public class MyBackingBean implements Serializable
{
...
private ExcelResource resource;
...
public List<MyItems> getFilteredList(List<MyItems> allSelectedItems)
{
...
//do some selection here and return a list containing only valid items
return validItemsList;
}
public ExcelResource getExcelResource()
{
return new ExcelResource(getFilteredList(allSelectedItems));
}
public boolean isEmptySelection()
{
//return true if the selection is EMPTY, false otherwise.
}
public boolean isValidSelection()
{
//return true if the selection is NOT EMPTY and it has
//only VALID items in it, false otherwise.
}
}
This way you can generate the ExcelResource "on the fly" with nothing but valid items in it. By the time the <ice:outputResource> is being rendered in the XHTML, it will contain only valid and exportable items!.
I hope someone will find this useful :)

Form fields are reset on validation error

I have a rather complex form in the way that the number of form fields is flexibel. In short, the model object is a TLabel (TranslationLabel) that contains a Map of values (translations). Language here is an enum so the idea is that the number of fields (text areas) for which a translation is given depends on the values in this enum.
This is my form (simplified):
public class TranslationEditForm extends Form {
private final static List<Language> LANGUAGES = newArrayList(Language.values());
public TranslationEditForm(String id, final TranslationLabelView label) {
super(id, new CompoundPropertyModel<TranslationLabelView>(label));
ListView<Language> textAreas = new ListView<Language>("translationRepeater", LANGUAGES) {
#Override
protected void populateItem(final ListItem<Language> itemLang) {
//loop through the languages and create 1 textarea per language
itemLang.add(new Label("language", itemLang.getModelObject().toString()));
Model<String> textModel = new Model<String>() {
#Override
public String getObject() {
//return the value for current language
return label.getValue(itemLang.getModelObject());
}
#Override
public void setObject(String object) {
//set the value for current language
label.getTranslations().put(itemLang.getModelObject(), object);
}
};
itemLang.add(new TextArea<String>("value", textModel).setRequired(true));
}
};
//add the repeater containing a textarea per language to the form
this.add(textAreas);
}
}
Now, it works fine, 1 text area is created per language and its value is also set nicely; even more when changed the model gets updated as intended.
If you submit the form after emptying a text area (so originally there was a value) then of course there is a validation error (required). Normal (wicket) behaviour would be that the invalid field is still empty but for some reason the original value is reset and I don't understand why.
If I override onError like this:
#Override
protected void onError() {
this.updateFormComponentModels();
}
then it is fine, the value of the field is set to the submitted value (empty) instead of the original value.
Any idea what is causing this? What is wicket failing to do because the way I've set up the form (because with a simple form/model this is working fine as are the wicket examples)?
Posted as answer, so the question can be marked as solved:
ListView does recreate all its items at render time. This means that the validation will be broken. Have a look at API doc of the ListView
Calling setReuseItems() on the ListView solves this.
Regards,
Bert

Wicket - Wrapped collection Model "transformation"

I have a domain object which has a collection of primitive values, which represent the primary keys of another domain object ("Person").
I have a Wicket component that takes IModel<List<Person>>, and allows you to view, remove, and add Persons to the list.
I would like to write a wrapper which implements IModel<List<Person>>, but which is backed by a PropertyModel<List<Long>> from the original domain object.
View-only is easy (Scala syntax for brevity):
class PersonModel(wrappedModel: IModel[List[Long]]) extends LoadableDetachableModel[List[Person]] {
#SpringBean dao: PersonDao =_
def load: List[Person] = {
// Returns a collection of Persons for each id
wrappedModel.getObject().map { id: Long =>
dao.getPerson(id)
}
}
}
But how might I write this to allow for adding and removing from the original List of Longs?
Or is a Model not the best place to do this translation?
Thanks!
You can do something like this:
class PersonModel extends Model<List<Person>> {
private transient List<Person> cache;
private IModel<List<String>> idModel;
public PersonModel( IModel<List<String>> idModel ) {
this.idModel = idModel;
}
public List<Person> getObject() {
if ( cache == null ) {
cache = convertIdsToPersons( idModel.getObject() );
return cache;
}
public void setObject( List<Person> ob ) {
cache = null;
idModel.setObject( convertPersonsToIds( ob ) );
}
}
This isn't very good code but it shows the general idea. One thing you need to consider is how this whole thing will be serialised between requests, you might be better off extending LoadableDetachableModel instead.
Another thing is the cache: it's there to avoid having to convert the list every time getObject() is called within a request. You may or may not need it in practice (depends on a lot of factors, including the speed of the conversion), but if you use it, it means that if something else is modifying the underlying collection, the changes may not be picked up by this model.
I'm not quite sure I understand your question and I don't understand the syntax of Scala.
But, to remove an entity from a list, you can provide a link that simply removes it using your dao. You must be using a repeater to populate your Person list so each repeater entry will have its own Model which can be passed to the deletion link.
Take a look at this Wicket example that uses a link with a repeater to select a contact. You just need to adapt it to delete your Person instead of selecting it.
As for modifying the original list of Longs, you can use the ListView.removeLink() method to get a link component that removes an entry from the backing list.

How can I use RequestFactory to create an object and initialize a collection whithin it with objects retrieved from another ReqFactory?

I am struggling with an issue using RequestFactory in GWT.
I have a User object : this object has login and password fields and other fields which are of collection type.
public class User {
private String login;
private String password;
private Set<Ressource> ressources;
// Getters and setters removed for brievety
}
I need to persist this object in db so I used RequestFactory because it seems like a CRUD-type operation to me.
Now for the RequestFactory part of the code, this is how I have tried to do it :
I create a UserRequestContext object to create a request object for the new User. Which gives something like :
public interface MyRequestFactory extends RequestFactory {
UserRequestContext userRequestContext();
RessourceRequestContext ressourceRequestContext();
}
and to create the user object I have something like this :
public class UserAddScreen extends Composite {
private UserProxy userProxy;
EventBus eventBus = new SimpleEventBus();
MyRequestFactory requestFactory = GWT.create(MyRequestFactory.class);
...
public UserAddScreen() {
...
requestFactory.initialize(eventBus);
}
public showUserAddScreen() {
// Textbox for password and login
// Listbox for ressources
}
}
I have tried to implement it as a wizard. So at the beginning of the UserAddScreen, I have a
a userProxy object.
This object fields are initialized at each step of the wizard :
the first step is adding the login and password
the second step is adding ressources to the userProxy object.
for this last step, I have two list boxes the first one containing the list of all the ressources i have in my DB table Ressources that I got from RessourceRequestContext.getAllRessource (I have a loop to display them as listbox item with the RessourceId as the value) and the second allows me to add the selected Ressources from this first listbox. Here is the first listbox :
final ListBox userRessourcesListBox = new ListBox(true);
Receiver<List<RessourceProxy>> receiver = new Receiver<List<RessourceProxy>>() {
#Override
public void onSuccess(List<RessourceProxy> response) {
for(RessourceProxy ressourceProxy : response) {
ressourcesListBox.addItem(ressourceProxy.getNom() + " " + ressourceProxy.getPrenom(), String.valueOf(ressourceProxy.getId()));
}
}
};
RessourceRequestContext request = requestFactory.ressourceRequestContext();
request.getAllRessource().fire(receiver);
So, as you can see, my code loops over the retrieved proxies from DB and initializes the items within the listbox.
Here are the control buttons :
final Button addButton = new Button(">");
addButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
for (int i = 0; i < ressourcesListBox.getItemCount(); i++) {
boolean foundInUserRessources = false;
if (ressourcesListBox.isItemSelected(i)) {
for (int j = 0; j < userRessourcesListBox
.getItemCount(); j++) {
if (ressourcesListBox.getValue(i).equals(
userRessourcesListBox.getValue(j)))
foundInUserRessources = true;
}
if (foundInUserRessources == false)
userRessourcesListBox.addItem(ressourcesListBox
.getItemText(i), ressourcesListBox
.getValue(i));
}
}
}
});
So when somebody selects one or more users and click on a ">" button, all the selected items go to the second listbox which is named userRessourceListBox
userRessourcesListBox.setWidth("350px");
userRessourcesListBox.setHeight("180px");
After that, I have a FINISH button, which loops over the items in the second listbox (which are the ones I have selected from the first one) and I try to make a request (again) with RequestFactory to retrieve the ressourceProxy object and initialize the userProxy ressources collection with the result
final Button nextButton = new Button("Finish");
nextButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
RessourceRequestContext request = requestFactory.ressourceRequestContext();
for(int i = 0; i < userRessourcesListBox.getItemCount(); i++) {
Receiver<RessourceProxy> receiver = new Receiver<RessourceProxy>() {
#Override
public void onSuccess(RessourceProxy response) {
userProxy.getRessource().add(response);
}
};
request.find(Long.parseLong(userRessourcesListBox.getValue(i))).fire(receiver);
}
creationRequest.save(newUserProxy).fire(new Receiver<Void>() {
#Override
public void onSuccess(Void response) {
Window.alert("Saved");
}
});
}
});
Finally, (in the code above) I try to save the UserProxy object (with the initial request context I have created userProxy with)... but it doesn't work
creationRequest.save(newUserProxy).fire(...)
It seems like when looping over the result in the onSuccess method :
userProxy.getRessource().add(response);
I retrieve the response (of type RessourceProxy) but beyond this method, for example when I try to save the userProxy object AFTER THE LOOP, there are no RessourceProxy objects in the ressourceProxy collection of userProxy...
Have you guys ever experienced something like this ?
Perhaps I am not doing it right : do I have to get the ressource with the UserRequestContext ? so that my newUser object and ressources are managed by the same request Context ?
if yes then I think it's a little bit weird to have something mixed together : I mean what is the benefit of having a Ressource-related operation in the User-related request context.
any help would be really really ... and I mean really appreciated ;-)
Thanks a lot
The message "… has been frozen" means that the object has been either edit()ed or passed as an argument to a service method, in another RequestContext instance (it doesn't matter whether it's of the same sub-type –i.e. UserRequestContext vs. RessourceRequestContext– or not) which hasn't yet been fire()d and/or the response has not yet come back from the server (or it came back with violations: when the receiver's onViolation is called, the objects are still editable, contrary to onSuccess and onFailure).
UPDATE: you have a race condition: you loop over the resource IDs and spawn as many requests as the number of items selected by the user, and then, without waiting for their response (remember: it's all asynchronous), you save the user proxy. As soon as you fire() that last request, the user proxy is no longer mutable (i.e. frozen).
IMO, you'd better keep the RessourceProxys retrieved initially and use them directly in the user proxy before saving it (i.e. no more find() request in the "finish" phase). Put them in a map by ID and get them from the map instead of finding them back from the server again.