17
JavaServer Faces Anti- Patterns Dennis Byrne - ThoughtWorks [email protected]

JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks [email protected]

Embed Size (px)

Citation preview

Page 1: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

JavaServer Faces Anti-Patterns

Dennis Byrne - ThoughtWorks

[email protected]

Page 2: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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>

Page 3: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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"); }

}

Page 4: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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 ... }}

Page 5: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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; }

}

Page 6: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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>

Page 7: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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>

Page 8: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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

Page 9: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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>

Page 10: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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"));

}}

Page 11: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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}

Page 12: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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);

}}

Page 13: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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); }}

Page 14: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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");

Page 15: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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 */ }}

}

Page 16: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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>

Page 17: JavaServer Faces Anti-Patterns Dennis Byrne - ThoughtWorks dennisbyrne@apache.org

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;}}