Jan 18, 2016
Validating Setter<managed-bean> <managed-bean-name>iterationBean</managed-bean-name> <managed-bean-class>com.thoughtworks.Iteration
</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property>
<property-name>start</property-name> <value>#{sprintBean.currentStart}</value>
</managed-property> <managed-property>
<property-name>end</property-name> <value>#{sprintBean.currentEnd}</value>
</managed-property><managed-property>
<property-name>last</property-name> <value>hack</value>
</managed-property> </managed-bean>
Validating Setterpublic class Iteration {
private Calendar start, end; // injected
// sans setters and getters for start, end
public void setLast(String last) {if(start == null)
throw new NullPointerException("start");if(end == null)
throw new NullPointerException("end");if(start.after(end))
throw new IllegalStateException("start > end"); }
}
Validating Setter Solutions<application> <variable-resolver>
org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver></application>
<application><el-resolver>
org.apache.myfaces.el.unified.resolver.GuiceResolver</el-resolver>
</application>
public class Iteration { private Calendar start, end; // injected
@PostConstruct public void initialize() {
// domain validation logic here ... }}
The Map Trick#{requestScopedMap.key} // calls get(‘key’)#{requestScopedMap[‘key’]}
public class MapTrick implements java.util.Map {
public Object get(Object key) { return new BusinessLogic().doSomething(key);
}
public void clear() { }public boolean containsKey(Object arg) { return false; }public boolean isEmpty() { return false; }public Set keySet() { return null; }public Object put(Object key, Object value) { return null; }public void putAll(Map arg) { }public Object remove(Object arg) { return null; }public int size() { return 0; }
}
déjà vu PhaseListener<context-param> <description>
comma separated list of JSF conf files</description>
<param-name>javax.faces.CONFIG_FILES</param-name> <param-value>
/WEB-INF/faces-config.xml </param-value></context-param>
<lifecycle> <phase-listener>
com.thoughtworks.PhaseListenerImpl </phase-listener></lifecycle>
XML Hell<navigation-rule><from-view-id>/home.xhtml</from-view-id>
<navigation-case><from-outcome>contact_us</from-outcome><to-view-id>/contact.xhtml</to-view-id>
</navigation-case></navigation-rule><navigation-rule><from-view-id>/site_map.xhtml</from-view-id>
<navigation-case><from-outcome>contact_us</from-outcome><to-view-id>/contact.xhtml</to-view-id>
</navigation-case></navigation-rule>
<navigation-rule><from-view-id>*</from-view-id><navigation-case> <!-- global nav rule -->
<from-outcome>contact_us</from-outcome><to-view-id>/contact.xhtml</to-view-id>
</navigation-case></navigation-rule>
Thread Safety• javax.faces.event.PhaseListener • javax.faces.render.Renderer
• Managed Beans• javax.faces.convert.Converter• javax.faces.validator.Validator
• javax.faces.FacesContext • JSF Tags
Thread Safety <h:inputText value="#{managedBean.value}"
converter="#{threadUnsafe}" /> <managed-bean> <managed-bean-name>threadUnsafe</managed-bean-name> <managed-bean-class>
org.apache.myfaces.book.ThreadUnsafeConverter </managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean>
<h:inputText value="#{managedBean.value}" > <f:converter converterId="threadUnsafe" > <!-- Always Safe --> </h:inputText> <converter> <converter-id>threadUnsafe</converter-id> <converter-class>org.apache.myfaces.book.ThreadUnsafeConverter </converter-class> </converter>
Facelets Migrationpublic class WidgetTag extends UIComponentELTag{
private String title, styleClass = "default_class";
protected void setProperties(UIComponent component) {
super.setProperties(component);Widget span = (Widget) component;
span.setStyleClass(styleClass);span.setTitle(title == null ? "no title" : title);
FacesContext ctx = FacesContext.getCurrentInstance();Map session =
ctx.getExternalContext().getSessionMap();span.setStyle((String) session.get("style"));
}}
Law of Demeter
• A “Train Wreck” - sensitive to changes in domain modelemployee.getDepartment().getManager()
.getOffice().getAddress().getZip();
• An EL “Train Wreck” - sensitive to changes in domain model#{employee.department.manager.office.address.zip}
• Encapsulated, insensitive to changes in domain model#{employee.officeManagersZipCode}
Vendor Lock-inimport org.apache.myfaces.component.html.ext.HtmlInputHidden;import org.apache.myfaces.component.html.ext.HtmlInputText;import org.apache.myfaces.component.html.ext.HtmlOutputText;
public class ImplementationDependentManagedBean {private HtmlInputText input ;private HtmlInputHidden hidden ;private HtmlOutputText output ;
/* getters and setters omitted */
public boolean recordTotal(ActionEvent event) { long total = ((Long)input.getValue()).longValue(); total += ((Long)hidden.getValue()).longValue(); total += ((Long)output.getValue()).longValue(); return new JmsUtil().broadcastTotal(total);
}}
Vendor Lock-in Solutionimport javax.faces.component.ValueHolder;
public class RefactoredManagedBean { private ValueHolder input ; private ValueHolder hidden ; private ValueHolder output ; /* getters & setters ommitted */ public boolean recordTotal(ActionEvent event) { long total = 0;
ValueHolder[] vh = new ValueHolder[] {input, hidden, output}; for(ValueHolder valued : vh) total += ((Long)valued.getValue()).longValue(); return new JmsUtil().broadcastTotal(total); }}
Portlet ClassCastException
FacesContext ctx = FacesContext.getCurrentInstance();ExternalContext ectx = ctx.getExternalContext();ServletRequest request = (ServletRequest)ectx .getRequest();String id = request.getParameter("id");
FacesContext ctx = FacesContext.getCurrentInstance();ExternalContext ectx = ctx.getExternalContext();String id = ectx.getRequestParameterMap().get("id");
OpenTransactionInViewFilter public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain){
try {
ObjectRelationalUtility.startTransaction(); chain.doFilter(request, response); ObjectRelationalUtility.commitTransaction();
} catch (Throwable throwable) { try {
ObjectRelationalUtility.rollbackTransaction(); } catch (Throwable _throwable) {
/* sans error handling */ }}
}
N + 1<!-- One trip to the database for the master record ... --><h:dataTable value="#{projectBean.projects}" var="project">
<h:column> <h:commandLink
action="#{projectBean.viewProject}" value="view project"/>
</h:column><h:column>
<!-- ... and + N trips for each child record --> <f:facet name="header">Project
Manager</f:facet> #{project.manager.name}
</h:column><h:column>
<f:facet name="header">Project Name</f:facet> #{project.name}
</h:column></h:dataTable>
N + 1- Npublic class OpenTransactionInApplicationPhaseListener
implements PhaseListener {
public void afterPhase(PhaseEvent event) {try {
ObjectRelationalUtility.startTransaction();} catch (Throwable throwable) { /* sans error handling */ }
}
public void beforePhase(PhaseEvent event) {try {
ObjectRelationalUtility.commitTransaction(); } catch (Throwable throwable) {
ObjectRelationalUtility.rollbackTransaction();}
}
public PhaseId getPhaseId(){return PhaseId.INVOKE_APPLICATION;}}