Using session objects from parent class in component - session

During my battle with tapestry 5 I created some setup which does not work, and I don't know why. I found few work-arounds, but still I would like to know why initial idea failed.
I have parent abstract page class with application-wide auth procedure:
public abstract class AuthPage {
#SessionState
protected UserAuth user;
public Object onActivate(){
if(user==null)
return LoginForm.class;
else if(user.getLoggedIn().equals(Boolean.FALSE))
return LoginForm.class;
else
return null;
}
}
Then I have index page class, using auth class as aprent:
public class Index extends AuthPage
{
}
This part works smoohtly - when user SSO is initialized then I got Index content, otherwise it goes to LoginForm. Now the problematic part - Index uses a layout component which takes care of showing personalized header and menu. Its logic looks like that:
public class Layout extends AuthPage
{
#Property
private Boolean loggedIn;
#Property
private String userName;
#SetupRender
public boolean checkNames(){
if(user==null){
loggedIn = false;
userName = "unlogged";
}
else if(user.getLoggedIn().equals(Boolean.FALSE)){
loggedIn=false;
userName = "unlogged";
}
else{
loggedIn = true;
userName = this.user.getUsername();
}
return true;
}
}
The idea was simple - session object user from AuthPage should be available in Layout, and used on setup-render stage to get user name and rising the flag for rendering menu etc. From my point of view everything should work, but in practice Layout class didn't get user object from session (despite that it was initialized for sure, because Index renders its content).
So my question is - why Layout class don't see UserAuth object stored in session, but gets it as null instead?
************ little update:
I've refactore layout to that shape:
public class Layout
{
#SessionState
protected UserAuth user;
#Property
private Boolean loggedIn;
#Property
private String userName;
#SetupRender
public boolean checkNames(){
if(user==null){
loggedIn = false;
userName = "unlogged";
}
else if(user.getLoggedIn().equals(Boolean.FALSE)){
loggedIn=false;
userName = "unlogged";
}
else{
loggedIn = true;
userName = this.user.getUsername();
}
return true;
}
}
and it works as I want - Layout (executet from Index page as component) takes user attribute from session, performs checkNames and sets up all properties properly. For me there is no technical difference between initial and second implementation, but somehow when user is defined in parent class is always set to null (no matter what is stored in session). The question is - why it works that way?

Layout is a component, not a page, and onActivate() is page event which won't be fired when the Layout component renders. For a component (Layout) to extend a page (AuthPage) does not make sense. I'm surprised that Tapestry allows it to tell the truth.
On another note, Tapestry has so many features (including filters, class transformations and mixins) that inheritance is almost always not required. Although quite complex, you might find the diagram at the bottom of this page useful. In particular you may be interested in the ComponentRequestFilters and PageRenderRequestFilters.
Here's a couple of options for securing pages:
Howard Lewis Ship's blog - Howard is the creator of Tapestry
Tynamo tapestry security

What version of tapestry are you using? In tapestry 5.3.1 and earlier, instance variables must be private. Only version 5.3.2+ supports protected and package private.
http://tapestry.apache.org/page-and-component-classes-faq.html#PageAndComponentClassesFAQ-Whydomyinstancevariableshavetobeprivate%3F

Ok, I solved the problem! Answer is a little bit unexpected for me, but well - everything seems to work fine now. The trick is the way I was refering to the user attribue ot AuthPage class. I used
this.user
to refere to it from child classes. It turned out that I've got it as null everywhere, even in pages (I had an impression that it worked correctly in Index page, but in fact it was just used in AuthPage class). The solution was to refere by:
super.user
When I switched to such referencing style suddenly every page and component started to work properly and got correct values from session. I'll just take it as it is, but if someone knows why it works that way and no other - I'll appreciate sharing this knowledge with me.

Related

Spring - how do I get the page to refresh after a form is submitted?

Using spring boot, I have a timetable page.
You can click a square, which opens up a small form with which you can add or remove the class objects from the timetable (which are saved in a database).
My issue is, when you click 'add' (or remove), while it successfully adds/removes that object from the timetable, you have to refresh the page in order to see the class be added/removed on the timetable. It appears like nothing happens when add is clicked from the user's perspective (until they manually refresh the page).
The post method redirects back to the timetable page get. I tried having it redirect to another mini get method, which then redirected back to the original timetable page; but from the browser side it still didn't look like anything was happening - just remaining on the same page with the original problem. Would love to hear a potential solution, thanks!
Edit: Here's an example of my get and post methods:
#GetMapping("/timetable/{id}/{semId}")//This method displays timetable.
public String timetable(#PathVariable(value = "id") String id, Model model,
#PathVariable(value = "semId") String semId) {
//code..
model.addAttribute("x", x);
return "timetable";
}
#PostMapping("/timetable/{id}/{semId}")
public String timetablePost(#ModelAttribute TimetableClassDto dto, #PathVariable(value = "id") String id,
Model model, #PathVariable(value = "semId") String semId) {
//code..
return "redirect://timetable/00/" + semId;
}
Are you supposed to have two // in your redirect? I have something similar in my code and it works fine. However, I create a url first then return that. Also, make sure your get mapping is properly filling out the object based on the new parameters gotten from the redirect.
Use the following:
??
String url = "redirect://timetable/00/" + semId;
return url;

Grail validate data already saved to database

I have this really complicated form. All the fields must be filled, but the process of filling can be saved and leter continued. So what i need s that when finally confirm is pressed, all the data get validated. But because it is already saved to database calling validate() wont work. I save the data by douing save(validate:false), because i dont need validation when the work is still in progremm.
How can i validate data that has already been saved to database ? Do i have to do it manually?
What happens when you validate an already persisted object?
Is there a way to make it appear dirty after retrieving and before validating?
I have edited the answer to explain form encapsulation in more detail, typically maybe from a tier step process of form input or more complex iterated objects that needs to be elsewhere. To begin if all you need is a one step that captures a variety of information and you are happy to then process all that manually and store in different classes on mass params dump then look into jquery-ui tabs. If you choose to use the dynamic feature of tabs i.e. <li><a href="someurl">click</li> which then loads content dynamically to a given tab then that would also cover single forms that are outside or maybe if you prefer more complex within DOM.
Anyhow the reason for my edit wasn't above it is for something a little more complex that captures multi tier forms.
So you had step 1 that sent params through to a controller that then passed those params to a new gsp or maybe even an iteration of something that belongs to another totally different object within the form.
Typically you would end up with:
<g:form action=myDomain" action="doThis">
<!-- this is some iteration that belongs to some other class outside of what i am actually trying to do: -->
<g:each in="someObject" var="p">
<g:hiddenField name="form1.firstName" value="${p.firstName}"/>
<!-- in this case hidden -->
<g:hiddenField name="form1.surName" value="${p.surName}"/>
</g:each>
<!-- this is my actual form -->
<g:textField name="username" />
</g:form>
When form is submitted to a controller
Class MyDomainController {
def doThis(MyBean bean) {
save(bean)
}
}
//this be in src/main/groovy
import grails.validation.Validateable
//important for some reason it needs entire collections
// have had issues initialising it without .*
import org.apache.commons.collections.*
Class MyBean implements Validateable {
//this is capturing the current form fields
String username
//This is now collecting our form1 fields
List<MyDetailsBean> form1 = ListUtils.lazyList([], { new MyDetailsBean() } as Factory)
//if that had been 1 instance of it or like as mentioned passed from pervious form and called form2
MyDetailsBean form2
static constraints={
username(nullable:false) //, validator: checkSomething)
}
}
This is again in src/main/groovy and was used to originally collect each iteration of an object:
import grails.validation.Validateable
Class MyDetailsBean implements Validateable {
String firstName
String surName
}
I have updated the answer since i suggested encapsulating the object in a bean without any details of how one would go about doing such a thing. I hope above is clear. It is all on the fly but if tested hope it all works as explained above.
TO add after next update explained form2 example. to finally validate both sets you call
if (bean.validate() && bean.form2.validate()) {
//all good
}
Because you are binding it to another validation class the rules of that class can now be applied as part of validation process.
Old answer
Quite simply put it is on the db why on earth would you want to validate a validated input. Anyhow the work around is a validation bean in Grails 2 src/groovy/package which is #Validateable or
Grails 3 : src/main/groovy/package which implements Validateable
class MyDmainBean {
// must declare id
def id
// Then each object in your real domain class
static constraints = {
id (nullable:true,bindable:true)
importFrom MyDomainClass//, exclude: ['field1']
//field 1 is not included but if field 1 was integer
// in actual domain class and BigDecimal in bean
//then exlude it since it won't bind
}
def formatObject(MyDomainClass domain) {
id=domain.id
..
}
}
Now you can call
MyDomain record = MyDomain.get(0L)
MyDmainBean bean = new MyDmainBean().formatObject(record)
bean.validate()

Regarding Events in Openbravo onSave need to get an client and set it into other class object

Here My code
Need to populate it from a class and then set in to another class especially all columns in product to product detail (Some How i managed other but My Problem is regarding Client. It is not showing anything)
public void onSave(#Observes EntityNewEvent event) {
if (!isValidEvent(event)) {
return;
}
final Entity product_Shift =ModelProvider.getInstance().getEntity(OCAProducts.ENTITY_NAME);
ProductsDetails pd = OBProvider.getInstance().get(ProductsDetails.class);
final Property pro_client = product_Shift.getProperty(OCAProducts.PROPERTY_CLIENT);
pd.setClient((Client) event.getCurrentState(pro_client));
OBDal.getInstance().save(pd);
OBDal.getInstance().flush();
Getter and setter methods do not work in Openbravo event handlers. Check the document from Openbravo's wiki here.
An excerpt from the wiki page.
don't call setters on the Greeting instance itself, this does not work
because when the event has been broadcasted, Hibernate has already
read the state of the object. So you must change the value through the
special setCurrentState method
Example
event.setCurrentState(clientProperty, clientId);
event.setCurrentState(pro_client, clientID)

The difference between object validation and persistence validation in DDD?

Right now, I have a domain entity named StyleBundle. This StyleBundle takes a list of Styles:
public class StyleBundle
{
public StyleBundle(List<Style> styles)
{
this.Styles = styles;
}
public IEnumerable<Style> Styles { get; private set;}
}
So, in my original design, a StyleBundle should never be created with an empty Style list. This was a rule that the domain experts basically said was good.
I wrote this using a guard clause in the constructor:
if (styles.Count() == 0)
throw new Exception("You must have at least one Style in a StyleBundle.");
which made sure I could not create StyleBundle in an invalid state. I thought an exception made sense here b/c a StyleBundle being created without at least one Style was exceptional in the system.
Of course, change came down the road during the rest of the project, and now it should be possible for a user to create a StyleBundle without Styles, but they should not be allowed to PERSIST a StyleBundle without Styles.
So now I'm looking at my guard clause and realizing that I can't have the exception thrown from the constructor anymore.
Moving forward, I have a Service/Application layer that my code-behinds interact with when they're working with StyleBundles. In my Service Layer, I have a StyleBundleService class, and that class exposes basic functionality to the UI... among them is "CreateStyleBundle".
It seems as if I'll have to have my Service Layer check to see if the StyleBundle does or does not have any Styles before it's persisted to the database, but something about this decision feels "wrong" to me.
Anyone run into a similar thing? Basically, the different between the state of an object being valid when "new'ed up" vs. the state of the same object when it comes to persistence?
Thanks!
Mike
I would add an IsValid method to your entity. This would check if the entity is currently in a valid state (in your case, check if there are styles).
This method can be called from your Repository to check if an entity may be persisted. You can add more rules to the IsValid method for specific entities and you can implement something like a collection of Validation errors is you want to throw a meaningful exception.
Expanding what Wouter said, plus handy BeforeSaving and BeforeDeleting methods:
public interface IDomainObject<T>
{
bool IsValid();
}
public interface IEntity<T> : IDomainObject<T>
{
}
public interface IAggregateRoot<T> : IEntity<T>
{
void BeforeSaving();
void BeforeDeleting();
}
public interface IAggregateRoot { //or simply IEntity depending on the model
bool IsValid();
}
public class StyleBundle : IAggregateRoot<T> {
return styles.Count() > 0
}
public class StyleBundleRepository : Repository<StyleBundle> {
}
public abstract class Repository<T> : IRepository<T> where T : class, IAggregateRoot<T> {
public T Save(T t)
{
t.BeforeSaving(); //for all AggregateRoots, maybe logging what the aggregate was like before the changes
if(!t.IsValid())
throw Exeception("Entity invalid");
EntityStore.Current.SaveChanges();
// "AfterSaving" here, i.e.: log how the entity looks after the update
}
}
Edit: I dont personally use the IsValid idea, I go with a full class of EntityValidationErrors where I can report back to the client what was wrong before attempting to save, things that shouldnt be null, shouldnt be empty (like your Styles etc)
There are multiple strategies:
Some developers prefer to create 2 methods in the entity itself, one called IsValid() which validates the entity in terms of business rules (general validation) and another one called IsValidForPersistence() which validates the entity for persistence.
Regarding IsValid() I prefer instead not to allow invalid state in the first place by validating all inputs, and to support invariants I use factory or builder.
you may check the link http://www.codethinked.com/thoughts-on-domain-validation-part-1
for some thoughts.
I know, this question is three years old, but seeing the current answer is something I like to respond to. We are talking about the domain data. Hence, there can't be a valid StyleBundle with 0 objects. I imagine, you have a frontend editor somewhere, were you create a "new" StyleBundle and have to add at least one style, before hitting the "save" button.
At this point in the frontend, you won't have a domain object. You may have a data transfer object, that will be send with a "CreateNewStyleBundle" command.
In my opinion, the domain object must be agnostic to persitance and should always be in a valid state. If you have to call a "IsValid" method, you circumvent the whole idea of having domain objects in the first place.
That's just my humble opinion.

Primefaces dataTable issues

What is the method to refresh data on subsequent pages - second page, third page, etc - of a Primefaces dataTable using the LazyDataModel method?
Also, if I select an item in a dataTable to view its detail on another page, then came back to the dataTable using either the browser's Back button or implement JavaScript's history.back() method, it seems that the dataTable always reset its position to the first page instead of going back to the page the user was on. How can I force the dataTable to stay on the last viewed page?
My codes for lazy loading are:
private final class LazyLoader extends LazyDataModel<BookModel>
{
private static final long serialVersionUID = 1L;
public LazyLoader(String sort, String category, String operator, String input) {
setListing(getBookService().getListing(sort, category, operator, input));
}
#Override
public List<BookModel> load(int first, int pageSize, String sortField, boolean sortOrder, Map<String, String> filters) {
return getListing();
}
}
And for the Submit method is:
public String Submit()
{
sort = sortBean.getSort();
category = categoryBean.getCategory();
operator = operatorBean.getOperator();
input = searchBean.getInput();
lazyModel = new LazyLoader(sort, category, operator, input);
lazyModel.setRowCount(listing.size());
return null;
}
I'm using #ViewScoped for listing the book records as well as showing detail of a book record.
Does anyone has similar issues with Primefaces dataTable?
Keep using #ViewScoped. You should not use #SessionScoped unless you have real needs for it.
To remember the last page, you have to set the first attribute of the load method. You can do that with request parameters. Something like: yourview.xhtml?f=3 .
About the refreshing, the thing is that you are using a lazy loader but you're loading everything at once... Your load method is the one that should do the query on demand, that is, page by page.
Does pagination work for you without lazy loading? I would verify that works as expected before you jump into the hardest case.
If you want your dataTable to remember the last pagination after you navigate away from the JSF page then you need to make your managed bean SessionScoped. The lifecycle of the ViewScoped managed bean ends after navigation leaves the view.
In order to keep the selected page you have to do 2 things.
First make the managedBean session scoped.
Second set a binding between the datatable and a UIData object. In your backend bean for example put
private UIData filasUIData = null;
public UIData getFilasUIData() {
return filasUIData;
}
public void setFilasUIData(UIData filasUIData) {
this.filasUIData = filasUIData;
}
Now in your data table
<ice:dataTable
binding="#{yourBean.filasUIData}"
that´s all.

Resources