Friday, November 7, 2008

EJB3 example of adding/removing elements from a ManyToMany relationship

Here's the use case: I have two entities in a many to many relationship, a User and a Referral. A User can have many referrals assigned to them and a Referral can have many Users assigned to it.

Database:
users (User Table)
id (primary key)
username
...

referral (Referral Table)
id (primary key)
name
...

Referral_User_Queue Table (join table)
referral_id (foreign key to the referral table, id column)
user_id (foreign key to the user table, id column)

EJB's

@Entity
@Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
...
@JoinTable(name = "referral_user_queue", joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "referral_id", referencedColumnName = "id")})
@ManyToMany
private Collection referralIdCollection;

...
getters and setters
}

@Entity
@Table(name = "referral")
public class Referral implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;

...

@ManyToMany(mappedBy = "referralIdCollection")
private List userIdCollection;

...
getters and setters
}


The User is the owning entity

I have stateless session beans that are facades to these entities (since netbeans generates them for me). The calls I make below are simply calls that end up as "merge" and "find" nothing more. So a call to userFacade.updateUser(User user) ends up being em.merge(user) where em is the injected EntityManager in the stateless session bean.

Here's the test method (I'm using a servlet as a test client for simplicity)

public class TestReferralServlet extends HttpServlet {

@EJB
private UserFacadeLocal userFacade;

@EJB
private ReferralFacadeLocal referralFacade;

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
this.moveReferral(out);
.....
}

private void moveReferral(PrintWriter out) {
//locate the referral, I just happen to have one with the
//primary key of 31
Referral referral = referralFacade.find(new Integer(31));

User me = userFacade.findUser(1);
User advisor = userFacade.findUser(3);
User drs = userFacade.findUser(5);
User counslor = userFacade.findUser(6);

out.println("
1) users size=" + referral.getUserIdCollection().size());

//you have to make a copy of the collection or you will get a
//concurrent update exception when removing from within a loop.
List users = new ArrayList(referral.getUserIdCollection().size());
for(User u : referral.getUserIdCollection()) {
users.add(u);
}

//clean out any users and start with a fresh slate
for(User u : users) {
referral.getUserIdCollection().remove(u);
u.getReferralIdCollection().remove(referral);
userFacade.updateUser(u);
referralFacade.updateReferral(referral);
}

//grab fresh copy just to make sure everything is clean
referral = referralFacade.find(new Integer(31));
out.println("
2) users size=" + referral.getUserIdCollection().size());

//add all users to the referral
referral.getUserIdCollection().add(me);
me.getReferralIdCollection().add(referral);

referral.getUserIdCollection().add(drs);
drs.getReferralIdCollection().add(referral);

referral.getUserIdCollection().add(counslor);
counslor.getReferralIdCollection().add(referral);

referral.getUserIdCollection().add(advisor);
advisor.getReferralIdCollection().add(referral);

userFacade.updateUser(me);
userFacade.updateUser(drs);
userFacade.updateUser(counslor);
userFacade.updateUser(advisor);
referralFacade.updateReferral(referral);

//grab fresh copy to make sure everyone was really added
referral = referralFacade.find(new Integer(31));
out.println("
3) users size=" + referral.getUserIdCollection().size());

for(User u : referral.getUserIdCollection()) {
out.println("
" + u.getFirstname());
}

//now remove drs only
drs.getReferralIdCollection().remove(referral);
referral.getUserIdCollection().remove(drs);
userFacade.updateUser(drs);
referralFacade.updateReferral(referral);

//grab fresh copy to make sure only drs was removed
referral = referralFacade.find(new Integer(31));
out.println("
4) users size=" + referral.getUserIdCollection().size());

for(User u : referral.getUserIdCollection()) {
out.println("
" + u.getFirstname());
}

//voila...everything is right in the world and I didn't write any jdbc code
}

}


Just remember to update both ends of the relationship or the cache will think it still has it. That's why I'm calling userFacade.update and referralFacade.update

Wednesday, November 5, 2008

Authentication Phase Listener - JSF

Use Case: trying to secure every page (except for the login page). The user must be logged in. I am using application level authentication and not container managed.

Basically, the blogs I found mostly worked, but I would get a blank screen if there was an authentication failure. It ends up that you need to ensure that the navigation case in the faces-config.xml file has the
tag listed. Otherwise, you are always one page behind.

Here's the faces-config.xml entry:

<navigation-rule>
<navigation-case>
<from-outcome>authFailure</from-outcome>
<to-view-id>/index.jsp</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>


Then in your phase listener, if authentication fails, do this:

context.responseComplete();
context.getApplication().getNavigationHandler().handleNavigation(context, null, "authFailure");


The "authFailure" is the from-outcome in the faces-config.xml file

That's it...