Stale Session Objects ? DataModelSelection Problems
kryptontri Jul 24, 2006 1:22 PMJBoss 4.0.4.GA
JBoss Seam 1.0.1 GA
Hi
I am having issues dealing with 1 to many relationships and DataModelSelection, in that
session data i think, doesn't seem to be cleared, this issue happens sporadically, i am looking for a pattern to see when it fails.
Basically, I have a set of rate objects that i can add for a contractor's page
This is the vew
<div class="section">
<fieldset>
<div class="entry_small">
<c:choose>
<c:when test='${ratesPageManager.rates == null or ratesPageManager.rowCount == 0}'>
Empty Rates
</c:when>
<c:otherwise>
<h:dataTable value="#{ratesPageManager.rates}" var="item" rendered="#{ratesPageManager.rowCount>0}" rowClasses="rvgRowOne,rvgRowTwo">
<h:column>
<f:facet name="header"><h:outputText value="Duration"/></f:facet>
<h:outputText value="#{item.duration}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Unit"/></f:facet>
<h:outputText value="#{item.unit}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Currency"/></f:facet>
<h:outputText value="#{item.currency}"/>
</h:column>
<h:column>
<f:facet name="header"><h:outputText value="Cost"/></f:facet>
<h:outputText value="#{item.cost}"/>
</h:column>
<h:column>
<f:facet name="header">Action</f:facet>
<h:commandLink action="#{ratesPageManager.deleteRate}">Delete</h:commandLink>
</h:column>
</h:dataTable>
</c:otherwise>
</c:choose>
</div>
</fieldset>
<br/>
<fieldset>
<div class="entry_small">
<h:form>
<table>
<tr>
<td><div class="label"><h:outputLabel for="duration">Duration:</h:outputLabel></div></td>
<td><div class="select"><h:selectOneMenu id="duration" value="#{rate.duration}"><f:selectItems value="#{ratesManager.durations}"/></h:selectOneMenu></div></td>
<td><div class="label"><h:outputLabel for="unit">Unit:</h:outputLabel></div></td>
<td><div class="select"><h:selectOneMenu id="unit" value="#{rate.unit}"><f:selectItems value="#{ratesManager.units}"/></h:selectOneMenu></div></td>
<td><div class="label"><h:outputLabel for="currency">Currency:</h:outputLabel></div></td>
<td><div class="select"><h:selectOneMenu id="currency" value="#{rate.currency}"><f:selectItems value="#{ratesManager.currencys}"/></h:selectOneMenu></div></td>
<td><div class="label"><h:outputLabel for="cost">Cost:</h:outputLabel></div></td>
<td><div class="select"><h:selectOneMenu id="cost" value="#{rate.cost}"><f:selectItems value="#{ratesManager.costs}"/></h:selectOneMenu></div></td>
</tr>
<tr>
<td colspan="8">
<h:commandButton value="Add Rate" action="#{ratesPageManager.addRate}" class="button"/>
</td>
</tr>
</table>
</h:form>
</div>
</fieldset>
</div>
For the Session bean behind this
@Stateful
@Scope(ScopeType.SESSION)
@Name("ratesPageManager")
@Interceptors(SeamInterceptor.class)
@LoggedIn
public class RatesPageManagerBean implements RatesPageManager {
private static final Log log = LogFactory.getLog(RatesPageManagerBean.class.getName());
@In
@Out
@Valid
private User user;
@In
@Out
@Valid
private Profile profile;
@In(required = false)
@Out
@Valid
private RatesPage ratesPage;
@PersistenceContext
private EntityManager em;
@In
private transient FacesContext facesContext;
@DataModel
private List<Rate> rates = new ArrayList();
@Out(required=false)
@DataModelSelection
private Rate selectedRate;
@In(required = false, create = true)
@Out(required=false)
private Rate rate;
public Rate getRate() {
return rate;
}
public void setRate(Rate rate) {
this.rate = rate;
}
@IfInvalid(outcome = REDISPLAY)
public String create() {
if (profile.getRatesPage() == null) {
em.persist(ratesPage);
profile.setRatesPage(ratesPage);
profile = em.merge(profile);
return "viewRatesPage";
} else {
facesContext.addMessage(null, new FacesMessage("RatesPage already exists"));
return "viewProfile";
}
}
public String select() {
profile = user.getProfile();
ratesPage = profile.getRatesPage();
rates.clear();
rates.addAll(ratesPage.getRates());
clearRatesOnDisplay();
return "viewRatesPage";
}
private boolean isUnSet(Rate rateToCheck) {
boolean isOk = (
(rateToCheck.getCost() == 0) || (rateToCheck.getCurrency().equals("-")) ||
(rateToCheck.getDuration() == 0) || (rateToCheck.getUnit().equals("-"))
);
log.info("Rate check:: cost=" + rateToCheck.getCost() + " currency=" +rateToCheck.getCurrency()
+ " duration=" + rateToCheck.getDuration() + " unit=" + rateToCheck.getUnit());
return isOk;
}
public String addRate() {
log.info("addRate() called");
if(isUnSet(rate)) {
log.warn("Failed rates check");
rate = null;
facesContext.addMessage(null, new FacesMessage("Please enter rates"));
return "viewRatesPage";
}
log.info("trying to add rate ..");
rate.setRatesPage(ratesPage);
em.persist(rate);
ratesPage.addRate(rate);
ratesPage = em.merge(ratesPage);
profile = em.merge(profile);
log.info("Persisted! and trying to remove");
clearRatesOnDisplay();
return select();
}
public String deleteRate() {
log.info("deleteRate() called, removing " + selectedRate);
rates.remove(selectedRate);
ratesPage.removeRate(selectedRate);
em.remove(selectedRate);
clearRatesOnDisplay();
return "viewRatesPage";
}
public List getRates() {
log.info("getRates() called size = " + this.rates.size());
return this.rates;
}
public void setRates(List in) {
this.rates = in;
}
public int getRowCount() {
return this.rates.size();
}
public String delete() {
profile.setGalleryPage(null);
em.remove(ratesPage);
profile = em.merge(profile);
return "viewProfile";
}
@IfInvalid(outcome = REDISPLAY)
public String edit() {
ratesPage = em.merge(ratesPage);
return "viewRatesPage";
}
@Destroy
@Remove
public void destroy() {
}
private void clearRatesOnDisplay() {
Contexts.getPageContext().remove("rate");
rate = null;
selectedRate = null;
}
The rate object which forms the many side of the relationship
@Entity
@Name("rate")
@Scope(SESSION)
public class Rate implements Serializable {
protected int Id = 0;
protected int duration = 0;
protected String unit = null;
protected String currency = null;
protected int cost = 0;
protected RatesPage ratesPage = null;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
@NotNull
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
@NotNull
public String getUnit() {
return unit;
}
public void setUnit(String unit) {
this.unit = unit;
}
@NotNull
public String getCurrency() {
return currency;
}
public void setCurrency(String currency) {
this.currency = currency;
}
@NotNull
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
@ManyToOne
@JoinColumn(name = "RATES_PAGE_ID")
public RatesPage getRatesPage() {
return ratesPage;
}
public void setRatesPage(RatesPage ratesPage) {
this.ratesPage = ratesPage;
}
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Rate rate = (Rate) o;
if (Id != rate.Id) return false;
if (cost != rate.cost) return false;
if (duration != rate.duration) return false;
if (currency != null ? !currency.equals(rate.currency) : rate.currency != null) return false;
if (ratesPage != null ? !ratesPage.equals(rate.ratesPage) : rate.ratesPage != null) return false;
if (unit != null ? !unit.equals(rate.unit) : rate.unit != null) return false;
return true;
}
public int hashCode() {
int result;
result = Id;
result = 29 * result + duration;
result = 29 * result + (unit != null ? unit.hashCode() : 0);
result = 29 * result + (currency != null ? currency.hashCode() : 0);
result = 29 * result + cost;
result = 29 * result + (ratesPage != null ? ratesPage.hashCode() : 0);
return result;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Id=").append(Id);
buffer.append(" Duration=").append(duration);
buffer.append(" Unit=").append(unit);
buffer.append(" Currency=").append(currency);
buffer.append(" Cost=").append(cost);
// buffer.append(" RatesPageId=").append(ratesPage.getId());
return buffer.toString();
}
}
What happens is that the debug server log will show an old rate, perhaps the
previous one add or that exists in the session and displays
2006-07-24 18:07:50,055 DEBUG [org.jboss.seam.Component] selected row: Id=39 Duration=1 Unit=Week Currency=$ Cost=150
I added the clearRatesOnDisplay method to nullify any existence of state, in
an attempt to ensure that the select menus in the view are cleaned.
Any ideas people?
TIA Krypt