Jump to content


Check out our Community Blogs

Register and join over 40,000 other developers!


Recent Status Updates

View All Updates

Photo
- - - - -

Best Practice: Jsf: Code In Jsp Vs Bean

JSP bean

  • Please log in to reply
8 replies to this topic

#1 johannes

johannes

    CC Lurker

  • Just Joined
  • Pip
  • 6 posts

Posted 04 July 2011 - 11:58 PM

Hello guys!


I'm new to this forum, I noticed you have lots of activity and quality feedback.. so here goes.


I have an include which allows a user to attach a document as a file or as a hyperlink to a record.

There are many types of records and many different jsps this include would be accessed from.

I want to attach documents to more than one different list per record.

I want to use this include wherever I attach documents to a record (currently each attachment is a unique implementation, effectively duplicating code in 20+ places).




To fully reuse the attach-a-document code I need to send 2 pieces of information from the 'record' context (bean1) to the 'attach-a-document' context (bean2).


1. the container to which an attachment should be attached

2. the page to return to (navigation rule from-outcome)


I found 2 solutions that can work (only implemented 1st):

1. send parameters directly in 'record' jsp by calling an 'attach-a-document' action

2. send parameters by calling an action in the record bean which transmits the parameters (by keeping local reference to bean2, or by implementing an 'AttachesDocuments' interface to allow bean2 to extract the data)


1st is simple because it is neatly contained. ex record1CreatePage.jsp


<h:commandLink action="attachDocument" actionListener="{attachmentBean.prepare}" >

  <f:param name="container" value="{record1Bean.firstListOfAttachments}" />

  <f:param name="returnToPage" value="record1CreatePage" />

</h:commandLink>


but it's in the JSP: notoriously difficult to automatically test.


2nd can be tested by JUnits but requires changes to every bean to add specific methods like recordBean.attachDocumentToFirstListOfAttachments for every container. The JSP would need to be changed also in this case.



Could you understand the problem? What is your take on this? Contained jsp-code or lots of stupid but testable actions?


Hope to hear from you soon

Cheers,

Johannes
  • 0

#2 wim DC

wim DC

    Roar

  • Expert Member
  • PipPipPipPipPipPipPipPip
  • 2681 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Python

Posted 05 July 2011 - 01:34 AM

So, the actual problem for #2 is that in a method from bean2 you need:
  • Info from bean 1
  • to know which page you came from
?

Also, do you use spring in the webproject?
  • 0

#3 johannes

johannes

    CC Lurker

  • Just Joined
  • Pip
  • 6 posts

Posted 05 July 2011 - 03:05 AM

Thanks for writing and yeah pretty much so! Except:

The info I need from bean1 is a reference to a container (so, careful not to pass-by-value or copy)

It is not sufficient to know which page I came from: Without redesign, I need to know which page to return to because it can be different from the page I came from. If it's really necessary I could suggest that change and do that refactoring in a couple of months.

Not using spring

Cheers,
Johannes
  • 0

#4 wim DC

wim DC

    Roar

  • Expert Member
  • PipPipPipPipPipPipPipPip
  • 2681 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Python

Posted 05 July 2011 - 10:54 PM

Okay, I think both problems are solvable without needing to provide the parameters in the jsp.
  • Getting info from bean A in bean B
    In JSF you are able to do some so called "dependency injection". What this will do is when you create a certain object, other references to objects, will be created or taken if they allready exist.
    I assume you allready have a faces-config.xml where you've put at least your beans.
        <managed-bean>
            <managed-bean-name>beanA</managed-bean-name>
            <managed-bean-class>my.package.beans.BeanA</managed-bean-class>
            <managed-bean-scope>request</managed-bean-scope>        
        </managed-bean>
        <managed-bean>
            <managed-bean-name>beanB</managed-bean-name>
            <managed-bean-class>my.package.beans.BeanB</managed-bean-class>
            <managed-bean-scope>request</managed-bean-scope>        
        </managed-bean>
    
    And BeanB has some kind of method where you currently expect a list from BeanA.
    public void prepare(List fromA, String pageToGoTo){
        ...
    }
    Now, instead you can just drop the list from the method declaration:
    public void prepare(String pageToGoTo){
        ...
    }
    And now your code won't work anymore :P
    Here's the trick:
    In BeanB create an extra class attribute:
    public class BeanB{
        private BeanA beanA;
    }
    
    (Not quite sure if getters and setters for this are required, it's plausible)

    And in your prepare method, just use it:
    public void prepare(String pageToGoTo){
        List stuff = beanA.firstListOfAttachMents;
        .... 
    }
    Now you got the list there and that part of the code should work again.

    The only thing left to do is make sure that there's something in beanA. Cause as it currently is, it will always be null.
    This is done in the faces-config.xml where you tell that when you use a beanB you want beanA to be "injected" into it.
        <managed-bean>
            <managed-bean-name>beanB</managed-bean-name>
            <managed-bean-class>my.package.beans.BeanB</managed-bean-class>
            <managed-bean-scope>request</managed-bean-scope>    
            <managed-property>
                <property-name>beanA</property-name>   //name of declaration
                <value>#{beanA}</value>  //name of bean in faces-config
            </managed-property>    
        </managed-bean>
    
    And I think that's it for #1. Just use beanA in beanB, and inject it with the help of 4 lines in faces-config.

  • Where to redirect to:
    Also in the faces-config are "navigation rules" which look like:
        <navigation-rule>
            <from-view-id>/changeDate.jsp</from-view-id>
            <navigation-case>
                <from-outcome>success</from-outcome>
                <to-view-id>/showCalendar.jsp</to-view-id>
            </navigation-case>
        </navigation-rule>
    
    What this will do is: When you call a method from a bean on the changeDate.jsp, and that method returns "success", (= from-outcome) it will redirect to showCalendar.jsp.

    So all you need to do is make sure the method you call returns a String.
    add a navigation rule describing the page where you come from, the string it returns, and where to go to next and you're done.

  • 0

#5 johannes

johannes

    CC Lurker

  • Just Joined
  • Pip
  • 6 posts

Posted 06 July 2011 - 01:28 AM

Hi there wim DC, and thanks for taking the time to post a complete solution!

This is exactly the kind of feedback I was hoping for, it helps to narrow down what is possible. I'd love to use your solution, and probably will to solve similar problems, but the task here calls for some adjustment. I'll explain

1. is actually more like
1. Getting info into bean B from other beans

beanB
public void prepare(String pageToGoTo){
   List stuff = beanA.firstListOfAttachMents;
   ....
}

The above assumes you only and always add items to the variable in beanA.firstListOfAttachMents. We have many lists in beanA so this would become something like

beanB
public void prepare(String pageToGoTo){
   List stuff = beanA.currentListOfAttachMents;
   ....
}

and currentListOfAttachMents would need to be set by first calling a prepare method in beanA, from the JSP.

beanA
public void prepareFirstListOfAttachMents(..){
   this.currentListOfAttachMents = firstListOfAttachMents;
   ....
}

As the prepare method cannot be in beanB anymore, a method must be created for every list in every bean. That's not necessarily a no-go, but maybe not the best solution?

I have 6 beans of type 'beanA' (potentially more later on) and 4-10 lists per 'beanA'.
I guess the 6 beans would all be injected as properties, which causes a 2nd issue: which bean is the current one?

  • Transmitting the data from beanA to beanB there must be no dependency on bean A in bean B.
  • The current list in bean A must be sent to bean B, and if required the pageToReturnTo
Right? any other ideas on this?.

My best (non-JSP) guess would be to do something like

beanA
public String goToAttachmentPageForFirstListOfAttachMents(..){
   beanB.currentListOfAttachMents = this.firstListOfAttachMents;
   beanB.pageToReturnTo = "somePage";
   ....
   return "attachmentPage";
}

This would not introduce dependency on bean A in bean B but the one-method-per-list issue remains. Maybe this cannot be avoided in a non-JSP solution? Anyway, I don't know if this is at all possible to do - in the next iteration the JSP will call bean B expecting those 2 values set, but I don't see how the JSP will be clever enough to work on the particular bean B into which I just injected the values....?



Cheers
Johannes
  • 0

#6 wim DC

wim DC

    Roar

  • Expert Member
  • PipPipPipPipPipPipPipPip
  • 2681 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Python

Posted 06 July 2011 - 03:40 AM

I don't know but if you have 6 beans with each 4-10 lists in them.. that sounds weird to have to be honest.
To me it looks like you have the whole model in 6 beans, and all jsp pages use beanB to get their data. So beanB must be constantly updated dependent on which page you're at so the page can get the correct data.
If a page needs its own data, it's not so uncommon to create a bean / page. Like in .NET and .aspx pages, they all have 1 so called "code behind" (~bean) / page.

What exactly are you trying to build there? Like a webshop, facturing system, whatever.?


Also, is there a reason why you were working with action and actionlistener instead of doing.
<h:commandLink action="#{bean.prepare}" value="clicky clicky"/>
?
  • 0

#7 johannes

johannes

    CC Lurker

  • Just Joined
  • Pip
  • 6 posts

Posted 06 July 2011 - 06:45 AM

Current system
Yeah well what can I say - the webapp is indeed poorly designed. Actually now that you asked I had a look - there are 12 main backing beans for the records and about 60 backing beans total. It's a system to fill out forms - a kind of planning and follow-up tool with lots and lots of input per page.

Best practice solution
Right so the 'real' solution translates into having 6*(4~10) subclasses of bean B to account for the differences in lists and callers. I did think of that but my conclusion was that it'd be a lot of new beans (20+) to add very little value, especially since it's the same JSP code (using alias and includes). Maybe I'm wrong?

Conclusion
I'll need one attach-a-document page per bean A type of bean (12 jsp's). Using an alias I can reuse the attach-a-document JSP anyway.
Each 'main' bean of type A keep a member bean B, transferring data by bean A calling setters on bean B (method per list per bean). Bean B is anyway dependent on its context: the list it's attaching to is in bean A. In this way bean B isn't really a bean anymore but rather a kind of utility class that is part of bean A classes.. The attach-a-document JSP code would be backed by bean A delegating to bean B, only the first call to prepare the attach-a-document page being called on bean A directly.

record page
<h:commandLink action="#{beanA.prepareAttachToListX}" />
attach-a-document page per bean A
<t:aliasBean alias="#{attachmentContextBean}" value="#{beanA}" >
<% include file="attach-a-document_include.jsp" %>
...
</t:aliasBean>


For your second question:

Also, is there a reason why you were working with action and actionlistener instead of doing.
Code:

<h:commandLink action="#{bean.prepare}" value="clicky clicky"/>

?


ActionListener is to catch the 2 parameters in the generalized prepare call.
If you write like that (no params) you need one specific method per list per bean.


Cheers
Johannes
  • 0

#8 wim DC

wim DC

    Roar

  • Expert Member
  • PipPipPipPipPipPipPipPip
  • 2681 posts
  • Programming Language:Java, JavaScript, PL/SQL
  • Learning:Python

Posted 06 July 2011 - 10:25 AM

there are 12 main backing beans for the records and about 60 backing beans total.

:blink: I hope that's one big ** project. I once had a, what I thought, big project it had 30 beans for ~25 JSPs.

Dealing with lots of input on a page is a piece of cake really.
Say you have a register page to create a User. Then you just have this User class, just a POJO.
public class User{
  private String name;
  private String street;
  private String state;
  private String city;
  private String country;
  private int age;
  private Date birthDate;
  private String email;
  private double height;
  private String language;
  private String username;

  public User(){
  }
   -getters and setters-
}
Okay, for such a registration form that's a lot of input fields. But the bean needs only 2 attributes for this:

public class UserBean{
  private User user;
  private UserService userService;

  public UserBean(){ 
    user = new User();
  }
  -getters and setters-

  public String saveUser(){
    userService.save(user); 
    return "registrationSuccessful";
  }
}
Where UserService is just a class doing stuff like saving, getting, updating,.. a user to a database, file or whatever - Doesn't matter here. - the object itself gets injected, such a service class usually has scope application
And on the register.jsp you just have
<h:form>
    <h:inputText id="name" value="userBean.user.name"/>
    <h:inputText id="street" value="userBean.user.street"/>
    <h:inputText id="state" value="userBean.user.state"/>
    <h:inputText id="city" value="userBean.user.city"/>
    <h:inputText id="country" value="userBean.user.country"/>
    <h:inputText id="age" value="userBean.user.age"/>
    <h:inputText id="birthDate" value="userBean.user.birthDate"/>
    <h:inputText id="email" value="userBean.user.email"/>
    <h:inputText id="height" value="userBean.user.height"/>
    <h:inputText id="language" value="userBean.user.language"/>
    <h:inputText id="username" value="userBean.user.username"/>
    <h:commandButton action="userBean.saveUser()" value="submit"/>
</h:form>

So ok, you deal with a lot of input here. But I really don't see where all those lists of you are coming from.

Now - to get a bit closer to your project I think - let's say I want to, instead of saving the user, add it to a list which another bean defines.

public class ListBean{
  private List<User> usersList;
  private ListService listService;

  public ListBean(){
  }

  public String chooseAdmin(){
     usersList =  listService.getAdminList();
     return "registerUser";
  }

  public String chooseplayer(){
     usersList =  listService.getPlayerList();
     return "registerUser";
  }

  public String chooseUser(){
     usersList =  listService.getUserList();
     return "registerUser";
  }
}
So this bean's methods get executed on some other page where you must choose first which kind of user you are...or so ... (weird scenario, I know)
New userbean:
public class UserBean{
  private User user;
  private List<User> userList;

  public UserBean(){ 
    user = new User();
  }
  -getters and setters-

  public String saveUser(){
    userList.add(user);
    return "registrationSuccessful";
  }
}
Then this list can just be injected:
    <managed-bean>
        <managed-bean-name>userBean</managed-bean-name>
        <managed-bean-class>my.package.beans.userBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>    
        <managed-property>
            <property-name>userList</property-name>  
            <value>#{listBean.usersList}</value>  
        </managed-property>    
    </managed-bean>

So I'm afraid I don't really see where all your beans and lists are coming from :S
  • 0

#9 johannes

johannes

    CC Lurker

  • Just Joined
  • Pip
  • 6 posts

Posted 06 July 2011 - 04:45 PM

Hi again wim DC, and thanks for making your thoughts clear by exemplifying.

Basically it's a filing system that follows a project through from start to end. You add desicions, metrics and results as you go along the phases. There's just a lot of attachments to go with that.

The solution as you suggest looks a lot like the one I'm leaning towards in my 'conclusion', which was also based on your comments.

I'll tailor your examples.. Mostly to see if I am getting this.

public class Record1EntityA{

 private List<Attachment> planningDocsList;
 private List<Attachment> reviewDocsList;
 ... 

}


public class Record1BeanA{

 private List<Attachment> selectedAttachment;


 private Record1EntityA entity;
 private Record1Service recordService;

 public Record1BeanA(){
 }

 public String prepareAttachPlanning(){
    selectedAttachment=  entity.getPlanningDocsList();
    return "prepareAttachment";
 }

 public String prepareAttachReview(){
    selectedAttachment=  entity.getReviewDocsList();
    return "prepareAttachment";
 }

 ...
}

public class AttachmentBean{
 private Attachment attachment;
 private List attachmentList;
 private AttachmentService attachmentService;

 public AttachmentBean(){
  attachment = new Attachment();
 }
 -getters and setters-

 public String saveAttachment(){
   attachmentService.save(attachment); //optional with cascade?
   attachmentList.add(attachment);
   return "attachmentSuccessful";
 }
}


    <managed-bean>
        <managed-bean-name>attachmentBean</managed-bean-name>
        <managed-bean-class>my.package.beansattachmentBean</managed-bean-class>
        <managed-bean-scope>request</managed-bean-scope>    
        <managed-property>
            <property-name>attachmentList</property-name>  
            <value>#{record1BeanA.selectedAttachment}</value>  
        </managed-property>    
    </managed-bean>
record1.jsp
..some display of record1BeanA.entity.planningDocsList.. 
<h:commandButton action="#{record1BeanA.prepareAttachPlanning}" />
..some display of record1BeanA.entity.reviewDocsList.. 
<h:commandButton action="#{record1BeanA.prepareAttachReview}" />

attachment.jsp
<h:inputText value="#{attachmentBean.attachment.name}" />
...
<h:commandButton action="#{attachmentBean.saveAttachment}" />


User clicks Record1BeanA.prepareAttachPlanning() - button
User fills in AttachmentBean.attachment
User clicks AttachmentBean.saveAttachment()
-> Record1BeanA.entity.planningDocsList has the new Attachment element?

Ok, this demonstrates a possible implementation with the one-method-per-list-per-bean approach that I have not yet tried. Looks really clean. But we'd need to do the same for the other 12 beans. The faces-config defines AttachmentBean as depending on Record1BeanA - but how can then Record2BeanA be handeled? Other AttachmentBeans? Add managed properites for each/which one is active?

Lets have a look at the example of just using the JSP


public class RecordBeanA{

 private RecordEntityA entity;
 private RecordService recordService;

 public RecordBeanA(){
 }

 // no attachment actions
 ...
}

public class AttachmentBean{
 private Attachment attachment;
 private List attachmentList;
 private AttachmentService attachmentService;

 public AttachmentBean(){
  attachment = new Attachment();
 }
 -getters and setters-

 public void prepare(ActionEvent event){
  attachmentList = (List) FacesUtil.getParamFromEvent("container", event);
 }

 public String saveAttachment(){
   attachmentService.save(attachment); //optional with cascade?
   attachmentList.add(attachment);
   return "attachmentSuccessful";
 }
}


<h:commandButton action="prepareAttachment" actionListener="#{attachmentBean.prepare}" >
  <f:param name="container" value="{recordBeanA.entity.planningDocsList}" />
</h:commandButton>

<h:commandButton action="prepareAttachment" actionListener="#{attachmentBean.prepare}" >
  <f:param name="container" value="{recordBeanA.entity.reviewDocsList}" />
</h:commandButton>



That's all. RecordBeanA is clean from attachment handling.


What do you think, which is better? Not sure where I stand atm..

Cheers
Johannes

Edited by johannes, 06 July 2011 - 11:35 PM.
Forgot about the different types of bean A in the first example

  • 0





Also tagged with one or more of these keywords: JSP, bean

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download